mirror of
https://github.com/iritmaximus/compilers.git
synced 2026-10-12 19:13:35 +03:00
Add more typechecking
This commit is contained in:
@@ -10,8 +10,30 @@ case class CBool() extends CType
|
||||
case class CUnit() extends CType
|
||||
case class CNone() extends CType
|
||||
|
||||
class CSymbol(val name: Identifier, val value: Expression, val valType: CType) {
|
||||
override def equals(other: Any): Boolean = other match {
|
||||
case that: CSymbol => if (that.name.name == this.name.name && that.value == this.value && that.valType == this.valType) then true else false
|
||||
case _ => false
|
||||
}
|
||||
override def toString: String =
|
||||
s"CSymbol($name, $value, $valType)"
|
||||
}
|
||||
|
||||
|
||||
class SymbolTable(var symbols: List[CSymbol], var childTable: Option[SymbolTable]) {
|
||||
override def equals(other: Any): Boolean = other match {
|
||||
case that: SymbolTable => if that.symbols.zip(this.symbols).forall((thatSymbol, thisSymbol) => thatSymbol == thisSymbol) then true else false
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TypeChecker() {
|
||||
private var symTab = SymbolTable(List(), None)
|
||||
|
||||
def getSymTable(): SymbolTable =
|
||||
return symTab
|
||||
|
||||
def typecheck(ast: Expression): CType =
|
||||
return ast match {
|
||||
case that: Literal => {
|
||||
@@ -71,6 +93,17 @@ class TypeChecker() {
|
||||
case conditionType => throw new Exception(s"WhileDo condition not bool: $conditionType")
|
||||
}
|
||||
}
|
||||
|
||||
case that: Declaration => {
|
||||
symTab.symbols ::: List(new CSymbol(that.name, that.body, typecheck(that.body)))
|
||||
CUnit() // var x = 1 shouldn't have a type (x can and will have a type)
|
||||
}
|
||||
|
||||
case that: Block => {
|
||||
// that.expressions.map(expr => typecheck(expr))
|
||||
return CUnit()
|
||||
}
|
||||
|
||||
// case _ => throw new Exception("NOT IMPLEMENTED")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import compiler.Tokenizer.TokenType as tt
|
||||
import compiler.Parser._
|
||||
import compiler.TypeChecker._
|
||||
|
||||
|
||||
abstract class BaseTypeCheckerTests extends munit.FunSuite {
|
||||
def getTokens(code: String): Option[List[Token]] =
|
||||
val tokens = Tokenizer.tokenize(code)
|
||||
@@ -18,12 +19,21 @@ abstract class BaseTypeCheckerTests extends munit.FunSuite {
|
||||
|
||||
def testTypeChecker(code: String): CType =
|
||||
val tokens = getTokens(code)
|
||||
val ast = Parser(tokens.getOrElse(List(Token("error", tt.Error, TokenLocationDebug())))).parseExpression()
|
||||
val ast = Parser(tokens.getOrElse(List(Token("error", tt.Error, TokenLocationDebug())))).parseExpression(topLevelOrBlock=true)
|
||||
val typechecker = TypeChecker()
|
||||
return typechecker.typecheck(ast)
|
||||
|
||||
def testTypeCheckerClass(code: String): (CType, TypeChecker) =
|
||||
val tokens = getTokens(code)
|
||||
val ast = Parser(tokens.getOrElse(List(Token("error", tt.Error, TokenLocationDebug())))).parseExpression(topLevelOrBlock=true)
|
||||
val typechecker = TypeChecker()
|
||||
val result = typechecker.typecheck(ast)
|
||||
return (result, typechecker)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
class TypeCheckerLiteralTests extends BaseTypeCheckerTests {
|
||||
test("should typecheck IntLiteral correctly") {
|
||||
List("1", "45910", "0").map(value => {
|
||||
@@ -47,6 +57,7 @@ class TypeCheckerLiteralTests extends BaseTypeCheckerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TypeCheckerBinaryOperatorTests extends BaseTypeCheckerTests {
|
||||
test("should typecheck BinaryOperator with simple operators") {
|
||||
List("1 + 1", "1580 + 0", "594 - 594", "5 * 5", "47 / 3", "2 % 5").map(value => {
|
||||
@@ -57,6 +68,7 @@ class TypeCheckerBinaryOperatorTests extends BaseTypeCheckerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TypeCheckerIfThenElseTests extends BaseTypeCheckerTests {
|
||||
test("should typecheck simple IfThenElse with bodies as IntLiterals") {
|
||||
val result = testTypeChecker("if true then 1 else 2")
|
||||
@@ -75,6 +87,7 @@ class TypeCheckerIfThenElseTests extends BaseTypeCheckerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TypeCheckerWhileDoTests extends BaseTypeCheckerTests {
|
||||
test("should typecheck WhileDo with correct condition") {
|
||||
val result = testTypeChecker("while true do 1")
|
||||
@@ -87,3 +100,74 @@ class TypeCheckerWhileDoTests extends BaseTypeCheckerTests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TypeCheckerCSymbolTests extends BaseTypeCheckerTests {
|
||||
test("should be equal when two symbols are the same") {
|
||||
List(
|
||||
List(
|
||||
new CSymbol(Identifier("x"), Literal(1), CInt()), new CSymbol(Identifier("x"), Literal(1), CInt()),
|
||||
),
|
||||
List(
|
||||
new CSymbol(Identifier("moi"), Literal(1058), CInt()), new CSymbol(Identifier("moi"), Literal(1058), CInt())
|
||||
)
|
||||
).map(twoSymbols => {
|
||||
assertEquals(twoSymbols(0), twoSymbols(1))
|
||||
assert(twoSymbols(0) == twoSymbols(1))
|
||||
})
|
||||
}
|
||||
test("should not be equal when two symbols are not the same") {
|
||||
List(
|
||||
List(
|
||||
new CSymbol(Identifier("x"), Literal(1), CInt()), new CSymbol(Identifier("x"), Literal(1230489), CInt()),
|
||||
),
|
||||
List(
|
||||
new CSymbol(Identifier("moi"), Literal(1058), CInt()), new CSymbol(Identifier("heimitästäs"), Literal(1058), CInt()),
|
||||
),
|
||||
List(
|
||||
new CSymbol(Identifier("mööö"), Literal(1), CBool()), new CSymbol(Identifier("mööö"), Literal(1), CInt()),
|
||||
),
|
||||
List(
|
||||
new CSymbol(Identifier("mööö"), Literal(10), CInt()), new CSymbol(Identifier("möööööööööö"), Literal(1000000), CInt()),
|
||||
)
|
||||
).map(twoSymbols => {
|
||||
assertNotEquals(twoSymbols(0), twoSymbols(1))
|
||||
assert(twoSymbols(0) != twoSymbols(1))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TypeCheckerDeclarationTests extends BaseTypeCheckerTests {
|
||||
test("should typecheck simple declaration") {
|
||||
val result = testTypeChecker("var x = 1")
|
||||
val expected = CUnit()
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
test("should add typechecked declaration to symTable (simple)") {
|
||||
val ast = testParser("var x = 1").parseExpression(topLevelOrBlock = true)
|
||||
val (resultType, ts) = testTypeCheckerClass("var x = 1")
|
||||
val expectedType = CUnit()
|
||||
|
||||
val resultTable = ts.getSymTable()
|
||||
val expectedTable = ast match {
|
||||
case Declaration(name, body) => SymbolTable(List(new CSymbol(name, body, CInt())), None)
|
||||
case _ => fail("Parser failed to parse even though it should have")
|
||||
}
|
||||
assertEquals(resultType, expectedType)
|
||||
assertEquals(resultTable, expectedTable)
|
||||
}
|
||||
test("should add typechecked declaration to symTable (BinaryOperation)") {
|
||||
val ast = testParser("var x = 2*5").parseExpression(topLevelOrBlock = true)
|
||||
val (resultType, ts) = testTypeCheckerClass("var x = 2*5")
|
||||
val expectedType = CUnit()
|
||||
|
||||
val resultTable = ts.getSymTable()
|
||||
val expectedTable = ast match {
|
||||
case Declaration(name, body) => SymbolTable(List(new CSymbol(name, body, CInt())), None)
|
||||
case _ => fail("Parser failed to parse even though it should have")
|
||||
}
|
||||
assertEquals(resultType, expectedType)
|
||||
assertEquals(resultTable, expectedTable)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user