mirror of
https://github.com/irmen/prog8.git
synced 2024-11-26 11:49:22 +00:00
fixes
This commit is contained in:
parent
d5b12fb01d
commit
78d7849197
@ -15,11 +15,6 @@ import java.io.File
|
||||
internal class AstChecker(private val program: Program,
|
||||
private val compilerOptions: CompilationOptions) : IAstVisitor {
|
||||
private val checkResult: MutableList<AstException> = mutableListOf()
|
||||
private val heapIdSentinel: Int
|
||||
init {
|
||||
val stringSentinel = program.heap.allEntries().firstOrNull {it.value.str==""}
|
||||
heapIdSentinel = stringSentinel?.key ?: program.heap.addString(DataType.STR, "")
|
||||
}
|
||||
|
||||
fun result(): List<AstException> {
|
||||
return checkResult
|
||||
@ -363,7 +358,7 @@ internal class AstChecker(private val program: Program,
|
||||
checkResult.add(ExpressionError("address out of range", assignTarget.position))
|
||||
}
|
||||
|
||||
val assignment = assignTarget.parent as Assignment
|
||||
val assignment = assignTarget.parent as IStatement
|
||||
if (assignTarget.identifier != null) {
|
||||
val targetName = assignTarget.identifier.nameInSource
|
||||
val targetSymbol = program.namespace.lookup(targetName, assignment)
|
||||
@ -385,6 +380,8 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if (assignment is Assignment) {
|
||||
|
||||
if (assignment.aug_op != null)
|
||||
throw FatalAstException("augmented assignment should have been converted into normal assignment")
|
||||
|
||||
@ -410,16 +407,15 @@ internal class AstChecker(private val program: Program,
|
||||
val targetStmt = (assignment.value as FunctionCall).target.targetStatement(program.namespace)
|
||||
if (targetStmt != null)
|
||||
checkResult.add(ExpressionError("function call doesn't return a suitable value to use in assignment", assignment.value.position))
|
||||
}
|
||||
else
|
||||
} else
|
||||
checkResult.add(ExpressionError("assignment value is invalid or has no proper datatype", assignment.value.position))
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
checkAssignmentCompatible(targetDatatype, assignTarget, sourceDatatype, assignment.value, assignment.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(addressOf: AddressOf) {
|
||||
val variable=addressOf.identifier.targetVarDecl(program.namespace)
|
||||
|
@ -2,7 +2,6 @@ package prog8.ast.processing
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.base.autoHeapValuePrefix
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.functions.BuiltinFunctions
|
||||
@ -12,7 +11,8 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
|
||||
private val checkResult: MutableList<AstException> = mutableListOf()
|
||||
|
||||
private var blocks = mutableMapOf<String, Block>()
|
||||
internal val anonymousVariablesFromHeap = mutableMapOf<String, Pair<ReferenceLiteralValue, VarDecl>>()
|
||||
internal val anonymousVariablesFromHeap = mutableMapOf<String, Pair<ReferenceLiteralValue, VarDecl>>() // TODO
|
||||
private val vardeclsToAdd = mutableMapOf<INameScope, MutableList<VarDecl>>()
|
||||
|
||||
internal fun result(): List<AstException> {
|
||||
return checkResult
|
||||
@ -23,8 +23,14 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
|
||||
}
|
||||
|
||||
override fun visit(module: Module) {
|
||||
vardeclsToAdd.clear()
|
||||
blocks.clear() // blocks may be redefined within a different module
|
||||
super.visit(module)
|
||||
// add any new vardecls to the various scopes
|
||||
for((where, decls) in vardeclsToAdd) {
|
||||
where.statements.addAll(0, decls)
|
||||
decls.forEach { it.linkParents(where as Node) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(block: Block): IStatement {
|
||||
@ -208,20 +214,13 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
|
||||
}
|
||||
|
||||
override fun visit(refLiteral: ReferenceLiteralValue): ReferenceLiteralValue {
|
||||
if(refLiteral.heapId!=null && refLiteral.parent !is VarDecl) {
|
||||
// a literal value that's not declared as a variable, which refers to something on the heap.
|
||||
// we need to introduce an auto-generated variable for this to be able to refer to the value!
|
||||
// (note: ususally, this has been taken care of already when the var was created)
|
||||
if(refLiteral.parent !is VarDecl) {
|
||||
// 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
|
||||
val declaredType = if(refLiteral.isArray) ArrayElementTypes.getValue(refLiteral.type) else refLiteral.type
|
||||
val variable = VarDecl(VarDeclType.VAR,
|
||||
declaredType,
|
||||
ZeropageWish.NOT_IN_ZEROPAGE,
|
||||
null,
|
||||
"$autoHeapValuePrefix${refLiteral.heapId}",
|
||||
null,
|
||||
refLiteral,
|
||||
isArray = refLiteral.isArray, autogeneratedDontRemove = true, position = refLiteral.position)
|
||||
anonymousVariablesFromHeap[variable.name] = Pair(refLiteral, variable)
|
||||
val variable = VarDecl.createAuto(refLiteral)
|
||||
addVarDecl(refLiteral.definingScope(), variable)
|
||||
// TODO anonymousVariablesFromHeap[variable.name] = Pair(refLiteral, variable)
|
||||
}
|
||||
return super.visit(refLiteral)
|
||||
}
|
||||
@ -243,4 +242,12 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
|
||||
return super.visit(structDecl)
|
||||
}
|
||||
|
||||
private fun addVarDecl(scope: INameScope, variable: VarDecl) {
|
||||
if(scope !in vardeclsToAdd)
|
||||
vardeclsToAdd[scope] = mutableListOf()
|
||||
val declList = vardeclsToAdd.getValue(scope)
|
||||
if(declList.all{it.name!=variable.name})
|
||||
declList.add(variable)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package prog8.ast.processing
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.base.autoHeapValuePrefix
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
|
||||
@ -24,7 +23,6 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
|
||||
override fun visit(module: Module) {
|
||||
vardeclsToAdd.clear()
|
||||
super.visit(module)
|
||||
|
||||
// add any new vardecls to the various scopes
|
||||
for((where, decls) in vardeclsToAdd) {
|
||||
where.statements.addAll(0, decls)
|
||||
@ -96,17 +94,15 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
|
||||
}
|
||||
else if(strvalue!=null) {
|
||||
if(strvalue.isString) {
|
||||
// add a vardecl so that the autovar can be resolved in later lookups
|
||||
val variable = VarDecl.createAuto(strvalue)
|
||||
addVarDecl(strvalue.definingScope(), variable)
|
||||
// replace the argument with &autovar
|
||||
val autoVarName = "$autoHeapValuePrefix${strvalue.heapId}"
|
||||
val autoHeapvarRef = IdentifierReference(listOf(autoVarName), strvalue.position)
|
||||
val autoHeapvarRef = IdentifierReference(listOf(variable.name), strvalue.position)
|
||||
val pointerExpr = AddressOf(autoHeapvarRef, strvalue.position)
|
||||
pointerExpr.scopedname = parent.makeScopedName(autoVarName)
|
||||
pointerExpr.scopedname = parent.makeScopedName(variable.name)
|
||||
pointerExpr.linkParents(arglist[argparam.first.index].parent)
|
||||
arglist[argparam.first.index] = pointerExpr
|
||||
// add a vardecl so that the autovar can be resolved in later lookups
|
||||
val variable = VarDecl(VarDeclType.VAR, strvalue.type, ZeropageWish.NOT_IN_ZEROPAGE, null, autoVarName, null, strvalue,
|
||||
isArray = false, autogeneratedDontRemove = false, position = strvalue.position)
|
||||
addVarDecl(strvalue.definingScope(), variable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -162,6 +162,14 @@ class VarDecl(val type: VarDeclType,
|
||||
override val expensiveToInline
|
||||
get() = value!=null && value !is NumericLiteralValue
|
||||
|
||||
companion object {
|
||||
fun createAuto(refLv: ReferenceLiteralValue): VarDecl {
|
||||
val autoVarName = "$autoHeapValuePrefix${refLv.heapId}"
|
||||
return VarDecl(VarDeclType.VAR, refLv.type, ZeropageWish.NOT_IN_ZEROPAGE, null, autoVarName, null, refLv,
|
||||
isArray = false, autogeneratedDontRemove = true, position = refLv.position)
|
||||
}
|
||||
}
|
||||
|
||||
val datatypeErrors = mutableListOf<SyntaxError>() // don't crash at init time, report them in the AstChecker
|
||||
val datatype =
|
||||
if (!isArray) declaredDatatype
|
||||
|
@ -116,7 +116,7 @@ class HeapValues {
|
||||
|
||||
fun get(heapId: Int): HeapValue {
|
||||
return heap[heapId] ?:
|
||||
throw IllegalArgumentException("heapId not found in heap")
|
||||
throw IllegalArgumentException("heapId $heapId not found in heap")
|
||||
}
|
||||
|
||||
fun allEntries() = heap.entries
|
||||
@ -746,7 +746,8 @@ internal class Compiler(private val program: Program) {
|
||||
val arg=args.single()
|
||||
when (arg.inferType(program)) {
|
||||
DataType.STR, DataType.STR_S -> createSyscall("${funcname}_str")
|
||||
else -> throw CompilerException("wrong datatype for len()")
|
||||
in ArrayDatatypes -> throw CompilerException("len() of an array type should have been const-folded")
|
||||
else -> throw CompilerException("wrong datatype for len() $arg")
|
||||
}
|
||||
}
|
||||
"any", "all" -> {
|
||||
|
@ -417,10 +417,10 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
||||
RuntimeValue(decl.datatype, heapId = litval.heapId)
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
val litval = (decl.value as ReferenceLiteralValue)
|
||||
if(litval.heapId==null)
|
||||
val litval = (decl.value as? ReferenceLiteralValue)
|
||||
if(litval!=null && litval.heapId==null)
|
||||
throw CompilerException("array should already be in the heap")
|
||||
RuntimeValue(decl.datatype, heapId = litval.heapId)
|
||||
RuntimeValue(decl.datatype, heapId = litval?.heapId ?: -999)
|
||||
}
|
||||
DataType.STRUCT -> {
|
||||
// struct variables have been flattened already
|
||||
|
@ -7,6 +7,7 @@ import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.ast.expressions.ReferenceLiteralValue
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.compiler.CompilerException
|
||||
import kotlin.math.*
|
||||
|
||||
|
||||
@ -336,44 +337,40 @@ private fun builtinLen(args: List<IExpression>, position: Position, program: Pro
|
||||
// note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE.
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("len requires one argument", position)
|
||||
var argument = args[0].constValue(program)
|
||||
if(argument==null) {
|
||||
val constArg = args[0].constValue(program)
|
||||
if(constArg!=null)
|
||||
throw SyntaxError("len of weird argument ${args[0]}", position)
|
||||
|
||||
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program.namespace)
|
||||
val arraySize = directMemVar?.arraysize?.size()
|
||||
var arraySize = directMemVar?.arraysize?.size()
|
||||
if(arraySize != null)
|
||||
return NumericLiteralValue.optimalInteger(arraySize, position)
|
||||
if(args[0] !is IdentifierReference)
|
||||
throw SyntaxError("len argument should be an identifier, but is ${args[0]}", position)
|
||||
val target = (args[0] as IdentifierReference).targetStatement(program.namespace)
|
||||
val argValue = (target as? VarDecl)?.value
|
||||
argument = argValue?.constValue(program)
|
||||
?: throw NotConstArgumentException()
|
||||
val target = (args[0] as IdentifierReference).targetStatement(program.namespace) as VarDecl
|
||||
|
||||
return when(target.datatype) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
arraySize = target.arraysize!!.size()!!
|
||||
if(arraySize>256)
|
||||
throw CompilerException("array length exceeds byte limit ${target.position}")
|
||||
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
arraySize = target.arraysize!!.size()!!
|
||||
if(arraySize>256)
|
||||
throw CompilerException("array length exceeds byte limit ${target.position}")
|
||||
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
||||
}
|
||||
in StringDatatypes -> {
|
||||
val refLv = target.value as ReferenceLiteralValue
|
||||
if(refLv.str!!.length>255)
|
||||
throw CompilerException("string length exceeds byte limit ${refLv.position}")
|
||||
NumericLiteralValue.optimalInteger(refLv.str.length, args[0].position)
|
||||
}
|
||||
in NumericDatatypes -> throw SyntaxError("len of weird argument ${args[0]}", position)
|
||||
else -> throw CompilerException("weird datatype")
|
||||
}
|
||||
|
||||
TODO("collection functions over iterables (array, string) $argument")
|
||||
|
||||
// return when(argument.type) {
|
||||
// DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
// val arraySize = argument.arrayvalue?.size ?: program.heap.get(argument.heapId!!).arraysize
|
||||
// if(arraySize>256)
|
||||
// throw CompilerException("array length exceeds byte limit ${argument.position}")
|
||||
// LiteralValue.optimalInteger(arraySize, args[0].position)
|
||||
// }
|
||||
// DataType.ARRAY_F -> {
|
||||
// val arraySize = argument.arrayvalue?.size ?: program.heap.get(argument.heapId!!).arraysize
|
||||
// if(arraySize>256)
|
||||
// throw CompilerException("array length exceeds byte limit ${argument.position}")
|
||||
// LiteralValue.optimalInteger(arraySize, args[0].position)
|
||||
// }
|
||||
// in StringDatatypes -> {
|
||||
// val str = argument.strvalue!!
|
||||
// if(str.length>255)
|
||||
// throw CompilerException("string length exceeds byte limit ${argument.position}")
|
||||
// LiteralValue.optimalInteger(str.length, args[0].position)
|
||||
// }
|
||||
// in NumericDatatypes -> throw SyntaxError("len of weird argument ${args[0]}", position)
|
||||
// else -> throw CompilerException("weird datatype")
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
@ -618,20 +618,19 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
||||
throw FatalAstException("missing array value")
|
||||
}
|
||||
|
||||
val array = litval.array!!
|
||||
val typesInArray = array.mapNotNull { it.inferType(program) }.toSet()
|
||||
val typesInArray = litval.array.mapNotNull { it.inferType(program) }.toSet()
|
||||
val arrayDt =
|
||||
when {
|
||||
array.any { it is AddressOf } -> DataType.ARRAY_UW
|
||||
litval.array.any { it is AddressOf } -> DataType.ARRAY_UW
|
||||
DataType.FLOAT in typesInArray -> DataType.ARRAY_F
|
||||
DataType.WORD in typesInArray -> DataType.ARRAY_W
|
||||
else -> {
|
||||
val allElementsAreConstantOrAddressOf = array.fold(true) { c, expr-> c and (expr is NumericLiteralValue|| expr is AddressOf)}
|
||||
val allElementsAreConstantOrAddressOf = litval.array.fold(true) { c, expr-> c and (expr is NumericLiteralValue|| expr is AddressOf)}
|
||||
if(!allElementsAreConstantOrAddressOf) {
|
||||
addError(ExpressionError("array literal can only consist of constant primitive numerical values or memory pointers", litval.position))
|
||||
return litval
|
||||
} else {
|
||||
val integerArray = array.map { it.constValue(program)!!.number.toInt() }
|
||||
val integerArray = litval.array.map { it.constValue(program)!!.number.toInt() }
|
||||
val maxValue = integerArray.max()!!
|
||||
val minValue = integerArray.min()!!
|
||||
if (minValue >= 0) {
|
||||
|
@ -226,18 +226,18 @@ class Program (val name: String,
|
||||
if(valueStr[0] !='"' && ':' !in valueStr)
|
||||
throw VmExecutionException("missing value type character")
|
||||
val value = when(val type = DataType.valueOf(typeStr.toUpperCase())) {
|
||||
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, valueStr.substring(3).toShort(16))
|
||||
DataType.BYTE -> RuntimeValue(DataType.BYTE, valueStr.substring(2).toShort(16))
|
||||
DataType.UWORD -> RuntimeValue(DataType.UWORD, valueStr.substring(3).toInt(16))
|
||||
DataType.WORD -> RuntimeValue(DataType.WORD, valueStr.substring(2).toInt(16))
|
||||
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, valueStr.substring(2).toDouble())
|
||||
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, valueStr.substring(3).substringBefore(' ').toShort(16))// TODO process ZP and struct info?
|
||||
DataType.BYTE -> RuntimeValue(DataType.BYTE, valueStr.substring(2).substringBefore(' ').toShort(16))// TODO process ZP and struct info?
|
||||
DataType.UWORD -> RuntimeValue(DataType.UWORD, valueStr.substring(3).substringBefore(' ').toInt(16))// TODO process ZP and struct info?
|
||||
DataType.WORD -> RuntimeValue(DataType.WORD, valueStr.substring(2).substringBefore(' ').toInt(16))// TODO process ZP and struct info?
|
||||
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, valueStr.substring(2).substringBefore(' ').toDouble())// TODO process ZP and struct info?
|
||||
in StringDatatypes -> {
|
||||
if(valueStr.startsWith('"') && valueStr.endsWith('"'))
|
||||
throw VmExecutionException("encountered a var with a string value, but all string values should already have been moved into the heap")
|
||||
else if(!valueStr.startsWith("heap:"))
|
||||
throw VmExecutionException("invalid string value, should be a heap reference")
|
||||
else {
|
||||
val heapId = valueStr.substring(5).toInt()
|
||||
val heapId = valueStr.substring(5).substringBefore(' ').toInt() // TODO process ZP and struct info?
|
||||
RuntimeValue(type, heapId = heapId)
|
||||
}
|
||||
}
|
||||
@ -245,7 +245,7 @@ class Program (val name: String,
|
||||
if(!valueStr.startsWith("heap:"))
|
||||
throw VmExecutionException("invalid array value, should be a heap reference")
|
||||
else {
|
||||
val heapId = valueStr.substring(5).toInt()
|
||||
val heapId = valueStr.substring(5).substringBefore(' ').toInt() // TODO process ZP and struct info?
|
||||
RuntimeValue(type, heapId = heapId)
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +215,7 @@ Variable declarations
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Variables should be declared with their exact type and size so the compiler can allocate storage
|
||||
for them. You must give them an initial value as well. That value can be a simple literal value,
|
||||
for them. You can give them an initial value as well. That value can be a simple literal value,
|
||||
or an expression. You can add a ``@zp`` zeropage-tag, to tell the compiler to prioritize it
|
||||
when selecting variables to be put into zeropage.
|
||||
The syntax is::
|
||||
|
@ -3,41 +3,17 @@
|
||||
|
||||
~ main {
|
||||
|
||||
struct Color {
|
||||
uword red
|
||||
ubyte green
|
||||
ubyte blue
|
||||
}
|
||||
|
||||
str naam = "irmen"
|
||||
word[] array = [1,2,3,4]
|
||||
uword uw = $ab12
|
||||
Color rgb = [255,128,0]
|
||||
Color rgb2 = [111,222,33]
|
||||
|
||||
ubyte @zp zpvar=99
|
||||
|
||||
sub start() {
|
||||
|
||||
uword fake_address
|
||||
|
||||
fake_address = &naam
|
||||
c64scr.print_uwhex(1, fake_address)
|
||||
c64scr.print(", ")
|
||||
|
||||
fake_address = &array
|
||||
c64scr.print_uwhex(1, fake_address)
|
||||
c64scr.print(", ")
|
||||
|
||||
fake_address = &rgb
|
||||
c64scr.print_uwhex(1, fake_address)
|
||||
c64scr.print("\n")
|
||||
|
||||
; @todo only works once reference types are actually references:
|
||||
;str name2 = naam ; @todo name2 points to same str as naam
|
||||
;str name2 = fake_address ; @todo fake_address hopefully points to a str
|
||||
;Color colz = fake_address ; @todo fake_address hopefully points to a Color
|
||||
str naam = "irmen"
|
||||
|
||||
ubyte length = len(naam)
|
||||
c64scr.print(naam)
|
||||
c64scr.print("irmen")
|
||||
c64scr.print("irmen2")
|
||||
c64scr.print("irmen2")
|
||||
ubyte length2 = len("irmen") ; @todo same string as 'naam'
|
||||
ubyte length3 = len("zxfdsfsf") ; @todo new string
|
||||
return
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user