Merge branch 'v12maintenance'

# Conflicts:
#	codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt
#	compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt
#	compiler/test/ast/TestVariousCompilerAst.kt
#	compiler/test/codegeneration/TestExecution6502.kt
#	examples/test.p8
#	gradle.properties
This commit is contained in:
Irmen de Jong committed 2026-09-10 21:45:47 +02:00
commit dbba6a94fa
13 files changed
+351 -89

No files matched your search

@@ -1504,7 +1504,7 @@ $repeatLabel""")
}
else if(swap.target1.pointerDeref!=null && swap.target2.pointerDeref!=null) {
val v1 = swap.target1.pointerDeref!!
val v2 = swap.target1.pointerDeref!!
val v2 = swap.target2.pointerDeref!!
if(v1.derefLast && v1.chain.isEmpty() && v2.derefLast && v2.chain.isEmpty() && isZpVar(v1.startpointer) && isZpVar(v2.startpointer)) {
// optimized case where v1 and v2 are both already zeropage pointer variables
val name1 = asmVariableName(v1.startpointer)
@@ -33,8 +33,8 @@ import prog8.codegen.cpu6502.assignment.*
return when (fcall.name) {
"msw" -> funcMsw(fcall, firstReturnRegister ?: RegisterOrPair.AY)
"lsw" -> funcLsw(fcall, firstReturnRegister ?: RegisterOrPair.AY)
"msb" -> funcMsb(fcall, firstReturnRegister ?: RegisterOrPair.A)
"msb__long" -> funcMsbLong(fcall, firstReturnRegister ?: RegisterOrPair.A)
"msb" -> funcMsb(fcall, false, firstReturnRegister ?: RegisterOrPair.A)
"msb__long" -> funcMsb(fcall, true, firstReturnRegister ?: RegisterOrPair.A)
"lsb" -> funcLsb(fcall, false, firstReturnRegister ?: RegisterOrPair.A)
"lsb__long" -> funcLsb(fcall,true, firstReturnRegister ?: RegisterOrPair.A)
"mkword" -> funcMkword(fcall, firstReturnRegister ?: RegisterOrPair.AY)
@@ -2527,55 +2527,36 @@ import prog8.codegen.cpu6502.assignment.*
return arrayOf(RegisterOrPair.AY)
}
private fun funcMsbLong(fcall: PtFunctionCall, resultReg: RegisterOrPair): Array<RegisterOrPair> {
private fun funcMsb(fcall: PtFunctionCall, fromLong: Boolean, resultReg: RegisterOrPair): Array<RegisterOrPair> {
val arg = fcall.args.single()
if (!arg.type.isLong)
throw AssemblyError("msb__long requires long argument")
if (arg is PtNumber)
throw AssemblyError("msb(const) should have been const-folded away")
if (arg is PtIdentifier) {
val sourceName = asmgen.asmVariableName(arg)
when(resultReg) {
RegisterOrPair.A -> asmgen.out(" lda $sourceName+3")
RegisterOrPair.X -> asmgen.out(" ldx $sourceName+3")
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+3")
else -> throw AssemblyError("invalid register for msb long: $resultReg ${arg.position}")
}
} else if(arg is PtArrayIndexer) {
TODO("msb of long array element ${fcall.position}")
if(fromLong) {
if (!arg.type.isLong)
throw AssemblyError("msb__long requires long argument")
} else {
asmgen.assignExpressionToRegister(arg, RegisterOrPair.R14R15, arg.type.isSigned)
when(resultReg) {
RegisterOrPair.A -> asmgen.out(" lda cx16.r14+3")
RegisterOrPair.X -> asmgen.out(" ldx cx16.r14+3")
RegisterOrPair.Y -> asmgen.out(" ldy cx16.r14+3")
else -> throw AssemblyError("invalid register for msb long: $resultReg ${arg.position}")
}
if (!arg.type.isWord && !arg.type.isPointer)
throw AssemblyError("msb requires word argument")
}
return arrayOf(resultReg)
}
private fun funcMsb(fcall: PtFunctionCall, resultReg: RegisterOrPair): Array<RegisterOrPair> {
val arg = fcall.args.single()
if (!arg.type.isWord)
throw AssemblyError("msb requires word argument")
if (arg is PtNumber)
throw AssemblyError("msb(const) should have been const-folded away")
val offset = if(fromLong) 3 else 1
if (arg is PtIdentifier) {
val sourceName = asmgen.asmVariableName(arg)
when(resultReg) {
RegisterOrPair.A -> asmgen.out(" lda $sourceName+1")
RegisterOrPair.X -> asmgen.out(" ldx $sourceName+1")
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+1")
RegisterOrPair.AX -> asmgen.out(" lda $sourceName+1 | ldx #0")
RegisterOrPair.AY -> asmgen.out(" lda $sourceName+1 | ldy #0")
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+1 | ldy #0")
else -> throw AssemblyError("invalid register for msb: $resultReg ${arg.position}")
RegisterOrPair.A -> asmgen.out(" lda $sourceName+$offset")
RegisterOrPair.X -> asmgen.out(" ldx $sourceName+$offset")
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+$offset")
RegisterOrPair.AX -> asmgen.out(" lda $sourceName+$offset | ldx #0")
RegisterOrPair.AY -> asmgen.out(" lda $sourceName+$offset | ldy #0")
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+$offset | ldy #0")
else -> throw AssemblyError("invalid register for msb${if(fromLong) " long" else ""}: $resultReg ${arg.position}")
}
} else {
if(arg is PtArrayIndexer) {
if(fromLong)
TODO("msb of long array element ${fcall.position}")
// just read the msb byte out of the word array
if(arg.splitWords) {
if(arg.variable==null)
@@ -2597,15 +2578,15 @@ import prog8.codegen.cpu6502.assignment.*
RegisterOrPair.AX -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
asmgen.out(" lda $arrayVar,y | ldx #0")
}
}
RegisterOrPair.AY -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
asmgen.out(" lda $arrayVar,y | ldy #0")
}
}
RegisterOrPair.XY -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.X)
asmgen.out(" ldy $arrayVar,x | ldx #0")
}
}
else -> throw AssemblyError("invalid register for msb: $resultReg ${arg.position}")
}
} else {
@@ -2641,6 +2622,14 @@ import prog8.codegen.cpu6502.assignment.*
else -> throw AssemblyError("invalid register for msb: $resultReg ${arg.position}")
}
}
} else if(fromLong) {
asmgen.assignExpressionToRegister(arg, RegisterOrPair.R14R15, arg.type.isSigned)
when(resultReg) {
RegisterOrPair.A -> asmgen.out(" lda cx16.r14+$offset")
RegisterOrPair.X -> asmgen.out(" ldx cx16.r14+$offset")
RegisterOrPair.Y -> asmgen.out(" ldy cx16.r14+$offset")
else -> throw AssemblyError("invalid register for msb long: $resultReg ${arg.position}")
}
} else {
asmgen.assignExpressionToRegister(arg, RegisterOrPair.AY)
asmgen.out(" tya")
@@ -2655,7 +2644,7 @@ import prog8.codegen.cpu6502.assignment.*
if(fromLong) {
if (!arg.type.isLong) throw AssemblyError("lsb__long requires long")
} else {
if (!arg.type.isWord) throw AssemblyError("lsb requires word")
if (!arg.type.isWord && !arg.type.isPointer) throw AssemblyError("lsb requires word")
}
if (arg is PtNumber)
throw AssemblyError("lsb(const) should have been const-folded away")
@@ -382,7 +382,7 @@ internal class AssignmentAsmGen(
throw AssemblyError("weird array element type for pointer indexed read $elementDt")
}
} else {
val (zpPtrVar, _) = pointergen.deref(pointerDeref, addOffsetToPointer = true)
val (zpPtrVar, _) = pointergen.deref(pointerDeref, addOffsetToPointer = true, loadPointerFieldValue = true)
when {
elementDt.isByteOrBool -> {
@@ -62,6 +62,63 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
}
private fun inplaceModificationIndexedPointerField(target: AsmAssignTarget, operator: String, value: AsmAssignSource) {
val array = target.array!!
val elementDt = target.datatype
if(!elementDt.isByteOrBool && !elementDt.isWord)
throw AssemblyError("inplace modification indexed pointer field for type $elementDt not supported")
val indexedTarget = IndexedPtrTarget(target)
val ptr = ptrgen.indexedEffectiveAddress(indexedTarget)
val elementTemp = asmgen.createTempVarReused(elementDt.base, false, array)
if(elementDt.isByteOrBool) {
asmgen.out("""
ldy #0
lda ($ptr),y
sta $elementTemp""")
} else {
asmgen.out("""
ldy #0
lda ($ptr),y
sta $elementTemp
iny
lda ($ptr),y
sta $elementTemp+1""")
}
// save the computed address on the hardware stack so later code can't clobber it
asmgen.out("""
lda $ptr
pha
lda $ptr+1
pha""")
// reuse the existing variable-target logic to modify the temp
val tempTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, elementDt, null, target.position, variableAsmName = elementTemp)
inplaceModification(tempTarget, operator, value)
// restore the address and store the temp back
asmgen.out("""
pla
sta $ptr+1
pla
sta $ptr""")
if(elementDt.isByteOrBool) {
asmgen.out("""
ldy #0
lda $elementTemp
sta ($ptr),y""")
} else {
asmgen.out("""
ldy #0
lda $elementTemp
sta ($ptr),y
iny
lda $elementTemp+1
sta ($ptr),y""")
}
}
private fun inplaceModification(target: AsmAssignTarget, operator: String, value: AsmAssignSource) {
// the asm-gen code can deal with situations where you want to assign a byte into a word.
@@ -247,7 +304,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
TargetStorageKind.ARRAY -> {
val deref = target.array!!.pointerderef
if(deref!=null) {
TODO("inplace modification array indexed pointer deref ${target.position}")
inplaceModificationIndexedPointerField(target, operator, value)
return
}
val targetArrayVar = target.array.variable!!
@@ -230,10 +230,11 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
}
internal fun deref(pointer: PtPointerDeref, addOffsetToPointer: Boolean=false): Pair<String, UByte> {
internal fun deref(pointer: PtPointerDeref, addOffsetToPointer: Boolean=false, loadPointerFieldValue: Boolean=false): Pair<String, UByte> {
// walk the pointer deref chain and leaves the final pointer value in a ZP var
// this will often be the temp var P8ZP_SCRATCH_PTR but can also be the original pointer variable if it is already in zeropage and there is nothing to add to it
// returns the ZP var to use as a pointer, and a Y register offset (which can be zero)
// loadPointerFieldValue: for addOffsetToPointer=true, also load the value if the final field is a pointer-typed field (rather than returning its address).
fun addFieldOffsetToScratchPointer(fieldoffset: UInt) {
if(fieldoffset==0u)
@@ -278,7 +279,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
if(addOffsetToPointer) {
asmgen.assignExpressionToVariable(pointer.startpointer, "P8ZP_SCRATCH_PTR", DataType.UWORD)
addFieldOffsetToScratchPointer(field.second.toUInt())
if(pointer.derefLast)
if(pointer.derefLast || (loadPointerFieldValue && field.first.isPointer))
updateScratchPointer()
return "P8ZP_SCRATCH_PTR" to 0u
} else {
@@ -328,6 +329,8 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
} else {
if(addOffsetToPointer) {
addFieldOffsetToScratchPointer(fieldinfo.second.toUInt())
if(loadPointerFieldValue && fieldinfo.first.isPointer)
updateScratchPointer()
return "P8ZP_SCRATCH_PTR" to 0u
} else
return "P8ZP_SCRATCH_PTR" to fieldinfo.second
@@ -359,7 +362,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
}
// save scaled index to W2, compute base (field-corrected) into P8ZP_SCRATCH_PTR, then add
asmgen.out(" lda P8ZP_SCRATCH_W1 | sta P8ZP_SCRATCH_W2 | lda P8ZP_SCRATCH_W1+1 | sta P8ZP_SCRATCH_W2+1")
val (basePtr, _) = deref(target.pointer, addOffsetToPointer = true)
val (basePtr, _) = deref(target.pointer, addOffsetToPointer = true, loadPointerFieldValue = true)
if(basePtr != "P8ZP_SCRATCH_PTR") {
// deref returned the original zp pointer variable; copy it so we don't mutate it
asmgen.out("""
@@ -452,7 +455,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
inplaceByteInc(target, amount)
} else {
if (target.dt.isByte) inplaceByteAdd(target, value)
else if (target.dt.isWord) inplaceWordAdd(target, value)
else if (target.dt.isWord || target.dt.isPointer) inplaceWordAdd(target, value)
else if (target.dt.isFloat) inplaceFloatAddOrMul(target, "FADD", value)
else if (target.dt.isLong) inplaceLongAdd(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
@@ -464,7 +467,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
inplaceByteDec(target, amount)
} else {
if (target.dt.isByte) inplaceByteSub(target, value)
else if (target.dt.isWord) inplaceWordSub(target, value)
else if (target.dt.isWord || target.dt.isPointer) inplaceWordSub(target, value)
else if (target.dt.isFloat) inplaceFloatSubOrDiv(target, "FSUB", value)
else if (target.dt.isLong) inplaceLongSub(target, value)
else throw AssemblyError("weird dt ${target.position}")
@@ -318,6 +318,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
codeGen.storeValueAtPointersLocation(inplaceInstrs, addressReg, fieldOffset, pointerDeref.type, false, oldvalueReg)
chunks = inplaceInstrs
} else if(array?.pointerderef != null) {
chunks = fallbackAssign(augAssign)
} else {
chunks = when (augAssign.operator) {
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
@@ -11,6 +11,7 @@ import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import prog8.code.core.*
import prog8.code.target.VMTarget
import prog8.parser.Prog8Keywords
/**
* This checks for naming conflicts, not for correct symbol references yet.
@@ -85,41 +86,8 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
super.visit(decl)
}
private val keywords = setOf(
"void",
"on",
"in",
"to",
"if",
"downto",
"else",
"step",
"call",
"goto",
"inline",
"struct",
"then",
"alias",
"defer",
"const",
"sizeof",
"as",
"return",
"break",
"continue",
"true",
"false",
"sub",
"asmsub",
"extsub",
"clobbers",
"while",
"do",
"until",
"repeat",
"unroll",
"when"
)
// the set of reserved keywords in the prog8 language
private val keywords: Set<String> by lazy { Prog8Keywords.reservedWords }
override fun visit(struct: StructDecl) {
if(struct.name in BuiltinFunctions)
@@ -131,7 +99,6 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
if(field.type.isArray && field.type.sub==BaseDataType.STRUCT_INSTANCE)
errors.err("arrays of struct instances are currently not supported as struct fields at '${field.name}'", struct.position)
}
super.visit(struct)
}
@@ -417,10 +417,16 @@ class AstPreprocessor(val program: Program,
val tgt = alias.target.targetStatement(program.builtinFunctions)
if(tgt==null) {
// The target may refer to an enum member that hasn't been desugared into consts yet.
// Defer the error in that case; a later iteration will resolve it after desugaring.
if(refersToNotYetDesugaredEnumMember(alias))
return noModifications
errors.undefined(alias.target.nameInSource, position = alias.target.position)
} else {
if(alias.alias == alias.target.nameInSource.first()) {
errors.err("alias loop", alias.position)
} else if(tgt is Enumeration) {
errors.err("cannot alias an enum '${alias.target.nameInSource.joinToString(".")}', alias individual members instead (e.g. ${alias.target.nameInSource.joinToString(".")}::Member)", alias.position)
} else if(tgt is Alias) {
var chainedAlias = alias
var chainedTargetName = alias.target
@@ -460,6 +466,23 @@ class AstPreprocessor(val program: Program,
return noModifications
}
private fun refersToNotYetDesugaredEnumMember(alias: Alias): Boolean {
val nameInSource = alias.target.nameInSource
if(nameInSource.isEmpty())
return false
val last = nameInSource.last()
if("::" !in last)
return false
val parts = last.split("::", limit = 2)
if(parts.size != 2 || parts[0].isEmpty() || parts[1].isEmpty())
return false
val enumName = parts[0]
val memberName = parts[1]
val enumPath = nameInSource.dropLast(1) + enumName
val enumNode = alias.definingScope.lookup(enumPath) as? Enumeration ?: return false
return enumNode.members.any { it.first == memberName }
}
override fun after(typecast: TypecastExpression, parent: Node): Iterable<AstModification> {
// convert all antlr names to structs (following struct aliases)
if(typecast.type.subTypeFromAntlr!=null) {
+55
View File
@@ -664,6 +664,61 @@ main {
}
test("alias to enum member works") {
val src = """
main {
enum Priority { LOW, NORMAL, HIGH, EXTREME }
alias myNormal = Priority::NORMAL
alias chainedNormal = myNormal
sub start() {
ubyte @shared value1 = Priority::NORMAL
ubyte @shared value2 = myNormal
ubyte @shared value3 = chainedNormal
}
}"""
val result = compileText(Cx16Target(), false, src, outputDir, writeAssembly = false)!!
val start = result.compilerAst.allBlocks.first { it.name == "main" }.statements.filterIsInstance<prog8.ast.statements.Subroutine>().first { it.name == "start" }
val assignments = start.statements.filterIsInstance<Assignment>()
val value1 = (assignments[0].value as? NumericLiteral)?.number
val value2 = (assignments[1].value as? NumericLiteral)?.number
val value3 = (assignments[2].value as? NumericLiteral)?.number
value1 shouldBe 1.0
value2 shouldBe 1.0
value3 shouldBe 1.0
}
test("alias to whole enum gives error") {
val src = """
main {
enum Priority { LOW, NORMAL, HIGH, EXTREME }
alias myEnum = Priority
sub start() {
}
}"""
val errors = ErrorReporterForTests()
compileText(Cx16Target(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "cannot alias an enum"
}
test("alias to nonexistent enum member gives undefined error") {
val src = """
main {
enum Priority { LOW, NORMAL, HIGH, EXTREME }
alias myBad = Priority::NONEXISTENT
sub start() {
}
}"""
val errors = ErrorReporterForTests()
compileText(Cx16Target(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "undefined symbol: Priority::NONEXISTENT"
}
test("double colon in name only allowed in desugared enum members 2") {
val src = """
main {
@@ -1718,6 +1718,39 @@ mkword {
errors.errors[1] shouldContain "builtin function cannot be redefined"
}
test("block names cannot be builtin functions or keywords") {
val src="""
main {
sub start() {
}
}
private {
sub foo() -> uword {
return 1
}
}
memory {
sub foo() -> uword {
return 1
}
}
mkword {
sub foo() -> uword {
return 2
}
}
"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), optimize=false, src, outputDir, errors=errors) shouldBe null
errors.errors.size shouldBe 3
errors.errors[0] shouldContain "name cannot be a keyword"
errors.errors[1] shouldContain "builtin function cannot be redefined"
errors.errors[2] shouldContain "builtin function cannot be redefined"
}
test("string and array multiplication require integer multiplicand") {
val src="""
main {
@@ -248,6 +248,116 @@ class TestExecution6502 : FunSpec({
machine.assertMemory(0x02, 1)
}
test("indexed pointer fields in structs") {
val src = $$"""
%option no_sysinit
%launcher none
%address $1000
%zeropage basicsafe
main {
struct Node {
bool flag
^^uword uptr
}
struct Wrapper {
bool z
^^Node node
}
^^Node @shared n = [true, 4000]
^^Node @shared inner = [true, 4500]
^^Wrapper @shared w = [false, 4000]
&ubyte poweroff = $f203
&uword slot_low = $0300
&uword slot_hi = $0302
uword index
sub start() {
n.uptr[3] = 9999
n.uptr[1] = 7777
n.uptr[9] = 5555
slot_low = n.uptr[3]
index = 1
n.uptr[index] = 1111
slot_hi = n.uptr[1]
w.node = inner
w.node.uptr[2] = 1234
poweroff = 1
}
}
""".trimIndent()
val compileResult = compileText(Cx16Target(), false, src, outputDir)
val machine = compileResult!!.simulate()
machine.assertMemory(0x0300, 9999 and 0xff)
machine.assertMemory(0x0301, 9999 shr 8)
machine.assertMemory(0x0302, 1111 and 0xff)
machine.assertMemory(0x0303, 1111 shr 8)
machine.assertMemory(4000+6, 9999 and 0xff)
machine.assertMemory(4000+7, 9999 shr 8)
machine.assertMemory(4000+2, 1111 and 0xff)
machine.assertMemory(4000+3, 1111 shr 8)
machine.assertMemory(4000+18, 5555 and 0xff)
machine.assertMemory(4000+19, 5555 shr 8)
machine.assertMemory(4500+4, 1234 and 0xff)
machine.assertMemory(4500+5, 1234 shr 8)
}
test("more struct pointer field operations") {
val src = $$"""
%option no_sysinit
%launcher none
%address $1000
%zeropage basicsafe
main {
struct Node {
bool flag
^^uword uptr
}
^^Node @shared n = [true, 4000]
^^Node @shared m = [true, 5000]
^^uword @shared normalptr = 4000
&ubyte poweroff = $f203
&uword n_after_swap = $0300
&uword m_after_swap = $0302
&uword n_after_add = $0304
&uword lsb_result = $0306
&uword msb_result = $0308
&uword idxinc_result = $030A
sub start() {
swap(n.uptr, m.uptr)
n_after_swap = n.uptr
m_after_swap = m.uptr
n.uptr += 2
n_after_add = n.uptr
n.uptr[3] = 100
n.uptr[3]++
idxinc_result = n.uptr[3]
lsb_result = lsb(normalptr)
msb_result = msb(normalptr)
poweroff = 1
}
}
""".trimIndent()
val compileResult = compileText(Cx16Target(), false, src, outputDir)
val machine = compileResult!!.simulate()
machine.assertMemory(0x0300, 5000 and 0xff)
machine.assertMemory(0x0301, 5000 shr 8)
machine.assertMemory(0x0302, 4000 and 0xff)
machine.assertMemory(0x0303, 4000 shr 8)
machine.assertMemory(0x0304, 5004 and 0xff)
machine.assertMemory(0x0305, 5004 shr 8)
machine.assertMemory(0x0306, 160)
machine.assertMemory(0x0308, 15)
machine.assertMemory(0x030A, 101)
machine.assertMemory(0x030B, 0)
machine.assertMemory(5010, 101)
machine.assertMemory(5011, 0)
}
test("serial output") {
val src = $$"""
%encoding iso
@@ -0,0 +1,23 @@
package prog8.parser
/**
* Provides the complete set of Prog8 reserved words, derived from the
* ANTLR grammar vocabulary so it never goes out of sync with the language.
*/
object Prog8Keywords {
val reservedWords: Set<String> by lazy {
val words = sortedSetOf<String>()
val vocab = Prog8ANTLRParser.VOCABULARY
for (i in 0..vocab.maxTokenType) {
val literal = vocab.getLiteralName(i) ?: continue
// literal is like "'for'" or "'^^'"; keep only word-like ones longer than 1 char
if (literal.length >= 2 && literal.startsWith("'") && literal.endsWith("'")) {
val word = literal.substring(1, literal.length - 1)
if (word.length > 1 && word.matches(Regex("[a-z][a-z_0-9]*"))) {
words.add(word)
}
}
}
words
}
}
+1 -1
View File
@@ -6,5 +6,5 @@ org.gradle.configuration-cache=false
org.gradle.jvmargs=-Xmx4g
kotlin.daemon.jvmargs=-Xmx4g
kotlin.code.style=official
version=14.0-SNAPSHOT
version=14.0-BETA1
testMaxHeap=8g