mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-03 19: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,
|
Certain operators (`/`, `%%`, `<<`, `>>`, `<<'`, `>>'`, `>>>>`, `:`, `!=`) cannot have more than 2 parameters,
|
||||||
i.e. `x / y / z` will not compile.
|
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
|
## Argument types
|
||||||
|
|
||||||
In the descriptions below, arguments to the operators are explained as follows:
|
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
|
On Ricoh-based targets (e.g. Famicom) they require the zeropage register to have size at least 4
|
||||||
|
|
||||||
* `+'`, `-'`: decimal addition/subtraction
|
* `+'`, `-'`: decimal addition/subtraction
|
||||||
|
`$+`, `$-` (since Millfork 0.3.22)
|
||||||
`byte +' byte`
|
`byte +' byte`
|
||||||
`constant word +' constant word`
|
`constant word +' constant word`
|
||||||
`constant long +' constant long`
|
`constant long +' constant long`
|
||||||
`word +' word` (zpreg)
|
`word +' word` (zpreg)
|
||||||
|
|
||||||
* `*'`: decimal multiplication
|
* `*'`: decimal multiplication
|
||||||
|
`$*` (since Millfork 0.3.22)
|
||||||
`constant *' constant`
|
`constant *' constant`
|
||||||
|
|
||||||
* `<<'`, `>>'`: decimal multiplication/division by power of two
|
* `<<'`, `>>'`: decimal multiplication/division by power of two
|
||||||
|
`$<<`, `$>>` (since Millfork 0.3.22)
|
||||||
`byte <<' constant byte`
|
`byte <<' constant byte`
|
||||||
|
|
||||||
## Comparison operators
|
## Comparison operators
|
||||||
@ -193,6 +202,7 @@ An expression of form `a[f()] += b` may call `f` an undefined number of times.
|
|||||||
`mutable long = long`
|
`mutable long = long`
|
||||||
|
|
||||||
* `+=`, `+'=`, `|=`, `^=`, `&=`: modification in place
|
* `+=`, `+'=`, `|=`, `^=`, `&=`: modification in place
|
||||||
|
`$+=` (since Millfork 0.3.22)
|
||||||
`mutable byte += byte`
|
`mutable byte += byte`
|
||||||
`mutable word += word`
|
`mutable word += word`
|
||||||
`mutable trivial long += long`
|
`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`
|
`mutable trivial long <<= byte`
|
||||||
|
|
||||||
* `<<'=`, `>>'=`: decimal shift in place
|
* `<<'=`, `>>'=`: decimal shift in place
|
||||||
|
`$<<=`, `$>>=` (since Millfork 0.3.22)
|
||||||
`mutable byte <<'= constant byte`
|
`mutable byte <<'= constant byte`
|
||||||
`mutable word <<'= constant byte`
|
`mutable word <<'= constant byte`
|
||||||
`mutable trivial long <<'= constant byte`
|
`mutable trivial long <<'= constant byte`
|
||||||
|
|
||||||
* `-=`, `-'=`: subtraction in place
|
* `-=`, `-'=`: subtraction in place
|
||||||
|
`$-=` (since Millfork 0.3.22)
|
||||||
`mutable byte -= byte`
|
`mutable byte -= byte`
|
||||||
`mutable word -= simple word`
|
`mutable word -= simple word`
|
||||||
`mutable trivial long -= simple long`
|
`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)
|
`mutable word *= word` (zpreg)
|
||||||
|
|
||||||
* `*'=`: decimal multiplication in place
|
* `*'=`: decimal multiplication in place
|
||||||
|
`$*=` (since Millfork 0.3.22)
|
||||||
`mutable byte *'= constant byte`
|
`mutable byte *'= constant byte`
|
||||||
|
|
||||||
* `/=`, `%%=`: unsigned division and modulo in place
|
* `/=`, `%%=`: 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
|
Outside of text strings and comments, the only allowed characters are U+0009 and U+0020–U+007E
|
||||||
(so-called printable ASCII).
|
(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
|
||||||
|
|
||||||
Comments start with `//` and last until the end of line.
|
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 {
|
for {
|
||||||
minus <- ("-".rep(min = 1).!.map(_.length().&(1).==(1)) ~/ HWS).?.map(_.getOrElse(false))
|
minus <- ("-".rep(min = 1).!.map(_.length().&(1).==(1)) ~/ HWS).?.map(_.getOrElse(false))
|
||||||
head <- tightMfExpression(allowIntelHex, allowTopLevelIndexing) ~/ HWS
|
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)))
|
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 {
|
} yield {
|
||||||
maybeTail.fold[SeparatedList[(Boolean, Expression), String]](SeparatedList.of(minus -> head))(t => SeparatedList(minus -> head, t))
|
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 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').?.! ~ "\"")
|
val doubleQuotedString: P[String] = P("\"" ~/ CharsWhile(c => c != '\"' && c != '\n' && c != '\r').?.! ~ "\"")
|
||||||
|
|
||||||
@ -895,13 +898,26 @@ object MfParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val mfOperators = List(
|
val mfOperators = List(
|
||||||
List("+=", "-=", "+'=", "-'=", "^=", "&=", "|=", "*=", "*'=", "<<=", ">>=", "<<'=", ">>'=", "/=", "%%=", "="),
|
List("+=", "-=", "+'=", "-'=", "^=", "&=", "|=", "*=", "*'=", "<<=", ">>=", "<<'=", ">>'=", "/=", "%%=", "=", "$*=", "$+=", "$-=", "$<<=", "$>>="),
|
||||||
List("||", "^^"),
|
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)
|
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
|
| byte a
|
||||||
| void main () {
|
| void main () {
|
||||||
| a = $36
|
| a = $36
|
||||||
| output = a +' a
|
| output = a $+ a
|
||||||
| }
|
| }
|
||||||
""".stripMargin)(_.readByte(0xc000) should equal(0x72))
|
""".stripMargin)(_.readByte(0xc000) should equal(0x72))
|
||||||
}
|
}
|
||||||
@ -28,7 +28,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers with AppendedClues {
|
|||||||
| byte a
|
| byte a
|
||||||
| void main () {
|
| void main () {
|
||||||
| a = 1
|
| a = 1
|
||||||
| output = a +' $69
|
| output = a$+$69
|
||||||
| }
|
| }
|
||||||
""".stripMargin)(_.readByte(0xc000) should equal(0x70))
|
""".stripMargin)(_.readByte(0xc000) should equal(0x70))
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package millfork.test
|
package millfork.test
|
||||||
|
|
||||||
import millfork.Cpu
|
import millfork.Cpu
|
||||||
import millfork.test.emu.{EmuUnoptimizedCrossPlatformRun, ShouldNotCompile, ShouldNotParse}
|
import millfork.test.emu.{EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedRun, ShouldNotCompile, ShouldNotParse}
|
||||||
import org.scalatest.{FunSuite, Matchers}
|
import org.scalatest.{FunSuite, Matchers}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,4 +60,30 @@ class ParserSuite extends FunSuite with Matchers {
|
|||||||
|const array a = ‟aa"
|
|const array a = ‟aa"
|
||||||
|""".stripMargin)
|
|""".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(_))
|
(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 resetNMethod = {
|
||||||
val clazz = classOf[Z80Core]
|
val clazz = classOf[Z80Core]
|
||||||
val method = clazz.getDeclaredMethod("resetN")
|
val method = clazz.getDeclaredMethod("resetN")
|
||||||
|
@ -214,7 +214,17 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
|||||||
}
|
}
|
||||||
case _ =>
|
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 += EmuRun.cachedBcd
|
||||||
tmp
|
tmp
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user