1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-25 19:29:49 +00:00

Allow semicolons in certain positions. Partially implements #6

This commit is contained in:
Karol Stasiak 2019-10-08 18:45:11 +02:00
parent e19ac75350
commit e9990dc9c1
5 changed files with 115 additions and 11 deletions

View File

@ -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

View File

@ -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("<horizontal whitespace>")
val HWS: P[Unit] = P(CharsWhileIn(" \t", min = 0)).opaque("<horizontal whitespace>")
val AWS: P[Unit] = P((CharIn(" \t\n\r;") | NoCut(comment)).rep(min = 0)).opaque("<any whitespace>")
val AWS: P[Unit] = P((CharIn(" \t\n\r") | NoCut(semicolon) | NoCut(comment)).rep(min = 0)).opaque("<any whitespace>")
val EOL: P[Unit] = P(HWS ~ ("\r\n" | "\r" | "\n" | comment).opaque("<first line break>") ~ AWS).opaque("<line break>")
val AWS_asm: P[Unit] = P((CharIn(" \t\n\r") | NoCut(semicolonComment) | NoCut(comment)).rep(min = 0)).opaque("<any whitespace>")
val EOLOrComma: P[Unit] = P(HWS ~ ("\r\n" | "\r" | "\n" | "," | comment).opaque("<first line break or comma>") ~ AWS).opaque("<line break or comma>")
val EOL: P[Unit] = P(HWS ~ ("\r\n" | "\r" | "\n" | semicolon | comment).opaque("<first line break>") ~ AWS).opaque("<line break>")
val EOL_asm: P[Unit] = P(HWS ~ ("\r\n" | "\r" | "\n" | comment | semicolonComment).opaque("<first line break>") ~ AWS).opaque("<line break>")
val EOLOrComma: P[Unit] = P(HWS ~ ("\r\n" | "\r" | "\n" | "," | semicolon | comment).opaque("<first line break or comma>") ~ AWS).opaque("<line break or comma>")
val letter: P[String] = P(CharIn("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_").!)

View File

@ -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))

View File

@ -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){_=>}
}
}

View File

@ -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))
}
}
}