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