diff --git a/src/main/wacc/Main.scala b/src/main/wacc/Main.scala index 3d16c33..68688bc 100644 --- a/src/main/wacc/Main.scala +++ b/src/main/wacc/Main.scala @@ -18,6 +18,7 @@ import org.typelevel.log4cats.Logger import assemblyIR as asm import cats.data.ValidatedNel +import java.io.File /* TODO: @@ -68,11 +69,12 @@ val outputOpt: Opts[Option[Path]] = .orNone def frontend( - contents: String + contents: String, file: File ): Either[NonEmptyList[Error], microWacc.Program] = parser.parse(contents) match { case Failure(msg) => Left(NonEmptyList.one(Error.SyntaxError(msg))) - case Success(ast.PartialProgram(_, prog)) => + case Success(fn) => + val ast.PartialProgram(_, prog) = fn(file) given errors: mutable.Builder[Error, List[Error]] = List.newBuilder val (names, funcs) = renamer.rename(prog) @@ -105,8 +107,8 @@ def compile( writer.writeTo(backend(typedProg), outputPath) *> logger.info(s"Success: ${outputPath.toAbsolutePath}") - def processProgram(contents: String, outDir: Path): IO[Int] = - frontend(contents) match { + def processProgram(contents: String, file: File, outDir: Path): IO[Int] = + frontend(contents, file) match { case Left(errors) => val code = errors.map(err => err.exitCode).toList.min given errorContent: String = contents @@ -127,7 +129,7 @@ def compile( for { contents <- readSourceFile _ <- logAction(s"Compiling file: ${filePath.toAbsolutePath}") - exitCode <- processProgram(contents, outputDir.getOrElse(filePath.getParent)) + exitCode <- processProgram(contents, filePath.toFile, outputDir.getOrElse(filePath.getParent)) } yield exitCode } diff --git a/src/main/wacc/frontend/parser.scala b/src/main/wacc/frontend/parser.scala index 9f3fa40..3b8ead4 100644 --- a/src/main/wacc/frontend/parser.scala +++ b/src/main/wacc/frontend/parser.scala @@ -1,10 +1,11 @@ package wacc +import java.io.File import parsley.Result import parsley.Parsley import parsley.Parsley.{atomic, many, notFollowedBy, pure, unit} import parsley.combinator.{countSome, sepBy, option} -import parsley.expr.{precedence, SOps, InfixL, InfixN, InfixR, Prefix, Atoms} +import parsley.expr.{precedence, SOps, InfixL, InfixN, /*InfixR,*/ Prefix, Atoms} import parsley.errors.combinator._ import parsley.errors.patterns.VerifiedErrors import parsley.syntax.zipped._ @@ -52,13 +53,30 @@ object parser { implicit val builder: ErrorBuilder[String] = new DefaultErrorBuilder with LexToken { def tokens = errTokens } - def parse(input: String): Result[String, PartialProgram] = parser.parse(input) + def parse(input: String): Result[String, File => PartialProgram] = parser.parse(input) private val parser = lexer.fully(``) + private type FParsley[A] = Parsley[File => A] + + private def fParsley[A](p: Parsley[A]): FParsley[A] = + p map { a => file => a } + + private def fList[A](p: Parsley[List[File => A]]): FParsley[List[A]] = + p map { l => file => l.map(_(file)) } + + private def fNonEmptyList[A](p: Parsley[NonEmptyList[File => A]]): FParsley[NonEmptyList[A]] = + p map { l => file => l.map(_(file)) } + + private def fPair[A, B](p: Parsley[(File => A, File => B)]): FParsley[(A, B)] = + p map { case (a, b) => file => (a(file), b(file)) } + + private def fOption[A](p: Parsley[Option[File => A]]): FParsley[Option[A]] = + p map { l => file => l.map(_(file)) } + // Expressions - private lazy val ``: Parsley[Expr] = precedence { - SOps(InfixR)(Or from "||") +: - SOps(InfixR)(And from "&&") +: + private lazy val ``: FParsley[Expr] = precedence { + // SOps(InfixR)(Or from "||") +: + // SOps(InfixR)(And from "&&") +: SOps(InfixN)(Eq from "==", Neq from "!=") +: SOps(InfixN)( Less from "<", @@ -83,7 +101,7 @@ object parser { } // Atoms - private lazy val ``: Atoms[Expr6] = Atoms( + private lazy val ``: Atoms[File => Expr6] = Atoms( IntLiter(integer).label("integer literal"), BoolLiter(("true" as true) | ("false" as false)).label("boolean literal"), CharLiter(charLit).label("character literal"), @@ -92,24 +110,24 @@ object parser { ``, Parens("(" ~> `` <~ ")") ) - private val `` = StrLiter(stringLit) - private val `` = + private lazy val `` = StrLiter(stringLit) + private lazy val `` = Ident(ident) | some("*" | "&").verifiedExplain("pointer operators are not allowed") private lazy val `` = (`` <~ ("(".verifiedExplain( "functions can only be called using 'call' keyword" ) | unit)) <**> (`` identity) - private val `` = ArrayElem(some("[" ~> `` <~ "]")) + private lazy val `` = ArrayElem(fNonEmptyList(some("[" ~> `` <~ "]"))) // Types - private lazy val ``: Parsley[Type] = + private lazy val ``: FParsley[Type] = (`` | (`` ~> ``)) <**> (`` identity) private val `` = (IntType from "int") | (BoolType from "bool") | (CharType from "char") | (StringType from "string") private lazy val `` = - ArrayType(countSome("[" ~> "]")) + ArrayType(fParsley(countSome("[" ~> "]"))) private val `` = "pair" - private val ``: Parsley[PairType] = PairType( + private val ``: FParsley[PairType] = PairType( "(" ~> `` <~ ",", `` <~ ")" ) @@ -117,7 +135,7 @@ object parser { (`` <**> (`` identity)) | ((UntypedPairType from ``) <**> ((`` <**> ``) - .map(arr => (_: UntypedPairType) => arr) identity)) + .map(arr => (_: File => UntypedPairType) => arr) identity)) /* Statements Atomic is used in two places here: @@ -129,25 +147,25 @@ object parser { concern. */ private lazy val `` = PartialProgram( - many(``), + fList(many(``)), `` ) private lazy val `` = Import( "import" ~> ``, - "(" ~> sepBy1(``, ",") <~ ")" + "(" ~> fNonEmptyList(sepBy1(``, ",")) <~ ")" ) private lazy val `` = ``.label("import file name") private lazy val `` = ImportedFunc( ``.label("imported function name"), - option("as" ~> ``).label("imported function alias") + fOption(option("as" ~> ``)).label("imported function alias") ) private lazy val `` = Program( "begin" ~> ( - many( - atomic( + fList(many( + fPair(atomic( ``.label("function declaration") <~> `` <~ "(" - ) <**> `` - ).label("function declaration") | + )) <**> `` + ).label("function declaration")) | atomic(`` <~ "(").verifiedExplain("function declaration is missing return type") ), ``.label( @@ -156,17 +174,18 @@ object parser { ) private lazy val `` = FuncDecl( - sepBy(``, ",") <~ ")" <~ "is", - ``.guardAgainst { - case stmts if !stmts.isReturning => Seq("all functions must end in a returning statement") - } <~ "end" + fPair((fList(sepBy(``, ",")) <~ ")" <~ "is") <~> + (``.guardAgainst { + // TODO: passing in an arbitrary file works but is ugly + case stmts if !(stmts(File("."))).isReturning => Seq("all functions must end in a returning statement") + } <~ "end")) ) private lazy val `` = Param(``, ``) - private lazy val ``: Parsley[NonEmptyList[Stmt]] = - ( + private lazy val ``: FParsley[NonEmptyList[Stmt]] = + fNonEmptyList(( ``.label("main program body"), (many(";" ~> ``.label("statement after ';'"))) Nil - ).zipped(NonEmptyList.apply) + ).zipped(NonEmptyList.apply)) private lazy val `` = (Skip from "skip") @@ -174,8 +193,8 @@ object parser { | Free("free" ~> ``.labelAndExplain(LabelType.Expr)) | Return("return" ~> ``.labelAndExplain(LabelType.Expr)) | Exit("exit" ~> ``.labelAndExplain(LabelType.Expr)) - | Print("print" ~> ``.labelAndExplain(LabelType.Expr), pure(false)) - | Print("println" ~> ``.labelAndExplain(LabelType.Expr), pure(true)) + | Print("print" ~> ``.labelAndExplain(LabelType.Expr), fParsley(pure(false))) + | Print("println" ~> ``.labelAndExplain(LabelType.Expr), fParsley(pure(true))) | If( "if" ~> ``.labelWithType(LabelType.Expr) <~ "then", `` <~ "else", @@ -199,9 +218,9 @@ object parser { ("call" ~> ``).verifiedExplain( "function calls' results must be assigned to a variable" ) - private lazy val ``: Parsley[LValue] = + private lazy val ``: FParsley[LValue] = `` | `` - private lazy val ``: Parsley[RValue] = + private lazy val ``: FParsley[RValue] = `` | NewPair( "newpair" ~> "(" ~> `` <~ ",", @@ -210,13 +229,13 @@ object parser { `` | Call( "call" ~> `` <~ "(", - sepBy(``, ",") <~ ")" + fList(sepBy(``, ",")) <~ ")" ) | ``.labelWithType(LabelType.Expr) private lazy val `` = Fst("fst" ~> ``.label("valid pair")) | Snd("snd" ~> ``.label("valid pair")) private lazy val `` = ArrayLiter( - "[" ~> sepBy(``, ",") <~ "]" + "[" ~> fList(sepBy(``, ",")) <~ "]" ) extension (stmts: NonEmptyList[Stmt]) {