multi var declarations ubyte x,y,z

This commit is contained in:
Irmen de Jong 2023-12-08 22:08:17 +01:00
parent 6a639ce533
commit 1bdc427d73
13 changed files with 72 additions and 33 deletions

View File

@ -593,6 +593,9 @@ internal class AstChecker(private val program: Program,
}
override fun visit(decl: VarDecl) {
if(decl.names.size>1)
throw InternalCompilerException("vardecls with multiple names should have been converted into individual vardecls")
if(decl.datatype==DataType.LONG)
errors.err("integer overflow", decl.position)

View File

@ -1,12 +1,9 @@
package prog8.compiler.astprocessing
import prog8.ast.IFunctionCall
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.*
import prog8.ast.base.FatalAstException
import prog8.ast.base.SyntaxError
import prog8.ast.expressions.*
import prog8.ast.findParentNode
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
@ -150,6 +147,19 @@ class AstPreprocessor(val program: Program,
}
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if(decl.names.size>1) {
// note: the desugaring of a multi-variable vardecl has to be done here
// and not in CodeDesugarer, that one is too late (identifiers can't be found otherwise)
if(decl.datatype !in IntegerDatatypes)
errors.err("can only multi declare integer variables", decl.position)
if(errors.noErrors()) {
// desugar into individual vardecl per name.
return decl.desugarMultiDecl().map {
IAstModification.InsertAfter(decl, it, parent as IStatementContainer)
} + IAstModification.Remove(decl, parent as IStatementContainer)
}
}
val nextAssignment = decl.nextSibling() as? Assignment
if(nextAssignment!=null && nextAssignment.origin!=AssignmentOrigin.VARINIT) {
// check if it's a proper initializer assignment for the variable
@ -177,7 +187,7 @@ class AstPreprocessor(val program: Program,
private fun makeSplitArray(decl: VarDecl): Iterable<IAstModification> {
val splitDt = ArrayToElementTypes.getValue(decl.datatype)
val newDecl = VarDecl(
decl.type, decl.origin, splitDt, decl.zeropage, decl.arraysize, decl.name,
decl.type, decl.origin, splitDt, decl.zeropage, decl.arraysize, decl.name, emptyList(),
decl.value, true, decl.sharedWithAsm, true, decl.position
)
return listOf(IAstModification.ReplaceNode(decl, newDecl, decl.parent))

View File

@ -29,7 +29,7 @@ internal class BoolRemover(val program: Program) : AstWalker() {
if(newvalue.number!=0.0)
newvalue = NumericLiteral(DataType.UBYTE, 1.0, newvalue.position)
}
val ubyteDecl = VarDecl(decl.type, decl.origin, DataType.UBYTE, decl.zeropage, decl.arraysize, decl.name,
val ubyteDecl = VarDecl(decl.type, decl.origin, DataType.UBYTE, decl.zeropage, decl.arraysize, decl.name, emptyList(),
newvalue, decl.isArray, decl.sharedWithAsm, decl.splitArray, decl.position)
return listOf(IAstModification.ReplaceNode(decl, ubyteDecl, parent))
}
@ -46,7 +46,7 @@ internal class BoolRemover(val program: Program) : AstWalker() {
}.toTypedArray()
newarray = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_UB), convertedArray, decl.position)
}
val ubyteArrayDecl = VarDecl(decl.type, decl.origin, DataType.ARRAY_UB, decl.zeropage, decl.arraysize, decl.name,
val ubyteArrayDecl = VarDecl(decl.type, decl.origin, DataType.ARRAY_UB, decl.zeropage, decl.arraysize, decl.name, emptyList(),
newarray, true, decl.sharedWithAsm, decl.splitArray, decl.position)
return listOf(IAstModification.ReplaceNode(decl, ubyteArrayDecl, parent))
}

View File

@ -155,6 +155,7 @@ internal class StatementReorderer(
it.zeropage,
null,
it.name,
emptyList(),
null,
false,
it.sharedWithAsm,

View File

@ -112,7 +112,7 @@ class TestMemory: FunSpec({
}
fun createTestProgramForMemoryRefViaVar(address: UInt, vartype: VarDeclType): AssignTarget {
val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
@ -150,7 +150,7 @@ class TestMemory: FunSpec({
}
test("regular variable not in mapped IO ram on C64") {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), null, false, false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@ -162,7 +162,7 @@ class TestMemory: FunSpec({
test("memory mapped variable not in mapped IO ram on C64") {
val address = 0x1000u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@ -174,7 +174,7 @@ class TestMemory: FunSpec({
test("memory mapped variable in mapped IO ram on C64") {
val address = 0xd020u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@ -185,7 +185,7 @@ class TestMemory: FunSpec({
}
test("array not in mapped IO ram") {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), null, false, false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
@ -198,7 +198,7 @@ class TestMemory: FunSpec({
test("memory mapped array not in mapped IO ram") {
val address = 0x1000u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
@ -211,7 +211,7 @@ class TestMemory: FunSpec({
test("memory mapped array in mapped IO ram") {
val address = 0xd800u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)

View File

@ -46,8 +46,8 @@ class TestAsmGenSymbols: StringSpec({
}
*/
val varInSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "localvar", NumericLiteral.optimalInteger(1234, Position.DUMMY), false, false, false, Position.DUMMY)
val var2InSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "tgt", null, false, false, false, Position.DUMMY)
val varInSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "localvar", emptyList(), NumericLiteral.optimalInteger(1234, Position.DUMMY), false, false, false, Position.DUMMY)
val var2InSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "tgt", emptyList(), null, false, false, false, Position.DUMMY)
val labelInSub = Label("locallabel", Position.DUMMY)
val tgt = AssignTarget(IdentifierReference(listOf("tgt"), Position.DUMMY), null, null, Position.DUMMY)
@ -63,7 +63,7 @@ class TestAsmGenSymbols: StringSpec({
val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8)
val subroutine = Subroutine("start", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, statements, Position.DUMMY)
val labelInBlock = Label("label_outside", Position.DUMMY)
val varInBlock = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", null, false, false, false, Position.DUMMY)
val varInBlock = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", emptyList(),null, false, false, false, Position.DUMMY)
val block = Block("main", null, mutableListOf(labelInBlock, varInBlock, subroutine), false, Position.DUMMY)
val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test"))

View File

@ -86,7 +86,7 @@ class Program(val name: String,
fun addNewInternedStringvar(string: StringLiteral): Pair<List<String>, VarDecl> {
val varName = "string_${internedStringsBlock.statements.size}"
val decl = VarDecl(
VarDeclType.VAR, VarDeclOrigin.STRINGLITERAL, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, varName, string,
VarDeclType.VAR, VarDeclOrigin.STRINGLITERAL, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, varName, emptyList(), string,
isArray = false, sharedWithAsm = false, splitArray = false, position = string.position
)
internedStringsBlock.statements.add(decl)

View File

@ -237,7 +237,10 @@ private fun Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
}
}
}
AsmSubroutineParameter(vardecl.varname.text, datatype, registerorpair, statusregister, toPosition())
val identifiers = vardecl.identifier()
if(identifiers.size>1)
throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
AsmSubroutineParameter(identifiers[0].NAME().text, datatype, registerorpair, statusregister, toPosition())
}
private fun Functioncall_stmtContext.toAst(): Statement {
@ -303,7 +306,11 @@ private fun Sub_paramsContext.toAst(): List<SubroutineParameter> =
var datatype = it.datatype()?.toAst() ?: DataType.UNDEFINED
if(it.ARRAYSIG()!=null || it.arrayindex()!=null)
datatype = ElementToArrayTypes.getValue(datatype)
SubroutineParameter(it.varname.text, datatype, it.toPosition())
val identifiers = it.identifier()
if(identifiers.size>1)
throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
SubroutineParameter(identifiers[0].NAME().text, datatype, it.toPosition())
}
private fun Assign_targetContext.toAst() : AssignTarget {
@ -644,12 +651,16 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
options.ZEROPAGE().isNotEmpty() -> ZeropageWish.PREFER_ZEROPAGE
else -> ZeropageWish.DONTCARE
}
val identifiers = identifier()
val name = if(identifiers.size==1) identifiers[0].NAME().text else "<multiple>"
return VarDecl(
type, VarDeclOrigin.USERCODE,
datatype()?.toAst() ?: DataType.UNDEFINED,
zp,
arrayindex()?.toAst(),
varname.text,
name,
identifiers.map { it.NAME().text },
value,
ARRAYSIG() != null || arrayindex() != null,
options.SHARED().isNotEmpty(),

View File

@ -208,6 +208,7 @@ class VarDecl(val type: VarDeclType,
var zeropage: ZeropageWish,
var arraysize: ArrayIndex?,
override val name: String,
val names: List<String>,
var value: Expression?,
val isArray: Boolean,
val sharedWithAsm: Boolean,
@ -222,7 +223,7 @@ class VarDecl(val type: VarDeclType,
private var autoHeapValueSequenceNumber = 0
fun fromParameter(param: SubroutineParameter): VarDecl {
return VarDecl(VarDeclType.VAR, VarDeclOrigin.SUBROUTINEPARAM, param.type, ZeropageWish.DONTCARE, null, param.name, null,
return VarDecl(VarDeclType.VAR, VarDeclOrigin.SUBROUTINEPARAM, param.type, ZeropageWish.DONTCARE, null, param.name, emptyList(), null,
isArray = false,
sharedWithAsm = false,
splitArray = false,
@ -235,7 +236,7 @@ class VarDecl(val type: VarDeclType,
val arrayDt = array.type.getOrElse { throw FatalAstException("unknown dt") }
val declaredType = ArrayToElementTypes.getValue(arrayDt)
val arraysize = ArrayIndex.forArray(array)
return VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, array,
return VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, emptyList(), array,
isArray = true, sharedWithAsm = false, splitArray = splitArray, position = array.position)
}
}
@ -297,7 +298,9 @@ class VarDecl(val type: VarDeclType,
}
override fun copy(): VarDecl {
val copy = VarDecl(type, origin, declaredDatatype, zeropage, arraysize?.copy(), name, value?.copy(),
if(names.size>1)
throw FatalAstException("should not copy a vardecl that still has multiple names")
val copy = VarDecl(type, origin, declaredDatatype, zeropage, arraysize?.copy(), name, emptyList(), value?.copy(),
isArray, sharedWithAsm, splitArray, position)
copy.allowInitializeWithZero = this.allowInitializeWithZero
return copy
@ -312,6 +315,15 @@ class VarDecl(val type: VarDeclType,
override fun referencesIdentifier(nameInSource: List<String>): Boolean =
value?.referencesIdentifier(nameInSource)==true ||
this.arraysize?.referencesIdentifier(nameInSource)==true
fun desugarMultiDecl(): List<VarDecl> {
return names.map {
val copy = VarDecl(type, origin, declaredDatatype, zeropage, arraysize?.copy(), it, emptyList(), value?.copy(),
isArray, sharedWithAsm, splitArray, position)
copy.allowInitializeWithZero = this.allowInitializeWithZero
copy
}
}
}
class ArrayIndex(var indexExpr: Expression,
@ -350,8 +362,7 @@ class ArrayIndex(var indexExpr: Expression,
enum class AssignmentOrigin {
USERCODE,
VARINIT,
OPTIMIZER,
ASMGEN
OPTIMIZER
}

View File

@ -333,12 +333,17 @@ The syntax is::
<datatype> [ @type-tag ] <variable name> [ = <initial value> ]
where type-tag is one of the tags mentioned earlier.
For boolean and numeric variables, you can actually declare them in one go by listing the names in a comma separated list.
Type tags, and the optional initialization value, are applied equally to all variables in such a list.
Various examples::
word thing = 0
byte counter = len([1, 2, 3]) * 20
byte age = 2018 - 1974
float wallet = 55.25
ubyte x,y,z ; declare three ubyte variables x y and z
str name = "my name is Alice"
uword address = &counter
byte[] values = [11, 22, 33, 44, 55]

View File

@ -78,7 +78,6 @@ What if we were to re-introduce Structs in prog8? Some thoughts:
Other language/syntax features to think about
---------------------------------------------
- declare multiple variables `ubyte x,y,z` (if init value present, all get that init value)
- chained comparisons `10<x<20` , `x==y==z` (desugars to `10<x and x<20`, `x==y and y==z`)
- chained comparisons `10<x<20` , `x==y==z` (desugars to `10<x and x<20`, `x==y and y==z`) BUT this changes the semantics of what it is right now ! (x==(y==z) 0> x==true)
- postincrdecr as expression, preincrdecr expression (`y = x++`, `y = ++x`) .... is this even possible, expression with side effects like this?
- negative array index to refer to an element from the end of the array. Python `[-1]` or Raku syntax `[\*-1]` , `[\*/2]` .... \*=size of the array

View File

@ -5,11 +5,8 @@ main {
const ubyte VAL = 11
sub start() {
uword w
ubyte x
ubyte y
ubyte z
ubyte @zp x,y,z = 99
w = x = y = z = 99+VAL
txt.print_ub(x)
txt.spc()
txt.print_ub(y)
@ -18,5 +15,7 @@ main {
txt.spc()
txt.print_uw(w)
txt.nl()
if x==y==z
x++
}
}

View File

@ -132,7 +132,7 @@ directive :
directivearg : stringliteral | identifier | integerliteral ;
vardecl: datatype (arrayindex | ARRAYSIG)? decloptions varname=identifier ;
vardecl: datatype (arrayindex | ARRAYSIG)? decloptions identifier (',' identifier)* ;
decloptions: (SHARED | ZEROPAGE | ZEROPAGEREQUIRE | SPLIT)* ;