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:
parent
0913c5037c
commit
fccbf7df7d
@ -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
|
||||
|
@ -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+0020–U+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.
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user