split array and string literal classes

This commit is contained in:
Irmen de Jong 2019-08-13 03:00:17 +02:00
parent 9fb9bcfebd
commit b64d611e02
22 changed files with 349 additions and 413 deletions

View File

@ -77,7 +77,7 @@ private fun compileMain(args: Array<String>) {
val event = watchservice.take() val event = watchservice.take()
for(changed in event.pollEvents()) { for(changed in event.pollEvents()) {
val changedPath = changed.context() as Path val changedPath = changed.context() as Path
println(" change detected: ${changedPath}") println(" change detected: $changedPath")
} }
event.reset() event.reset()
println("\u001b[H\u001b[2J") // clear the screen println("\u001b[H\u001b[2J") // clear the screen

View File

@ -132,7 +132,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) { for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
val reg = val reg =
when { when {
true==param.second.stack -> "stack" param.second.stack -> "stack"
param.second.registerOrPair!=null -> param.second.registerOrPair.toString() param.second.registerOrPair!=null -> param.second.registerOrPair.toString()
param.second.statusflag!=null -> param.second.statusflag.toString() param.second.statusflag!=null -> param.second.statusflag.toString()
else -> "?????1" else -> "?????1"
@ -256,15 +256,12 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
output(numLiteral.number.toString()) output(numLiteral.number.toString())
} }
override fun visit(refLiteral: ReferenceLiteralValue) { override fun visit(string: StringLiteralValue) {
when { output("\"${escape(string.value)}\"")
refLiteral.isString -> output("\"${escape(refLiteral.str!!)}\"") }
refLiteral.isArray -> {
if(refLiteral.array!=null) { override fun visit(array: ArrayLiteralValue) {
outputListMembers(refLiteral.array.asSequence(), '[', ']') outputListMembers(array.value.asSequence(), '[', ']')
}
}
}
} }
private fun outputListMembers(array: Sequence<Expression>, openchar: Char, closechar: Char) { private fun outputListMembers(array: Sequence<Expression>, openchar: Char, closechar: Char) {

View File

@ -429,7 +429,7 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
else -> throw FatalAstException("invalid datatype for numeric literal") else -> throw FatalAstException("invalid datatype for numeric literal")
} }
litval.floatliteral()!=null -> NumericLiteralValue(DataType.FLOAT, litval.floatliteral().toAst(), litval.toPosition()) litval.floatliteral()!=null -> NumericLiteralValue(DataType.FLOAT, litval.floatliteral().toAst(), litval.toPosition())
litval.stringliteral()!=null -> ReferenceLiteralValue(DataType.STR, unescape(litval.stringliteral().text, litval.toPosition()), position = litval.toPosition()) litval.stringliteral()!=null -> StringLiteralValue(DataType.STR, unescape(litval.stringliteral().text, litval.toPosition()), position = litval.toPosition())
litval.charliteral()!=null -> { litval.charliteral()!=null -> {
try { try {
NumericLiteralValue(DataType.UBYTE, Petscii.encodePetscii(unescape(litval.charliteral().text, litval.toPosition()), true)[0], litval.toPosition()) NumericLiteralValue(DataType.UBYTE, Petscii.encodePetscii(unescape(litval.charliteral().text, litval.toPosition()), true)[0], litval.toPosition())
@ -438,10 +438,10 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
} }
} }
litval.arrayliteral()!=null -> { litval.arrayliteral()!=null -> {
val array = litval.arrayliteral()?.toAst() val array = litval.arrayliteral().toAst()
// the actual type of the arraysize can not yet be determined here (missing namespace & heap) // the actual type of the arraysize can not yet be determined here (missing namespace & heap)
// the ConstantFold takes care of that and converts the type if needed. // the ConstantFold takes care of that and converts the type if needed.
ReferenceLiteralValue(DataType.ARRAY_UB, array = array, position = litval.toPosition()) ArrayLiteralValue(DataType.ARRAY_UB, array, position = litval.toPosition())
} }
litval.structliteral()!=null -> { litval.structliteral()!=null -> {
val values = litval.structliteral().expression().map { it.toAst() } val values = litval.structliteral().expression().map { it.toAst() }
@ -596,10 +596,10 @@ private fun prog8Parser.WhenstmtContext.toAst(): WhenStatement {
private fun prog8Parser.When_choiceContext.toAst(): WhenChoice { private fun prog8Parser.When_choiceContext.toAst(): WhenChoice {
val values = expression_list()?.toAst() val values = expression_list()?.toAst()
val stmt = statement()?.toAst() val stmt = statement()?.toAst()
val stmt_block = statement_block()?.toAst()?.toMutableList() ?: mutableListOf() val stmtBlock = statement_block()?.toAst()?.toMutableList() ?: mutableListOf()
if(stmt!=null) if(stmt!=null)
stmt_block.add(stmt) stmtBlock.add(stmt)
val scope = AnonymousScope(stmt_block, toPosition()) val scope = AnonymousScope(stmtBlock, toPosition())
return WhenChoice(values, scope, toPosition()) return WhenChoice(values, scope, toPosition())
} }

View File

@ -406,119 +406,98 @@ class StructLiteralValue(var values: List<Expression>,
} }
} }
class ReferenceLiteralValue(val type: DataType, // only reference types allowed here class StringLiteralValue(val type: DataType, // only string types
val str: String? = null, val value: String,
val array: Array<Expression>? = null, initHeapId: Int? =null,
initHeapId: Int? =null, override val position: Position) : Expression() {
override val position: Position) : Expression() {
override lateinit var parent: Node override lateinit var parent: Node
override fun referencesIdentifiers(vararg name: String) = array?.any { it.referencesIdentifiers(*name) } ?: false
val isString = type in StringDatatypes
val isArray = type in ArrayDatatypes
var heapId = initHeapId var heapId = initHeapId
private set private set
init { override fun linkParents(parent: Node) {
when(type){ this.parent = parent
in StringDatatypes ->
if(str==null) throw FatalAstException("literal value missing strvalue/heapId")
in ArrayDatatypes ->
if(array==null) throw FatalAstException("literal value missing arrayvalue/heapId")
else -> throw FatalAstException("invalid type $type")
}
if(array==null && str==null)
throw FatalAstException("literal ref value without actual value")
} }
override fun referencesIdentifiers(vararg name: String) = false
override fun constValue(program: Program): NumericLiteralValue? = null
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun toString(): String = "'${escape(value)}'"
override fun inferType(program: Program) = type
operator fun compareTo(other: StringLiteralValue): Int = value.compareTo(other.value)
override fun hashCode(): Int = Objects.hash(value, type)
override fun equals(other: Any?): Boolean {
if(other==null || other !is StringLiteralValue)
return false
return value==other.value && type==other.type
}
fun addToHeap(heap: HeapValues) {
if (heapId != null)
return
else
heapId = heap.addString(type, value)
}
}
class ArrayLiteralValue(val type: DataType, // only array types
val value: Array<Expression>,
initHeapId: Int? =null,
override val position: Position) : Expression() {
override lateinit var parent: Node
var heapId = initHeapId
private set
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
array?.forEach {it.linkParents(this)} value.forEach {it.linkParents(this)}
} }
override fun referencesIdentifiers(vararg name: String) = value.any { it.referencesIdentifiers(*name) }
override fun constValue(program: Program): NumericLiteralValue? { override fun constValue(program: Program): NumericLiteralValue? = null
// note that we can't handle arrays that only contain constant numbers here anymore
// so they're not treated as constants anymore
return null
}
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun toString(): String = "$value"
override fun toString(): String {
val valueStr = when(type) {
in StringDatatypes -> "'${escape(str!!)}'"
in ArrayDatatypes -> "$array"
else -> throw FatalAstException("weird ref type")
}
return "RefValueLit($type, $valueStr)"
}
override fun inferType(program: Program) = type override fun inferType(program: Program) = type
operator fun compareTo(other: ArrayLiteralValue): Int = throw ExpressionError("cannot order compare arrays", position)
override fun hashCode(): Int = Objects.hash(str, array, type) override fun hashCode(): Int = Objects.hash(value, type)
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if(other==null || other !is ReferenceLiteralValue) if(other==null || other !is ArrayLiteralValue)
return false return false
if(isArray && other.isArray) return type==other.type && value.contentEquals(other.value)
return array!!.contentEquals(other.array!!) && heapId==other.heapId
if(isString && other.isString)
return str==other.str && heapId==other.heapId
if(type!=other.type)
return false
return compareTo(other) == 0
} }
operator fun compareTo(other: ReferenceLiteralValue): Int { fun cast(targettype: DataType): ArrayLiteralValue? {
throw ExpressionError("cannot order compare type $type with ${other.type}", other.position)
}
fun cast(targettype: DataType): ReferenceLiteralValue? {
if(type==targettype) if(type==targettype)
return this return this
when(type) { if(targettype in ArrayDatatypes) {
in StringDatatypes -> { val elementType = ArrayElementTypes.getValue(targettype)
if(targettype in StringDatatypes) val castArray = value.map{
return ReferenceLiteralValue(targettype, str, position = position) val num = it as? NumericLiteralValue
} if(num==null) {
in ArrayDatatypes -> { // an array of UWORDs could possibly also contain AddressOfs
if(targettype in ArrayDatatypes) { if (elementType != DataType.UWORD || it !is AddressOf)
val elementType = ArrayElementTypes.getValue(targettype) throw FatalAstException("weird array element $it")
val castArray = array!!.map{ it
val num = it as? NumericLiteralValue } else {
if(num==null) { try {
// an array of UWORDs could possibly also contain AddressOfs num.cast(elementType)
if (elementType != DataType.UWORD || it !is AddressOf) } catch(x: ExpressionError) {
throw FatalAstException("weird array element $it") return null
it }
} else {
try {
num.cast(elementType)
} catch(x: ExpressionError) {
return null
}
}
}.toTypedArray()
return ReferenceLiteralValue(targettype, null, array=castArray, position = position)
} }
} }.toTypedArray()
else -> {} return ArrayLiteralValue(targettype, castArray, position = position)
} }
return null // invalid type conversion from $this to $targettype return null // invalid type conversion from $this to $targettype
} }
fun addToHeap(heap: HeapValues) { fun addToHeap(heap: HeapValues) {
if (heapId != null) return if (heapId != null)
if (str != null) { return
heapId = heap.addString(type, str) else {
} if(value.any {it is AddressOf }) {
else if (array!=null) { val intArrayWithAddressOfs = value.map {
if(array.any {it is AddressOf }) {
val intArrayWithAddressOfs = array.map {
when (it) { when (it) {
is AddressOf -> IntegerOrAddressOf(null, it) is AddressOf -> IntegerOrAddressOf(null, it)
is NumericLiteralValue -> IntegerOrAddressOf(it.number.toInt(), null) is NumericLiteralValue -> IntegerOrAddressOf(it.number.toInt(), null)
@ -527,7 +506,7 @@ class ReferenceLiteralValue(val type: DataType, // only reference types allo
} }
heapId = heap.addIntegerArray(type, intArrayWithAddressOfs.toTypedArray()) heapId = heap.addIntegerArray(type, intArrayWithAddressOfs.toTypedArray())
} else { } else {
val valuesInArray = array.map { (it as? NumericLiteralValue)?.number } val valuesInArray = value.map { (it as? NumericLiteralValue)?.number }
if(null !in valuesInArray) { if(null !in valuesInArray) {
heapId = if (type == DataType.ARRAY_F) { heapId = if (type == DataType.ARRAY_F) {
val doubleArray = valuesInArray.map { it!!.toDouble() }.toDoubleArray() val doubleArray = valuesInArray.map { it!!.toDouble() }.toDoubleArray()
@ -588,12 +567,12 @@ class RangeExpr(var from: Expression,
fun toConstantIntegerRange(): IntProgression? { fun toConstantIntegerRange(): IntProgression? {
val fromVal: Int val fromVal: Int
val toVal: Int val toVal: Int
val fromRlv = from as? ReferenceLiteralValue val fromString = from as? StringLiteralValue
val toRlv = to as? ReferenceLiteralValue val toString = to as? StringLiteralValue
if(fromRlv!=null && fromRlv.isString && toRlv!=null && toRlv.isString) { if(fromString!=null && toString!=null ) {
// string range -> int range over petscii values // string range -> int range over petscii values
fromVal = Petscii.encodePetscii(fromRlv.str!!, true)[0].toInt() fromVal = Petscii.encodePetscii(fromString.value, true)[0].toInt()
toVal = Petscii.encodePetscii(toRlv.str!!, true)[0].toInt() toVal = Petscii.encodePetscii(toString.value, true)[0].toInt()
} else { } else {
val fromLv = from as? NumericLiteralValue val fromLv = from as? NumericLiteralValue
val toLv = to as? NumericLiteralValue val toLv = to as? NumericLiteralValue
@ -689,17 +668,11 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
val value = (node as? VarDecl)?.value ?: throw FatalAstException("requires a reference value") val value = (node as? VarDecl)?.value ?: throw FatalAstException("requires a reference value")
return when (value) { return when (value) {
is IdentifierReference -> value.heapId(namespace) is IdentifierReference -> value.heapId(namespace)
is ReferenceLiteralValue -> value.heapId ?: throw FatalAstException("refLv is not on the heap: $value") is StringLiteralValue -> value.heapId ?: throw FatalAstException("string is not on the heap: $value")
is ArrayLiteralValue -> value.heapId ?: throw FatalAstException("array is not on the heap: $value")
else -> throw FatalAstException("requires a reference value") else -> throw FatalAstException("requires a reference value")
} }
} }
fun withPrefixedName(nameprefix: String): IdentifierReference {
val prefixed = nameInSource.dropLast(1) + listOf(nameprefix+nameInSource.last())
val new = IdentifierReference(prefixed, position)
new.parent = parent
return new
}
} }
class FunctionCall(override var target: IdentifierReference, class FunctionCall(override var target: IdentifierReference,

View File

@ -7,7 +7,6 @@ import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.compiler.CompilationOptions import prog8.compiler.CompilationOptions
import prog8.compiler.HeapValues
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_POSITIVE import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_POSITIVE
import prog8.functions.BuiltinFunctions import prog8.functions.BuiltinFunctions
@ -526,14 +525,12 @@ internal class AstChecker(private val program: Program,
} }
when(decl.value) { when(decl.value) {
is RangeExpr -> throw FatalAstException("range expression should have been converted to a true array value") is RangeExpr -> throw FatalAstException("range expression should have been converted to a true array value")
is ReferenceLiteralValue -> { is StringLiteralValue -> {
val arraySpec = decl.arraysize ?: ( checkValueTypeAndRangeString(decl.datatype, decl.value as StringLiteralValue)
if((decl.value as ReferenceLiteralValue).isArray) }
ArrayIndex.forArray(decl.value as ReferenceLiteralValue, program.heap) is ArrayLiteralValue -> {
else val arraySpec = decl.arraysize ?: ArrayIndex.forArray(decl.value as ArrayLiteralValue)
ArrayIndex(NumericLiteralValue.optimalInteger(-2, decl.position), decl.position) checkValueTypeAndRangeArray(decl.datatype, decl.struct, arraySpec, decl.value as ArrayLiteralValue)
)
checkValueTypeAndRange(decl.datatype, decl.struct, arraySpec, decl.value as ReferenceLiteralValue, program.heap)
} }
is NumericLiteralValue -> { is NumericLiteralValue -> {
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue) checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue)
@ -682,30 +679,24 @@ internal class AstChecker(private val program: Program,
checkResult.add(NameError("included file not found: $filename", directive.position)) checkResult.add(NameError("included file not found: $filename", directive.position))
} }
override fun visit(refLiteral: ReferenceLiteralValue) { override fun visit(array: ArrayLiteralValue) {
if(!compilerOptions.floats && refLiteral.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) { if(!compilerOptions.floats && array.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
checkResult.add(SyntaxError("floating point used, but that is not enabled via options", refLiteral.position)) checkResult.add(SyntaxError("floating point used, but that is not enabled via options", array.position))
} }
val arrayspec = val arrayspec = ArrayIndex.forArray(array)
if(refLiteral.isArray) checkValueTypeAndRangeArray(array.type, null, arrayspec, array)
ArrayIndex.forArray(refLiteral, program.heap)
else
ArrayIndex(NumericLiteralValue.optimalInteger(-3, refLiteral.position), refLiteral.position)
checkValueTypeAndRange(refLiteral.type, null, arrayspec, refLiteral, program.heap)
super.visit(refLiteral) super.visit(array)
when(refLiteral.type) { if(array.heapId==null)
in StringDatatypes -> { throw FatalAstException("array should have been moved to heap at ${array.position}")
if(refLiteral.heapId==null) }
throw FatalAstException("string should have been moved to heap at ${refLiteral.position}")
} override fun visit(string: StringLiteralValue) {
in ArrayDatatypes -> { checkValueTypeAndRangeString(string.type, string)
if(refLiteral.heapId==null) super.visit(string)
throw FatalAstException("array should have been moved to heap at ${refLiteral.position}") if(string.heapId==null)
} throw FatalAstException("string should have been moved to heap at ${string.position}")
else -> {}
}
} }
override fun visit(expr: PrefixExpression) { override fun visit(expr: PrefixExpression) {
@ -956,9 +947,9 @@ internal class AstChecker(private val program: Program,
if(index!=null && (index<0 || index>=arraysize)) if(index!=null && (index<0 || index>=arraysize))
checkResult.add(ExpressionError("array index out of bounds", arrayIndexedExpression.arrayspec.position)) checkResult.add(ExpressionError("array index out of bounds", arrayIndexedExpression.arrayspec.position))
} else if(target.datatype in StringDatatypes) { } else if(target.datatype in StringDatatypes) {
if(target.value is ReferenceLiteralValue) { if(target.value is StringLiteralValue) {
// check string lengths for non-memory mapped strings // check string lengths for non-memory mapped strings
val heapId = (target.value as ReferenceLiteralValue).heapId!! val heapId = (target.value as StringLiteralValue).heapId!!
val stringLen = program.heap.get(heapId).str!!.length val stringLen = program.heap.get(heapId).str!!.length
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt() val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
if (index != null && (index < 0 || index >= stringLen)) if (index != null && (index < 0 || index >= stringLen))
@ -1037,26 +1028,37 @@ internal class AstChecker(private val program: Program,
return null return null
} }
private fun checkValueTypeAndRange(targetDt: DataType, struct: StructDecl?, private fun checkValueTypeAndRangeString(targetDt: DataType, value: StringLiteralValue) : Boolean {
arrayspec: ArrayIndex, value: ReferenceLiteralValue, heap: HeapValues) : Boolean { fun err(msg: String): Boolean {
checkResult.add(ExpressionError(msg, value.position))
return false
}
return when (targetDt) {
in StringDatatypes -> {
return if (value.value.length > 255)
err("string length must be 0-255")
else
true
}
else -> false
}
}
private fun checkValueTypeAndRangeArray(targetDt: DataType, struct: StructDecl?,
arrayspec: ArrayIndex, value: ArrayLiteralValue) : Boolean {
fun err(msg: String) : Boolean { fun err(msg: String) : Boolean {
checkResult.add(ExpressionError(msg, value.position)) checkResult.add(ExpressionError(msg, value.position))
return false return false
} }
when (targetDt) { when (targetDt) {
in StringDatatypes -> { in StringDatatypes -> return err("string value expected")
if(!value.isString)
return err("string value expected")
if (value.str!!.length > 255)
return err("string length must be 0-255")
}
DataType.ARRAY_UB, DataType.ARRAY_B -> { DataType.ARRAY_UB, DataType.ARRAY_B -> {
// value may be either a single byte, or a byte arraysize (of all constant values), or a range // value may be either a single byte, or a byte arraysize (of all constant values), or a range
if(value.type==targetDt) { if(value.type==targetDt) {
if(!checkArrayValues(value, targetDt)) if(!checkArrayValues(value, targetDt))
return false return false
val arraySpecSize = arrayspec.size() val arraySpecSize = arrayspec.size()
val arraySize = value.array?.size ?: heap.get(value.heapId!!).arraysize val arraySize = value.value.size
if(arraySpecSize!=null && arraySpecSize>0) { if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize<1 || arraySpecSize>256) if(arraySpecSize<1 || arraySpecSize>256)
return err("byte array length must be 1-256") return err("byte array length must be 1-256")
@ -1078,7 +1080,7 @@ internal class AstChecker(private val program: Program,
if(!checkArrayValues(value, targetDt)) if(!checkArrayValues(value, targetDt))
return false return false
val arraySpecSize = arrayspec.size() val arraySpecSize = arrayspec.size()
val arraySize = value.array?.size ?: heap.get(value.heapId!!).arraysize val arraySize = value.value.size
if(arraySpecSize!=null && arraySpecSize>0) { if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize<1 || arraySpecSize>128) if(arraySpecSize<1 || arraySpecSize>128)
return err("word array length must be 1-128") return err("word array length must be 1-128")
@ -1099,7 +1101,7 @@ internal class AstChecker(private val program: Program,
if(value.type==targetDt) { if(value.type==targetDt) {
if(!checkArrayValues(value, targetDt)) if(!checkArrayValues(value, targetDt))
return false return false
val arraySize = value.array?.size ?: heap.get(value.heapId!!).doubleArray!!.size val arraySize = value.value.size
val arraySpecSize = arrayspec.size() val arraySpecSize = arrayspec.size()
if(arraySpecSize!=null && arraySpecSize>0) { if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize < 1 || arraySpecSize>51) if(arraySpecSize < 1 || arraySpecSize>51)
@ -1114,10 +1116,7 @@ internal class AstChecker(private val program: Program,
return err("invalid float array size, must be 1-51") return err("invalid float array size, must be 1-51")
// check if the floating point values are all within range // check if the floating point values are all within range
val doubles = if(value.array!=null) val doubles = value.value.map {it.constValue(program)?.number!!.toDouble()}.toDoubleArray()
value.array.map {it.constValue(program)?.number!!.toDouble()}.toDoubleArray()
else
heap.get(value.heapId!!).doubleArray!!
if(doubles.any { it < FLOAT_MAX_NEGATIVE || it> FLOAT_MAX_POSITIVE }) if(doubles.any { it < FLOAT_MAX_NEGATIVE || it> FLOAT_MAX_POSITIVE })
return err("floating point value overflow") return err("floating point value overflow")
return true return true
@ -1126,9 +1125,9 @@ internal class AstChecker(private val program: Program,
} }
DataType.STRUCT -> { DataType.STRUCT -> {
if(value.type in ArrayDatatypes) { if(value.type in ArrayDatatypes) {
if(value.array!!.size != struct!!.numberOfElements) if(value.value.size != struct!!.numberOfElements)
return err("number of values is not the same as the number of members in the struct") return err("number of values is not the same as the number of members in the struct")
for(elt in value.array.zip(struct.statements)) { for(elt in value.value.zip(struct.statements)) {
val vardecl = elt.second as VarDecl val vardecl = elt.second as VarDecl
val valuetype = elt.first.inferType(program)!! val valuetype = elt.first.inferType(program)!!
if (!(valuetype isAssignableTo vardecl.datatype)) { if (!(valuetype isAssignableTo vardecl.datatype)) {
@ -1142,7 +1141,6 @@ internal class AstChecker(private val program: Program,
} }
else -> return false else -> return false
} }
return true
} }
private fun checkValueTypeAndRange(targetDt: DataType, value: NumericLiteralValue) : Boolean { private fun checkValueTypeAndRange(targetDt: DataType, value: NumericLiteralValue) : Boolean {
@ -1189,10 +1187,10 @@ internal class AstChecker(private val program: Program,
return true return true
} }
private fun checkArrayValues(value: ReferenceLiteralValue, type: DataType): Boolean { private fun checkArrayValues(value: ArrayLiteralValue, type: DataType): Boolean {
if(value.isArray && value.heapId==null) { if(value.heapId==null) {
// hmm weird, array literal that hasn't been moved to the heap yet? // hmm weird, array literal that hasn't been moved to the heap yet?
val array = value.array!!.map { it.constValue(program)!! } val array = value.value.map { it.constValue(program)!! }
val correct: Boolean val correct: Boolean
when(type) { when(type) {
DataType.ARRAY_UB -> { DataType.ARRAY_UB -> {

View File

@ -247,75 +247,83 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
return super.visit(returnStmt) return super.visit(returnStmt)
} }
override fun visit(refLiteral: ReferenceLiteralValue): Expression { override fun visit(arrayLiteral: ArrayLiteralValue): Expression {
val litval = super.visit(refLiteral) val array = super.visit(arrayLiteral)
if(litval is ReferenceLiteralValue) { if(array is ArrayLiteralValue) {
val vardecl = litval.parent as? VarDecl val vardecl = array.parent as? VarDecl
if (litval.isString) { return if (vardecl!=null) {
// intern the string; move it into the heap fixupArrayDatatype(array, vardecl, program.heap)
if (litval.str!!.length !in 1..255) } else {
checkResult.add(ExpressionError("string literal length must be between 1 and 255", litval.position)) // fix the datatype of the array (also on the heap) to the 'biggest' datatype in the array
else { // (we don't know the desired datatype here exactly so we guess)
litval.addToHeap(program.heap) val datatype = determineArrayDt(array.value)
} val litval2 = array.cast(datatype)!!
return if(vardecl!=null) litval2.parent = array.parent
litval // finally, replace the literal array by a identifier reference.
else makeIdentifierFromRefLv(litval2)
makeIdentifierFromRefLv(litval) // replace the literal string by a identifier reference.
} else if (litval.isArray) {
if (vardecl!=null) {
return fixupArrayDatatype(litval, vardecl, program.heap)
} else {
// fix the datatype of the array (also on the heap) to the 'biggest' datatype in the array
// (we don't know the desired datatype here exactly so we guess)
val datatype = determineArrayDt(litval.array!!) ?: return litval
val litval2 = litval.cast(datatype)!!
litval2.parent = litval.parent
// finally, replace the literal array by a identifier reference.
return makeIdentifierFromRefLv(litval2)
}
} }
} }
return array
return litval
} }
private fun determineArrayDt(array: Array<Expression>): DataType? { override fun visit(stringLiteral: StringLiteralValue): Expression {
val string = super.visit(stringLiteral)
if(string is StringLiteralValue) {
val vardecl = string.parent as? VarDecl
// intern the string; move it into the heap
if (string.value.length !in 1..255)
checkResult.add(ExpressionError("string literal length must be between 1 and 255", string.position))
else {
string.addToHeap(program.heap)
}
return if (vardecl != null)
string
else
makeIdentifierFromRefLv(string) // replace the literal string by a identifier reference.
}
return string
}
private fun determineArrayDt(array: Array<Expression>): DataType {
val datatypesInArray = array.mapNotNull { it.inferType(program) } val datatypesInArray = array.mapNotNull { it.inferType(program) }
if(datatypesInArray.isEmpty()) if(datatypesInArray.isEmpty())
return null throw IllegalArgumentException("can't determine type of empty array")
if(DataType.FLOAT in datatypesInArray) return when {
return DataType.ARRAY_F DataType.FLOAT in datatypesInArray -> DataType.ARRAY_F
if(DataType.WORD in datatypesInArray) DataType.WORD in datatypesInArray -> DataType.ARRAY_W
return DataType.ARRAY_W DataType.UWORD in datatypesInArray -> DataType.ARRAY_UW
if(DataType.UWORD in datatypesInArray) DataType.BYTE in datatypesInArray -> DataType.ARRAY_B
return DataType.ARRAY_UW DataType.UBYTE in datatypesInArray -> DataType.ARRAY_UB
if(DataType.BYTE in datatypesInArray) else -> throw IllegalArgumentException("can't determine type of array")
return DataType.ARRAY_B }
if(DataType.UBYTE in datatypesInArray)
return DataType.ARRAY_UB
return null
} }
private fun makeIdentifierFromRefLv(refLiteral: ReferenceLiteralValue): IdentifierReference { private fun makeIdentifierFromRefLv(array: ArrayLiteralValue): IdentifierReference {
// a referencetype literal value that's not declared as a variable // a referencetype literal value that's not declared as a variable
// we need to introduce an auto-generated variable for this to be able to refer to the value // we need to introduce an auto-generated variable for this to be able to refer to the value
// note: if the var references the same literal value, it is not yet de-duplicated here. // note: if the var references the same literal value, it is not yet de-duplicated here.
refLiteral.addToHeap(program.heap) array.addToHeap(program.heap)
val scope = refLiteral.definingScope() val scope = array.definingScope()
var variable = VarDecl.createAuto(refLiteral, program.heap) val variable = VarDecl.createAuto(array)
val existing = scope.lookup(listOf(variable.name), refLiteral) return replaceWithIdentifier(variable, scope, array.parent)
variable = addVarDecl(scope, variable)
// replace the reference literal by a identifier reference
val identifier = IdentifierReference(listOf(variable.name), variable.position)
identifier.parent = refLiteral.parent
return identifier
} }
override fun visit(addressOf: AddressOf): Expression { private fun makeIdentifierFromRefLv(string: StringLiteralValue): IdentifierReference {
// register the scoped name of the referenced identifier // a referencetype literal value that's not declared as a variable
val variable= addressOf.identifier.targetVarDecl(program.namespace) ?: return addressOf // we need to introduce an auto-generated variable for this to be able to refer to the value
return super.visit(addressOf) // note: if the var references the same literal value, it is not yet de-duplicated here.
string.addToHeap(program.heap)
val scope = string.definingScope()
val variable = VarDecl.createAuto(string)
return replaceWithIdentifier(variable, scope, string.parent)
}
private fun replaceWithIdentifier(variable: VarDecl, scope: INameScope, parent: Node): IdentifierReference {
val variable1 = addVarDecl(scope, variable)
// replace the reference literal by a identifier reference
val identifier = IdentifierReference(listOf(variable1.name), variable1.position)
identifier.parent = parent
return identifier
} }
override fun visit(structDecl: StructDecl): Statement { override fun visit(structDecl: StructDecl): Statement {
@ -330,33 +338,28 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
override fun visit(expr: BinaryExpression): Expression { override fun visit(expr: BinaryExpression): Expression {
return when { return when {
expr.left is ReferenceLiteralValue -> expr.left is StringLiteralValue ->
processBinaryExprWithReferenceVal(expr.left as ReferenceLiteralValue, expr.right, expr) processBinaryExprWithString(expr.left as StringLiteralValue, expr.right, expr)
expr.right is ReferenceLiteralValue -> expr.right is StringLiteralValue ->
processBinaryExprWithReferenceVal(expr.right as ReferenceLiteralValue, expr.left, expr) processBinaryExprWithString(expr.right as StringLiteralValue, expr.left, expr)
else -> super.visit(expr) else -> super.visit(expr)
} }
} }
private fun processBinaryExprWithReferenceVal(refLv: ReferenceLiteralValue, operand: Expression, expr: BinaryExpression): Expression { private fun processBinaryExprWithString(string: StringLiteralValue, operand: Expression, expr: BinaryExpression): Expression {
// expressions on strings or arrays val constvalue = operand.constValue(program)
if(refLv.isString) { if(constvalue!=null) {
val constvalue = operand.constValue(program) if (expr.operator == "*") {
if(constvalue!=null) { // repeat a string a number of times
if (expr.operator == "*") { return StringLiteralValue(string.inferType(program),
// repeat a string a number of times string.value.repeat(constvalue.number.toInt()), null, expr.position)
return ReferenceLiteralValue(refLv.inferType(program),
refLv.str!!.repeat(constvalue.number.toInt()), null, null, expr.position)
}
}
if(expr.operator == "+" && operand is ReferenceLiteralValue) {
if (operand.isString) {
// concatenate two strings
return ReferenceLiteralValue(refLv.inferType(program),
"${refLv.str}${operand.str}", null, null, expr.position)
}
} }
} }
if(expr.operator == "+" && operand is StringLiteralValue) {
// concatenate two strings
return StringLiteralValue(string.inferType(program),
"${string.value}${operand.value}", null, expr.position)
}
return expr return expr
} }
@ -375,7 +378,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
} }
internal fun fixupArrayDatatype(array: ReferenceLiteralValue, vardecl: VarDecl, heap: HeapValues): ReferenceLiteralValue { internal fun fixupArrayDatatype(array: ArrayLiteralValue, vardecl: VarDecl, heap: HeapValues): ArrayLiteralValue {
if(array.heapId!=null) { if(array.heapId!=null) {
val arrayDt = array.type val arrayDt = array.type
if(arrayDt!=vardecl.datatype) { if(arrayDt!=vardecl.datatype) {
@ -386,7 +389,7 @@ internal fun fixupArrayDatatype(array: ReferenceLiteralValue, vardecl: VarDecl,
} catch(x: ExpressionError) { } catch(x: ExpressionError) {
// couldn't cast permanently. // couldn't cast permanently.
// instead, simply adjust the array type and trust the AstChecker to report the exact error // instead, simply adjust the array type and trust the AstChecker to report the exact error
ReferenceLiteralValue(vardecl.datatype, null, array.array, array.heapId, array.position) ArrayLiteralValue(vardecl.datatype, array.value, array.heapId, array.position)
} }
vardecl.value = litval2 vardecl.value = litval2
litval2.linkParents(vardecl) litval2.linkParents(vardecl)

View File

@ -110,14 +110,16 @@ interface IAstModifyingVisitor {
return literalValue return literalValue
} }
fun visit(refLiteral: ReferenceLiteralValue): Expression { fun visit(stringLiteral: StringLiteralValue): Expression {
if(refLiteral.array!=null) { return stringLiteral
for(av in refLiteral.array.withIndex()) { }
val newvalue = av.value.accept(this)
refLiteral.array[av.index] = newvalue fun visit(arrayLiteral: ArrayLiteralValue): Expression {
} for(av in arrayLiteral.value.withIndex()) {
val newvalue = av.value.accept(this)
arrayLiteral.value[av.index] = newvalue
} }
return refLiteral return arrayLiteral
} }
fun visit(assignment: Assignment): Statement { fun visit(assignment: Assignment): Statement {

View File

@ -79,8 +79,11 @@ interface IAstVisitor {
fun visit(numLiteral: NumericLiteralValue) { fun visit(numLiteral: NumericLiteralValue) {
} }
fun visit(refLiteral: ReferenceLiteralValue) { fun visit(string: StringLiteralValue) {
refLiteral.array?.let { it.forEach { v->v.accept(this) }} }
fun visit(array: ArrayLiteralValue) {
array.value.forEach { v->v.accept(this) }
} }
fun visit(assignment: Assignment) { fun visit(assignment: Assignment) {

View File

@ -42,7 +42,7 @@ internal class VarInitValueAndAddressOfCreator(private val program: Program): IA
if(decl.isArray && decl.value==null) { if(decl.isArray && decl.value==null) {
// array datatype without initialization value, add list of zeros // array datatype without initialization value, add list of zeros
val arraysize = decl.arraysize!!.size()!! val arraysize = decl.arraysize!!.size()!!
val array = ReferenceLiteralValue(decl.datatype, null, val array = ArrayLiteralValue(decl.datatype,
Array(arraysize) { NumericLiteralValue.optimalInteger(0, decl.position) }, Array(arraysize) { NumericLiteralValue.optimalInteger(0, decl.position) },
null, decl.position) null, decl.position)
array.addToHeap(program.heap) array.addToHeap(program.heap)
@ -107,7 +107,7 @@ internal class VarInitValueAndAddressOfCreator(private val program: Program): IA
if(argparam.second is AddressOf) if(argparam.second is AddressOf)
continue continue
val idref = argparam.second as? IdentifierReference val idref = argparam.second as? IdentifierReference
val strvalue = argparam.second as? ReferenceLiteralValue val strvalue = argparam.second as? StringLiteralValue
if(idref!=null) { if(idref!=null) {
val variable = idref.targetVarDecl(program.namespace) val variable = idref.targetVarDecl(program.namespace)
if(variable!=null && (variable.datatype in StringDatatypes || variable.datatype in ArrayDatatypes)) { if(variable!=null && (variable.datatype in StringDatatypes || variable.datatype in ArrayDatatypes)) {
@ -117,16 +117,14 @@ internal class VarInitValueAndAddressOfCreator(private val program: Program): IA
} }
} }
else if(strvalue!=null) { else if(strvalue!=null) {
if(strvalue.isString) { // add a vardecl so that the autovar can be resolved in later lookups
// add a vardecl so that the autovar can be resolved in later lookups val variable = VarDecl.createAuto(strvalue)
val variable = VarDecl.createAuto(strvalue, program.heap) addVarDecl(strvalue.definingScope(), variable)
addVarDecl(strvalue.definingScope(), variable) // replace the argument with &autovar
// replace the argument with &autovar val autoHeapvarRef = IdentifierReference(listOf(variable.name), strvalue.position)
val autoHeapvarRef = IdentifierReference(listOf(variable.name), strvalue.position) val pointerExpr = AddressOf(autoHeapvarRef, strvalue.position)
val pointerExpr = AddressOf(autoHeapvarRef, strvalue.position) pointerExpr.linkParents(arglist[argparam.first.index].parent)
pointerExpr.linkParents(arglist[argparam.first.index].parent) arglist[argparam.first.index] = pointerExpr
arglist[argparam.first.index] = pointerExpr
}
} }
} }
} }

View File

@ -5,7 +5,6 @@ import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.processing.IAstModifyingVisitor import prog8.ast.processing.IAstModifyingVisitor
import prog8.ast.processing.IAstVisitor import prog8.ast.processing.IAstVisitor
import prog8.compiler.HeapValues
sealed class Statement : Node { sealed class Statement : Node {
@ -111,8 +110,6 @@ data class Label(val name: String, override val position: Position) : Statement(
override fun toString(): String { override fun toString(): String {
return "Label(name=$name, pos=$position)" return "Label(name=$name, pos=$position)"
} }
val scopedname: String by lazy { makeScopedName(name) }
} }
open class Return(var value: Expression?, override val position: Position) : Statement() { open class Return(var value: Expression?, override val position: Position) : Statement() {
@ -197,20 +194,23 @@ class VarDecl(val type: VarDeclType,
companion object { companion object {
private var autoHeapValueSequenceNumber = 0 private var autoHeapValueSequenceNumber = 0
fun createAuto(refLv: ReferenceLiteralValue, heap: HeapValues): VarDecl { fun createAuto(string: StringLiteralValue): VarDecl {
if(refLv.heapId==null) if(string.heapId==null)
throw FatalAstException("can only create autovar for a ref lv that has a heapid $refLv") throw FatalAstException("can only create autovar for a string that has a heapid $string")
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
return VarDecl(VarDeclType.VAR, string.type, ZeropageWish.NOT_IN_ZEROPAGE, null, autoVarName, null, string,
isArray = false, autogeneratedDontRemove = true, position = string.position)
}
fun createAuto(array: ArrayLiteralValue): VarDecl {
if(array.heapId==null)
throw FatalAstException("can only create autovar for an array that has a heapid $array")
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}" val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
return if(refLv.isArray) { val declaredType = ArrayElementTypes.getValue(array.type)
val declaredType = ArrayElementTypes.getValue(refLv.type) val arraysize = ArrayIndex.forArray(array)
val arraysize = ArrayIndex.forArray(refLv, heap) return VarDecl(VarDeclType.VAR, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, null, array,
VarDecl(VarDeclType.VAR, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, null, refLv, isArray = true, autogeneratedDontRemove = true, position = array.position)
isArray = true, autogeneratedDontRemove = true, position = refLv.position)
} else {
VarDecl(VarDeclType.VAR, refLv.type, ZeropageWish.NOT_IN_ZEROPAGE, null, autoVarName, null, refLv,
isArray = false, autogeneratedDontRemove = true, position = refLv.position)
}
} }
} }
@ -284,12 +284,6 @@ class VarDecl(val type: VarDeclType,
structHasBeenFlattened = true structHasBeenFlattened = true
return result return result
} }
fun withPrefixedName(nameprefix: String): Statement {
val new = VarDecl(type, declaredDatatype, zeropage, arraysize, nameprefix+name, structName, value, isArray, autogeneratedDontRemove, position)
new.parent = parent
return new
}
} }
class ArrayIndex(var index: Expression, override val position: Position) : Node { class ArrayIndex(var index: Expression, override val position: Position) : Node {
@ -301,9 +295,8 @@ class ArrayIndex(var index: Expression, override val position: Position) : Node
} }
companion object { companion object {
fun forArray(v: ReferenceLiteralValue, heap: HeapValues): ArrayIndex { fun forArray(v: ArrayLiteralValue): ArrayIndex {
val arraySize = v.array?.size ?: heap.get(v.heapId!!).arraysize return ArrayIndex(NumericLiteralValue.optimalNumeric(v.value.size, v.position), v.position)
return ArrayIndex(NumericLiteralValue.optimalNumeric(arraySize, v.position), v.position)
} }
} }

View File

@ -21,7 +21,7 @@ class AnonymousScopeVarsCleanup(val program: Program): IAstModifyingVisitor {
super.visit(program) super.visit(program)
for((scope, decls) in varsToMove) { for((scope, decls) in varsToMove) {
val sub = scope.definingSubroutine()!! val sub = scope.definingSubroutine()!!
val existingVariables = sub.statements.filterIsInstance<VarDecl>().associate { it.name to it } val existingVariables = sub.statements.filterIsInstance<VarDecl>().associateBy { it.name }
var conflicts = false var conflicts = false
decls.forEach { decls.forEach {
val existing = existingVariables[it.name] val existing = existingVariables[it.name]

View File

@ -251,7 +251,7 @@ internal class AsmGen2(val program: Program,
DataType.FLOAT -> out("${decl.name}\t.byte 0,0,0,0,0 ; float") DataType.FLOAT -> out("${decl.name}\t.byte 0,0,0,0,0 ; float")
DataType.STRUCT -> {} // is flattened DataType.STRUCT -> {} // is flattened
DataType.STR, DataType.STR_S -> { DataType.STR, DataType.STR_S -> {
val string = (decl.value as ReferenceLiteralValue).str!! val string = (decl.value as StringLiteralValue).value
val encoded = encodeStr(string, decl.datatype) val encoded = encodeStr(string, decl.datatype)
outputStringvar(decl, encoded) outputStringvar(decl, encoded)
} }
@ -296,7 +296,7 @@ internal class AsmGen2(val program: Program,
} }
} }
DataType.ARRAY_F -> { DataType.ARRAY_F -> {
val array = (decl.value as ReferenceLiteralValue).array ?: throw AssemblyError("array should not be null?") val array = (decl.value as ArrayLiteralValue).value
val floatFills = array.map { val floatFills = array.map {
val number = (it as NumericLiteralValue).number val number = (it as NumericLiteralValue).number
makeFloatFill(MachineDefinition.Mflpt5.fromNumber(number)) makeFloatFill(MachineDefinition.Mflpt5.fromNumber(number))
@ -337,7 +337,7 @@ internal class AsmGen2(val program: Program,
// special treatment for string types: merge strings that are identical // special treatment for string types: merge strings that are identical
val encodedstringVars = normalVars val encodedstringVars = normalVars
.filter {it.datatype in StringDatatypes } .filter {it.datatype in StringDatatypes }
.map { it to encodeStr((it.value as ReferenceLiteralValue).str!!, it.datatype) } .map { it to encodeStr((it.value as StringLiteralValue).value, it.datatype) }
.groupBy({it.second}, {it.first}) .groupBy({it.second}, {it.first})
for((encoded, variables) in encodedstringVars) { for((encoded, variables) in encodedstringVars) {
variables.dropLast(1).forEach { out(it.name) } variables.dropLast(1).forEach { out(it.name) }
@ -353,7 +353,7 @@ internal class AsmGen2(val program: Program,
} }
private fun outputStringvar(lastvar: VarDecl, encoded: List<Short>) { private fun outputStringvar(lastvar: VarDecl, encoded: List<Short>) {
val string = (lastvar.value as ReferenceLiteralValue).str!! val string = (lastvar.value as StringLiteralValue).value
out("${lastvar.name}\t; ${lastvar.datatype} \"${escape(string).replace("\u0000", "<NULL>")}\"") out("${lastvar.name}\t; ${lastvar.datatype} \"${escape(string).replace("\u0000", "<NULL>")}\"")
val outputBytes = encoded.map { "$" + it.toString(16).padStart(2, '0') } val outputBytes = encoded.map { "$" + it.toString(16).padStart(2, '0') }
for (chunk in outputBytes.chunked(16)) for (chunk in outputBytes.chunked(16))
@ -361,7 +361,7 @@ internal class AsmGen2(val program: Program,
} }
private fun makeArrayFillDataUnsigned(decl: VarDecl): List<String> { private fun makeArrayFillDataUnsigned(decl: VarDecl): List<String> {
val array = (decl.value as ReferenceLiteralValue).array ?: throw AssemblyError("array should not be null?") val array = (decl.value as ArrayLiteralValue).value
return when { return when {
decl.datatype == DataType.ARRAY_UB -> decl.datatype == DataType.ARRAY_UB ->
// byte array can never contain pointer-to types, so treat values as all integers // byte array can never contain pointer-to types, so treat values as all integers
@ -381,7 +381,7 @@ internal class AsmGen2(val program: Program,
} }
private fun makeArrayFillDataSigned(decl: VarDecl): List<String> { private fun makeArrayFillDataSigned(decl: VarDecl): List<String> {
val array = (decl.value as ReferenceLiteralValue).array ?: throw AssemblyError("array should not be null?") val array = (decl.value as ArrayLiteralValue).value
return when { return when {
decl.datatype == DataType.ARRAY_UB -> decl.datatype == DataType.ARRAY_UB ->
@ -808,22 +808,31 @@ internal class AsmGen2(val program: Program,
is NumericLiteralValue -> { is NumericLiteralValue -> {
// optimize when the argument is a constant literal // optimize when the argument is a constant literal
val hex = value.number.toHex() val hex = value.number.toHex()
if (register == RegisterOrPair.AX) out(" lda #<$hex | ldx #>$hex") when (register) {
else if (register == RegisterOrPair.AY) out(" lda #<$hex | ldy #>$hex") RegisterOrPair.AX -> out(" lda #<$hex | ldx #>$hex")
else if (register == RegisterOrPair.XY) out(" ldx #<$hex | ldy #>$hex") RegisterOrPair.AY -> out(" lda #<$hex | ldy #>$hex")
RegisterOrPair.XY -> out(" ldx #<$hex | ldy #>$hex")
else -> {}
}
} }
is AddressOf -> { is AddressOf -> {
// optimize when the argument is an address of something // optimize when the argument is an address of something
val sourceName = asmIdentifierName(value.identifier) val sourceName = asmIdentifierName(value.identifier)
if (register == RegisterOrPair.AX) out(" lda #<$sourceName | ldx #>$sourceName") when (register) {
else if (register == RegisterOrPair.AY) out(" lda #<$sourceName | ldy #>$sourceName") RegisterOrPair.AX -> out(" lda #<$sourceName | ldx #>$sourceName")
else if (register == RegisterOrPair.XY) out(" ldx #<$sourceName | ldy #>$sourceName") RegisterOrPair.AY -> out(" lda #<$sourceName | ldy #>$sourceName")
RegisterOrPair.XY -> out(" ldx #<$sourceName | ldy #>$sourceName")
else -> {}
}
} }
is IdentifierReference -> { is IdentifierReference -> {
val sourceName = asmIdentifierName(value) val sourceName = asmIdentifierName(value)
if (register == RegisterOrPair.AX) out(" lda $sourceName | ldx $sourceName+1") when (register) {
else if (register == RegisterOrPair.AY) out(" lda $sourceName | ldy $sourceName+1") RegisterOrPair.AX -> out(" lda $sourceName | ldx $sourceName+1")
else if (register == RegisterOrPair.XY) out(" ldx $sourceName | ldy $sourceName+1") RegisterOrPair.AY -> out(" lda $sourceName | ldy $sourceName+1")
RegisterOrPair.XY -> out(" ldx $sourceName | ldy $sourceName+1")
else -> {}
}
} }
else -> { else -> {
translateExpression(value) translateExpression(value)
@ -1748,7 +1757,7 @@ $endLabel""")
translateExpression(assign.value as FunctionCall) translateExpression(assign.value as FunctionCall)
assignFromEvalResult(assign.target) assignFromEvalResult(assign.target)
} }
is ReferenceLiteralValue -> TODO("string/array/struct assignment?") is ArrayLiteralValue, is StringLiteralValue -> TODO("string/array/struct assignment?")
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened") is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened")
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values") is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
} }
@ -1851,7 +1860,7 @@ $endLabel""")
translateSubroutineCall(expression) translateSubroutineCall(expression)
val sub = expression.target.targetSubroutine(program.namespace)!! val sub = expression.target.targetSubroutine(program.namespace)!!
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters) val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
for((t, reg) in returns) { for((_, reg) in returns) {
if(!reg.stack) { if(!reg.stack) {
// result value in cpu or status registers, put it on the stack // result value in cpu or status registers, put it on the stack
if(reg.registerOrPair!=null) { if(reg.registerOrPair!=null) {
@ -1867,7 +1876,7 @@ $endLabel""")
} }
} }
} }
is ReferenceLiteralValue -> TODO("string/array/struct assignment?") is ArrayLiteralValue, is StringLiteralValue -> TODO("string/array/struct assignment?")
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened") is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened")
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values") is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
} }

View File

@ -115,9 +115,9 @@ val BuiltinFunctions = mapOf(
fun builtinFunctionReturnType(function: String, args: List<Expression>, program: Program): DataType? { fun builtinFunctionReturnType(function: String, args: List<Expression>, program: Program): DataType? {
fun datatypeFromIterableArg(arglist: Expression): DataType { fun datatypeFromIterableArg(arglist: Expression): DataType {
if(arglist is ReferenceLiteralValue) { if(arglist is ArrayLiteralValue) {
if(arglist.type== DataType.ARRAY_UB || arglist.type== DataType.ARRAY_UW || arglist.type== DataType.ARRAY_F) { if(arglist.type== DataType.ARRAY_UB || arglist.type== DataType.ARRAY_UW || arglist.type== DataType.ARRAY_F) {
val dt = arglist.array!!.map {it.inferType(program)} val dt = arglist.value.map {it.inferType(program)}
if(dt.any { it!= DataType.UBYTE && it!= DataType.UWORD && it!= DataType.FLOAT}) { if(dt.any { it!= DataType.UBYTE && it!= DataType.UWORD && it!= DataType.FLOAT}) {
throw FatalAstException("fuction $function only accepts arraysize of numeric values") throw FatalAstException("fuction $function only accepts arraysize of numeric values")
} }
@ -271,10 +271,10 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
NumericLiteralValue.optimalInteger(arraySize, args[0].position) NumericLiteralValue.optimalInteger(arraySize, args[0].position)
} }
in StringDatatypes -> { in StringDatatypes -> {
val refLv = target.value as ReferenceLiteralValue val refLv = target.value as StringLiteralValue
if(refLv.str!!.length>255) if(refLv.value.length>255)
throw CompilerException("string length exceeds byte limit ${refLv.position}") throw CompilerException("string length exceeds byte limit ${refLv.position}")
NumericLiteralValue.optimalInteger(refLv.str.length, args[0].position) NumericLiteralValue.optimalInteger(refLv.value.length, args[0].position)
} }
in NumericDatatypes -> throw SyntaxError("len of weird argument ${args[0]}", position) in NumericDatatypes -> throw SyntaxError("len of weird argument ${args[0]}", position)
else -> throw CompilerException("weird datatype") else -> throw CompilerException("weird datatype")

View File

@ -39,11 +39,9 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
if(decl.isArray){ if(decl.isArray){
if(decl.arraysize==null) { if(decl.arraysize==null) {
// for arrays that have no size specifier (or a non-constant one) attempt to deduce the size // for arrays that have no size specifier (or a non-constant one) attempt to deduce the size
val arrayval = (decl.value as? ReferenceLiteralValue)?.array val arrayval = (decl.value as ArrayLiteralValue).value
if(arrayval!=null) { decl.arraysize = ArrayIndex(NumericLiteralValue.optimalInteger(arrayval.size, decl.position), decl.position)
decl.arraysize = ArrayIndex(NumericLiteralValue.optimalInteger(arrayval.size, decl.position), decl.position) optimizationsDone++
optimizationsDone++
}
} }
else if(decl.arraysize?.size()==null) { else if(decl.arraysize?.size()==null) {
val size = decl.arraysize!!.index.accept(this) val size = decl.arraysize!!.index.accept(this)
@ -83,13 +81,13 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
if(constRange!=null) { if(constRange!=null) {
val eltType = rangeExpr.inferType(program)!! val eltType = rangeExpr.inferType(program)!!
if(eltType in ByteDatatypes) { if(eltType in ByteDatatypes) {
decl.value = ReferenceLiteralValue(decl.datatype, decl.value = ArrayLiteralValue(decl.datatype,
array = constRange.map { NumericLiteralValue(eltType, it.toShort(), decl.value!!.position) } constRange.map { NumericLiteralValue(eltType, it.toShort(), decl.value!!.position) }.toTypedArray(),
.toTypedArray(), position = decl.value!!.position) position = decl.value!!.position)
} else { } else {
decl.value = ReferenceLiteralValue(decl.datatype, decl.value = ArrayLiteralValue(decl.datatype,
array = constRange.map { NumericLiteralValue(eltType, it, decl.value!!.position) } constRange.map { NumericLiteralValue(eltType, it, decl.value!!.position) }.toTypedArray(),
.toTypedArray(), position = decl.value!!.position) position = decl.value!!.position)
} }
decl.value!!.linkParents(decl) decl.value!!.linkParents(decl)
optimizationsDone++ optimizationsDone++
@ -123,7 +121,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
} }
// create the array itself, filled with the fillvalue. // create the array itself, filled with the fillvalue.
val array = Array(size) {fillvalue}.map { NumericLiteralValue.optimalInteger(it, numericLv.position) as Expression}.toTypedArray() val array = Array(size) {fillvalue}.map { NumericLiteralValue.optimalInteger(it, numericLv.position) as Expression}.toTypedArray()
val refValue = ReferenceLiteralValue(decl.datatype, array = array, position = numericLv.position) val refValue = ArrayLiteralValue(decl.datatype, array, position = numericLv.position)
refValue.addToHeap(program.heap) refValue.addToHeap(program.heap)
decl.value = refValue decl.value = refValue
refValue.parent=decl refValue.parent=decl
@ -145,7 +143,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
else { else {
// create the array itself, filled with the fillvalue. // create the array itself, filled with the fillvalue.
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) as Expression}.toTypedArray() val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) as Expression}.toTypedArray()
val refValue = ReferenceLiteralValue(DataType.ARRAY_F, array = array, position = litval.position) val refValue = ArrayLiteralValue(DataType.ARRAY_F, array, position = litval.position)
refValue.addToHeap(program.heap) refValue.addToHeap(program.heap)
decl.value = refValue decl.value = refValue
refValue.parent=decl refValue.parent=decl
@ -311,7 +309,8 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
return try { return try {
super.visit(expr) super.visit(expr)
if(expr.left is ReferenceLiteralValue || expr.right is ReferenceLiteralValue) if(expr.left is StringLiteralValue || expr.left is ArrayLiteralValue
|| expr.right is StringLiteralValue || expr.right is ArrayLiteralValue)
throw FatalAstException("binexpr with reference litval instead of numeric") throw FatalAstException("binexpr with reference litval instead of numeric")
val leftconst = expr.left.constValue(program) val leftconst = expr.left.constValue(program)
@ -597,17 +596,15 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
return resultStmt return resultStmt
} }
override fun visit(refLiteral: ReferenceLiteralValue): Expression { override fun visit(arrayLiteral: ArrayLiteralValue): Expression {
val litval = super.visit(refLiteral) val array = super.visit(arrayLiteral)
if(litval is ReferenceLiteralValue) { if(array is ArrayLiteralValue) {
if (litval.isArray) { val vardecl = array.parent as? VarDecl
val vardecl = litval.parent as? VarDecl if (vardecl!=null) {
if (vardecl!=null) { return fixupArrayDatatype(array, vardecl, program.heap)
return fixupArrayDatatype(litval, vardecl, program.heap)
}
} }
} }
return litval return array
} }
override fun visit(assignment: Assignment): Statement { override fun visit(assignment: Assignment): Statement {

View File

@ -1,6 +1,9 @@
package prog8.optimizer package prog8.optimizer
import prog8.ast.* import prog8.ast.INameScope
import prog8.ast.Module
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.processing.IAstModifyingVisitor import prog8.ast.processing.IAstModifyingVisitor

View File

@ -1,8 +1,9 @@
package prog8.vm package prog8.vm
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.ArrayLiteralValue
import prog8.ast.expressions.NumericLiteralValue import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.ReferenceLiteralValue import prog8.ast.expressions.StringLiteralValue
import prog8.compiler.HeapValues import prog8.compiler.HeapValues
import prog8.compiler.target.c64.Petscii import prog8.compiler.target.c64.Petscii
import java.util.* import java.util.*
@ -28,13 +29,8 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
return RuntimeValue(literalValue.type, num = literalValue.number) return RuntimeValue(literalValue.type, num = literalValue.number)
} }
fun fromLv(literalValue: ReferenceLiteralValue, heap: HeapValues): RuntimeValue { fun fromLv(string: StringLiteralValue, heap: HeapValues): RuntimeValue = fromHeapId(string.heapId!!, heap)
return when(literalValue.type) { fun fromLv(array: ArrayLiteralValue, heap: HeapValues): RuntimeValue = fromHeapId(array.heapId!!, heap)
in StringDatatypes -> fromHeapId(literalValue.heapId!!, heap)
in ArrayDatatypes -> fromHeapId(literalValue.heapId!!, heap)
else -> throw IllegalArgumentException("weird source value $literalValue")
}
}
fun fromHeapId(heapId: Int, heap: HeapValues): RuntimeValue { fun fromHeapId(heapId: Int, heap: HeapValues): RuntimeValue {
val value = heap.get(heapId) val value = heap.get(heapId)

View File

@ -166,10 +166,10 @@ class AstVm(val program: Program) {
fun memwrite(address: Int, value: Short): Short { fun memwrite(address: Int, value: Short): Short {
if(address==0xa0 || address==0xa1 || address==0xa2) { if(address==0xa0 || address==0xa1 || address==0xa2) {
// a write to the jiffy clock, update the clock offset for the irq // a write to the jiffy clock, update the clock offset for the irq
val time_hi = if(address==0xa0) value else mem.getUByte_DMA(0xa0) val timeHi = if(address==0xa0) value else mem.getUByte_DMA(0xa0)
val time_mid = if(address==0xa1) value else mem.getUByte_DMA(0xa1) val timeMid = if(address==0xa1) value else mem.getUByte_DMA(0xa1)
val time_lo = if(address==0xa2) value else mem.getUByte_DMA(0xa2) val timeLo = if(address==0xa2) value else mem.getUByte_DMA(0xa2)
val jiffies = (time_hi.toInt() shl 16) + (time_mid.toInt() shl 8) + time_lo val jiffies = (timeHi.toInt() shl 16) + (timeMid.toInt() shl 8) + timeLo
rtcOffset = bootTime - (jiffies*1000/60) rtcOffset = bootTime - (jiffies*1000/60)
} }
if(address in 1024..2023) { if(address in 1024..2023) {

View File

@ -30,12 +30,9 @@ fun evaluate(expr: Expression, ctx: EvalContext): RuntimeValue {
return RuntimeValue.fromLv(constval) return RuntimeValue.fromLv(constval)
when(expr) { when(expr) {
is NumericLiteralValue -> { is NumericLiteralValue -> return RuntimeValue.fromLv(expr)
return RuntimeValue.fromLv(expr) is StringLiteralValue -> return RuntimeValue.fromLv(expr, ctx.program.heap)
} is ArrayLiteralValue -> return RuntimeValue.fromLv(expr, ctx.program.heap)
is ReferenceLiteralValue -> {
return RuntimeValue.fromLv(expr, ctx.program.heap)
}
is PrefixExpression -> { is PrefixExpression -> {
return when(expr.operator) { return when(expr.operator) {
"-" -> evaluate(expr.expression, ctx).neg() "-" -> evaluate(expr.expression, ctx).neg()

View File

@ -5,8 +5,9 @@ import prog8.ast.base.DataType
import prog8.ast.base.Position import prog8.ast.base.Position
import prog8.ast.base.Register import prog8.ast.base.Register
import prog8.ast.base.VarDeclType import prog8.ast.base.VarDeclType
import prog8.ast.expressions.ArrayLiteralValue
import prog8.ast.expressions.NumericLiteralValue import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.ReferenceLiteralValue import prog8.ast.expressions.StringLiteralValue
import prog8.ast.processing.IAstModifyingVisitor import prog8.ast.processing.IAstModifyingVisitor
import prog8.ast.statements.Statement import prog8.ast.statements.Statement
import prog8.ast.statements.StructDecl import prog8.ast.statements.StructDecl
@ -50,8 +51,10 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v
val value = if(numericLv!=null) { val value = if(numericLv!=null) {
RuntimeValue.fromLv(numericLv) RuntimeValue.fromLv(numericLv)
} else { } else {
val referenceLv = decl.value as ReferenceLiteralValue if(decl.value is StringLiteralValue)
RuntimeValue.fromLv(referenceLv, heap) RuntimeValue.fromLv(decl.value as StringLiteralValue, heap)
else
RuntimeValue.fromLv(decl.value as ArrayLiteralValue, heap)
} }
runtimeVariables.define(decl.definingScope(), decl.name, value) runtimeVariables.define(decl.definingScope(), decl.name, value)
} }

View File

@ -4,8 +4,9 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance
import prog8.ast.base.DataType import prog8.ast.base.DataType
import prog8.ast.base.Position import prog8.ast.base.Position
import prog8.ast.expressions.ArrayLiteralValue
import prog8.ast.expressions.NumericLiteralValue import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.ReferenceLiteralValue import prog8.ast.expressions.StringLiteralValue
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertNotEquals import kotlin.test.assertNotEquals
@ -16,10 +17,6 @@ private fun sameValueAndType(lv1: NumericLiteralValue, lv2: NumericLiteralValue)
return lv1.type==lv2.type && lv1==lv2 return lv1.type==lv2.type && lv1==lv2
} }
private fun sameValueAndType(rv1: ReferenceLiteralValue, rv2: ReferenceLiteralValue): Boolean {
return rv1.type==rv2.type && rv1==rv2
}
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestParserNumericLiteralValue { class TestParserNumericLiteralValue {
@ -86,8 +83,8 @@ class TestParserNumericLiteralValue {
@Test @Test
fun testEqualsRef() { fun testEqualsRef() {
assertTrue(sameValueAndType(ReferenceLiteralValue(DataType.STR, str = "hello", position = dummyPos), ReferenceLiteralValue(DataType.STR, str = "hello", position = dummyPos))) assertTrue(StringLiteralValue(DataType.STR, "hello", position = dummyPos) == StringLiteralValue(DataType.STR, "hello", position = dummyPos))
assertFalse(sameValueAndType(ReferenceLiteralValue(DataType.STR, str = "hello", position = dummyPos), ReferenceLiteralValue(DataType.STR, str = "bye", position = dummyPos))) assertFalse(StringLiteralValue(DataType.STR, "hello", position = dummyPos) == StringLiteralValue(DataType.STR, "bye", position = dummyPos))
val lvOne = NumericLiteralValue(DataType.UBYTE, 1, dummyPos) val lvOne = NumericLiteralValue(DataType.UBYTE, 1, dummyPos)
val lvTwo = NumericLiteralValue(DataType.UBYTE, 2, dummyPos) val lvTwo = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
@ -96,9 +93,9 @@ class TestParserNumericLiteralValue {
val lvTwoR = NumericLiteralValue(DataType.UBYTE, 2, dummyPos) val lvTwoR = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
val lvThreeR = NumericLiteralValue(DataType.UBYTE, 3, dummyPos) val lvThreeR = NumericLiteralValue(DataType.UBYTE, 3, dummyPos)
val lvFour= NumericLiteralValue(DataType.UBYTE, 4, dummyPos) val lvFour= NumericLiteralValue(DataType.UBYTE, 4, dummyPos)
val lv1 = ReferenceLiteralValue(DataType.ARRAY_UB, array = arrayOf(lvOne, lvTwo, lvThree), position = dummyPos) val lv1 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOne, lvTwo, lvThree), position = dummyPos)
val lv2 = ReferenceLiteralValue(DataType.ARRAY_UB, array = arrayOf(lvOneR, lvTwoR, lvThreeR), position = dummyPos) val lv2 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvThreeR), position = dummyPos)
val lv3 = ReferenceLiteralValue(DataType.ARRAY_UB, array = arrayOf(lvOneR, lvTwoR, lvFour), position = dummyPos) val lv3 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvFour), position = dummyPos)
assertEquals(lv1, lv2) assertEquals(lv1, lv2)
assertNotEquals(lv1, lv3) assertNotEquals(lv1, lv3)
} }

View File

@ -8,7 +8,7 @@ import org.junit.jupiter.api.TestInstance
import prog8.ast.base.DataType import prog8.ast.base.DataType
import prog8.ast.base.Position import prog8.ast.base.Position
import prog8.ast.expressions.NumericLiteralValue import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.ReferenceLiteralValue import prog8.ast.expressions.StringLiteralValue
import prog8.compiler.* import prog8.compiler.*
import prog8.compiler.target.c64.MachineDefinition.C64Zeropage import prog8.compiler.target.c64.MachineDefinition.C64Zeropage
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
@ -371,8 +371,8 @@ class TestPetscii {
assertTrue(ten <= ten) assertTrue(ten <= ten)
assertFalse(ten < ten) assertFalse(ten < ten)
val abc = ReferenceLiteralValue(DataType.STR, str = "abc", position = Position("", 0, 0, 0)) val abc = StringLiteralValue(DataType.STR, "abc", position = Position("", 0, 0, 0))
val abd = ReferenceLiteralValue(DataType.STR, str = "abd", position = Position("", 0, 0, 0)) val abd = StringLiteralValue(DataType.STR, "abd", position = Position("", 0, 0, 0))
assertEquals(abc, abc) assertEquals(abc, abc)
assertTrue(abc!=abd) assertTrue(abc!=abd)
assertFalse(abc!=abc) assertFalse(abc!=abc)

View File

@ -6,42 +6,9 @@
main { main {
sub start() { sub start() {
byte[] barr = [-100, 0, 99, -122, 22]
ubyte[] ubarr = [100, 0, 99, 199, 22]
word[] warr = [-1000, 0, 999, -4444, 222]
uword[] uwarr = [1000, 0, 222, 4444, 999]
float[] farr = [-1000.1, 0, 999.9, -4444.4, 222.2]
str name = "irmen"
ubyte ub
byte bb
word ww
uword uw
float ff
; LEN/STRLEN
ubyte length = len(name)
if(length!=5) c64scr.print("error len1\n")
length = len(uwarr)
if(length!=5) c64scr.print("error len2\n")
length=strlen(name)
if(length!=5) c64scr.print("error strlen1\n")
name[3] = 0
length=strlen(name)
if(length!=3) c64scr.print("error strlen2\n")
; MAX
; ub = max(ubarr)
; bb = max(barr)
; ww = max(warr)
; uw = max(uwarr)
; ff = max(farr)
; word ww = sum(barr)
; uword uw = sum(ubarr)
; ww = sum(warr)
; uw = sum(uwarr)
; float ff = sum(farr)
c64scr.print("\nbreakpoint after this.")
%breakpoint
c64scr.print("\nyou should see no errors above.") c64scr.print("\nyou should see no errors above.")
} }
} }