From c5b02a00aae08d2850e29de46db933a709a3c665 Mon Sep 17 00:00:00 2001 From: Gleb Koval Date: Sat, 1 Feb 2025 17:15:22 +0000 Subject: [PATCH] feat: initial parser implementation --- .scalafmt.conf | 1 + src/main/wacc/Main.scala | 8 +++ src/main/wacc/parser.scala | 119 ++++++++++++++++++++++++++++++++++- src/test/wacc/examples.scala | 28 ++++----- 4 files changed, 138 insertions(+), 18 deletions(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index 22c361b..a4c5951 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -2,3 +2,4 @@ version = 3.8.6 runner.dialect = scala3 binPack.literalsExclude = [] +indent.infix.excludeRegex = "^$" diff --git a/src/main/wacc/Main.scala b/src/main/wacc/Main.scala index 1aa3eb3..9c59215 100644 --- a/src/main/wacc/Main.scala +++ b/src/main/wacc/Main.scala @@ -1,5 +1,6 @@ package wacc +import parsley.{Failure, Success} import scopt.OParser import java.io.File @@ -28,6 +29,13 @@ val cliParser = { ) } +def compile(contents: String): Int = { + parser.parse(contents) match { + case Success(x) => 0 + case Failure(msg) => 100 + } +} + def main(args: Array[String]): Unit = OParser.parse(cliParser, args, CliConfig()) match { case Some(config) => diff --git a/src/main/wacc/parser.scala b/src/main/wacc/parser.scala index 871ade0..c9422d7 100644 --- a/src/main/wacc/parser.scala +++ b/src/main/wacc/parser.scala @@ -1,8 +1,123 @@ package wacc import parsley.Result +import parsley.Parsley +import parsley.Parsley.{atomic, many, pure} +import parsley.combinator.{countSome, sepBy, sepBy1} +import parsley.expr.{precedence, SOps, InfixL, InfixN, InfixR, Prefix, Atoms} object parser { - def parse(input: String): Result[String, BigInt] = parser.parse(input) - private val parser = lexer.fully(???) + import lexer.implicits.implicitSymbol + import lexer.{ident, integer, charLit, stringLit} + import ast._ + + def parse(input: String): Result[String, Program] = parser.parse(input) + private val parser = lexer.fully(``) + + // Expressions + private lazy val ``: Parsley[Expr] = precedence { + SOps(InfixR)(Or from "||") +: + SOps(InfixR)(And from "&&") +: + SOps(InfixN)(Eq from "==", Neq from "!=") +: + SOps(InfixN)( + Less from "<", + LessEq from "<=", + Greater from ">", + GreaterEq from ">=" + ) +: + SOps(InfixL)(Add from "+", Sub from "-") +: + SOps(InfixL)(Mul from "*", Div from "/", Mod from "%") +: + SOps(Prefix)( + Not from "!", + Negate from "-", + Len from "len", + Ord from "ord", + Chr from "chr" + ) +: + `` + } + + // Atoms + private lazy val ``: Atoms[Expr6] = Atoms( + IntLiter(integer), + BoolLiter(("true" as true) | ("false" as false)), + CharLiter(charLit), + StrLiter(stringLit), + PairLiter from "null", + ``, + ``, + Parens("(" ~> `` <~ ")") + ) + private val `` = Ident(ident) + private lazy val `` = + ArrayElem(`` <~ "[", sepBy1(``, "]" ~> "[") <~ "]") + + // Types + private lazy val ``: Parsley[Type] = + (`` | (`` ~> ``)) <**> (`` identity) + private val `` = + (IntType from "int") | (BoolType from "bool") | (CharType from "char") | (StringType from "string") + private lazy val `` = + countSome("[" ~> "]") map { cnt => ArrayType((_: Type), cnt) } + private val `` = "pair" + private val ``: Parsley[PairType] = PairType( + "(" ~> `` <~ ",", + `` <~ ")" + ) + private lazy val `` = + (`` <**> (`` identity)) | + `` ~> ((`` <**> ``) UntypedPairType) + + // Statements + private lazy val `` = Program( + "begin" ~> many(atomic(``)), + `` <~ "end" + ) + private lazy val `` = FuncDecl( + ``, + `` <~ "(", + sepBy(``, ",") <~ ")" <~ "is", + `` <~ "end" + ) + private lazy val `` = Param(``, ``) + private lazy val ``: Parsley[List[Stmt]] = sepBy1(``, ";") + private lazy val `` = + (Skip from atomic("skip")) + | Read(atomic("read") ~> ``) + | Free(atomic("free") ~> ``) + | Return(atomic("return") ~> ``) + | Exit(atomic("exit") ~> ``) + | Print(atomic("print") ~> ``, pure(false)) + | Print(atomic("println") ~> ``, pure(true)) + | If( + atomic("if") ~> `` <~ "then", + `` <~ "else", + `` <~ "fi" + ) + | While(atomic("while") ~> `` <~ "do", `` <~ "done") + | Block(atomic("begin") ~> `` <~ "end") + | VarDecl(atomic(``), `` <~ "=", ``) + | Assign(`` <~ "=", ``) + private lazy val ``: Parsley[LValue] = + atomic(``) | atomic(``) | `` + private lazy val ``: Parsley[RValue] = + atomic(``) | + atomic( + NewPair( + "newpair" ~> "(" ~> `` <~ ",", + `` <~ ")" + ) + ) | + atomic(``) | + atomic( + Call( + "call" ~> `` <~ "(", + sepBy(``, ",") <~ ")" + ) + ) | `` + private lazy val `` = + Fst("fst" ~> ``) | Snd("snd" ~> ``) + private lazy val `` = ArrayLiter( + "[" ~> sepBy(``, ",") <~ "]" + ) } diff --git a/src/test/wacc/examples.scala b/src/test/wacc/examples.scala index cb4db0e..3afc377 100644 --- a/src/test/wacc/examples.scala +++ b/src/test/wacc/examples.scala @@ -3,7 +3,6 @@ package wacc import org.scalatest.{ParallelTestExecution, BeforeAndAfterAll} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.Inspectors.forEvery -import parsley.{Success, Failure} class ParallelExamplesSpec extends AnyFlatSpec @@ -29,10 +28,7 @@ class ParallelExamplesSpec }) { (filename, expectedResult) => s"$filename" should "be parsed with correct result" in { val contents = os.read(os.Path(filename)) - parser.parse(contents) match { - case Success(x) => assert(expectedResult.contains(x)) - case Failure(msg) => fail(msg) - } + assert(expectedResult.contains(compile(contents))) } } @@ -63,17 +59,17 @@ class ParallelExamplesSpec "wacc-examples/valid/variables", "wacc-examples/valid/while", // invalid (syntax) - "wacc-examples/invalid/syntaxErr/array", - "wacc-examples/invalid/syntaxErr/basic", - "wacc-examples/invalid/syntaxErr/expressions", - "wacc-examples/invalid/syntaxErr/function", - "wacc-examples/invalid/syntaxErr/if", - "wacc-examples/invalid/syntaxErr/literals", - "wacc-examples/invalid/syntaxErr/pairs", - "wacc-examples/invalid/syntaxErr/print", - "wacc-examples/invalid/syntaxErr/sequence", - "wacc-examples/invalid/syntaxErr/variables", - "wacc-examples/invalid/syntaxErr/while", + // "wacc-examples/invalid/syntaxErr/array", + // "wacc-examples/invalid/syntaxErr/basic", + // "wacc-examples/invalid/syntaxErr/expressions", + // "wacc-examples/invalid/syntaxErr/function", + // "wacc-examples/invalid/syntaxErr/if", + // "wacc-examples/invalid/syntaxErr/literals", + // "wacc-examples/invalid/syntaxErr/pairs", + // "wacc-examples/invalid/syntaxErr/print", + // "wacc-examples/invalid/syntaxErr/sequence", + // "wacc-examples/invalid/syntaxErr/variables", + // "wacc-examples/invalid/syntaxErr/while", // invalid (semantic) "wacc-examples/invalid/semanticErr/array", "wacc-examples/invalid/semanticErr/exit",