1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-12-22 16:31:02 +00:00

Add dollar syntax for decimal operators, disallow identifiers ending in a dollar sign.

This commit is contained in:
Karol Stasiak 2020-08-14 02:22:13 +02:00
parent 0913c5037c
commit fccbf7df7d
7 changed files with 96 additions and 11 deletions

View File

@ -41,6 +41,12 @@ Note that you cannot mix `+'` and `-'` with `+` and `-`.
Certain operators (`/`, `%%`, `<<`, `>>`, `<<'`, `>>'`, `>>>>`, `:`, `!=`) cannot have more than 2 parameters,
i.e. `x / y / z` will not compile.
The decimal operators have two different forms:
* apostrophe form (e.g. `+'`) the original one, to be deprecated in the future, may be removed in Millfork 0.4
* dollar form (e.g. `$+`) available since Millfork 0.3.22
## Argument types
In the descriptions below, arguments to the operators are explained as follows:
@ -130,15 +136,18 @@ These operators work using the decimal arithmetic (packed BCD).
On Ricoh-based targets (e.g. Famicom) they require the zeropage register to have size at least 4
* `+'`, `-'`: decimal addition/subtraction
`$+`, `$-` (since Millfork 0.3.22)
`byte +' byte`
`constant word +' constant word`
`constant long +' constant long`
`word +' word` (zpreg)
* `*'`: decimal multiplication
`$*` (since Millfork 0.3.22)
`constant *' constant`
* `<<'`, `>>'`: decimal multiplication/division by power of two
`$<<`, `$>>` (since Millfork 0.3.22)
`byte <<' constant byte`
## Comparison operators
@ -193,6 +202,7 @@ An expression of form `a[f()] += b` may call `f` an undefined number of times.
`mutable long = long`
* `+=`, `+'=`, `|=`, `^=`, `&=`: modification in place
`$+=` (since Millfork 0.3.22)
`mutable byte += byte`
`mutable word += word`
`mutable trivial long += long`
@ -203,11 +213,13 @@ An expression of form `a[f()] += b` may call `f` an undefined number of times.
`mutable trivial long <<= byte`
* `<<'=`, `>>'=`: decimal shift in place
`$<<=`, `$>>=` (since Millfork 0.3.22)
`mutable byte <<'= constant byte`
`mutable word <<'= constant byte`
`mutable trivial long <<'= constant byte`
* `-=`, `-'=`: subtraction in place
`$-=` (since Millfork 0.3.22)
`mutable byte -= byte`
`mutable word -= simple word`
`mutable trivial long -= simple long`
@ -219,6 +231,7 @@ An expression of form `a[f()] += b` may call `f` an undefined number of times.
`mutable word *= word` (zpreg)
* `*'=`: decimal multiplication in place
`$*=` (since Millfork 0.3.22)
`mutable byte *'= constant byte`
* `/=`, `%%=`: unsigned division and modulo in place

View File

@ -13,6 +13,26 @@ Allowed line endings are U+000A, U+000D and U+000D/U+000A.
Outside of text strings and comments, the only allowed characters are U+0009 and U+0020U+007E
(so-called printable ASCII).
## Valid identifiers
Identifiers are used for variable names, function names, array names, segment names.
Identifiers have to start with a letter or an underscore, and they can contain letters, underscores, digits and dollar signs.
An identifier cannot end with a dollar sign, nor can it contain two consecutive dollar signs.
Identifiers using dollar signs are reserved for internal use, do not use them without a good reason.
There is no hard limit on the identifier length.
a // valid
1a // invalid
a1 // valid
_1 // valid
a$1 // valid, but discouraged
a$$a // invalid
a$ // invalid
## Comments
Comments start with `//` and last until the end of line.

View File

@ -435,7 +435,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
for {
minus <- ("-".rep(min = 1).!.map(_.length().&(1).==(1)) ~/ HWS).?.map(_.getOrElse(false))
head <- tightMfExpression(allowIntelHex, allowTopLevelIndexing) ~/ HWS
maybeOperator <- (StringIn(allowedOperators: _*).! ~ !CharIn(Seq('/', '=', '-', '+', ':', '>', '<', '\''))).?
maybeOperator <- (StringIn(allowedOperators: _*).! ~ !CharIn(Seq('/', '=', '-', '+', ':', '>', '<', '\''))).map(op => if (mfOperatorNormalizations.contains(op)) mfOperatorNormalizations(op) else op).?
maybeTail <- maybeOperator.fold[P[Option[List[(String, (Boolean, Expression))]]]](Pass.map(_ => None))(o => (AWS ~/ inner ~/ HWS).map(x2 => Some((o -> x2.head) :: x2.tail)))
} yield {
maybeTail.fold[SeparatedList[(Boolean, Expression), String]](SeparatedList.of(minus -> head))(t => SeparatedList(minus -> head, t))
@ -799,9 +799,12 @@ object MfParser {
val letterOrDigit: P[Unit] = P(CharIn("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_.$1234567890"))
val lettersOrDigits: P[String] = P(CharsWhileIn("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_.$1234567890", min = 0).!)
val realLetterOrDigit: P[Unit] = P(CharIn("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_.1234567890"))
val identifier: P[String] = P((letter ~ lettersOrDigits).map { case (a, b) => a + b }).opaque("<identifier>")
val identifierTail: P[String] =
CharsWhileIn("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_.1234567890", min = 1).rep(min = 1, sep = "$").!
val identifier: P[String] = (letter ~ ("$".? ~ identifierTail).?).!.opaque("<identifier>")
val doubleQuotedString: P[String] = P("\"" ~/ CharsWhile(c => c != '\"' && c != '\n' && c != '\r').?.! ~ "\"")
@ -895,13 +898,26 @@ object MfParser {
}
val mfOperators = List(
List("+=", "-=", "+'=", "-'=", "^=", "&=", "|=", "*=", "*'=", "<<=", ">>=", "<<'=", ">>'=", "/=", "%%=", "="),
List("+=", "-=", "+'=", "-'=", "^=", "&=", "|=", "*=", "*'=", "<<=", ">>=", "<<'=", ">>'=", "/=", "%%=", "=", "$*=", "$+=", "$-=", "$<<=", "$>>="),
List("||", "^^"),
List("&&"),
List("==", "<=", ">=", "!=", "<", ">"),
List(":"),
List("+'", "-'", "<<'", ">>'", ">>>>", "+", "-", "&", "|", "^", "<<", ">>"),
List("*'", "*", "/", "%%"))
List("+'", "-'", "<<'", ">>'", ">>>>", "+", "-", "&", "|", "^", "<<", ">>", "$+", "$-", "$<<", "$>>"),
List("*'", "$*", "*", "/", "%%"))
val mfOperatorNormalizations = Map(
"$+" -> "+'",
"$-" -> "-'",
"$+=" -> "+'=",
"$-=" -> "-'=",
"$<<" -> "<<'",
"$>>" -> ">>'",
"$<<=" -> "<<'=",
"$>>=" -> ">>'=",
"$*" -> "*'",
"$*=" -> "*'=",
)
val mfOperatorsDropFlatten: IndexedSeq[List[String]] = mfOperators.indices.map(i => mfOperators.drop(i).flatten)

View File

@ -16,7 +16,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers with AppendedClues {
| byte a
| void main () {
| a = $36
| output = a +' a
| output = a $+ a
| }
""".stripMargin)(_.readByte(0xc000) should equal(0x72))
}
@ -28,7 +28,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers with AppendedClues {
| byte a
| void main () {
| a = 1
| output = a +' $69
| output = a$+$69
| }
""".stripMargin)(_.readByte(0xc000) should equal(0x70))
}

View File

@ -1,7 +1,7 @@
package millfork.test
import millfork.Cpu
import millfork.test.emu.{EmuUnoptimizedCrossPlatformRun, ShouldNotCompile, ShouldNotParse}
import millfork.test.emu.{EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedRun, ShouldNotCompile, ShouldNotParse}
import org.scalatest.{FunSuite, Matchers}
/**
@ -60,4 +60,30 @@ class ParserSuite extends FunSuite with Matchers {
|const array a = aa"
|""".stripMargin)
}
test("Various valid identifiers") {
// yes, this spews warnings.
// I'll deal with them later.
EmuUnoptimizedRun(
"""
|byte a
|byte a$a
|byte a.$a
|byte a.a
|byte a.$a
|byte a$a$a
|byte a$.$a
|byte a...
|void main(){
| a += 0
| a$a += 0
| a.$a += 0
| a.a += 0
| a.$a += 0
| a$a$a += 0
| a$.$a += 0
| a... += 0
|}
|""".stripMargin)
}
}

View File

@ -162,7 +162,7 @@ class EmuI86Run(nodeOptimizations: List[NodeOptimization], assemblyOptimizations
}
(0x100 until 0x2000).takeWhile(memoryBank.occupied(_)).map(memoryBank.output).grouped(16).map(_.map(i => f"$i%02x").mkString(" ")).foreach(log.debug(_))
val resetN = source.contains("-'") && !options.flag(CompilationFlag.EmitExtended80Opcodes)
val resetN = (source.contains("-'") || source.contains("$-")) && !options.flag(CompilationFlag.EmitExtended80Opcodes)
val resetNMethod = {
val clazz = classOf[Z80Core]
val method = clazz.getDeclaredMethod("resetN")

View File

@ -214,7 +214,17 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
}
case _ =>
}
if(!options.flag(CompilationFlag.DecimalMode) && (source.contains("+'") || source.contains("-'") || source.contains("<<'") || source.contains("*'")))
if(!options.flag(CompilationFlag.DecimalMode) && (
source.contains("+'") ||
source.contains("-'") ||
source.contains("<<'") ||
source.contains(">>'") ||
source.contains("*'") ||
source.contains("$+") ||
source.contains("$-") ||
source.contains("$<<") ||
source.contains("$>>") ||
source.contains("$*")))
tmp += EmuRun.cachedBcd
tmp
}