diff --git a/src/main/scala/millfork/parser/MfParser.scala b/src/main/scala/millfork/parser/MfParser.scala index 8becf726..fdf8660a 100644 --- a/src/main/scala/millfork/parser/MfParser.scala +++ b/src/main/scala/millfork/parser/MfParser.scala @@ -50,6 +50,52 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri newPosition } + val comment: P[Unit] = P("//" ~/ CharsWhile(c => c != '\n' && c != '\r', min = 0) ~ ("\r\n" | "\r" | "\n")) + + val semicolon: P[Unit] = P(";" ~/ CharsWhileIn("; \t", min = 0) ~/ position("line break after a semicolon").map(_ => ()) ~/ (comment | "\r\n" | "\r" | "\n").opaque("")) + + val semicolonComment: P[Unit] = P(";" ~/ CharsWhile(c => c != '\n' && c != '\r' && c != '{' && c != '}', min = 0) ~/ position("line break instead of braces").map(_ => ()) ~/ ("\r\n" | "\r" | "\n").opaque("")) + + val AWS: P[Unit] = P((CharIn(" \t\n\r") | NoCut(semicolon) | NoCut(comment)).rep(min = 0)).opaque("") + + val AWS_asm: P[Unit] = P((CharIn(" \t\n\r") | NoCut(semicolonComment) | NoCut(comment)).rep(min = 0)).opaque("") + + val EOL: P[Unit] = P(HWS ~ ("\r\n" | "\r" | "\n" | semicolon | comment).opaque("") ~ AWS).opaque("") + + val EOL_asm: P[Unit] = P(HWS ~ ("\r\n" | "\r" | "\n" | comment | semicolonComment).opaque("") ~ AWS).opaque("") + + val EOLOrComma: P[Unit] = P(HWS ~ ("\r\n" | "\r" | "\n" | "," | semicolon | comment).opaque("") ~ AWS).opaque("") + + + val elidable: P[Elidability.Value] = (("!" | "?").! ~/ HWS).?.map{ + case Some("?") => Elidability.Elidable + case Some("!") => Elidability.Volatile + case _ => Elidability.Fixed + } + + val externFunctionBody: P[Option[List[Statement]]] = P("extern" ~/ PassWith(None)) + + val bankDeclaration: P[Option[String]] = ("segment" ~/ AWS ~/ "(" ~/ AWS ~/ identifier ~/ AWS ~/ ")" ~/ AWS).? + + val breakStatement: P[Seq[ExecutableStatement]] = ("break" ~ !letterOrDigit ~/ HWS ~ identifier.?).map(l => Seq(BreakStatement(l.getOrElse("")))) + + val continueStatement: P[Seq[ExecutableStatement]] = ("continue" ~ !letterOrDigit ~/ HWS ~ identifier.?).map(l => Seq(ContinueStatement(l.getOrElse("")))) + + val importStatement: P[Seq[ImportStatement]] = ("import" ~ !letterOrDigit ~/ SWS ~/ identifier).map(x => Seq(ImportStatement(x))) + + val forDirection: P[ForDirection.Value] = + ("parallel" ~ HWS ~ "to").!.map(_ => ForDirection.ParallelTo) | + ("parallel" ~ HWS ~ "until").!.map(_ => ForDirection.ParallelUntil) | + "until".!.map(_ => ForDirection.Until) | + "to".!.map(_ => ForDirection.To) | + ("down" ~/ HWS ~/ "to").!.map(_ => ForDirection.DownTo) + + private def flags_(allowed: String*): P[Set[String]] = StringIn(allowed: _*).!.rep(min = 0, sep = SWS).map(_.toSet).opaque("") + + val variableFlags: P[Set[String]] = flags_("const", "static", "volatile", "stack", "register") + + val functionFlags: P[Set[String]] = flags_("asm", "inline", "interrupt", "macro", "noinline", "reentrant", "kernal_interrupt") + val codec: P[((TextCodec, Boolean), Boolean)] = P(position("text codec identifier") ~ identifier.?.map(_.getOrElse(""))).map { case (_, "" | "default") => (options.platform.defaultCodec -> false) -> options.flag(CompilationFlag.LenientTextEncoding) case (_, "z" | "defaultz") => (options.platform.defaultCodec -> true) -> options.flag(CompilationFlag.LenientTextEncoding) @@ -117,7 +163,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri p <- position() bank <- bankDeclaration flags <- variableFlags ~ HWS - typ <- identifier ~ SWS + typ <- identifier ~/ SWS vars <- singleVariableDefinition.rep(min = 1, sep = "," ~/ HWS) _ <- &(EOL) ~/ "" } yield { @@ -585,26 +631,10 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri object MfParser { - val comment: P[Unit] = P("//" ~/ CharsWhile(c => c != '\n' && c != '\r', min = 0) ~ ("\r\n" | "\r" | "\n")) - - val semicolon: P[Unit] = P(";" ~/ CharsWhileIn("; \t", min = 0) ~ (comment | "\r\n" | "\r" | "\n")) - - val semicolonComment: P[Unit] = P(";" ~/ CharsWhile(c => c != '\n' && c != '\r' && c != '{' && c != '}', min = 0) ~ ("\r\n" | "\r" | "\n")) - val SWS: P[Unit] = P(CharsWhileIn(" \t", min = 1)).opaque("") val HWS: P[Unit] = P(CharsWhileIn(" \t", min = 0)).opaque("") - val AWS: P[Unit] = P((CharIn(" \t\n\r") | NoCut(semicolon) | NoCut(comment)).rep(min = 0)).opaque("") - - val AWS_asm: P[Unit] = P((CharIn(" \t\n\r") | NoCut(semicolonComment) | NoCut(comment)).rep(min = 0)).opaque("") - - val EOL: P[Unit] = P(HWS ~ ("\r\n" | "\r" | "\n" | semicolon | comment).opaque("") ~ AWS).opaque("") - - val EOL_asm: P[Unit] = P(HWS ~ ("\r\n" | "\r" | "\n" | comment | semicolonComment).opaque("") ~ AWS).opaque("") - - val EOLOrComma: P[Unit] = P(HWS ~ ("\r\n" | "\r" | "\n" | "," | semicolon | comment).opaque("") ~ AWS).opaque("") - val letter: P[String] = P(CharIn("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_").!) val letterOrDigit: P[Unit] = P(CharIn("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_.$1234567890")) @@ -714,33 +744,4 @@ object MfParser { val nonStatementLevel = 1 // everything but not `=` val mathLevel = 4 // the `:` operator - val elidable: P[Elidability.Value] = (("!" | "?").! ~/ HWS).?.map{ - case Some("?") => Elidability.Elidable - case Some("!") => Elidability.Volatile - case _ => Elidability.Fixed - } - - val externFunctionBody: P[Option[List[Statement]]] = P("extern" ~/ PassWith(None)) - - val bankDeclaration: P[Option[String]] = ("segment" ~/ AWS ~/ "(" ~/ AWS ~/ identifier ~/ AWS ~/ ")" ~/ AWS).? - - val breakStatement: P[Seq[ExecutableStatement]] = ("break" ~ !letterOrDigit ~/ HWS ~ identifier.?).map(l => Seq(BreakStatement(l.getOrElse("")))) - - val continueStatement: P[Seq[ExecutableStatement]] = ("continue" ~ !letterOrDigit ~/ HWS ~ identifier.?).map(l => Seq(ContinueStatement(l.getOrElse("")))) - - val importStatement: P[Seq[ImportStatement]] = ("import" ~ !letterOrDigit ~/ SWS ~/ identifier).map(x => Seq(ImportStatement(x))) - - val forDirection: P[ForDirection.Value] = - ("parallel" ~ HWS ~ "to").!.map(_ => ForDirection.ParallelTo) | - ("parallel" ~ HWS ~ "until").!.map(_ => ForDirection.ParallelUntil) | - "until".!.map(_ => ForDirection.Until) | - "to".!.map(_ => ForDirection.To) | - ("down" ~/ HWS ~/ "to").!.map(_ => ForDirection.DownTo) - - private def flags_(allowed: String*): P[Set[String]] = StringIn(allowed: _*).!.rep(min = 0, sep = SWS).map(_.toSet).opaque("") - - val variableFlags: P[Set[String]] = flags_("const", "static", "volatile", "stack", "register") - - val functionFlags: P[Set[String]] = flags_("asm", "inline", "interrupt", "macro", "noinline", "reentrant", "kernal_interrupt") - } diff --git a/src/test/scala/millfork/test/emu/ShouldNotParse.scala b/src/test/scala/millfork/test/emu/ShouldNotParse.scala index 2218e179..9fb3ca18 100644 --- a/src/test/scala/millfork/test/emu/ShouldNotParse.scala +++ b/src/test/scala/millfork/test/emu/ShouldNotParse.scala @@ -39,11 +39,11 @@ object ShouldNotParse extends Matchers { } parserF.toAst match { case Success(program, _) => - fail("Parse succeded") + fail("Parse succeeded") case f: Failure[_, _] => println(f.extra.toString) - println(f.lastParser.toString) - log.info("Expected syntax error: " + parserF.lastLabel, Some(parserF.lastPosition)) + log.warn("Last parser: " + f.lastParser, Some(parserF.indexToPosition(f.index, f.lastParser.toString))) + log.warn("Expected syntax error: " + parserF.lastLabel, Some(parserF.lastPosition)) } } }