package wacc import parsley.Result import parsley.Parsley import parsley.Parsley.{atomic, many, pure} import parsley.combinator.{countSome, sepBy} import parsley.expr.{precedence, SOps, InfixL, InfixN, InfixR, Prefix, Atoms} import parsley.errors.combinator._ import parsley.cats.combinator.{sepBy1, some} import cats.data.NonEmptyList object parser { 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 `` = `` <**> (`` identity) private val `` = some("[" ~> `` <~ "]") map { indices => ArrayElem((_: Ident), indices) } // 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 `` = (sepBy(``, ",") <~ ")" <~ "is" <~> ``.guardAgainst { case stmts if !stmts.isReturning => Seq("All functions must end in a returning statement") } <~ "end") map { (params, stmt) => (FuncDecl((_: Type), (_: Ident), params, stmt)).tupled } private lazy val `` = Param(``, ``) private lazy val ``: Parsley[NonEmptyList[Stmt]] = sepBy1(``, ";") private lazy val `` = (Skip from "skip") | Read("read" ~> ``) | Free("free" ~> ``) | Return("return" ~> ``) | Exit("exit" ~> ``) | Print("print" ~> ``, pure(false)) | Print("println" ~> ``, pure(true)) | If( "if" ~> `` <~ "then", `` <~ "else", `` <~ "fi" ) | While("while" ~> `` <~ "do", `` <~ "done") | Block("begin" ~> `` <~ "end") | VarDecl(``, `` <~ "=", ``) | Assign(`` <~ "=", ``) private lazy val ``: Parsley[LValue] = `` | `` private lazy val ``: Parsley[RValue] = `` | NewPair( "newpair" ~> "(" ~> `` <~ ",", `` <~ ")" ) | `` | Call( "call" ~> `` <~ "(", sepBy(``, ",") <~ ")" ) | `` private lazy val `` = Fst("fst" ~> ``) | Snd("snd" ~> ``) private lazy val `` = ArrayLiter( "[" ~> sepBy(``, ",") <~ "]" ) extension (stmts: NonEmptyList[Stmt]) { def isReturning: Boolean = stmts.last match { case Return(_) | Exit(_) => true case If(_, thenStmt, elseStmt) => thenStmt.isReturning && elseStmt.isReturning case While(_, body) => body.isReturning case Block(body) => body.isReturning case _ => false } } }