refactor: implemented labelAndExplain(), combining label and explain, and...
Merge request lab2425_spring/WACC_37!9 Co-authored-by: Barf-Vader <47476490+Barf-Vader@users.noreply.github.com> Co-authored-by: Guy C <gc1523@ic.ac.uk>
This commit is contained in:
commit
4dc07c249a
@ -4,7 +4,36 @@ import parsley.Parsley
|
|||||||
import parsley.character
|
import parsley.character
|
||||||
import parsley.token.{Basic, Lexer}
|
import parsley.token.{Basic, Lexer}
|
||||||
import parsley.token.descriptions.*
|
import parsley.token.descriptions.*
|
||||||
|
import parsley.token.errors._
|
||||||
|
|
||||||
|
val errConfig = new ErrorConfig {
|
||||||
|
override def labelSymbol = Map(
|
||||||
|
"!=" -> Label("binary operator"),
|
||||||
|
"%" -> Label("binary operator"),
|
||||||
|
"&&" -> Label("binary operator"),
|
||||||
|
"*" -> Label("binary operator"),
|
||||||
|
"/" -> Label("binary operator"),
|
||||||
|
"<" -> Label("binary operator"),
|
||||||
|
"<=" -> Label("binary operator"),
|
||||||
|
"==" -> Label("binary operator"),
|
||||||
|
">" -> Label("binary operator"),
|
||||||
|
">=" -> Label("binary operator"),
|
||||||
|
"||" -> Label("binary operator"),
|
||||||
|
"!" -> Label("unary operator"),
|
||||||
|
"len" -> Label("unary operator"),
|
||||||
|
"ord" -> Label("unary operator"),
|
||||||
|
"chr" -> Label("unary operator"),
|
||||||
|
"bool" -> Label("valid type"),
|
||||||
|
"char" -> Label("valid type"),
|
||||||
|
"int" -> Label("valid type"),
|
||||||
|
"pair" -> Label("valid type"),
|
||||||
|
"string" -> Label("valid type"),
|
||||||
|
"fst" -> Label("pair extraction"),
|
||||||
|
"snd" -> Label("pair extraction"),
|
||||||
|
"false" -> Label("boolean literal"),
|
||||||
|
"true" -> Label("boolean literal")
|
||||||
|
)
|
||||||
|
}
|
||||||
object lexer {
|
object lexer {
|
||||||
private val desc = LexicalDesc.plain.copy(
|
private val desc = LexicalDesc.plain.copy(
|
||||||
nameDesc = NameDesc.plain.copy(
|
nameDesc = NameDesc.plain.copy(
|
||||||
@ -43,7 +72,7 @@ object lexer {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val lexer = Lexer(desc)
|
private val lexer = Lexer(desc, errConfig)
|
||||||
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 negateCheck = lexer.nonlexeme.symbol("-") ~> character.digit
|
||||||
@ -51,5 +80,15 @@ object lexer {
|
|||||||
val stringLit = lexer.lexeme.string.ascii
|
val stringLit = lexer.lexeme.string.ascii
|
||||||
val implicits = lexer.lexeme.symbol.implicits
|
val implicits = lexer.lexeme.symbol.implicits
|
||||||
|
|
||||||
|
val errTokens = Seq(
|
||||||
|
lexer.nonlexeme.names.identifier.map(v => s"identifier $v"),
|
||||||
|
lexer.nonlexeme.integer.decimal32[Int].map(n => s"integer $n"),
|
||||||
|
lexer.nonlexeme.character.ascii.map(c => s"character literal $c"),
|
||||||
|
lexer.nonlexeme.string.ascii.map(s => s"string literal $s"),
|
||||||
|
character.whitespace.map(_ => "")
|
||||||
|
) ++ desc.symbolDesc.hardKeywords.map { k =>
|
||||||
|
lexer.nonlexeme.symbol(k).as(s"keyword $k")
|
||||||
|
}
|
||||||
|
|
||||||
def fully[A](p: Parsley[A]): Parsley[A] = lexer.fully(p)
|
def fully[A](p: Parsley[A]): Parsley[A] = lexer.fully(p)
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,51 @@ import parsley.Parsley.{atomic, many, notFollowedBy, pure}
|
|||||||
import parsley.combinator.{countSome, sepBy}
|
import parsley.combinator.{countSome, sepBy}
|
||||||
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.cats.combinator.{sepBy1, some}
|
import parsley.syntax.zipped._
|
||||||
|
import parsley.cats.combinator.{some}
|
||||||
import cats.data.NonEmptyList
|
import cats.data.NonEmptyList
|
||||||
|
import parsley.errors.DefaultErrorBuilder
|
||||||
|
import parsley.errors.ErrorBuilder
|
||||||
|
import parsley.errors.tokenextractors.LexToken
|
||||||
|
|
||||||
object parser {
|
object parser {
|
||||||
import lexer.implicits.implicitSymbol
|
import lexer.implicits.implicitSymbol
|
||||||
import lexer.{ident, integer, charLit, stringLit, negateCheck}
|
import lexer.{ident, integer, charLit, stringLit, negateCheck, errTokens}
|
||||||
import ast._
|
import ast._
|
||||||
|
|
||||||
|
// error extensions
|
||||||
|
extension [A](p: Parsley[A]) {
|
||||||
|
// combines label and explain together into one function call
|
||||||
|
def labelAndExplain(label: String, explanation: String): Parsley[A] = {
|
||||||
|
p.label(label).explain(explanation)
|
||||||
|
}
|
||||||
|
def labelAndExplain(t: LabelType): Parsley[A] = {
|
||||||
|
t match {
|
||||||
|
case LabelType.Expr =>
|
||||||
|
labelWithType(t).explain(
|
||||||
|
"a valid expression can start with: null, literals, identifiers, unary operators, or parentheses. " +
|
||||||
|
"Expressions can also contain array indexing and binary operators. " +
|
||||||
|
"Pair extraction is not allowed in expressions, only in assignments."
|
||||||
|
)
|
||||||
|
case _ => labelWithType(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def labelWithType(t: LabelType): Parsley[A] = {
|
||||||
|
t match {
|
||||||
|
case LabelType.Expr => p.label("valid expression")
|
||||||
|
case LabelType.Pair => p.label("valid pair")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LabelType:
|
||||||
|
case Expr
|
||||||
|
case Pair
|
||||||
|
|
||||||
|
implicit val builder: ErrorBuilder[String] = new DefaultErrorBuilder with LexToken {
|
||||||
|
def tokens = errTokens
|
||||||
|
}
|
||||||
def parse(input: String): Result[String, Program] = parser.parse(input)
|
def parse(input: String): Result[String, Program] = parser.parse(input)
|
||||||
private val parser = lexer.fully(`<program>`)
|
private val parser = lexer.fully(`<program>`)
|
||||||
|
|
||||||
@ -28,11 +65,14 @@ object parser {
|
|||||||
Greater from ">",
|
Greater from ">",
|
||||||
GreaterEq from ">="
|
GreaterEq from ">="
|
||||||
) +:
|
) +:
|
||||||
SOps(InfixL)(Add from "+", Sub from "-") +:
|
SOps(InfixL)(
|
||||||
|
(Add from "+").label("binary operator"),
|
||||||
|
(Sub from "-").label("binary operator")
|
||||||
|
) +:
|
||||||
SOps(InfixL)(Mul from "*", Div from "/", Mod from "%") +:
|
SOps(InfixL)(Mul from "*", Div from "/", Mod from "%") +:
|
||||||
SOps(Prefix)(
|
SOps(Prefix)(
|
||||||
Not from "!",
|
Not from "!",
|
||||||
Negate from (notFollowedBy(negateCheck) ~> "-"),
|
(Negate from (notFollowedBy(negateCheck) ~> "-")).hide,
|
||||||
Len from "len",
|
Len from "len",
|
||||||
Ord from "ord",
|
Ord from "ord",
|
||||||
Chr from "chr"
|
Chr from "chr"
|
||||||
@ -42,10 +82,10 @@ object parser {
|
|||||||
|
|
||||||
// Atoms
|
// Atoms
|
||||||
private lazy val `<atom>`: Atoms[Expr6] = Atoms(
|
private lazy val `<atom>`: Atoms[Expr6] = Atoms(
|
||||||
IntLiter(integer),
|
IntLiter(integer).label("integer literal"),
|
||||||
BoolLiter(("true" as true) | ("false" as false)),
|
BoolLiter(("true" as true) | ("false" as false)).label("boolean literal"),
|
||||||
CharLiter(charLit),
|
CharLiter(charLit).label("character literal"),
|
||||||
StrLiter(stringLit),
|
StrLiter(stringLit).label("string literal"),
|
||||||
PairLiter from "null",
|
PairLiter from "null",
|
||||||
`<ident-or-array-elem>`,
|
`<ident-or-array-elem>`,
|
||||||
Parens("(" ~> `<expr>` <~ ")")
|
Parens("(" ~> `<expr>` <~ ")")
|
||||||
@ -75,8 +115,10 @@ object parser {
|
|||||||
|
|
||||||
// Statements
|
// Statements
|
||||||
private lazy val `<program>` = Program(
|
private lazy val `<program>` = Program(
|
||||||
"begin" ~> many(atomic(`<type>` <~> `<ident>` <~ "(") <**> `<partial-func-decl>`),
|
"begin" ~> many(
|
||||||
`<stmt>` <~ "end"
|
atomic(`<type>`.label("function declaration") <~> `<ident>` <~ "(") <**> `<partial-func-decl>`
|
||||||
|
).label("function declaration"),
|
||||||
|
`<stmt>`.label("main program body") <~ "end"
|
||||||
)
|
)
|
||||||
private lazy val `<partial-func-decl>` =
|
private lazy val `<partial-func-decl>` =
|
||||||
FuncDecl(
|
FuncDecl(
|
||||||
@ -87,23 +129,28 @@ object parser {
|
|||||||
)
|
)
|
||||||
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>`: Parsley[NonEmptyList[Stmt]] =
|
||||||
sepBy1(`<basic-stmt>`, ";")
|
(
|
||||||
|
`<basic-stmt>`.label("main program body"),
|
||||||
|
(many(";" ~> `<basic-stmt>`.label("statement after ';'"))) </> Nil
|
||||||
|
).zipped(NonEmptyList.apply)
|
||||||
|
|
||||||
private lazy val `<basic-stmt>` =
|
private lazy val `<basic-stmt>` =
|
||||||
(Skip from "skip")
|
(Skip from "skip")
|
||||||
| Read("read" ~> `<lvalue>`)
|
| Read("read" ~> `<lvalue>`)
|
||||||
| Free("free" ~> `<expr>`)
|
| Free("free" ~> `<expr>`.labelAndExplain(LabelType.Expr))
|
||||||
| Return("return" ~> `<expr>`)
|
| Return("return" ~> `<expr>`.labelAndExplain(LabelType.Expr))
|
||||||
| Exit("exit" ~> `<expr>`)
|
| Exit("exit" ~> `<expr>`.labelAndExplain(LabelType.Expr))
|
||||||
| Print("print" ~> `<expr>`, pure(false))
|
| Print("print" ~> `<expr>`.labelAndExplain(LabelType.Expr), pure(false))
|
||||||
| Print("println" ~> `<expr>`, pure(true))
|
| Print("println" ~> `<expr>`.labelAndExplain(LabelType.Expr), pure(true))
|
||||||
| If(
|
| If(
|
||||||
"if" ~> `<expr>` <~ "then",
|
"if" ~> `<expr>`.labelWithType(LabelType.Expr) <~ "then",
|
||||||
`<stmt>` <~ "else",
|
`<stmt>` <~ "else",
|
||||||
`<stmt>` <~ "fi"
|
`<stmt>` <~ "fi"
|
||||||
)
|
)
|
||||||
| While("while" ~> `<expr>` <~ "do", `<stmt>` <~ "done")
|
| While("while" ~> `<expr>`.labelWithType(LabelType.Expr) <~ "do", `<stmt>` <~ "done")
|
||||||
| Block("begin" ~> `<stmt>` <~ "end")
|
| Block("begin" ~> `<stmt>` <~ "end")
|
||||||
| VarDecl(`<type>`, `<ident>` <~ "=", `<rvalue>`)
|
| VarDecl(`<type>`, `<ident>` <~ "=", `<rvalue>`.label("valid initial value for variable"))
|
||||||
|
// TODO: Can we inline the name of the variable in the message
|
||||||
| Assign(`<lvalue>` <~ "=", `<rvalue>`)
|
| Assign(`<lvalue>` <~ "=", `<rvalue>`)
|
||||||
private lazy val `<lvalue>`: Parsley[LValue] =
|
private lazy val `<lvalue>`: Parsley[LValue] =
|
||||||
`<pair-elem>` | `<ident-or-array-elem>`
|
`<pair-elem>` | `<ident-or-array-elem>`
|
||||||
@ -117,9 +164,10 @@ object parser {
|
|||||||
Call(
|
Call(
|
||||||
"call" ~> `<ident>` <~ "(",
|
"call" ~> `<ident>` <~ "(",
|
||||||
sepBy(`<expr>`, ",") <~ ")"
|
sepBy(`<expr>`, ",") <~ ")"
|
||||||
) | `<expr>`
|
) | `<expr>`.labelWithType(LabelType.Expr)
|
||||||
private lazy val `<pair-elem>` =
|
private lazy val `<pair-elem>` =
|
||||||
Fst("fst" ~> `<lvalue>`) | Snd("snd" ~> `<lvalue>`)
|
Fst("fst" ~> `<lvalue>`.label("valid pair"))
|
||||||
|
| Snd("snd" ~> `<lvalue>`.label("valid pair"))
|
||||||
private lazy val `<array-liter>` = ArrayLiter(
|
private lazy val `<array-liter>` = ArrayLiter(
|
||||||
"[" ~> sepBy(`<expr>`, ",") <~ "]"
|
"[" ~> sepBy(`<expr>`, ",") <~ "]"
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user