refactor: implemented labelAndExplain(), combining the two, and provided explanations for expr

Co-authored-by: gc1523
This commit is contained in:
Barf-Vader 2025-02-06 16:59:04 +00:00
parent ded35dcc6e
commit e787d7168f

View File

@ -10,11 +10,42 @@ import parsley.syntax.zipped._
import parsley.cats.combinator.{some} import parsley.cats.combinator.{some}
import cats.data.NonEmptyList import cats.data.NonEmptyList
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}
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
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>`)
@ -77,7 +108,7 @@ object parser {
private lazy val `<pair-elem-type>` = private lazy val `<pair-elem-type>` =
(`<base-type>` <**> (`<array-type>` </> identity)) | (`<base-type>` <**> (`<array-type>` </> identity)) |
`<pair-type>` ~> ((`<pair-elems-type>` <**> `<array-type>`.explain( `<pair-type>` ~> ((`<pair-elems-type>` <**> `<array-type>`.explain(
"for a pair to contain a pair type, it must be an array or erased pair" "non-erased pair types cannot be nested"
)) </> UntypedPairType) )) </> UntypedPairType)
// TODO: better explanation here? // TODO: better explanation here?
// Statements // Statements
@ -89,7 +120,7 @@ object parser {
) )
private lazy val `<partial-func-decl>` = private lazy val `<partial-func-decl>` =
(sepBy(`<param>`, ",") <~ ")" <~ "is" <~> `<stmt>`.guardAgainst { (sepBy(`<param>`, ",") <~ ")" <~ "is" <~> `<stmt>`.guardAgainst {
case stmts if !stmts.isReturning => Seq("All functions must end in a returning statement") case stmts if !stmts.isReturning => Seq("all functions must end in a returning statement")
} <~ "end") map { (params, stmt) => } <~ "end") map { (params, stmt) =>
(FuncDecl((_: Type), (_: Ident), params, stmt)).tupled (FuncDecl((_: Type), (_: Ident), params, stmt)).tupled
} }
@ -103,19 +134,19 @@ object parser {
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>`.label("a valid expression")) | Free("free" ~> `<expr>`.labelAndExplain(LabelType.Expr))
| Return("return" ~> `<expr>`.label("a valid expression")) | Return("return" ~> `<expr>`.labelAndExplain(LabelType.Expr))
| Exit("exit" ~> `<expr>`.label("a valid expression")) | Exit("exit" ~> `<expr>`.labelAndExplain(LabelType.Expr))
| Print("print" ~> `<expr>`.label("a valid expression"), pure(false)) | Print("print" ~> `<expr>`.labelAndExplain(LabelType.Expr), pure(false))
| Print("println" ~> `<expr>`.label("a valid expression"), pure(true)) | Print("println" ~> `<expr>`.labelAndExplain(LabelType.Expr), pure(true))
| If( | If(
"if" ~> `<expr>`.label("a valid expression") <~ "then", "if" ~> `<expr>`.labelWithType(LabelType.Expr) <~ "then",
`<stmt>` <~ "else", `<stmt>` <~ "else",
`<stmt>` <~ "fi" `<stmt>` <~ "fi"
) )
| While("while" ~> `<expr>`.label("a valid expression") <~ "do", `<stmt>` <~ "done") | While("while" ~> `<expr>`.labelWithType(LabelType.Expr) <~ "do", `<stmt>` <~ "done")
| Block("begin" ~> `<stmt>` <~ "end") | Block("begin" ~> `<stmt>` <~ "end")
| VarDecl(`<type>`, `<ident>` <~ "=", `<rvalue>`.label("a valid initial value for variable")) | VarDecl(`<type>`, `<ident>` <~ "=", `<rvalue>`.label("valid initial value for variable"))
// TODO: Can we inline the name of the variable in the message // 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] =
@ -130,10 +161,10 @@ object parser {
Call( Call(
"call" ~> `<ident>` <~ "(", "call" ~> `<ident>` <~ "(",
sepBy(`<expr>`, ",") <~ ")" sepBy(`<expr>`, ",") <~ ")"
) | `<expr>`.label("valid expression") ) | `<expr>`.labelWithType(LabelType.Expr)
private lazy val `<pair-elem>` = private lazy val `<pair-elem>` =
Fst("fst" ~> `<lvalue>`.label("a valid pair")) Fst("fst" ~> `<lvalue>`.label("valid pair"))
| Snd("snd" ~> `<lvalue>`.label("a valid pair")) | Snd("snd" ~> `<lvalue>`.label("valid pair"))
private lazy val `<array-liter>` = ArrayLiter( private lazy val `<array-liter>` = ArrayLiter(
"[" ~> sepBy(`<expr>`, ",") <~ "]" "[" ~> sepBy(`<expr>`, ",") <~ "]"
) )