diff --git a/packages/cosmo/src/main/scala/cosmo/Expr.scala b/packages/cosmo/src/main/scala/cosmo/Expr.scala index 8234ee0..d57164c 100644 --- a/packages/cosmo/src/main/scala/cosmo/Expr.scala +++ b/packages/cosmo/src/main/scala/cosmo/Expr.scala @@ -46,6 +46,7 @@ class ExprEnv(val env: Env) { def expr(node: s.Node): Expr = { node match { + case s.Err(msg) => err(msg) // literals case s.TodoLit => TodoLit.e case s.BoolLit(value) => Bool(value).e diff --git a/packages/cosmo/src/main/scala/cosmo/Parser.scala b/packages/cosmo/src/main/scala/cosmo/Parser.scala index b8b2ab7..ae67490 100644 --- a/packages/cosmo/src/main/scala/cosmo/Parser.scala +++ b/packages/cosmo/src/main/scala/cosmo/Parser.scala @@ -7,6 +7,10 @@ import fastparse._, fastparse.ScalaWhitespace._ import cosmo.syntax._ import cosmo.syntax.NodeParse._ +implicit class SoftErr[$: P](p: => P[Unit]) { + def err(msg: String): P[Unit] = p.map(_ => Err(msg)) +} + object Parser { // Entry point def root[$: P]: P[Block] = @@ -30,7 +34,8 @@ object Parser { def idCont[$: P] = P(letter | digit | "_") def delimStr[T, $: P](d: String, body: => P[T]) = d ~~/ delimStrCont(d, body) - def delimStrCont[T, $: P](d: String, body: => P[T]) = (!d ~~ body).repX.! ~~ d + def delimStrCont[T, $: P](d: String, body: => P[T]) = + (!End ~ !d ~~ body).repX.! ~~ (d | End.err("Unclosed string")) def shortStr[$: P] = P(shortStr0.map(unescapeStr)) def shortStr0[$: P] = delimStr("\"", strEscape) def longStr[$: P] = P("\"".repX(3).!./.flatMapX(longStr0)) @@ -53,7 +58,7 @@ object Parser { P(longChars("\"$", delim).repX.! ~~/ tmplExpr) def longChars[$: P](mores: String, delim: String): P[Unit] = - P(litCharsWhile(mores) | !delim ~~ "\"".repX) + !End ~ P(litCharsWhile(mores) | !delim ~~ "\"".repX) def litCharsWhile[$: P](mores: String) = P(CharsWhile(!mores.contains(_))) def escapeseq[$: P]: P[Unit] = P("\\" ~ AnyChar) def tmplExpr[$: P]: P[Option[(Node, Option[String])]] = diff --git a/packages/cosmo/src/main/scala/cosmo/Syntax.scala b/packages/cosmo/src/main/scala/cosmo/Syntax.scala index 912a6e4..18b9048 100644 --- a/packages/cosmo/src/main/scala/cosmo/Syntax.scala +++ b/packages/cosmo/src/main/scala/cosmo/Syntax.scala @@ -17,6 +17,7 @@ sealed abstract class Node { def children: Iterator[Node] = this match { case Decorate(lhs, rhs) => Iterator(lhs, rhs) case Semi(semi) => semi.iterator + case Err(msg) => Iterator.empty case Ident(name) => Iterator.empty case BoolLit(value) => Iterator.empty case IntLit(value) => Iterator.empty @@ -78,6 +79,7 @@ final case class Semi(semi: No) extends Node final case class Decorate(lhs: Node, rhs: Node) extends Node // Kind: Literals +final case class Err(msg: String) extends Node // Just panic on problematic impls object TodoLit extends Node // Identifier diff --git a/packages/cosmo/src/main/scala/cosmo/SyntaxSerialization.scala b/packages/cosmo/src/main/scala/cosmo/SyntaxSerialization.scala index ce57939..fb21449 100644 --- a/packages/cosmo/src/main/scala/cosmo/SyntaxSerialization.scala +++ b/packages/cosmo/src/main/scala/cosmo/SyntaxSerialization.scala @@ -49,6 +49,7 @@ private def j(node: NodeKinds, buf: StringBuilder): Unit = { j(rhs, buf) buf.append("}") case Ident(name) => buf.append(s""" "ident", "name": "$name"}""") + case Err(msg) => buf.append(s""" "err", "msg": "$msg"}""") case BoolLit(value) => buf.append(s""" "bool", "value": $value}""") case IntLit(value) => buf.append(s""" "int", "value": "$value"}""") case FloatLit(value) => diff --git a/packages/cosmo/src/test/scala/cosmo/SampleTests.scala b/packages/cosmo/src/test/scala/cosmo/SampleTests.scala index 05aa11b..68c5723 100644 --- a/packages/cosmo/src/test/scala/cosmo/SampleTests.scala +++ b/packages/cosmo/src/test/scala/cosmo/SampleTests.scala @@ -44,9 +44,12 @@ class SampleTest extends munit.FunSuite: test("Syntax/lambda.syntax") { runTestOnFile("samples/Syntax/lambda.syntax.cos") } - test("Syntax/try-catch.syntax".only) { + test("Syntax/try-catch.syntax") { runTestOnFile("samples/Syntax/try-catch.syntax.cos") } + test("Syntax/errs/tmplLit01".only) { + runTestOnFile("samples/Syntax/errs/tmplLit01.cos-ast") + } test("Syntax/tmplLit.syntax") { runTestOnFile("samples/Syntax/tmplLit.syntax.cos") } diff --git a/samples/Syntax/errs/tmplLit01.cos-ast b/samples/Syntax/errs/tmplLit01.cos-ast new file mode 100644 index 0000000..beaf269 --- /dev/null +++ b/samples/Syntax/errs/tmplLit01.cos-ast @@ -0,0 +1,3 @@ + +""" +terminate by EOF