Continue work on precedence

This commit is contained in:
Aaro Koivukangas
2026-02-28 12:09:54 +02:00
parent 65ef476ee7
commit eee5c1a0fc
2 changed files with 109 additions and 28 deletions
+24 -21
View File
@@ -30,6 +30,16 @@ case class Other() extends Expression {
class Parser(tokens: List[Token]):
// Keeps track of current token, index to tokens: List[Token]
private var pos = 0
// leftAssociativeBinaryOperators
private val operators = List(
List("or"),
List("and"),
List("==", "!="),
List("<", "<=", ">", ">="),
List("+", "-"),
List("*", "/", "%"),
)
def peek(): Token =
return if pos < tokens.length then tokens(pos) else Token("", TokenType.End, tokens.last.location)
@@ -90,18 +100,6 @@ class Parser(tokens: List[Token]):
case _ => throw new Exception(s"Incorrect token type: Expected (, int literal or identifier, got ${token.tokenType} token")
}
def parseTerm(): Expression =
val operators = List("/", "*")
var left = parseFactor()
while
operators.contains(peek().value)
do
val operatorToken = consume(Some(operators)).get
val right = parseFactor()
left = BinaryOperator(left, operatorToken.value, right)
return left
def parseIfThenElse(): Expression =
@@ -130,24 +128,29 @@ class Parser(tokens: List[Token]):
consume(Some(")")).get
return Function(fnName, args)
def parseExpression(): Expression =
val operators = List("+", "-")
var left = parseTerm()
def parseExpression(depth: Int = 0): Expression =
var mutDepth = depth
var left = parseFactor()
// If identifier is followed by ( => it is a function call
// 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
}
while
operators.contains(peek().value)
mutDepth < operators.length
do
val operatorToken = consume(Some(operators)).get
val right = parseTerm()
left = BinaryOperator(left, operatorToken.value, right)
while
operators(mutDepth).contains(peek().value)
do
val operatorToken = consume(Some(operators(mutDepth))).get
val right = parseExpression(mutDepth + 1)
left = BinaryOperator(left, operatorToken.value, right)
// mutDepth = 0
// if peek().tokenType != TokenType.End then throw new Exception("Tokens remaining when there should not be")
mutDepth += 1
// if peek().tokenType != TokenType.End then throw new Exception("Tokens remaining when there should not be")
return left
+85 -7
View File
@@ -127,37 +127,68 @@ class ParserTests extends munit.FunSuite {
assertEquals(List(r1, r2), expected)
}
// TERM
// TERM (there is no parseTerm anymore but oh well)
test("should parse either identifier or int literal (identifier)") {
val p = testParser("parse_int")
val result = p.parseTerm()
val result = p.parseExpression()
val expected = Identifier("parse_int")
assertEquals(result, expected)
}
test("should parse either identifier or int literal (int_literal)") {
val p = testParser("51080")
val result = p.parseTerm()
val result = p.parseExpression()
val expected = Literal(51080)
assertEquals(result, expected)
}
test("should parse either identifier or int literal (both)") {
val p = testParser("parse_int 14580")
val r1 = p.parseTerm()
val r2 = p.parseTerm()
val r1 = p.parseExpression()
val r2 = p.parseExpression()
val expected = List(Identifier("parse_int"), Literal(14580))
assertEquals(List(r1, r2), expected)
}
test("should parse multiple operators") {
test("should parse multiple operators with correct associativity") {
val p = testParser("1 + 2 - 40")
val result = p.parseExpression()
val expected = BinaryOperator(BinaryOperator(Literal(1), "+", Literal(2)), "-", Literal(40))
assertEquals(result, expected)
}
test("should parse multiple operators with different precedence levels (* first)") {
val p = testParser("1 * 2 + 40")
val result = p.parseExpression()
val expected = BinaryOperator(BinaryOperator(Literal(1), "*", Literal(2)), "+", Literal(40))
assertEquals(result, expected)
}
test("should parse multiple operators with different precedence levels (* last)") {
val p = testParser("1 + 2 * 40")
val result = p.parseExpression()
val expected = BinaryOperator(Literal(1), "+", BinaryOperator(Literal(2), "*", Literal(40)))
assertEquals(result, expected)
}
test("should parse multiple operators with different precedence levels (* in the middle)") {
// (1 + (2 * 40)) - 1
val p = testParser("1 + 2 * 40 - 1")
val result = p.parseExpression()
val expected = BinaryOperator(
BinaryOperator(
Literal(1),
"+",
BinaryOperator(
Literal(2),
"*",
Literal(40))),
"-",
Literal(1))
assertEquals(result, expected)
}
// IfThenElse
test("should parse simple if then expression") {
@@ -231,7 +262,6 @@ class ParserTests extends munit.FunSuite {
assertEquals(result, expected)
}
// TODO: Fails with function as a parameter
test("should parse function with multiple complex parameters + incl. functions") {
val p = testParser("print_int(x+y, 1 * (xy+z), parse_int())")
val result = p.parseFunction()
@@ -243,4 +273,52 @@ class ParserTests extends munit.FunSuite {
assertEquals(result, expected)
}
// REMAINDER
test("should parse simple % remainder operator") {
val p = testParser("5 % 2")
val result = p.parseExpression()
val expected = BinaryOperator(Literal(5), "%", Literal(2))
assertEquals(result, expected)
}
test("should parse % remainder with other operators") {
// (1 * (20 % 4)) + 3
val p = testParser("1 * 20 % 4 + 3")
val result = p.parseExpression()
val expected = BinaryOperator(BinaryOperator(BinaryOperator(Literal(1), "*", Literal(20)), "%", Literal(4)), "+", Literal(3))
assertEquals(result, expected)
}
// COMPARISONS
test("should parse simple equal comparison") {
val p = testParser("20 == 20")
val result = p.parseExpression()
val expected = BinaryOperator(Literal(20), "==", Literal(20))
assertEquals(result, expected)
}
test("should parse equal comparison with simple expressions (with parenthesis)") {
val p = testParser("(1 + 2 * 40) == parse_int")
val result = p.parseExpression()
val expected = BinaryOperator(
BinaryOperator(Literal(1), "+", BinaryOperator(Literal(2), "*", Literal(40))),
"==",
Identifier("parse_int")
)
assertEquals(result, expected)
}
test("should parse equal comparison with simple expressions (without parenthesis)") {
val p = testParser("1 + 2 * 40 == parse_int")
val result = p.parseExpression()
val expected = BinaryOperator(
BinaryOperator(Literal(1), "+", BinaryOperator(Literal(2), "*", Literal(40))),
"==",
Identifier("parse_int")
)
assertEquals(result, expected)
}
}