Add while-do and declarations

This commit is contained in:
Aaro Koivukangas
2026-02-28 15:55:15 +02:00
parent 479bb541b7
commit 9d2fc31eae
2 changed files with 87 additions and 1 deletions
+24 -1
View File
@@ -21,7 +21,13 @@ case class UnaryOperator(operator: String, expr: Expression) extends Expression
override def toString: String = s"UnaryOperator($operator: $expr)" override def toString: String = s"UnaryOperator($operator: $expr)"
} }
case class IfThenElse(condition: Expression, body: Expression, elseBody: Option[Expression]) extends Expression { case class IfThenElse(condition: Expression, body: Expression, elseBody: Option[Expression]) extends Expression {
override def toString: String = s"If($condition then $body)" override def toString: String = s"If($condition then $body, else $elseBody)"
}
case class WhileDo(condition: Expression, body: Expression) extends Expression {
override def toString: String = s"While($condition do $body)"
}
case class Declaration(name: Identifier, body: Expression) extends Expression {
override def toString: String = s"Var($name = $body)"
} }
case class Function(name: String, arguments: List[Expression]) extends Expression { case class Function(name: String, arguments: List[Expression]) extends Expression {
override def toString: String = s"Function($name(${arguments.mkString(", ")}))" override def toString: String = s"Function($name(${arguments.mkString(", ")}))"
@@ -110,6 +116,7 @@ class Parser(tokens: List[Token]):
return token.tokenType match { return token.tokenType match {
case TokenType.IntLiteral => parseIntLiteral() case TokenType.IntLiteral => parseIntLiteral()
case TokenType.Identifier if token.value == "if" => parseIfThenElse() case TokenType.Identifier if token.value == "if" => parseIfThenElse()
case TokenType.Identifier if token.value == "while" => parseWhileDo()
case TokenType.Identifier if token.value == "not" => parseUnaryOperator() case TokenType.Identifier if token.value == "not" => parseUnaryOperator()
case TokenType.Identifier => parseIdentifier() case TokenType.Identifier => parseIdentifier()
case TokenType.Operator if token.value == "-" => parseUnaryOperator() case TokenType.Operator if token.value == "-" => parseUnaryOperator()
@@ -131,6 +138,14 @@ class Parser(tokens: List[Token]):
return IfThenElse(condition, body, elseBody) return IfThenElse(condition, body, elseBody)
def parseWhileDo(): Expression =
consume(Some("while")).get
val condition = parseExpression()
consume(Some("do")).get
val body = parseExpression()
return WhileDo(condition, body)
def parseFunction(parsedFnName: Option[String] = None): Expression = def parseFunction(parsedFnName: Option[String] = None): Expression =
val fnName = parsedFnName.getOrElse(consume(Some(TokenType.Identifier)).get.value) val fnName = parsedFnName.getOrElse(consume(Some(TokenType.Identifier)).get.value)
consume(Some("(")).get consume(Some("(")).get
@@ -160,6 +175,13 @@ class Parser(tokens: List[Token]):
return right return right
def parseDeclaration(): Expression =
val name = parseIdentifier()
consume(Some("=")).get
val body = parseExpression()
return Declaration(name, body)
def parseExpression(initialDepth: Int = 0): Expression = def parseExpression(initialDepth: Int = 0): Expression =
var depth = initialDepth var depth = initialDepth
var left = parseFactor() var left = parseFactor()
@@ -167,6 +189,7 @@ class Parser(tokens: List[Token]):
// If identifier is followed by ( => it is a function call (or syntax error :D) // If identifier is followed by ( => it is a function call (or syntax error :D)
left = left match { left = left match {
case left: Identifier if peek().value == "(" => parseFunction(Some(left.name)) case left: Identifier if peek().value == "(" => parseFunction(Some(left.name))
case left: Identifier if left.name == "var" => parseDeclaration()
case left if peek().value == "=" => parseEquals(Some(left)) case left if peek().value == "=" => parseEquals(Some(left))
case _ => left case _ => left
} }
+63
View File
@@ -560,3 +560,66 @@ class ParserEqualTests extends BaseParserTests {
assertEquals(result, expected) assertEquals(result, expected)
} }
} }
class ParseWhileDoTests extends BaseParserTests {
test("should parse simple while do") {
val p = testParser("while true do print()")
val result = p.parseExpression()
val expected = WhileDo(Identifier("true"), Function("print", List()))
assertEquals(result, expected)
}
test("should parse while do with expressions") {
val p = testParser("while i+3 <= x do parse_bool(true) != false")
val result = p.parseExpression()
val expected = WhileDo(
BinaryOperator(
BinaryOperator(Identifier("i"), "+", Literal(3)),
"<=",
Identifier("x")
),
BinaryOperator(Function("parse_bool", List(Identifier("true"))), "!=", Identifier("false"))
)
assertEquals(result, expected)
}
}
class ParserDeclarationTests extends BaseParserTests {
test("should parse simple declaration") {
val p = testParser("var x = 1")
val result = p.parseExpression()
val expected = Declaration(Identifier("x"), Literal(1))
assertEquals(result, expected)
}
test("should parse declaration with expression body") {
val p = testParser("var result = parse(1+x)")
val result = p.parseExpression()
val expected = Declaration(
Identifier("result"),
Function("parse", List(BinaryOperator(Literal(1), "+", Identifier("x"))))
)
assertEquals(result, expected)
}
test("should not parse declaration with non-identifier name") {
val token = Tokenizer.tokenize("var x>").get(2)
// var (1+1) is function...
val p = testParser("var x>y = parse(1+x)")
interceptMessage[java.lang.Exception](s"Token $token was not expected: =") {
val result = p.parseExpression()
}
}
test("should not parse declaration with non-identifier name (function)".fail) {
val token = Tokenizer.tokenize("var (").get(1)
// var (1+1) is function...
val p = testParser("var (1+1) = parse(1+x)")
interceptMessage[java.lang.Exception](s"Token $token was not expected: Identifier") {
val result = p.parseExpression()
}
}
}