mirror of
https://github.com/iritmaximus/compilers.git
synced 2026-10-12 19:13:35 +03:00
Change to use throws instead of Try
This commit is contained in:
@@ -17,6 +17,9 @@ case class Identifier(name: String) extends Expression {
|
||||
case class BinaryOperator(left: Expression, operator: String, right: Expression) extends Expression {
|
||||
override def toString: String = s"BinaryOperator(l: $left, op: $operator, r: $right)"
|
||||
}
|
||||
// case class ControlFlow(name: String, condition: Expression, body: Expression, elseBody: Option[Expression]) {
|
||||
// override def toString: String = s"ControlFlow($name $condition: $body)"
|
||||
// }
|
||||
case class Other() extends Expression {
|
||||
override def toString: String = s"Other()"
|
||||
}
|
||||
@@ -27,13 +30,12 @@ class Parser(tokens: List[Token]):
|
||||
|
||||
def peek(): Token =
|
||||
return if pos < tokens.length then tokens(pos) else Token("", TokenType.End, tokens.last.location)
|
||||
return tokens(pos)
|
||||
|
||||
def consume(expected: Option[TokenType | String | List[String]] = None): Try[Token] =
|
||||
val token = peek()
|
||||
|
||||
if token.tokenType == TokenType.Error || token.tokenType == TokenType.End then
|
||||
return Failure(new Exception(s"Encountered End or End token: ${token}"))
|
||||
return Failure(new Exception(s"Encountered Error or End token: ${token}"))
|
||||
|
||||
expected match {
|
||||
case Some(that) => {
|
||||
@@ -42,7 +44,7 @@ class Parser(tokens: List[Token]):
|
||||
case expected: TokenType if expected == token.tokenType => {}
|
||||
case expected: List[String] if expected.contains(token.value) => {}
|
||||
// case expected: List[TokenType] if expected.contains(token.tokenType) => {}
|
||||
case _ => return Failure(new Exception(s"Token ${token} was not expected ${expected}"))
|
||||
case _ => return Failure(new Exception(s"Token ${token} was not expected: ${that}"))
|
||||
}
|
||||
}
|
||||
case None => return Failure(new Exception(s"Incorrect expected value: ${expected}"))
|
||||
@@ -51,81 +53,66 @@ class Parser(tokens: List[Token]):
|
||||
pos += 1
|
||||
return Success(token)
|
||||
|
||||
def parseIntLiteral(): Try[Literal] =
|
||||
def parseIntLiteral(): Literal =
|
||||
val token = consume(Some(TokenType.IntLiteral))
|
||||
return token match {
|
||||
case Success(that) => {
|
||||
that.value.toIntOption match {
|
||||
case Some(int) => Success(Literal(int))
|
||||
case None => Failure(new Exception(s"Token value not int: ${token}"))
|
||||
}
|
||||
case Success(that) => that.value.toIntOption match {
|
||||
case Some(int) => Literal(int)
|
||||
case None => throw new Exception(s"Token value not int: ${that.value}")
|
||||
}
|
||||
case Failure(that) => Failure(that)
|
||||
case Failure(that) => throw that
|
||||
}
|
||||
|
||||
def parseIdentifier(): Try[Identifier] =
|
||||
def parseIdentifier(): Identifier =
|
||||
val token = consume(Some(TokenType.Identifier))
|
||||
return token match {
|
||||
case Success(that) => Success(Identifier(that.value))
|
||||
case Failure(that) => Failure(new Exception(s"Incorrect identifier token: ${token}"))
|
||||
case Success(that) => Identifier(that.value)
|
||||
case Failure(that) => throw that
|
||||
}
|
||||
|
||||
def parseParenthisized(): Try[Expression] =
|
||||
consume(Some("("))
|
||||
def parseParenthisized(): Expression =
|
||||
// Throw error if consume returns Failure
|
||||
consume(Some("(")).get
|
||||
val expression = parseExpression()
|
||||
consume(Some(")"))
|
||||
consume(Some(")")).get
|
||||
return expression
|
||||
|
||||
def parseFactor(): Try[Expression] =
|
||||
def parseFactor(): Expression =
|
||||
val token = peek()
|
||||
return token.tokenType match {
|
||||
case TokenType.IntLiteral => parseIntLiteral()
|
||||
case TokenType.Identifier => parseIdentifier()
|
||||
case TokenType.Punctuation if token.value == "(" => parseParenthisized()
|
||||
case _ => Failure(new Exception(s"Incorrect token type, expected (, int literal or identifier, got ${token.tokenType} token"))
|
||||
case _ => throw new Exception(s"Incorrect token type: Expected (, int literal or identifier, got ${token.tokenType} token")
|
||||
}
|
||||
|
||||
|
||||
def parseTerm(): Try[Expression] =
|
||||
def parseTerm(): Expression =
|
||||
val operators = List("/", "*")
|
||||
var left = parseFactor()
|
||||
|
||||
while
|
||||
operators.contains(peek().value)
|
||||
do
|
||||
val operatorToken = consume(Some(operators))
|
||||
val operatorToken = consume(Some(operators)).get
|
||||
val right = parseFactor()
|
||||
left = (left, operatorToken, right) match {
|
||||
case (Success(l), Success(op), Success(r)) => Success(BinaryOperator(l, op.value, r))
|
||||
case _ => Failure(new Exception(s"Failure in some value: l:${left}, op:${operatorToken}, r:${right}"))
|
||||
}
|
||||
left = BinaryOperator(left, operatorToken.value, right)
|
||||
|
||||
return left match {
|
||||
case Success(that) => Success(that)
|
||||
case Failure(that) => Failure(new Exception(s"$left"))
|
||||
}
|
||||
return left
|
||||
|
||||
def parseExpression(): Try[Expression] =
|
||||
def parseExpression(): Expression =
|
||||
val operators = List("+", "-")
|
||||
var left = parseTerm()
|
||||
|
||||
while
|
||||
operators.contains(peek().value)
|
||||
do
|
||||
val operatorToken = consume(Some(operators))
|
||||
val operatorToken = consume(Some(operators)).get
|
||||
val right = parseTerm()
|
||||
left = (left, operatorToken, right) match {
|
||||
case (Success(l), Success(op), Success(r)) => Success(BinaryOperator(l, op.value, r))
|
||||
case _ => Failure(new Exception(s"Faiure in some value: l:${left}, op:${operatorToken}, r:${right}"))
|
||||
}
|
||||
left = BinaryOperator(left, operatorToken.value, right)
|
||||
operators.contains(peek().value)
|
||||
do ()
|
||||
|
||||
if peek().tokenType != TokenType.End then return Failure(new Exception("Tokens left when there should not be"))
|
||||
|
||||
return left match {
|
||||
case Success(that)=> Success(that)
|
||||
case Failure(that) => Failure(new Exception(s"Incorrect value ${left}"))
|
||||
}
|
||||
if peek().tokenType != TokenType.End then throw new Exception("Tokens left when there should not be")
|
||||
|
||||
return left
|
||||
|
||||
object Parser:
|
||||
def parse(tokens: List[Token]): Try[Expression] =
|
||||
|
||||
@@ -38,7 +38,7 @@ enum TokenType:
|
||||
import compiler.Tokenizer.TokenType._
|
||||
|
||||
class Token(val value: String, val tokenType: TokenType ,val location: TokenLocation | TokenLocationDebug):
|
||||
override def toString: String = s"${this.getClass.getName.stripPrefix("compiler.Tokenizer.")}($value, $location)"
|
||||
override def toString: String = s"${this.getClass.getName.stripPrefix("compiler.Tokenizer.")}($value, $tokenType, $location)"
|
||||
override def equals(other: Any): Boolean = other match {
|
||||
case that: Token => {
|
||||
that.location match {
|
||||
|
||||
@@ -23,40 +23,31 @@ class ParserTests extends munit.FunSuite {
|
||||
val result = p.parseIntLiteral()
|
||||
val expected = Literal(7)
|
||||
|
||||
result match {
|
||||
case Success(that) => assertEquals(that, expected)
|
||||
case Failure(that) => fail("Parsing failed: " + that)
|
||||
}
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
|
||||
test("should parse bigger ints correctly") {
|
||||
val p = testParser("59819")
|
||||
val result = p.parseIntLiteral()
|
||||
val expected = Literal(59819)
|
||||
|
||||
result match {
|
||||
case Success(that) => assertEquals(that, expected)
|
||||
case Failure(that) => fail("Parsing failed: " + that)
|
||||
}
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
test("should parse separate ints correctly") {
|
||||
val p = testParser("5 1 40")
|
||||
val result1 = p.parseIntLiteral()
|
||||
val result2 = p.parseIntLiteral()
|
||||
val result3 = p.parseIntLiteral()
|
||||
val r1 = p.parseIntLiteral()
|
||||
val r2 = p.parseIntLiteral()
|
||||
val r3 = p.parseIntLiteral()
|
||||
val expected = List(Literal(5), Literal(1), Literal(40))
|
||||
|
||||
(result1, result2, result3) match {
|
||||
case (Success(r1), Success(r2), Success(r3)) => assertEquals(List(r1, r2, r3), expected)
|
||||
case _ => fail("Parsing failed")
|
||||
}
|
||||
assertEquals(List(r1, r2, r3), expected)
|
||||
}
|
||||
test("should not parse non-int values") {
|
||||
val token = Tokenizer.tokenize("else").get(0)
|
||||
val p = testParser("else")
|
||||
val result = p.parseIntLiteral()
|
||||
|
||||
result match {
|
||||
case Success(that) => fail("Parsing should have failed but succeeded: " + that)
|
||||
case Failure(that) => assert(true)
|
||||
interceptMessage[java.lang.Exception](s"Token $token was not expected: IntLiteral") {
|
||||
val result = p.parseIntLiteral()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,93 +56,72 @@ class ParserTests extends munit.FunSuite {
|
||||
val p = testParser("1 + 2")
|
||||
val result = p.parseExpression()
|
||||
val expected = BinaryOperator(Literal(1), "+", Literal(2))
|
||||
|
||||
result match {
|
||||
case Success(that) => assertEquals(that, expected)
|
||||
case Failure(that) => fail("Parsing failed: " + that)
|
||||
}
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
test("should parse simple sum with identifiers") {
|
||||
val p = testParser("1 + x")
|
||||
val result = p.parseExpression()
|
||||
val expected = BinaryOperator(Literal(1), "+", Identifier("x"))
|
||||
|
||||
result match {
|
||||
case Success(that) => assertEquals(that, expected)
|
||||
case Failure(that) => fail("Parsing failed: " + that)
|
||||
}
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
test("should parse simple sum with only identifiers") {
|
||||
val p = testParser("value + tokenLen")
|
||||
val result = p.parseExpression()
|
||||
val expected = BinaryOperator(Identifier("value"), "+", Identifier("tokenLen"))
|
||||
|
||||
result match {
|
||||
case Success(that) => assertEquals(that, expected)
|
||||
case Failure(that) => fail("Parsing failed: " + that)
|
||||
}
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
test("should fail with missing operator") {
|
||||
val errorToken = Tokenizer.tokenize("1 2").get(1)
|
||||
val p = testParser("1 2")
|
||||
val result = p.parseExpression()
|
||||
|
||||
result match {
|
||||
case Success(that) => fail("Parsing should have failed but succeeded: " + that)
|
||||
case Failure(that) => assert(true)
|
||||
interceptMessage[java.lang.Exception](s"Token $errorToken was not expected: List(+, -)") {
|
||||
val result = p.parseExpression()
|
||||
}
|
||||
}
|
||||
test("should fail with missing ints") {
|
||||
val p1 = testParser("1 +")
|
||||
val result1 = p1.parseExpression()
|
||||
|
||||
val p2 = testParser("+ 2")
|
||||
val result2 = p2.parseExpression()
|
||||
|
||||
(result1, result2) match {
|
||||
case (Failure(r1), Failure(r2)) => assert(true)
|
||||
case _ => fail("Parsing should have failed but succeeded" + result1 + result2)
|
||||
interceptMessage[java.lang.Exception](s"Incorrect token type: Expected (, int literal or identifier, got End token") {
|
||||
p1.parseExpression()
|
||||
}
|
||||
interceptMessage[java.lang.Exception](s"Incorrect token type: Expected (, int literal or identifier, got Operator token") {
|
||||
p2.parseExpression()
|
||||
}
|
||||
}
|
||||
test("should fail with incorrect operator") {
|
||||
val token = Tokenizer.tokenize("1 while").get(1)
|
||||
val p = testParser("1 while 2")
|
||||
val result = p.parseExpression()
|
||||
|
||||
result match {
|
||||
case Success(that) => fail("Parsing should have failed but succeeded: " + that)
|
||||
case Failure(that) => assert(true)
|
||||
interceptMessage[java.lang.Exception](s"Token $token was not expected: List(+, -)") {
|
||||
p.parseExpression()
|
||||
}
|
||||
}
|
||||
test("should fail with extra int") {
|
||||
val p = testParser("1 + 2 3")
|
||||
val result = p.parseExpression()
|
||||
|
||||
result match {
|
||||
case Success(that) => fail("Parsing should have failed but succeeded: " + that)
|
||||
case Failure(that) => assert(true)
|
||||
interceptMessage[java.lang.Exception]("Tokens left when there should not be") {
|
||||
p.parseExpression()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// IDENTIFIER
|
||||
test("should parse simple identifier") {
|
||||
val p = testParser("parse_int")
|
||||
val result = p.parseIdentifier()
|
||||
val expected = Identifier("parse_int")
|
||||
|
||||
result match {
|
||||
case Success(that) => assertEquals(that, expected)
|
||||
case Failure(that) => fail("Parsing failed: " + that)
|
||||
}
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
test("should parse multiple simple identifiers in a row") {
|
||||
val p = testParser("parse_int BigInt")
|
||||
val result1 = p.parseIdentifier()
|
||||
val result2 = p.parseIdentifier()
|
||||
val r1 = p.parseIdentifier()
|
||||
val r2 = p.parseIdentifier()
|
||||
val expected = List(Identifier("parse_int"), Identifier("BigInt"))
|
||||
|
||||
(result1, result2) match {
|
||||
case (Success(r1), Success(r2)) => assertEquals(List(r1, r2), expected)
|
||||
case (r1, r2) => fail(s"Parsing failed: ${r1}, ${r2}")
|
||||
}
|
||||
assertEquals(List(r1, r2), expected)
|
||||
}
|
||||
|
||||
// TERM
|
||||
@@ -160,43 +130,30 @@ class ParserTests extends munit.FunSuite {
|
||||
val result = p.parseTerm()
|
||||
val expected = Identifier("parse_int")
|
||||
|
||||
result match {
|
||||
case Success(that) => assertEquals(that, expected)
|
||||
case Failure(that) => fail("Parsing failed: " + that)
|
||||
}
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
test("should parse either identifier or int literal (int_literal)") {
|
||||
val p = testParser("51080")
|
||||
val result = p.parseTerm()
|
||||
val expected = Literal(51080)
|
||||
|
||||
result match {
|
||||
case Success(that) => assertEquals(that, expected)
|
||||
case Failure(that) => fail("Parsing failed: " + that)
|
||||
}
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
test("should parse either identifier or int literal (both)") {
|
||||
val p = testParser("parse_int 14580")
|
||||
val result1 = p.parseTerm()
|
||||
val result2 = p.parseTerm()
|
||||
val r1 = p.parseTerm()
|
||||
val r2 = p.parseTerm()
|
||||
val expected = List(Identifier("parse_int"), Literal(14580))
|
||||
|
||||
(result1, result2) match {
|
||||
case (Success(r1), Success(r2)) => assertEquals(List(r1, r2), expected)
|
||||
case (r1, r2) => fail(s"Parsing failed: ${r1}, ${r2}")
|
||||
}
|
||||
assertEquals(List(r1, r2), expected)
|
||||
}
|
||||
|
||||
|
||||
test("should parse multiple operators") {
|
||||
val p = testParser("1 + 2 - 40")
|
||||
val result = p.parseExpression()
|
||||
val expected = BinaryOperator(BinaryOperator(Literal(1), "+", Literal(2)), "-", Literal(40))
|
||||
|
||||
result match {
|
||||
case Success(that) => assertEquals(that, expected)
|
||||
case Failure(that) => fail("Parsing failed: " + that)
|
||||
}
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user