From e9990dc9c1623f62a877b577749fdef02e2b9e98 Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Tue, 8 Oct 2019 18:45:11 +0200 Subject: [PATCH] Allow semicolons in certain positions. Partially implements #6 --- docs/lang/syntax.md | 9 ++++ src/main/scala/millfork/parser/MfParser.scala | 16 ++++-- .../scala/millfork/test/AssemblySuite.scala | 7 +-- .../scala/millfork/test/BasicSymonTest.scala | 45 +++++++++++++++-- .../millfork/test/emu/ShouldNotParse.scala | 49 +++++++++++++++++++ 5 files changed, 115 insertions(+), 11 deletions(-) create mode 100644 src/test/scala/millfork/test/emu/ShouldNotParse.scala diff --git a/docs/lang/syntax.md b/docs/lang/syntax.md index 18e75159..b4d2dd93 100644 --- a/docs/lang/syntax.md +++ b/docs/lang/syntax.md @@ -10,6 +10,9 @@ For information about assembly, see [Using assembly within Millfork programs](./ Comments start with `//` and last until the end of line. +Inside assembly blocks, including assembly functions, you can alternatively start comments with `;`. +Such comments cannot contain braces. + ## Declarations @@ -220,6 +223,12 @@ All starting modules are considered to be imported by all source files explicitl ## Statements +Statements are separated from each other with a new line, or with a semicolon followed by a new line. + +You cannot put two statements on one line. + +In statement blocks, the opening and closing braces do not need to be separated from the statements. + ### Expression statement TODO diff --git a/src/main/scala/millfork/parser/MfParser.scala b/src/main/scala/millfork/parser/MfParser.scala index 72594843..c5fcddfd 100644 --- a/src/main/scala/millfork/parser/MfParser.scala +++ b/src/main/scala/millfork/parser/MfParser.scala @@ -399,7 +399,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri def statement: P[Seq[Statement]] = (position() ~ P(keywordStatement | arrayDefinition | localVariableDefinition | expressionStatement)).map { case (p, s) => s.map(_.pos(p)) } - def asmStatements: P[List[ExecutableStatement]] = ("{" ~/ AWS ~/ asmStatement.rep(sep = NoCut(EOL) ~ !"}" ~/ Pass) ~/ AWS ~/ "}" ~/ Pass).map(_.toList) + def asmStatements: P[List[ExecutableStatement]] = ("{" ~/ AWS_asm ~/ asmStatement.rep(sep = NoCut(EOL_asm) ~ !"}" ~/ Pass) ~/ AWS_asm ~/ "}" ~/ Pass).map(_.toList) def statements: P[List[Statement]] = ("{" ~/ AWS ~ statement.rep(sep = NoCut(EOL) ~ !"}" ~/ Pass) ~/ AWS ~/ "}" ~/ Pass).map(_.flatten.toList) @@ -587,15 +587,23 @@ 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(comment)).rep(min = 0)).opaque("") + val AWS: P[Unit] = P((CharIn(" \t\n\r") | NoCut(semicolon) | NoCut(comment)).rep(min = 0)).opaque("") - val EOL: P[Unit] = P(HWS ~ ("\r\n" | "\r" | "\n" | comment).opaque("") ~ AWS).opaque("") + val AWS_asm: P[Unit] = P((CharIn(" \t\n\r") | NoCut(semicolonComment) | NoCut(comment)).rep(min = 0)).opaque("") - val EOLOrComma: P[Unit] = P(HWS ~ ("\r\n" | "\r" | "\n" | "," | comment).opaque("") ~ AWS).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_").!) diff --git a/src/test/scala/millfork/test/AssemblySuite.scala b/src/test/scala/millfork/test/AssemblySuite.scala index 375d7b0f..4b6ca2a1 100644 --- a/src/test/scala/millfork/test/AssemblySuite.scala +++ b/src/test/scala/millfork/test/AssemblySuite.scala @@ -1,5 +1,6 @@ package millfork.test -import millfork.test.emu.{EmuBenchmarkRun, EmuOptimizedCmosRun, EmuOptimizedRun} +import millfork.Cpu +import millfork.test.emu.{EmuBenchmarkRun, EmuCrossPlatformBenchmarkRun, EmuOptimizedCmosRun, EmuOptimizedRun} import org.scalatest.{AppendedClues, FunSuite, Matchers} /** @@ -8,13 +9,13 @@ import org.scalatest.{AppendedClues, FunSuite, Matchers} class AssemblySuite extends FunSuite with Matchers with AppendedClues { test("Inline assembly") { - EmuBenchmarkRun( + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Motorola6809)( """ | byte output @$c000 | void main () { | output = 0 | asm { - | inc $c000 + | inc $c000 ; this is an assembly-style comment | } | } """.stripMargin)(_.readByte(0xc000) should equal(1)) diff --git a/src/test/scala/millfork/test/BasicSymonTest.scala b/src/test/scala/millfork/test/BasicSymonTest.scala index babcbb8e..d8175e05 100644 --- a/src/test/scala/millfork/test/BasicSymonTest.scala +++ b/src/test/scala/millfork/test/BasicSymonTest.scala @@ -1,7 +1,7 @@ package millfork.test import millfork.Cpu -import millfork.test.emu.{EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedRun} +import millfork.test.emu.{EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedRun, ShouldNotParse} import org.scalatest.{FunSuite, Matchers} /** @@ -53,9 +53,9 @@ class BasicSymonTest extends FunSuite with Matchers { output += 1 output += 1 output += 1 - output += 1 - output += 1 - output += 1 + output += 1 ; + output += 1 ;; + output += 1 ; //comment output += 1 output += 1 output += 1 @@ -254,4 +254,41 @@ class BasicSymonTest extends FunSuite with Matchers { m.readByte(0xc000) should equal(1) } } + + test("Semicolon syntax test") { + ShouldNotParse( + """ + |void main() { + | a ; b + |} + |""".stripMargin) + ShouldNotParse( + """ + |void main() { + | asm { ; } + |} + |""".stripMargin) + ShouldNotParse( + """ + |void main() { + | nop ; do absolutely nothing{} + |} + |""".stripMargin) + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)( + """ + |byte a,b + |void main() { + | a=b; // test + | b=a + | } + |""".stripMargin){_=>} + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)( + """ + | void main() { + | asm { ;hello + | nop ; do absolutely nothing + | } + | } + |""".stripMargin){_=>} + } } diff --git a/src/test/scala/millfork/test/emu/ShouldNotParse.scala b/src/test/scala/millfork/test/emu/ShouldNotParse.scala new file mode 100644 index 00000000..2218e179 --- /dev/null +++ b/src/test/scala/millfork/test/emu/ShouldNotParse.scala @@ -0,0 +1,49 @@ +package millfork.test.emu + +import fastparse.core.Parsed.{Failure, Success} +import millfork.compiler.LabelGenerator +import millfork.parser._ +import millfork._ +import org.scalatest.Matchers + +/** + * @author Karol Stasiak + */ +object ShouldNotParse extends Matchers { + def apply(source: String): Unit = { + checkCase(Cpu.Mos, source) + checkCase(Cpu.Z80, source) + checkCase(Cpu.Motorola6809, source) + } + + private def checkCase(cpu: Cpu.Value, source: String) { + Console.out.flush() + Console.err.flush() + val log = TestErrorReporting.log + println(source) + val platform = EmuPlatform.get(cpu) + val options = CompilationOptions(platform, Map(CompilationFlag.LenientTextEncoding -> true), None, platform.zpRegisterSize, Map(), JobContext(log, new LabelGenerator)) + log.hasErrors = false + log.verbosity = 999 + var effectiveSource = source + log.setSource(Some(effectiveSource.linesIterator.toIndexedSeq)) + val PreprocessingResult(preprocessedSource, features, _) = Preprocessor.preprocessForTest(options, effectiveSource) + val parserF = + platform.cpuFamily match { + case CpuFamily.M6502 => + MosParser("", preprocessedSource, "", options, features) + case CpuFamily.M6809 => + M6809Parser("", preprocessedSource, "", options, features) + case CpuFamily.I80 => + Z80Parser("", preprocessedSource, "", options, features, options.flag(CompilationFlag.UseIntelSyntaxForInput)) + } + parserF.toAst match { + case Success(program, _) => + fail("Parse succeded") + case f: Failure[_, _] => + println(f.extra.toString) + println(f.lastParser.toString) + log.info("Expected syntax error: " + parserF.lastLabel, Some(parserF.lastPosition)) + } + } +}