From c9a65d5971d2ad88c3e0186e6147fccef914f8ea Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Wed, 26 Jun 2019 13:43:15 +0200 Subject: [PATCH] Added #define directive --- CHANGELOG.md | 2 ++ docs/lang/preprocessor.md | 9 ++++++++ examples/crossplatform/guess.mfk | 7 ++++++ .../scala/millfork/parser/Preprocessor.scala | 22 +++++++++++++++++-- .../scala/millfork/test/BasicSymonTest.scala | 15 +++++++++++++ 5 files changed, 53 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bed9597d..00c22190 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,8 @@ * Preprocessor: Added `if` function +* Preprocessor: Added `#define` directive. + * Fixed volatile-related bugs. * Fixed optimizations removing jumps to jumps. diff --git a/docs/lang/preprocessor.md b/docs/lang/preprocessor.md index dd75a66d..dc3fcabf 100644 --- a/docs/lang/preprocessor.md +++ b/docs/lang/preprocessor.md @@ -148,6 +148,15 @@ Emits a diagnostic message. Evaluates an expression and emits the result as a diagnostic message. +### `#define` + + #define = + +Defines a new feature value or redefines a previous feature value. + +The feature value is visible only to the preprocessor, only when processing the current file, +and only in lines preprocessed after this one. + ### `#use` #use = diff --git a/examples/crossplatform/guess.mfk b/examples/crossplatform/guess.mfk index 812d2e55..e4e85ca9 100644 --- a/examples/crossplatform/guess.mfk +++ b/examples/crossplatform/guess.mfk @@ -10,6 +10,8 @@ import c64_basic import c264_basic #endif +#define READKEY = CBM_64 | CBM_VIC | CBM_264 | CBM_128 | ZX_SPECTRUM | NEC_PC_88 | ATARI_8 + void main () { init_rand_seed() ensure_mixedcase() @@ -60,5 +62,10 @@ void play_round() { putword(guess_count) putstrz(" attempts!"z) new_line() + #if READKEY + putstrz("Press any key to play again."z) + readkey() + new_line() + #endif new_line() } \ No newline at end of file diff --git a/src/main/scala/millfork/parser/Preprocessor.scala b/src/main/scala/millfork/parser/Preprocessor.scala index 3f866e7d..f8a2487c 100644 --- a/src/main/scala/millfork/parser/Preprocessor.scala +++ b/src/main/scala/millfork/parser/Preprocessor.scala @@ -37,11 +37,12 @@ object Preprocessor { var enabled = true val ifStack = mutable.Stack[IfContext]() var lineNo = 0 + var currentFeatures = options.features def evalParam(param: String, pos: Some[Position]): Long = { new PreprocessorParser(options).expression.parse(param) match { case Success(q, _) => - val value = q.apply(options.features).getOrElse(0L) + val value = q.apply(currentFeatures).getOrElse(0L) // log.trace(param + " ===> " + value) value case Failure(_, _, _) => @@ -50,6 +51,17 @@ object Preprocessor { } } + def assertIdentifier(ident: String, pos: Option[Position]) : String = { + ident.foreach{ + case ' ' => log.error("Unexpected space in a preprocessor identifier", pos) + case '_' => // ok + case c if c < 128 && Character.isDigit(c) => // ok + case c if c < 128 && Character.isLetter(c) => // ok + case _ => log.error("Invalid character in a preprocessor identifier", pos) + } + ident + } + for (line <- lines) { lineNo += 1 var resulting = "" @@ -65,7 +77,13 @@ object Preprocessor { log.warn(s"Undefined parameter $param, assuming 0", pos) 0L }) - case Array(p0,p1) => featureConstants += p0.trim() -> evalParam(p1, pos) + case Array(p0,p1) => featureConstants += assertIdentifier(p0.trim(), pos) -> evalParam(p1, pos) + } + } + case "define" => if (enabled) { + param.split("=", 2) match { + case Array(p0,p1) => currentFeatures += assertIdentifier(p0.trim(), pos) -> evalParam(p1, pos) + case _ => log.error("#define should have a parameter", pos) } } case "fatal" => if (enabled) log.fatal(param, pos) diff --git a/src/test/scala/millfork/test/BasicSymonTest.scala b/src/test/scala/millfork/test/BasicSymonTest.scala index 6cd22281..d922c6d1 100644 --- a/src/test/scala/millfork/test/BasicSymonTest.scala +++ b/src/test/scala/millfork/test/BasicSymonTest.scala @@ -214,6 +214,21 @@ class BasicSymonTest extends FunSuite with Matchers { | } | #endif | + | #if 1 + | #define A=4 + | #endif + | #if 0 + | #define B=1 + | #endif + | + | #if B + | #error B should not be defined + | #endif + | + | #if A != 4 + | #error A should be defined + | #endif + | | #if 1 + 1 == 2 | #info 1 | #if 1 == 3