This commit is contained in:
Irmen de Jong 2019-07-15 02:26:51 +02:00
parent d5b12fb01d
commit 78d7849197
11 changed files with 121 additions and 141 deletions

View File

@ -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
@ -358,13 +353,13 @@ internal class AstChecker(private val program: Program,
override fun visit(assignTarget: AssignTarget) {
val memAddr = assignTarget.memoryAddress?.addressExpression?.constValue(program)?.number?.toInt()
if(memAddr!=null) {
if(memAddr<0 || memAddr>=65536)
if (memAddr != null) {
if (memAddr < 0 || memAddr >= 65536)
checkResult.add(ExpressionError("address out of range", assignTarget.position))
}
val assignment = assignTarget.parent as Assignment
if(assignTarget.identifier!=null) {
val assignment = assignTarget.parent as IStatement
if (assignTarget.identifier != null) {
val targetName = assignTarget.identifier.nameInSource
val targetSymbol = program.namespace.lookup(targetName, assignment)
when (targetSymbol) {
@ -377,7 +372,7 @@ internal class AstChecker(private val program: Program,
return
}
else -> {
if(targetSymbol.type == VarDeclType.CONST) {
if (targetSymbol.type == VarDeclType.CONST) {
checkResult.add(ExpressionError("cannot assign new value to a constant", assignment.position))
return
}
@ -385,15 +380,17 @@ internal class AstChecker(private val program: Program,
}
}
if(assignment.aug_op!=null)
throw FatalAstException("augmented assignment should have been converted into normal assignment")
if (assignment is Assignment) {
val targetDatatype = assignTarget.inferType(program, assignment)
if(targetDatatype!=null) {
val constVal = assignment.value.constValue(program)
if(constVal!=null) {
checkValueTypeAndRange(targetDatatype, constVal)
// TODO what about arrays etc:
if (assignment.aug_op != null)
throw FatalAstException("augmented assignment should have been converted into normal assignment")
val targetDatatype = assignTarget.inferType(program, assignment)
if (targetDatatype != null) {
val constVal = assignment.value.constValue(program)
if (constVal != null) {
checkValueTypeAndRange(targetDatatype, constVal)
// TODO what about arrays etc:
// val targetVar =
// if(target.identifier!=null)
// program.namespace.lookup(target.identifier.nameInSource, assignment) as? VarDecl
@ -403,19 +400,18 @@ internal class AstChecker(private val program: Program,
// checkValueTypeAndRange(targetDatatype, targetVar?.struct,
// arrayspec ?: ArrayIndex(NumericLiteralValue.optimalInteger(-1, assignment.position), assignment.position),
// constVal, program.heap)
} else {
val sourceDatatype: DataType? = assignment.value.inferType(program)
if(sourceDatatype==null) {
if (assignment.value is FunctionCall) {
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 {
val sourceDatatype: DataType? = assignment.value.inferType(program)
if (sourceDatatype == null) {
if (assignment.value is FunctionCall) {
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
checkResult.add(ExpressionError("assignment value is invalid or has no proper datatype", assignment.value.position))
} else {
checkAssignmentCompatible(targetDatatype, assignTarget, sourceDatatype, assignment.value, assignment.position)
}
else
checkResult.add(ExpressionError("assignment value is invalid or has no proper datatype", assignment.value.position))
}
else {
checkAssignmentCompatible(targetDatatype, assignTarget, sourceDatatype, assignment.value, assignment.position)
}
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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" -> {

View File

@ -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

View File

@ -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 directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program.namespace)
val 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 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)
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) 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")
// }
}

View File

@ -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) {

View File

@ -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)
}
}

View File

@ -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::

View File

@ -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
}