From 9d2fc31eaee986a4ee89d8ac9952867ba8b9d8d3 Mon Sep 17 00:00:00 2001 From: Aaro Koivukangas Date: Sat, 28 Feb 2026 15:55:08 +0200 Subject: [PATCH] Add while-do and declarations --- compiler/src/main/scala/Parser.scala | 25 ++++++++- compiler/src/test/scala/ParserTests.scala | 63 +++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/compiler/src/main/scala/Parser.scala b/compiler/src/main/scala/Parser.scala index 6b7af71..a01cc85 100644 --- a/compiler/src/main/scala/Parser.scala +++ b/compiler/src/main/scala/Parser.scala @@ -21,7 +21,13 @@ case class UnaryOperator(operator: String, expr: Expression) extends Expression override def toString: String = s"UnaryOperator($operator: $expr)" } 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 { override def toString: String = s"Function($name(${arguments.mkString(", ")}))" @@ -110,6 +116,7 @@ class Parser(tokens: List[Token]): return token.tokenType match { case TokenType.IntLiteral => parseIntLiteral() 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 => parseIdentifier() case TokenType.Operator if token.value == "-" => parseUnaryOperator() @@ -131,6 +138,14 @@ class Parser(tokens: List[Token]): 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 = val fnName = parsedFnName.getOrElse(consume(Some(TokenType.Identifier)).get.value) consume(Some("(")).get @@ -160,6 +175,13 @@ class Parser(tokens: List[Token]): return right + def parseDeclaration(): Expression = + val name = parseIdentifier() + consume(Some("=")).get + val body = parseExpression() + return Declaration(name, body) + + def parseExpression(initialDepth: Int = 0): Expression = var depth = initialDepth 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) left = left match { 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 } diff --git a/compiler/src/test/scala/ParserTests.scala b/compiler/src/test/scala/ParserTests.scala index 33fa642..eedc4d9 100644 --- a/compiler/src/test/scala/ParserTests.scala +++ b/compiler/src/test/scala/ParserTests.scala @@ -560,3 +560,66 @@ class ParserEqualTests extends BaseParserTests { 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() + } + } +}