Improve block parsing

This commit is contained in:
Aaro Koivukangas
2026-03-03 16:30:57 +02:00
parent 1fa0b35482
commit 76459b6716
3 changed files with 81 additions and 8 deletions
+12 -6
View File
@@ -60,6 +60,8 @@ class Parser(tokens: List[Token]):
def peek(): Token =
return if pos < tokens.length then tokens(pos) else Token("", TokenType.End, tokens.last.location)
def previous(): Token =
return if pos-1 < 0 then tokens(0) else tokens(pos-1)
def consume(expected: Option[TokenType | String | List[String]] = None): Try[Token] =
val token = peek()
@@ -193,18 +195,22 @@ class Parser(tokens: List[Token]):
def parseBlock(): Expression =
consume(Some("{")).get
var expressions: List[Expression] = List()
var hasSemicolon: Boolean = false
var endsInSemicolon: Boolean = false
while
peek().value != "}"
do
hasSemicolon = false
endsInSemicolon = false
expressions = expressions ::: List(parseExpression(topLevelOrBlock=true))
if peek().value != "}" then
consume(Some(";")).get
hasSemicolon = true
if hasSemicolon then
if peek().value != "}" && previous().value != "}" && previous().value != ";" then
consume(Some(";")).get
endsInSemicolon = true
if previous().value == "}" then
if peek().value == ";" then consume(Some(";")).get
if endsInSemicolon then
expressions = expressions ::: List(Literal(Unit()))
consume(Some("}")).get
+1 -1
View File
@@ -15,7 +15,7 @@ class Position(var line: Int, var column: Int):
}
class TokenLocation(val start: Position, val end: Position):
override def toString: String = s"TokenLoc(S:$start, E:$end)"
override def toString: String = s"L(S:$start, E:$end)"
override def equals(other: Any): Boolean = other match {
case that: TokenLocation => (that.start == this.start) && (that.end == this.end)
case that: TokenLocationDebug => true
+68 -1
View File
@@ -744,7 +744,74 @@ class ParserBlockTests extends BaseParserTests {
val p = testParser("if true then var x = 1")
interceptMessage[java.lang.Exception]("Declaration found in not toplevel or block") {
val result = p.parseExpression(topLevelOrBlock=true)
println(s"Result: $result")
}
}
}
class ParserBlockSemicolonTests extends BaseParserTests {
test("should parse two blocks inside a block") {
val p = testParser("{ { a } { b } }")
val result = p.parseExpression(topLevelOrBlock=true)
val expected = Block(List(
Block(List(Identifier("a"))),
Block(List(Identifier("b")))
))
assertEquals(result, expected)
}
test("should parse two blocks inside a block (with semicolons)") {
val p = testParser("{ { a }; { b } }")
val result = p.parseExpression(topLevelOrBlock=true)
val expected = Block(List(
Block(List(Identifier("a"))),
Block(List(Identifier("b")))
))
assertEquals(result, expected)
}
test("should parse identifier after IfThen block") {
// { if true then { a } b }
val p = testParser("{ if true then { a } b }")
val result = p.parseExpression(topLevelOrBlock=true)
val expected = Block(List(
IfThenElse(
Identifier("true"),
Block(List(Identifier("a"))),
None
),
Identifier("b")
))
assertEquals(result, expected)
}
test("should parse identifier after IfThen block (with semicolon)") {
// { if true then { a } b }
val p = testParser("{ if true then { a }; b }")
val result = p.parseExpression(topLevelOrBlock=true)
val expected = Block(List(
IfThenElse(
Identifier("true"),
Block(List(Identifier("a"))),
None
),
Identifier("b")
))
assertEquals(result, expected)
}
test("should not parse identifiers inside block") {
val token = Tokenizer.tokenize("{ a b }").get(2)
val p = testParser("{ a b }")
interceptMessage[java.lang.Exception](s"Token $token was not expected: ;") {
val result = p.parseExpression(topLevelOrBlock=true)
}
}
test("should not parse two identifiers after block in IfThenElse") {
// { if true then { a } b c }
val token = Tokenizer.tokenize("{ if true then { a } b c }").get(8)
val p = testParser("{ if true then { a } b c }")
interceptMessage[java.lang.Exception](s"Token $token was not expected: ;") {
val result = p.parseExpression(topLevelOrBlock=true)
}
}
}