feat: implement parser
Merge request lab2425_spring/WACC_37!6 Co-authored-by: Jonny <j.sinteix@gmail.com>
This commit is contained in:
commit
d4b410bca9
@ -2,4 +2,5 @@ version = 3.8.6
|
|||||||
runner.dialect = scala3
|
runner.dialect = scala3
|
||||||
|
|
||||||
binPack.literalsExclude = []
|
binPack.literalsExclude = []
|
||||||
|
indent.infix.excludeRegex = "^$"
|
||||||
maxColumn = 100
|
maxColumn = 100
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package wacc
|
package wacc
|
||||||
|
|
||||||
|
import parsley.{Failure, Success}
|
||||||
import scopt.OParser
|
import scopt.OParser
|
||||||
import java.io.File
|
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 =
|
def main(args: Array[String]): Unit =
|
||||||
OParser.parse(cliParser, args, CliConfig()) match {
|
OParser.parse(cliParser, args, CliConfig()) match {
|
||||||
case Some(config) =>
|
case Some(config) =>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package wacc
|
package wacc
|
||||||
|
|
||||||
import parsley.Parsley
|
import parsley.Parsley
|
||||||
|
import parsley.character
|
||||||
import parsley.token.{Basic, Lexer}
|
import parsley.token.{Basic, Lexer}
|
||||||
import parsley.token.descriptions.*
|
import parsley.token.descriptions.*
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ object lexer {
|
|||||||
private val lexer = Lexer(desc)
|
private val lexer = Lexer(desc)
|
||||||
val ident = lexer.lexeme.names.identifier
|
val ident = lexer.lexeme.names.identifier
|
||||||
val integer = lexer.lexeme.integer.decimal32[Int]
|
val integer = lexer.lexeme.integer.decimal32[Int]
|
||||||
|
val negateCheck = lexer.nonlexeme.symbol("-") ~> character.digit
|
||||||
val charLit = lexer.lexeme.character.ascii
|
val charLit = lexer.lexeme.character.ascii
|
||||||
val stringLit = lexer.lexeme.string.ascii
|
val stringLit = lexer.lexeme.string.ascii
|
||||||
val implicits = lexer.lexeme.symbol.implicits
|
val implicits = lexer.lexeme.symbol.implicits
|
||||||
|
@ -1,8 +1,136 @@
|
|||||||
package wacc
|
package wacc
|
||||||
|
|
||||||
import parsley.Result
|
import parsley.Result
|
||||||
|
import parsley.Parsley
|
||||||
|
import parsley.Parsley.{atomic, many, notFollowedBy, 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 {
|
object parser {
|
||||||
def parse(input: String): Result[String, BigInt] = parser.parse(input)
|
import lexer.implicits.implicitSymbol
|
||||||
private val parser = lexer.fully(???)
|
import lexer.{ident, integer, charLit, stringLit, negateCheck}
|
||||||
|
import ast._
|
||||||
|
|
||||||
|
def parse(input: String): Result[String, Program] = parser.parse(input)
|
||||||
|
private val parser = lexer.fully(`<program>`)
|
||||||
|
|
||||||
|
// Expressions
|
||||||
|
private lazy val `<expr>`: 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 (notFollowedBy(negateCheck) ~> "-"),
|
||||||
|
Len from "len",
|
||||||
|
Ord from "ord",
|
||||||
|
Chr from "chr"
|
||||||
|
) +:
|
||||||
|
`<atom>`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Atoms
|
||||||
|
private lazy val `<atom>`: Atoms[Expr6] = Atoms(
|
||||||
|
IntLiter(integer),
|
||||||
|
BoolLiter(("true" as true) | ("false" as false)),
|
||||||
|
CharLiter(charLit),
|
||||||
|
StrLiter(stringLit),
|
||||||
|
PairLiter from "null",
|
||||||
|
`<ident-or-array-elem>`,
|
||||||
|
Parens("(" ~> `<expr>` <~ ")")
|
||||||
|
)
|
||||||
|
private val `<ident>` = Ident(ident)
|
||||||
|
private lazy val `<ident-or-array-elem>` =
|
||||||
|
`<ident>` <**> (`<array-indices>` </> identity)
|
||||||
|
private val `<array-indices>` =
|
||||||
|
some("[" ~> `<expr>` <~ "]") map { indices =>
|
||||||
|
ArrayElem((_: Ident), indices)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Types
|
||||||
|
private lazy val `<type>`: Parsley[Type] =
|
||||||
|
(`<base-type>` | (`<pair-type>` ~> `<pair-elems-type>`)) <**> (`<array-type>` </> identity)
|
||||||
|
private val `<base-type>` =
|
||||||
|
(IntType from "int") | (BoolType from "bool") | (CharType from "char") | (StringType from "string")
|
||||||
|
private lazy val `<array-type>` =
|
||||||
|
countSome("[" ~> "]") map { cnt => ArrayType((_: Type), cnt) }
|
||||||
|
private val `<pair-type>` = "pair"
|
||||||
|
private val `<pair-elems-type>`: Parsley[PairType] = PairType(
|
||||||
|
"(" ~> `<pair-elem-type>` <~ ",",
|
||||||
|
`<pair-elem-type>` <~ ")"
|
||||||
|
)
|
||||||
|
private lazy val `<pair-elem-type>` =
|
||||||
|
(`<base-type>` <**> (`<array-type>` </> identity)) |
|
||||||
|
`<pair-type>` ~> ((`<pair-elems-type>` <**> `<array-type>`) </> UntypedPairType)
|
||||||
|
|
||||||
|
// Statements
|
||||||
|
private lazy val `<program>` = Program(
|
||||||
|
"begin" ~> many(atomic(`<type>` <~> `<ident>` <~ "(") <**> `<partial-func-decl>`),
|
||||||
|
`<stmt>` <~ "end"
|
||||||
|
)
|
||||||
|
private lazy val `<partial-func-decl>` =
|
||||||
|
(sepBy(`<param>`, ",") <~ ")" <~ "is" <~> `<stmt>`.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>` = Param(`<type>`, `<ident>`)
|
||||||
|
private lazy val `<stmt>`: Parsley[NonEmptyList[Stmt]] =
|
||||||
|
sepBy1(`<basic-stmt>`, ";")
|
||||||
|
private lazy val `<basic-stmt>` =
|
||||||
|
(Skip from "skip")
|
||||||
|
| Read("read" ~> `<lvalue>`)
|
||||||
|
| Free("free" ~> `<expr>`)
|
||||||
|
| Return("return" ~> `<expr>`)
|
||||||
|
| Exit("exit" ~> `<expr>`)
|
||||||
|
| Print("print" ~> `<expr>`, pure(false))
|
||||||
|
| Print("println" ~> `<expr>`, pure(true))
|
||||||
|
| If(
|
||||||
|
"if" ~> `<expr>` <~ "then",
|
||||||
|
`<stmt>` <~ "else",
|
||||||
|
`<stmt>` <~ "fi"
|
||||||
|
)
|
||||||
|
| While("while" ~> `<expr>` <~ "do", `<stmt>` <~ "done")
|
||||||
|
| Block("begin" ~> `<stmt>` <~ "end")
|
||||||
|
| VarDecl(`<type>`, `<ident>` <~ "=", `<rvalue>`)
|
||||||
|
| Assign(`<lvalue>` <~ "=", `<rvalue>`)
|
||||||
|
private lazy val `<lvalue>`: Parsley[LValue] =
|
||||||
|
`<pair-elem>` | `<ident-or-array-elem>`
|
||||||
|
private lazy val `<rvalue>`: Parsley[RValue] =
|
||||||
|
`<array-liter>` |
|
||||||
|
NewPair(
|
||||||
|
"newpair" ~> "(" ~> `<expr>` <~ ",",
|
||||||
|
`<expr>` <~ ")"
|
||||||
|
) |
|
||||||
|
`<pair-elem>` |
|
||||||
|
Call(
|
||||||
|
"call" ~> `<ident>` <~ "(",
|
||||||
|
sepBy(`<expr>`, ",") <~ ")"
|
||||||
|
) | `<expr>`
|
||||||
|
private lazy val `<pair-elem>` =
|
||||||
|
Fst("fst" ~> `<lvalue>`) | Snd("snd" ~> `<lvalue>`)
|
||||||
|
private lazy val `<array-liter>` = ArrayLiter(
|
||||||
|
"[" ~> sepBy(`<expr>`, ",") <~ "]"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package wacc
|
|||||||
import org.scalatest.{ParallelTestExecution, BeforeAndAfterAll}
|
import org.scalatest.{ParallelTestExecution, BeforeAndAfterAll}
|
||||||
import org.scalatest.flatspec.AnyFlatSpec
|
import org.scalatest.flatspec.AnyFlatSpec
|
||||||
import org.scalatest.Inspectors.forEvery
|
import org.scalatest.Inspectors.forEvery
|
||||||
import parsley.{Success, Failure}
|
|
||||||
|
|
||||||
class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll with ParallelTestExecution {
|
class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll with ParallelTestExecution {
|
||||||
val files =
|
val files =
|
||||||
@ -26,10 +25,7 @@ class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll with Paral
|
|||||||
}) { (filename, expectedResult) =>
|
}) { (filename, expectedResult) =>
|
||||||
s"$filename" should "be parsed with correct result" in {
|
s"$filename" should "be parsed with correct result" in {
|
||||||
val contents = os.read(os.Path(filename))
|
val contents = os.read(os.Path(filename))
|
||||||
parser.parse(contents) match {
|
assert(expectedResult.contains(compile(contents)))
|
||||||
case Success(x) => assert(expectedResult.contains(x))
|
|
||||||
case Failure(msg) => fail(msg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,36 +37,36 @@ class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll with Paral
|
|||||||
Seq(
|
Seq(
|
||||||
// format: off
|
// format: off
|
||||||
// disable formatting to avoid binPack
|
// disable formatting to avoid binPack
|
||||||
"wacc-examples/valid/advanced",
|
// "wacc-examples/valid/advanced",
|
||||||
"wacc-examples/valid/array",
|
// "wacc-examples/valid/array",
|
||||||
"wacc-examples/valid/basic/exit",
|
// "wacc-examples/valid/basic/exit",
|
||||||
"wacc-examples/valid/basic/skip",
|
// "wacc-examples/valid/basic/skip",
|
||||||
"wacc-examples/valid/expressions",
|
// "wacc-examples/valid/expressions",
|
||||||
"wacc-examples/valid/function/nested_functions",
|
// "wacc-examples/valid/function/nested_functions",
|
||||||
"wacc-examples/valid/function/simple_functions",
|
// "wacc-examples/valid/function/simple_functions",
|
||||||
"wacc-examples/valid/if",
|
// "wacc-examples/valid/if",
|
||||||
"wacc-examples/valid/IO/print",
|
// "wacc-examples/valid/IO/print",
|
||||||
"wacc-examples/valid/IO/read",
|
// "wacc-examples/valid/IO/read",
|
||||||
"wacc-examples/valid/IO/IOLoop.wacc",
|
// "wacc-examples/valid/IO/IOLoop.wacc",
|
||||||
"wacc-examples/valid/IO/IOSequence.wacc",
|
// "wacc-examples/valid/IO/IOSequence.wacc",
|
||||||
"wacc-examples/valid/pairs",
|
// "wacc-examples/valid/pairs",
|
||||||
"wacc-examples/valid/runtimeErr",
|
// "wacc-examples/valid/runtimeErr",
|
||||||
"wacc-examples/valid/scope",
|
// "wacc-examples/valid/scope",
|
||||||
"wacc-examples/valid/sequence",
|
// "wacc-examples/valid/sequence",
|
||||||
"wacc-examples/valid/variables",
|
// "wacc-examples/valid/variables",
|
||||||
"wacc-examples/valid/while",
|
// "wacc-examples/valid/while",
|
||||||
// invalid (syntax)
|
// invalid (syntax)
|
||||||
"wacc-examples/invalid/syntaxErr/array",
|
// "wacc-examples/invalid/syntaxErr/array",
|
||||||
"wacc-examples/invalid/syntaxErr/basic",
|
// "wacc-examples/invalid/syntaxErr/basic",
|
||||||
"wacc-examples/invalid/syntaxErr/expressions",
|
// "wacc-examples/invalid/syntaxErr/expressions",
|
||||||
"wacc-examples/invalid/syntaxErr/function",
|
// "wacc-examples/invalid/syntaxErr/function",
|
||||||
"wacc-examples/invalid/syntaxErr/if",
|
// "wacc-examples/invalid/syntaxErr/if",
|
||||||
"wacc-examples/invalid/syntaxErr/literals",
|
// "wacc-examples/invalid/syntaxErr/literals",
|
||||||
"wacc-examples/invalid/syntaxErr/pairs",
|
// "wacc-examples/invalid/syntaxErr/pairs",
|
||||||
"wacc-examples/invalid/syntaxErr/print",
|
// "wacc-examples/invalid/syntaxErr/print",
|
||||||
"wacc-examples/invalid/syntaxErr/sequence",
|
// "wacc-examples/invalid/syntaxErr/sequence",
|
||||||
"wacc-examples/invalid/syntaxErr/variables",
|
// "wacc-examples/invalid/syntaxErr/variables",
|
||||||
"wacc-examples/invalid/syntaxErr/while",
|
// "wacc-examples/invalid/syntaxErr/while",
|
||||||
// invalid (semantic)
|
// invalid (semantic)
|
||||||
"wacc-examples/invalid/semanticErr/array",
|
"wacc-examples/invalid/semanticErr/array",
|
||||||
"wacc-examples/invalid/semanticErr/exit",
|
"wacc-examples/invalid/semanticErr/exit",
|
||||||
@ -88,5 +84,6 @@ class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll with Paral
|
|||||||
// invalid (whack)
|
// invalid (whack)
|
||||||
"wacc-examples/invalid/whack"
|
"wacc-examples/invalid/whack"
|
||||||
// format: on
|
// format: on
|
||||||
|
// format: on
|
||||||
).find(filename.contains).isDefined
|
).find(filename.contains).isDefined
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user