mirror of
https://github.com/iritmaximus/compilers.git
synced 2026-10-12 19:13:35 +03:00
Continue work on precedence
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user