some more program node cleanups

This commit is contained in:
Irmen de Jong 2019-06-20 21:06:51 +02:00
parent e96d3d4455
commit b35430214b
14 changed files with 325 additions and 341 deletions

View File

@ -36,8 +36,7 @@ fun main(args: Array<String>) {
fun printSoftwareHeader(what: String) {
val buildVersion = object {}.javaClass.getResource("/version.txt").readText().trim()
println("\nProg8 $what by Irmen de Jong (irmen@razorvine.net)")
println("Version: $buildVersion")
println("\nProg8 $what v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
}

View File

@ -806,17 +806,17 @@ data class AssignTarget(val register: Register?,
}
}
fun determineDatatype(namespace: INameScope, heap: HeapValues, stmt: IStatement): DataType? {
fun determineDatatype(program: Program, stmt: IStatement): DataType? {
if(register!=null)
return DataType.UBYTE
if(identifier!=null) {
val symbol = namespace.lookup(identifier.nameInSource, stmt) ?: return null
val symbol = program.namespace.lookup(identifier.nameInSource, stmt) ?: return null
if (symbol is VarDecl) return symbol.datatype
}
if(arrayindexed!=null) {
val dt = arrayindexed.resultingDatatype(namespace, heap)
val dt = arrayindexed.resultingDatatype(program)
if(dt!=null)
return dt
}
@ -846,12 +846,11 @@ data class AssignTarget(val register: Register?,
interface IExpression: Node {
// TODO pass programAst instead of namespace + heap?
fun isIterable(namespace: INameScope, heap: HeapValues): Boolean
fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue?
fun isIterable(program: Program): Boolean
fun constValue(program: Program): LiteralValue?
fun process(processor: IAstProcessor): IExpression
fun referencesIdentifier(name: String): Boolean
fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType?
fun resultingDatatype(program: Program): DataType?
}
@ -865,11 +864,11 @@ class PrefixExpression(val operator: String, var expression: IExpression, overri
expression.linkParents(this)
}
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = null
override fun constValue(program: Program): LiteralValue? = null
override fun process(processor: IAstProcessor) = processor.process(this)
override fun referencesIdentifier(name: String) = expression.referencesIdentifier(name)
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? = expression.resultingDatatype(namespace, heap)
override fun isIterable(namespace: INameScope, heap: HeapValues) = false
override fun resultingDatatype(program: Program): DataType? = expression.resultingDatatype(program)
override fun isIterable(program: Program) = false
override fun toString(): String {
return "Prefix($operator $expression)"
@ -891,13 +890,13 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
}
// binary expression should actually have been optimized away into a single value, before const value was requested...
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = null
override fun isIterable(namespace: INameScope, heap: HeapValues) = false
override fun constValue(program: Program): LiteralValue? = null
override fun isIterable(program: Program) = false
override fun process(processor: IAstProcessor) = processor.process(this)
override fun referencesIdentifier(name: String) = left.referencesIdentifier(name) || right.referencesIdentifier(name)
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? {
val leftDt = left.resultingDatatype(namespace, heap)
val rightDt = right.resultingDatatype(namespace, heap)
override fun resultingDatatype(program: Program): DataType? {
val leftDt = left.resultingDatatype(program)
val rightDt = right.resultingDatatype(program)
return when(operator) {
"+", "-", "*", "**", "%" -> if(leftDt==null || rightDt==null) null else {
try {
@ -998,13 +997,13 @@ class ArrayIndexedExpression(val identifier: IdentifierReference,
arrayspec.linkParents(this)
}
override fun isIterable(namespace: INameScope, heap: HeapValues) = false
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = null
override fun isIterable(program: Program) = false
override fun constValue(program: Program): LiteralValue? = null
override fun process(processor: IAstProcessor): IExpression = processor.process(this)
override fun referencesIdentifier(name: String) = identifier.referencesIdentifier(name)
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? {
val target = identifier.targetStatement(namespace)
override fun resultingDatatype(program: Program): DataType? {
val target = identifier.targetStatement(program.namespace)
if (target is VarDecl) {
return when (target.datatype) {
in NumericDatatypes -> null
@ -1036,10 +1035,10 @@ class TypecastExpression(var expression: IExpression, var type: DataType, overri
override fun process(processor: IAstProcessor) = processor.process(this)
override fun referencesIdentifier(name: String) = expression.referencesIdentifier(name)
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? = type
override fun isIterable(namespace: INameScope, heap: HeapValues) = type in IterableDatatypes
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? {
val cv = expression.constValue(namespace, heap) ?: return null
override fun resultingDatatype(program: Program): DataType? = type
override fun isIterable(program: Program) = type in IterableDatatypes
override fun constValue(program: Program): LiteralValue? {
val cv = expression.constValue(program) ?: return null
val value = Value(cv.type, cv.asNumericValue!!).cast(type)
return LiteralValue.fromNumber(value.numericValue(), value.type, position)
}
@ -1059,10 +1058,10 @@ data class AddressOf(val identifier: IdentifierReference, override val position:
}
var scopedname: String? = null // will be set in a later state by the compiler
override fun isIterable(namespace: INameScope, heap: HeapValues) = false
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = null
override fun isIterable(program: Program) = false
override fun constValue(program: Program): LiteralValue? = null
override fun referencesIdentifier(name: String) = false
override fun resultingDatatype(namespace: INameScope, heap: HeapValues) = DataType.UWORD
override fun resultingDatatype(program: Program) = DataType.UWORD
override fun process(processor: IAstProcessor) = processor.process(this)
}
@ -1077,9 +1076,9 @@ class DirectMemoryRead(var addressExpression: IExpression, override val position
override fun process(processor: IAstProcessor) = processor.process(this)
override fun referencesIdentifier(name: String) = false
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? = DataType.UBYTE
override fun isIterable(namespace: INameScope, heap: HeapValues) = false
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = null
override fun resultingDatatype(program: Program): DataType? = DataType.UBYTE
override fun isIterable(program: Program) = false
override fun constValue(program: Program): LiteralValue? = null
override fun toString(): String {
return "DirectMemoryRead($addressExpression)"
@ -1097,9 +1096,9 @@ class DirectMemoryWrite(var addressExpression: IExpression, override val positio
override fun process(processor: IAstProcessor) = processor.process(this)
override fun referencesIdentifier(name: String) = false
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? = DataType.UBYTE
override fun isIterable(namespace: INameScope, heap: HeapValues) = false
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = null
override fun resultingDatatype(program: Program): DataType? = DataType.UBYTE
override fun isIterable(program: Program) = false
override fun constValue(program: Program): LiteralValue? = null
override fun toString(): String {
return "DirectMemoryWrite($addressExpression)"
@ -1209,7 +1208,7 @@ class LiteralValue(val type: DataType,
arrayvalue?.forEach {it.linkParents(this)}
}
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = this
override fun constValue(program: Program): LiteralValue? = this
override fun process(processor: IAstProcessor) = processor.process(this)
override fun toString(): String {
@ -1232,9 +1231,9 @@ class LiteralValue(val type: DataType,
return "LiteralValue($vstr)"
}
override fun resultingDatatype(namespace: INameScope, heap: HeapValues) = type
override fun resultingDatatype(program: Program) = type
override fun isIterable(namespace: INameScope, heap: HeapValues): Boolean = type in IterableDatatypes
override fun isIterable(program: Program): Boolean = type in IterableDatatypes
override fun hashCode(): Int {
val bh = bytevalue?.hashCode() ?: 0x10001234
@ -1363,13 +1362,13 @@ class RangeExpr(var from: IExpression,
step.linkParents(this)
}
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = null
override fun isIterable(namespace: INameScope, heap: HeapValues) = true
override fun constValue(program: Program): LiteralValue? = null
override fun isIterable(program: Program) = true
override fun process(processor: IAstProcessor) = processor.process(this)
override fun referencesIdentifier(name: String): Boolean = from.referencesIdentifier(name) || to.referencesIdentifier(name)
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? {
val fromDt=from.resultingDatatype(namespace, heap)
val toDt=to.resultingDatatype(namespace, heap)
override fun resultingDatatype(program: Program): DataType? {
val fromDt=from.resultingDatatype(program)
val toDt=to.resultingDatatype(program)
return when {
fromDt==null || toDt==null -> null
fromDt==DataType.UBYTE && toDt==DataType.UBYTE -> DataType.UBYTE
@ -1433,15 +1432,15 @@ class RegisterExpr(val register: Register, override val position: Position) : IE
this.parent = parent
}
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = null
override fun constValue(program: Program): LiteralValue? = null
override fun process(processor: IAstProcessor) = this
override fun referencesIdentifier(name: String): Boolean = false
override fun isIterable(namespace: INameScope, heap: HeapValues) = false
override fun isIterable(program: Program) = false
override fun toString(): String {
return "RegisterExpr(register=$register, pos=$position)"
}
override fun resultingDatatype(namespace: INameScope, heap: HeapValues) = DataType.UBYTE
override fun resultingDatatype(program: Program) = DataType.UBYTE
}
@ -1461,8 +1460,8 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
this.parent = parent
}
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? {
val node = namespace.lookup(nameInSource, this)
override fun constValue(program: Program): LiteralValue? {
val node = program.namespace.lookup(nameInSource, this)
?: throw UndefinedSymbolError(this)
val vardecl = node as? VarDecl
if(vardecl==null) {
@ -1470,7 +1469,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
} else if(vardecl.type!=VarDeclType.CONST) {
return null
}
return vardecl.value?.constValue(namespace, heap)
return vardecl.value?.constValue(program)
}
override fun toString(): String {
@ -1480,8 +1479,8 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
override fun process(processor: IAstProcessor) = processor.process(this)
override fun referencesIdentifier(name: String): Boolean = nameInSource.last() == name // @todo is this correct all the time?
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? {
val targetStmt = targetStatement(namespace)
override fun resultingDatatype(program: Program): DataType? {
val targetStmt = targetStatement(program.namespace)
if(targetStmt is VarDecl) {
return targetStmt.datatype
} else {
@ -1489,7 +1488,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
}
}
override fun isIterable(namespace: INameScope, heap: HeapValues): Boolean = resultingDatatype(namespace, heap) in IterableDatatypes
override fun isIterable(program: Program): Boolean = resultingDatatype(program) in IterableDatatypes
fun heapId(namespace: INameScope): Int {
val node = namespace.lookup(nameInSource, this) ?: throw UndefinedSymbolError(this)
@ -1544,9 +1543,9 @@ class FunctionCall(override var target: IdentifierReference,
arglist.forEach { it.linkParents(this) }
}
override fun constValue(namespace: INameScope, heap: HeapValues) = constValue(namespace, heap, true)
override fun constValue(program: Program) = constValue(program, true)
private fun constValue(namespace: INameScope, heap: HeapValues, withDatatypeCheck: Boolean): LiteralValue? {
private fun constValue(program: Program, withDatatypeCheck: Boolean): LiteralValue? {
// if the function is a built-in function and the args are consts, should try to const-evaluate!
// lenghts of arrays and strings are constants that are determined at compile time!
if(target.nameInSource.size>1) return null
@ -1556,13 +1555,13 @@ class FunctionCall(override var target: IdentifierReference,
if(func!=null) {
val exprfunc = func.constExpressionFunc
if(exprfunc!=null)
resultValue = exprfunc(arglist, position, namespace, heap)
resultValue = exprfunc(arglist, position, program)
else if(func.returntype==null)
throw ExpressionError("builtin function ${target.nameInSource[0]} can't be used here because it doesn't return a value", position)
}
if(withDatatypeCheck) {
val resultDt = this.resultingDatatype(namespace, heap)
val resultDt = this.resultingDatatype(program)
if(resultValue==null || resultDt == resultValue.type)
return resultValue
throw FatalAstException("evaluated const expression result value doesn't match expected datatype $resultDt, pos=$position")
@ -1583,18 +1582,18 @@ class FunctionCall(override var target: IdentifierReference,
override fun process(processor: IAstProcessor) = processor.process(this)
override fun referencesIdentifier(name: String): Boolean = target.referencesIdentifier(name) || arglist.any{it.referencesIdentifier(name)}
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? {
val constVal = constValue(namespace, heap,false)
override fun resultingDatatype(program: Program): DataType? {
val constVal = constValue(program ,false)
if(constVal!=null)
return constVal.type
val stmt = target.targetStatement(namespace) ?: return null
val stmt = target.targetStatement(program.namespace) ?: return null
when (stmt) {
is BuiltinFunctionStatementPlaceholder -> {
if(target.nameInSource[0] == "set_carry" || target.nameInSource[0]=="set_irqd" ||
target.nameInSource[0] == "clear_carry" || target.nameInSource[0]=="clear_irqd") {
return null // these have no return value
}
return builtinFunctionReturnType(target.nameInSource[0], this.arglist, namespace, heap)
return builtinFunctionReturnType(target.nameInSource[0], this.arglist, program)
}
is Subroutine -> {
if(stmt.returntypes.isEmpty())
@ -1608,7 +1607,7 @@ class FunctionCall(override var target: IdentifierReference,
return null // calling something we don't recognise...
}
override fun isIterable(namespace: INameScope, heap: HeapValues) = resultingDatatype(namespace, heap) in IterableDatatypes
override fun isIterable(program: Program) = resultingDatatype(program) in IterableDatatypes
}

View File

@ -14,7 +14,7 @@ import java.io.File
*/
fun Program.checkValid(compilerOptions: CompilationOptions) {
val checker = AstChecker(namespace, compilerOptions, heap)
val checker = AstChecker(this, compilerOptions)
checker.process(this)
printErrors(checker.result(), name)
}
@ -52,14 +52,13 @@ fun printWarning(msg: String) {
print("\u001b[0m\n") // normal
}
private class AstChecker(private val namespace: INameScope,
private val compilerOptions: CompilationOptions,
private val heap: HeapValues) : IAstProcessor {
private class AstChecker(private val program: Program,
private val compilerOptions: CompilationOptions) : IAstProcessor {
private val checkResult: MutableList<AstException> = mutableListOf()
private val heapStringSentinel: Int
init {
val stringSentinel = heap.allEntries().firstOrNull {it.value.str==""}
heapStringSentinel = stringSentinel?.key ?: heap.addString(DataType.STR, "")
val stringSentinel = program.heap.allEntries().firstOrNull {it.value.str==""}
heapStringSentinel = stringSentinel?.key ?: program.heap.addString(DataType.STR, "")
}
fun result(): List<AstException> {
@ -67,13 +66,14 @@ private class AstChecker(private val namespace: INameScope,
}
override fun process(program: Program) {
assert(program === this.program)
// there must be a single 'main' block with a 'start' subroutine for the program entry point.
val mainBlocks = program.modules.flatMap { it.statements }.filter { b -> b is Block && b.name=="main" }.map { it as Block }
if(mainBlocks.size>1)
checkResult.add(SyntaxError("more than one 'main' block", mainBlocks[0].position))
for(mainBlock in mainBlocks) {
val startSub = mainBlock.subScopes().get("start") as? Subroutine
val startSub = mainBlock.subScopes()["start"] as? Subroutine
if (startSub == null) {
checkResult.add(SyntaxError("missing program entrypoint ('start' subroutine in 'main' block)", mainBlock.position))
} else {
@ -106,7 +106,7 @@ private class AstChecker(private val namespace: INameScope,
if(irqBlocks.size>1)
checkResult.add(SyntaxError("more than one 'irq' block", irqBlocks[0].position))
for(irqBlock in irqBlocks) {
val irqSub = irqBlock?.subScopes()?.get("irq") as? Subroutine
val irqSub = irqBlock.subScopes()["irq"] as? Subroutine
if (irqSub != null) {
if (irqSub.parameters.isNotEmpty() || irqSub.returntypes.isNotEmpty())
checkResult.add(SyntaxError("irq entrypoint subroutine can't have parameters and/or return values", irqSub.position))
@ -132,7 +132,7 @@ private class AstChecker(private val namespace: INameScope,
if(expectedReturnValues.size != returnStmt.values.size) {
// if the return value is a function call, check the result of that call instead
if(returnStmt.values.size==1 && returnStmt.values[0] is FunctionCall) {
val dt = (returnStmt.values[0] as FunctionCall).resultingDatatype(namespace, heap)
val dt = (returnStmt.values[0] as FunctionCall).resultingDatatype(program)
if(dt!=null && expectedReturnValues.isEmpty())
checkResult.add(SyntaxError("invalid number of return values", returnStmt.position))
} else
@ -140,7 +140,7 @@ private class AstChecker(private val namespace: INameScope,
}
for (rv in expectedReturnValues.withIndex().zip(returnStmt.values)) {
val valueDt=rv.second.resultingDatatype(namespace, heap)
val valueDt=rv.second.resultingDatatype(program)
if(rv.first.value!=valueDt)
checkResult.add(ExpressionError("type $valueDt of return value #${rv.first.index+1} doesn't match subroutine return type ${rv.first.value}", rv.second.position))
}
@ -151,10 +151,10 @@ private class AstChecker(private val namespace: INameScope,
if(forLoop.body.isEmpty())
printWarning("for loop body is empty", forLoop.position)
if(!forLoop.iterable.isIterable(namespace, heap)) {
if(!forLoop.iterable.isIterable(program)) {
checkResult.add(ExpressionError("can only loop over an iterable type", forLoop.position))
} else {
val iterableDt = forLoop.iterable.resultingDatatype(namespace, heap)
val iterableDt = forLoop.iterable.resultingDatatype(program)
if (forLoop.loopRegister != null) {
printWarning("using a register as loop variable is risky (it could get clobbered in the body)", forLoop.position)
// loop register
@ -162,7 +162,7 @@ private class AstChecker(private val namespace: INameScope,
checkResult.add(ExpressionError("register can only loop over bytes", forLoop.position))
} else {
// loop variable
val loopvar = forLoop.loopVar!!.targetVarDecl(namespace)
val loopvar = forLoop.loopVar!!.targetVarDecl(program.namespace)
if(loopvar==null || loopvar.type==VarDeclType.CONST) {
checkResult.add(SyntaxError("for loop requires a variable to loop with", forLoop.position))
@ -366,14 +366,14 @@ private class AstChecker(private val namespace: INameScope,
// assigning from a functioncall COULD return multiple values (from an asm subroutine)
if(assignment.value is FunctionCall) {
val stmt = (assignment.value as FunctionCall).target.targetStatement(namespace)
val stmt = (assignment.value as FunctionCall).target.targetStatement(program.namespace)
if (stmt is Subroutine) {
if (stmt.isAsmSubroutine) {
if (stmt.returntypes.size != assignment.targets.size)
checkResult.add(ExpressionError("number of return values doesn't match number of assignment targets", assignment.value.position))
else {
for (thing in stmt.returntypes.zip(assignment.targets)) {
if (thing.second.determineDatatype(namespace, heap, assignment) != thing.first)
if (thing.second.determineDatatype(program, assignment) != thing.first)
checkResult.add(ExpressionError("return type mismatch for target ${thing.second.shortString()}", assignment.value.position))
}
}
@ -390,7 +390,7 @@ private class AstChecker(private val namespace: INameScope,
}
private fun processAssignmentTarget(assignment: Assignment, target: AssignTarget): Assignment {
val memAddr = target.memoryAddress?.addressExpression?.constValue(namespace, heap)?.asIntegerValue
val memAddr = target.memoryAddress?.addressExpression?.constValue(program)?.asIntegerValue
if(memAddr!=null) {
if(memAddr<0 || memAddr>=65536)
checkResult.add(ExpressionError("address out of range", target.position))
@ -398,7 +398,7 @@ private class AstChecker(private val namespace: INameScope,
if(target.identifier!=null) {
val targetName = target.identifier.nameInSource
val targetSymbol = namespace.lookup(targetName, assignment)
val targetSymbol = program.namespace.lookup(targetName, assignment)
when (targetSymbol) {
null -> {
checkResult.add(ExpressionError("undefined symbol: ${targetName.joinToString(".")}", assignment.position))
@ -436,23 +436,23 @@ private class AstChecker(private val namespace: INameScope,
return assignment2
}
val targetDatatype = target.determineDatatype(namespace, heap, assignment)
val targetDatatype = target.determineDatatype(program, assignment)
if(targetDatatype!=null) {
val constVal = assignment.value.constValue(namespace, heap)
val constVal = assignment.value.constValue(program)
if(constVal!=null) {
val arrayspec = if(target.identifier!=null) {
val targetVar = namespace.lookup(target.identifier.nameInSource, assignment) as? VarDecl
val targetVar = program.namespace.lookup(target.identifier.nameInSource, assignment) as? VarDecl
targetVar?.arraysize
} else null
checkValueTypeAndRange(targetDatatype,
arrayspec ?: ArrayIndex(LiteralValue.optimalInteger(-1, assignment.position), assignment.position),
constVal, heap)
constVal, program.heap)
} else {
val sourceDatatype: DataType? = assignment.value.resultingDatatype(namespace, heap)
val sourceDatatype: DataType? = assignment.value.resultingDatatype(program)
if(sourceDatatype==null) {
if(assignment.targets.size<=1) {
if (assignment.value is FunctionCall) {
val targetStmt = (assignment.value as FunctionCall).target.targetStatement(namespace)
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))
}
@ -468,7 +468,7 @@ private class AstChecker(private val namespace: INameScope,
}
override fun process(addressOf: AddressOf): IExpression {
val variable=addressOf.identifier.targetVarDecl(namespace)
val variable=addressOf.identifier.targetVarDecl(program.namespace)
if(variable==null)
checkResult.add(ExpressionError("pointer-of operand must be the name of a heap variable", addressOf.position))
else {
@ -548,11 +548,11 @@ private class AstChecker(private val namespace: INameScope,
decl.value is LiteralValue -> {
val arraySpec = decl.arraysize ?: (
if((decl.value as LiteralValue).isArray)
ArrayIndex.forArray(decl.value as LiteralValue, heap)
ArrayIndex.forArray(decl.value as LiteralValue, program.heap)
else
ArrayIndex(LiteralValue.optimalInteger(-2, decl.position), decl.position)
)
checkValueTypeAndRange(decl.datatype, arraySpec, decl.value as LiteralValue, heap)
checkValueTypeAndRange(decl.datatype, arraySpec, decl.value as LiteralValue, program.heap)
}
else -> {
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!::class.simpleName}")
@ -682,10 +682,10 @@ private class AstChecker(private val namespace: INameScope,
}
val arrayspec =
if(literalValue.isArray)
ArrayIndex.forArray(literalValue, heap)
ArrayIndex.forArray(literalValue, program.heap)
else
ArrayIndex(LiteralValue.optimalInteger(-3, literalValue.position), literalValue.position)
checkValueTypeAndRange(literalValue.type, arrayspec, literalValue, heap)
checkValueTypeAndRange(literalValue.type, arrayspec, literalValue, program.heap)
val lv = super.process(literalValue)
when(lv.type) {
@ -704,7 +704,7 @@ private class AstChecker(private val namespace: INameScope,
override fun process(expr: PrefixExpression): IExpression {
if(expr.operator=="-") {
val dt = expr.resultingDatatype(namespace, heap)
val dt = expr.resultingDatatype(program)
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
checkResult.add(ExpressionError("can only take negative of a signed number type", expr.position))
}
@ -713,12 +713,12 @@ private class AstChecker(private val namespace: INameScope,
}
override fun process(expr: BinaryExpression): IExpression {
val leftDt = expr.left.resultingDatatype(namespace, heap)
val rightDt = expr.right.resultingDatatype(namespace, heap)
val leftDt = expr.left.resultingDatatype(program)
val rightDt = expr.right.resultingDatatype(program)
when(expr.operator){
"/", "%" -> {
val constvalRight = expr.right.constValue(namespace, heap)
val constvalRight = expr.right.constValue(program)
val divisor = constvalRight?.asNumericValue?.toDouble()
if(divisor==0.0)
checkResult.add(ExpressionError("division by zero", expr.right.position))
@ -735,8 +735,8 @@ private class AstChecker(private val namespace: INameScope,
// only integer numeric operands accepted, and if literal constants, only boolean values accepted (0 or 1)
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
checkResult.add(ExpressionError("logical operator can only be used on boolean operands", expr.right.position))
val constLeft = expr.left.constValue(namespace, heap)
val constRight = expr.right.constValue(namespace, heap)
val constLeft = expr.left.constValue(program)
val constRight = expr.right.constValue(program)
if(constLeft!=null && constLeft.asIntegerValue !in 0..1 || constRight!=null && constRight.asIntegerValue !in 0..1)
checkResult.add(ExpressionError("const literal argument of logical operator must be boolean (0 or 1)", expr.position))
}
@ -765,9 +765,9 @@ private class AstChecker(private val namespace: INameScope,
checkResult.add(SyntaxError(msg, range.position))
}
super.process(range)
val from = range.from.constValue(namespace, heap)
val to = range.to.constValue(namespace, heap)
val stepLv = range.step.constValue(namespace, heap) ?: LiteralValue(DataType.UBYTE, 1, position = range.position)
val from = range.from.constValue(program)
val to = range.to.constValue(program)
val stepLv = range.step.constValue(program) ?: LiteralValue(DataType.UBYTE, 1, position = range.position)
if (stepLv.asIntegerValue == null || stepLv.asIntegerValue == 0) {
err("range step must be an integer != 0")
return range
@ -784,8 +784,8 @@ private class AstChecker(private val namespace: INameScope,
err("descending range requires step < 0")
}
from.isString && to.isString -> {
val fromString = from.strvalue(heap)
val toString = to.strvalue(heap)
val fromString = from.strvalue(program.heap)
val toString = to.strvalue(program.heap)
if(fromString.length!=1 || toString.length!=1)
err("range from and to must be a single character")
if(fromString[0] == toString[0])
@ -832,18 +832,18 @@ private class AstChecker(private val namespace: INameScope,
checkResult.add(SyntaxError("invalid number of arguments", position))
else {
for (arg in args.withIndex().zip(func.parameters)) {
val argDt=arg.first.value.resultingDatatype(namespace, heap)
val argDt=arg.first.value.resultingDatatype(program)
if(argDt!=null && !argDt.assignableTo(arg.second.possibleDatatypes)) {
checkResult.add(ExpressionError("builtin function '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.possibleDatatypes}", position))
}
}
if(target.name=="swap") {
// swap() is a bit weird because this one is translated into a sequence of bytecodes, instead of being an actual function call
val dt1 = args[0].resultingDatatype(namespace, heap)!!
val dt2 = args[1].resultingDatatype(namespace, heap)!!
val dt1 = args[0].resultingDatatype(program)!!
val dt2 = args[1].resultingDatatype(program)!!
if (dt1 != dt2)
checkResult.add(ExpressionError("swap requires 2 args of identical type", position))
else if (args[0].constValue(namespace, heap) != null || args[1].constValue(namespace, heap) != null)
else if (args[0].constValue(program) != null || args[1].constValue(program) != null)
checkResult.add(ExpressionError("swap requires 2 variables, not constant value(s)", position))
else if(same(args[0], args[1]))
checkResult.add(ExpressionError("swap should have 2 different args", position))
@ -856,7 +856,7 @@ private class AstChecker(private val namespace: INameScope,
checkResult.add(SyntaxError("invalid number of arguments", position))
else {
for (arg in args.withIndex().zip(target.parameters)) {
val argDt = arg.first.value.resultingDatatype(namespace, heap)
val argDt = arg.first.value.resultingDatatype(program)
if(argDt!=null && !argDt.assignableTo(arg.second.type)) {
// for asm subroutines having STR param it's okay to provide a UWORD too (pointer value)
if(!(target.isAsmSubroutine && arg.second.type in StringDatatypes && argDt==DataType.UWORD))
@ -890,7 +890,7 @@ private class AstChecker(private val namespace: INameScope,
override fun process(postIncrDecr: PostIncrDecr): IStatement {
if(postIncrDecr.target.identifier != null) {
val targetName = postIncrDecr.target.identifier!!.nameInSource
val target = namespace.lookup(targetName, postIncrDecr)
val target = program.namespace.lookup(targetName, postIncrDecr)
if(target==null) {
checkResult.add(SyntaxError("undefined symbol: ${targetName.joinToString(".")}", postIncrDecr.position))
} else {
@ -901,7 +901,7 @@ private class AstChecker(private val namespace: INameScope,
}
}
} else if(postIncrDecr.target.arrayindexed != null) {
val target = postIncrDecr.target.arrayindexed?.identifier?.targetStatement(namespace)
val target = postIncrDecr.target.arrayindexed?.identifier?.targetStatement(program.namespace)
if(target==null) {
checkResult.add(SyntaxError("undefined symbol", postIncrDecr.position))
}
@ -917,7 +917,7 @@ private class AstChecker(private val namespace: INameScope,
}
override fun process(arrayIndexedExpression: ArrayIndexedExpression): IExpression {
val target = arrayIndexedExpression.identifier.targetStatement(namespace)
val target = arrayIndexedExpression.identifier.targetStatement(program.namespace)
if(target is VarDecl) {
if(target.datatype !in IterableDatatypes)
checkResult.add(SyntaxError("indexing requires an iterable variable", arrayIndexedExpression.position))
@ -930,7 +930,7 @@ private class AstChecker(private val namespace: INameScope,
} else if(target.datatype in StringDatatypes) {
// check string lengths
val heapId = (target.value as LiteralValue).heapId!!
val stringLen = heap.get(heapId).str!!.length
val stringLen = program.heap.get(heapId).str!!.length
val index = (arrayIndexedExpression.arrayspec.index as? LiteralValue)?.asIntegerValue
if(index!=null && (index<0 || index>=stringLen))
checkResult.add(ExpressionError("index out of bounds", arrayIndexedExpression.arrayspec.position))
@ -939,7 +939,7 @@ private class AstChecker(private val namespace: INameScope,
checkResult.add(SyntaxError("indexing requires a variable to act upon", arrayIndexedExpression.position))
// check index value 0..255
val dtx = arrayIndexedExpression.arrayspec.index.resultingDatatype(namespace, heap)
val dtx = arrayIndexedExpression.arrayspec.index.resultingDatatype(program)
if(dtx!=DataType.UBYTE && dtx!=DataType.BYTE)
checkResult.add(SyntaxError("array indexing is limited to byte size 0..255", arrayIndexedExpression.position))
@ -947,7 +947,7 @@ private class AstChecker(private val namespace: INameScope,
}
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: IStatement): IStatement? {
val targetStatement = target.targetStatement(namespace)
val targetStatement = target.targetStatement(program.namespace)
if(targetStatement is Label || targetStatement is Subroutine || targetStatement is BuiltinFunctionStatementPlaceholder)
return targetStatement
checkResult.add(NameError("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", statement.position))
@ -955,8 +955,8 @@ private class AstChecker(private val namespace: INameScope,
}
private fun checkValueTypeAndRange(targetDt: DataType, arrayspec: ArrayIndex, range: RangeExpr) : Boolean {
val from = range.from.constValue(namespace, heap)
val to = range.to.constValue(namespace, heap)
val from = range.from.constValue(program)
val to = range.to.constValue(program)
if(from==null || to==null) {
checkResult.add(SyntaxError("range from and to values must be constants", range.position))
return false
@ -973,7 +973,7 @@ private class AstChecker(private val namespace: INameScope,
checkResult.add(ExpressionError("range for string must have single characters from and to values", range.position))
return false
}
val rangeSize=range.size(heap)
val rangeSize=range.size(program.heap)
if(rangeSize!=null && (rangeSize<0 || rangeSize>255)) {
checkResult.add(ExpressionError("size of range for string must be 0..255, instead of $rangeSize", range.position))
return false
@ -983,7 +983,7 @@ private class AstChecker(private val namespace: INameScope,
in ArrayDatatypes -> {
// range and length check bytes
val expectedSize = arrayspec.size()
val rangeSize=range.size(heap)
val rangeSize=range.size(program.heap)
if(rangeSize!=null && rangeSize != expectedSize) {
checkResult.add(ExpressionError("range size doesn't match array size, expected $expectedSize found $rangeSize", range.position))
return false
@ -1059,7 +1059,7 @@ private class AstChecker(private val namespace: INameScope,
if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize<1 || arraySpecSize>256)
return err("byte array length must be 1-256")
val constX = arrayspec.index.constValue(namespace, heap)
val constX = arrayspec.index.constValue(program)
if(constX?.asIntegerValue==null)
return err("array size specifier must be constant integer value")
val expectedSize = constX.asIntegerValue
@ -1081,7 +1081,7 @@ private class AstChecker(private val namespace: INameScope,
if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize<1 || arraySpecSize>128)
return err("word array length must be 1-128")
val constX = arrayspec.index.constValue(namespace, heap)
val constX = arrayspec.index.constValue(program)
if(constX?.asIntegerValue==null)
return err("array size specifier must be constant integer value")
val expectedSize = constX.asIntegerValue
@ -1103,7 +1103,7 @@ private class AstChecker(private val namespace: INameScope,
if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize < 1 || arraySpecSize>51)
return err("float array length must be 1-51")
val constX = arrayspec.index.constValue(namespace, heap)
val constX = arrayspec.index.constValue(program)
if(constX?.asIntegerValue==null)
return err("array size specifier must be constant integer value")
val expectedSize = constX.asIntegerValue
@ -1114,7 +1114,7 @@ private class AstChecker(private val namespace: INameScope,
// check if the floating point values are all within range
val doubles = if(value.arrayvalue!=null)
value.arrayvalue.map {it.constValue(namespace, heap)?.asNumericValue!!.toDouble()}.toDoubleArray()
value.arrayvalue.map {it.constValue(program)?.asNumericValue!!.toDouble()}.toDoubleArray()
else
heap.get(value.heapId!!).doubleArray!!
if(doubles.any { it < FLOAT_MAX_NEGATIVE || it> FLOAT_MAX_POSITIVE})
@ -1128,7 +1128,7 @@ private class AstChecker(private val namespace: INameScope,
}
private fun checkArrayValues(value: LiteralValue, type: DataType): Boolean {
val array = heap.get(value.heapId!!)
val array = program.heap.get(value.heapId!!)
val correct: Boolean
when(type) {
DataType.ARRAY_UB -> {

View File

@ -30,7 +30,7 @@ private class DirectedGraph<VT> {
fun print() {
println("#vertices: $numVertices")
graph.forEach { from, to ->
graph.forEach { (from, to) ->
println("$from CALLS:")
to.forEach { println(" $it") }
}
@ -41,8 +41,8 @@ private class DirectedGraph<VT> {
}
fun checkForCycle(): MutableList<VT> {
val visited = uniqueVertices.associate { it to false }.toMutableMap()
val recStack = uniqueVertices.associate { it to false }.toMutableMap()
val visited = uniqueVertices.associateWith { false }.toMutableMap()
val recStack = uniqueVertices.associateWith { false }.toMutableMap()
val cycle = mutableListOf<VT>()
for(node in uniqueVertices) {
if(isCyclicUntil(node, visited, recStack, cycle))

View File

@ -1,19 +1,17 @@
package prog8.ast
import prog8.compiler.HeapValues
fun Program.reorderStatements() {
val initvalueCreator = VarInitValueAndAddressOfCreator(namespace)
initvalueCreator.process(this)
val checker = StatementReorderer(namespace, heap)
val checker = StatementReorderer(this)
checker.process(this)
}
const val initvarsSubName="prog8_init_vars" // the name of the subroutine that should be called for every block to initialize its variables
private class StatementReorderer(private val namespace: INameScope, private val heap: HeapValues): IAstProcessor {
private class StatementReorderer(private val program: Program): IAstProcessor {
// Reorders the statements in a way the compiler needs.
// - 'main' block must be the very first statement UNLESS it has an address set.
// - blocks are ordered by address, where blocks without address are put at the end.
@ -97,7 +95,7 @@ private class StatementReorderer(private val namespace: INameScope, private val
}
}
val varDecls = block.statements.filter { it is VarDecl }
val varDecls = block.statements.filterIsInstance<VarDecl>()
block.statements.removeAll(varDecls)
block.statements.addAll(0, varDecls)
val directives = block.statements.filter {it is Directive && it.directive in directivesToMove}
@ -160,7 +158,7 @@ private class StatementReorderer(private val namespace: INameScope, private val
if(decl.arraysize==null) {
val array = decl.value as? LiteralValue
if(array!=null && array.isArray) {
val size = heap.get(array.heapId!!).arraysize
val size = program.heap.get(array.heapId!!).arraysize
decl.arraysize = ArrayIndex(LiteralValue.optimalInteger(size, decl.position), decl.position)
}
}
@ -172,8 +170,8 @@ private class StatementReorderer(private val namespace: INameScope, private val
val result = mutableListOf<IStatement>()
val stmtIter = statements.iterator()
for(stmt in stmtIter) {
if(stmt is Assignment && !stmt.targets.any { it.isMemoryMapped(namespace) }) {
val constval = stmt.value.constValue(namespace, heap)
if(stmt is Assignment && !stmt.targets.any { it.isMemoryMapped(program.namespace) }) {
val constval = stmt.value.constValue(program)
if(constval!=null) {
val (sorted, trailing) = sortConstantAssignmentSequence(stmt, stmtIter)
result.addAll(sorted)
@ -196,7 +194,7 @@ private class StatementReorderer(private val namespace: INameScope, private val
while(stmtIter.hasNext()) {
val next = stmtIter.next()
if(next is Assignment) {
val constValue = next.value.constValue(namespace, heap)
val constValue = next.value.constValue(program)
if(constValue==null) {
trailing = next
break
@ -208,7 +206,7 @@ private class StatementReorderer(private val namespace: INameScope, private val
break
}
}
val sorted = sequence.sortedWith(compareBy({it.value.resultingDatatype(namespace, heap)}, {it.singleTarget?.shortString(true)}))
val sorted = sequence.sortedWith(compareBy({it.value.resultingDatatype(program)}, {it.singleTarget?.shortString(true)}))
return Pair(sorted, trailing)
}

View File

@ -145,11 +145,11 @@ data class CompilationOptions(val output: OutputType,
val floats: Boolean)
internal class Compiler(private val programAst2: Program): IAstProcessor {
internal class Compiler(private val program: Program): IAstProcessor {
val prog: IntermediateProgram = IntermediateProgram(programAst2.name, programAst2.loadAddress, programAst2.heap, programAst2.modules.first().source)
val namespace = programAst2.namespace
val heap = programAst2.heap
val prog: IntermediateProgram = IntermediateProgram(program.name, program.loadAddress, program.heap, program.modules.first().source)
val namespace = program.namespace
val heap = program.heap
private var generatedLabelSequenceNumber = 0
private val breakStmtLabelStack : Stack<String> = Stack()
@ -157,7 +157,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
fun compile(options: CompilationOptions) : IntermediateProgram {
println("Creating stackVM code...")
programAst2.modules.forEach {
program.modules.forEach {
process(it)
}
return prog
@ -519,7 +519,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
val trueGoto = stmt.truepart.statements.singleOrNull() as? Jump
if(trueGoto!=null) {
// optimization for if (condition) goto ....
val conditionJumpOpcode = when(stmt.condition.resultingDatatype(namespace, heap)) {
val conditionJumpOpcode = when(stmt.condition.resultingDatatype(program)) {
in ByteDatatypes -> Opcode.JNZ
in WordDatatypes -> Opcode.JNZW
else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt")
@ -529,7 +529,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
return
}
val conditionJumpOpcode = when(stmt.condition.resultingDatatype(namespace, heap)) {
val conditionJumpOpcode = when(stmt.condition.resultingDatatype(program)) {
in ByteDatatypes -> Opcode.JZ
in WordDatatypes -> Opcode.JZW
else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt")
@ -622,11 +622,11 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
}
is PrefixExpression -> {
translate(expr.expression)
translatePrefixOperator(expr.operator, expr.expression.resultingDatatype(namespace, heap))
translatePrefixOperator(expr.operator, expr.expression.resultingDatatype(program))
}
is BinaryExpression -> {
val leftDt = expr.left.resultingDatatype(namespace, heap)!!
val rightDt = expr.right.resultingDatatype(namespace, heap)!!
val leftDt = expr.left.resultingDatatype(program)!!
val rightDt = expr.right.resultingDatatype(program)!!
val commonDt =
if(expr.operator=="/")
BinaryExpression.divisionOpDt(leftDt, rightDt)
@ -639,7 +639,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
if(rightDt!=commonDt)
convertType(rightDt, commonDt)
if(expr.operator=="<<" || expr.operator==">>")
translateBitshiftedOperator(expr.operator, leftDt, expr.right.constValue(namespace, heap))
translateBitshiftedOperator(expr.operator, leftDt, expr.right.constValue(program))
else
translateBinaryOperator(expr.operator, commonDt)
}
@ -662,7 +662,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
is DirectMemoryWrite -> translate(expr)
is AddressOf -> translate(expr)
else -> {
val lv = expr.constValue(namespace, heap) ?: throw CompilerException("constant expression required, not $expr")
val lv = expr.constValue(program) ?: throw CompilerException("constant expression required, not $expr")
when(lv.type) {
in ByteDatatypes -> prog.instr(Opcode.PUSH_BYTE, Value(lv.type, lv.bytevalue!!))
in WordDatatypes -> prog.instr(Opcode.PUSH_WORD, Value(lv.type, lv.wordvalue!!))
@ -798,7 +798,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
// cast type if needed
if(builtinFuncParams!=null) {
val paramDts = builtinFuncParams[index].possibleDatatypes
val argDt = arg.resultingDatatype(namespace, heap)!!
val argDt = arg.resultingDatatype(program)!!
if(argDt !in paramDts) {
for(paramDt in paramDts.sorted())
if(tryConvertType(argDt, paramDt))
@ -811,7 +811,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
"len" -> {
// 1 argument, type determines the exact syscall to use
val arg=args.single()
when (arg.resultingDatatype(namespace, heap)) {
when (arg.resultingDatatype(program)) {
DataType.STR, DataType.STR_S -> createSyscall("${funcname}_str")
else -> throw CompilerException("wrong datatype for len()")
}
@ -822,7 +822,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
val target=arg.targetVarDecl(namespace)!!
val length=Value(DataType.UBYTE, target.arraysize!!.size()!!)
prog.instr(Opcode.PUSH_BYTE, length)
when (arg.resultingDatatype(namespace, heap)) {
when (arg.resultingDatatype(program)) {
DataType.ARRAY_B, DataType.ARRAY_UB -> createSyscall("${funcname}_b")
DataType.ARRAY_W, DataType.ARRAY_UW -> createSyscall("${funcname}_w")
DataType.ARRAY_F -> createSyscall("${funcname}_f")
@ -834,7 +834,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
val arg=args.single() as IdentifierReference
val target=arg.targetVarDecl(namespace)!!
val length=Value(DataType.UBYTE, target.arraysize!!.size()!!)
val arrayDt=arg.resultingDatatype(namespace, heap)
val arrayDt=arg.resultingDatatype(program)
prog.instr(Opcode.PUSH_BYTE, length)
when (arrayDt) {
DataType.ARRAY_UB -> {
@ -866,7 +866,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
val target=arg.targetVarDecl(namespace)!!
val length=Value(DataType.UBYTE, target.arraysize!!.size()!!)
prog.instr(Opcode.PUSH_BYTE, length)
when (arg.resultingDatatype(namespace, heap)) {
when (arg.resultingDatatype(program)) {
DataType.ARRAY_UB -> createSyscall("${funcname}_ub")
DataType.ARRAY_B -> createSyscall("${funcname}_b")
DataType.ARRAY_UW -> createSyscall("${funcname}_uw")
@ -878,7 +878,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
"abs" -> {
// 1 argument, type determines the exact opcode to use
val arg = args.single()
when (arg.resultingDatatype(namespace, heap)) {
when (arg.resultingDatatype(program)) {
DataType.UBYTE, DataType.UWORD -> {}
DataType.BYTE -> prog.instr(Opcode.ABS_B)
DataType.WORD -> prog.instr(Opcode.ABS_W)
@ -890,7 +890,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
"mkword" -> prog.instr(Opcode.MKWORD)
"lsl" -> {
val arg = args.single()
val dt = arg.resultingDatatype(namespace, heap)
val dt = arg.resultingDatatype(program)
when (dt) {
in ByteDatatypes -> prog.instr(Opcode.SHL_BYTE)
in WordDatatypes -> prog.instr(Opcode.SHL_WORD)
@ -901,7 +901,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
}
"lsr" -> {
val arg = args.single()
val dt = arg.resultingDatatype(namespace, heap)
val dt = arg.resultingDatatype(program)
when (dt) {
DataType.UBYTE -> prog.instr(Opcode.SHR_UBYTE)
DataType.BYTE -> prog.instr(Opcode.SHR_SBYTE)
@ -914,7 +914,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
}
"rol" -> {
val arg = args.single()
val dt = arg.resultingDatatype(namespace, heap)
val dt = arg.resultingDatatype(program)
when (dt) {
DataType.UBYTE -> prog.instr(Opcode.ROL_BYTE)
DataType.UWORD -> prog.instr(Opcode.ROL_WORD)
@ -925,7 +925,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
}
"ror" -> {
val arg = args.single()
val dt = arg.resultingDatatype(namespace, heap)
val dt = arg.resultingDatatype(program)
when (dt) {
in ByteDatatypes -> prog.instr(Opcode.ROR_BYTE)
in WordDatatypes -> prog.instr(Opcode.ROR_WORD)
@ -936,7 +936,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
}
"rol2" -> {
val arg = args.single()
val dt = arg.resultingDatatype(namespace, heap)
val dt = arg.resultingDatatype(program)
when (dt) {
in ByteDatatypes -> prog.instr(Opcode.ROL2_BYTE)
in WordDatatypes -> prog.instr(Opcode.ROL2_WORD)
@ -947,7 +947,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
}
"ror2" -> {
val arg = args.single()
val dt = arg.resultingDatatype(namespace, heap)
val dt = arg.resultingDatatype(program)
when (dt) {
in ByteDatatypes -> prog.instr(Opcode.ROR2_BYTE)
in WordDatatypes -> prog.instr(Opcode.ROR2_WORD)
@ -970,11 +970,11 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
// swap(x,y) is treated differently, it's not a normal function call
if (args.size != 2)
throw AstException("swap requires 2 arguments")
val dt1 = args[0].resultingDatatype(namespace, heap)!!
val dt2 = args[1].resultingDatatype(namespace, heap)!!
val dt1 = args[0].resultingDatatype(program)!!
val dt2 = args[1].resultingDatatype(program)!!
if (dt1 != dt2)
throw AstException("swap requires 2 args of identical type")
if (args[0].constValue(namespace, heap) != null || args[1].constValue(namespace, heap) != null)
if (args[0].constValue(program) != null || args[1].constValue(program) != null)
throw AstException("swap requires 2 variables, not constant value(s)")
if(same(args[0], args[1]))
throw AstException("swap should have 2 different args")
@ -1004,7 +1004,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
// (subroutine arguments are not passed via the stack!)
for (arg in arguments.zip(subroutine.parameters)) {
translate(arg.first)
convertType(arg.first.resultingDatatype(namespace, heap)!!, arg.second.type) // convert types of arguments to required parameter type
convertType(arg.first.resultingDatatype(program)!!, arg.second.type) // convert types of arguments to required parameter type
val opcode = opcodePopvar(arg.second.type)
prog.instr(opcode, callLabel = subroutine.scopedname + "." + arg.second.name)
}
@ -1046,7 +1046,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
for (arg in arguments.zip(subroutine.asmParameterRegisters)) {
if (arg.second.statusflag != null) {
if (arg.second.statusflag == Statusflag.Pc)
carryParam = arg.first.constValue(namespace, heap)!!.asBooleanValue
carryParam = arg.first.constValue(program)!!.asBooleanValue
else
throw CompilerException("no support for status flag parameter: ${arg.second.statusflag}")
} else {
@ -1077,7 +1077,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
}
val valueA: IExpression
val valueX: IExpression
val paramDt = arg.first.resultingDatatype(namespace, heap)
val paramDt = arg.first.resultingDatatype(program)
when (paramDt) {
DataType.UBYTE -> {
valueA = arg.first
@ -1100,7 +1100,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
AY -> {
val valueA: IExpression
val valueY: IExpression
val paramDt = arg.first.resultingDatatype(namespace, heap)
val paramDt = arg.first.resultingDatatype(program)
when (paramDt) {
DataType.UBYTE -> {
valueA = arg.first
@ -1127,7 +1127,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
}
val valueX: IExpression
val valueY: IExpression
val paramDt = arg.first.resultingDatatype(namespace, heap)
val paramDt = arg.first.resultingDatatype(program)
when (paramDt) {
DataType.UBYTE -> {
valueX = arg.first
@ -1450,7 +1450,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
}
}
stmt.target.memoryAddress != null -> {
val address = stmt.target.memoryAddress?.addressExpression?.constValue(namespace, heap)?.asIntegerValue
val address = stmt.target.memoryAddress?.addressExpression?.constValue(program)?.asIntegerValue
if(address!=null) {
when(stmt.operator) {
"++" -> prog.instr(Opcode.INC_MEMORY, Value(DataType.UWORD, address))
@ -1479,8 +1479,8 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
return
}
val valueDt = stmt.value.resultingDatatype(namespace, heap)
val targetDt = assignTarget.determineDatatype(namespace, heap, stmt)
val valueDt = stmt.value.resultingDatatype(program)
val targetDt = assignTarget.determineDatatype(program, stmt)
if(valueDt!=targetDt) {
// convert value to target datatype if possible
// @todo use convertType()????
@ -1531,7 +1531,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
throw CompilerException("augmented assignment should have been converted to regular assignment already")
// pop the result value back into the assignment target
val datatype = assignTarget.determineDatatype(namespace, heap, stmt)!!
val datatype = assignTarget.determineDatatype(program, stmt)!!
popValueIntoTarget(assignTarget, datatype)
}
@ -1566,7 +1566,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
if(stmt.targets.size!=targetStmt.asmReturnvaluesRegisters.size)
throw CompilerException("asmsub number of return values doesn't match number of assignment targets ${stmt.position}")
for(target in stmt.targets) {
val dt = target.determineDatatype(namespace, heap, stmt)
val dt = target.determineDatatype(program, stmt)
popValueIntoTarget(target, dt!!)
}
} else throw CompilerException("can only use multiple assignment targets on an asmsub call")
@ -1584,7 +1584,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
}
VarDeclType.MEMORY -> {
val opcode = opcodePopmem(datatype)
val address = target.value?.constValue(namespace, heap)!!.asIntegerValue!!
val address = target.value?.constValue(program)!!.asIntegerValue!!
prog.instr(opcode, Value(DataType.UWORD, address))
}
VarDeclType.CONST -> throw CompilerException("cannot assign to const")
@ -1594,7 +1594,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
assignTarget.register != null -> prog.instr(Opcode.POP_VAR_BYTE, callLabel = assignTarget.register.name)
assignTarget.arrayindexed != null -> translate(assignTarget.arrayindexed, true) // write value to it
assignTarget.memoryAddress != null -> {
val address = assignTarget.memoryAddress?.addressExpression?.constValue(namespace, heap)?.asIntegerValue
val address = assignTarget.memoryAddress?.addressExpression?.constValue(program)?.asIntegerValue
if(address!=null) {
// const integer address given
prog.instr(Opcode.POP_MEM_BYTE, arg=Value(DataType.UWORD, address))
@ -1681,7 +1681,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
val idRef = loop.iterable as IdentifierReference
val vardecl = idRef.targetVarDecl(namespace)!!
val iterableValue = vardecl.value as LiteralValue
if(!iterableValue.isIterable(namespace, heap))
if(!iterableValue.isIterable(program))
throw CompilerException("loop over something that isn't iterable ${loop.iterable}")
translateForOverIterableVar(loop, loopVarDt, iterableValue)
}
@ -2016,7 +2016,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
translate(stmt.body)
prog.label(continueLabel)
translate(stmt.condition)
val conditionJumpOpcode = when(stmt.condition.resultingDatatype(namespace, heap)) {
val conditionJumpOpcode = when(stmt.condition.resultingDatatype(program)) {
in ByteDatatypes -> Opcode.JNZ
in WordDatatypes -> Opcode.JNZW
else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt")
@ -2053,7 +2053,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
translate(stmt.body)
prog.label(continueLabel)
translate(stmt.untilCondition)
val conditionJumpOpcode = when(stmt.untilCondition.resultingDatatype(namespace, heap)) {
val conditionJumpOpcode = when(stmt.untilCondition.resultingDatatype(program)) {
in ByteDatatypes -> Opcode.JZ
in WordDatatypes -> Opcode.JZW
else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt")
@ -2067,7 +2067,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
private fun translate(expr: TypecastExpression) {
translate(expr.expression)
val sourceDt = expr.expression.resultingDatatype(namespace, heap) ?: throw CompilerException("don't know what type to cast")
val sourceDt = expr.expression.resultingDatatype(program) ?: throw CompilerException("don't know what type to cast")
if(sourceDt==expr.type)
return
@ -2118,7 +2118,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
private fun translate(memread: DirectMemoryRead) {
// for now, only a single memory location (ubyte) is read at a time.
val address = memread.addressExpression.constValue(namespace, heap)?.asIntegerValue
val address = memread.addressExpression.constValue(program)?.asIntegerValue
if(address!=null) {
prog.instr(Opcode.PUSH_MEM_UB, arg = Value(DataType.UWORD, address))
} else {
@ -2129,7 +2129,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
private fun translate(memwrite: DirectMemoryWrite) {
// for now, only a single memory location (ubyte) is written at a time.
val address = memwrite.addressExpression.constValue(namespace, heap)?.asIntegerValue
val address = memwrite.addressExpression.constValue(program)?.asIntegerValue
if(address!=null) {
prog.instr(Opcode.POP_MEM_BYTE, arg = Value(DataType.UWORD, address))
} else {

View File

@ -2,11 +2,7 @@ package prog8.functions
import prog8.ast.*
import prog8.compiler.CompilerException
import prog8.compiler.HeapValues
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.log2
import kotlin.math.sin
import kotlin.math.*
class BuiltinFunctionParam(val name: String, val possibleDatatypes: Set<DataType>)
@ -14,7 +10,7 @@ class BuiltinFunctionParam(val name: String, val possibleDatatypes: Set<DataType
class FunctionSignature(val pure: Boolean, // does it have side effects?
val parameters: List<BuiltinFunctionParam>,
val returntype: DataType?,
val constExpressionFunc: ((args: List<IExpression>, position: Position, namespace: INameScope, heap: HeapValues) -> LiteralValue)? = null)
val constExpressionFunc: ((args: List<IExpression>, position: Position, program: Program) -> LiteralValue)? = null)
val BuiltinFunctions = mapOf(
@ -26,38 +22,38 @@ val BuiltinFunctions = mapOf(
"lsl" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null),
"lsr" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null),
// these few have a return value depending on the argument(s):
"max" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.max()!! }}, // type depends on args
"min" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.min()!! }}, // type depends on args
"sum" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.sum() }}, // type depends on args
"max" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArgOutputNumber(a, p, prg) { it.max()!! }}, // type depends on args
"min" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArgOutputNumber(a, p, prg) { it.min()!! }}, // type depends on args
"sum" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArgOutputNumber(a, p, prg) { it.sum() }}, // type depends on args
"abs" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
// normal functions follow:
"sin" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::sin) },
"sin" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
"sin8" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ),
"sin8u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ),
"sin16" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinSin16 ),
"sin16u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinSin16u ),
"cos" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::cos) },
"cos" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::cos) },
"cos8" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinCos8 ),
"cos8u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCos8u ),
"cos16" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinCos16 ),
"cos16u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinCos16u ),
"tan" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::tan) },
"atan" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::atan) },
"ln" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::log) },
"log2" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, ::log2) },
"sqrt16" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD))), DataType.UBYTE) { a, p, n, h -> oneIntArgOutputInt(a, p, n, h) { Math.sqrt(it.toDouble()).toInt() } },
"sqrt" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::sqrt) },
"rad" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::toRadians) },
"deg" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::toDegrees) },
"tan" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::tan) },
"atan" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::atan) },
"ln" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::log) },
"log2" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, ::log2) },
"sqrt16" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()).toInt() } },
"sqrt" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) },
"rad" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) },
"deg" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) },
"avg" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.FLOAT, ::builtinAvg),
"round" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::round) },
"floor" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::floor) },
"ceil" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::ceil) },
"any" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.any { v -> v != 0.0} }},
"all" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.all { v -> v != 0.0} }},
"lsb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, n, h -> oneIntArgOutputInt(a, p, n, h) { x: Int -> x and 255 }},
"msb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, n, h -> oneIntArgOutputInt(a, p, n, h) { x: Int -> x ushr 8 and 255}},
"round" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::round) },
"floor" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
"ceil" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
"any" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArgOutputBoolean(a, p, prg) { it.any { v -> v != 0.0} }},
"all" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArgOutputBoolean(a, p, prg) { it.all { v -> v != 0.0} }},
"lsb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 }},
"msb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255}},
"mkword" to FunctionSignature(true, listOf(
BuiltinFunctionParam("lsb", setOf(DataType.UBYTE)),
BuiltinFunctionParam("msb", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
@ -111,12 +107,12 @@ val BuiltinFunctions = mapOf(
)
fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespace: INameScope, heap: HeapValues): DataType? {
fun builtinFunctionReturnType(function: String, args: List<IExpression>, program: Program): DataType? {
fun datatypeFromIterableArg(arglist: IExpression): DataType {
if(arglist is LiteralValue) {
if(arglist.type==DataType.ARRAY_UB || arglist.type==DataType.ARRAY_UW || arglist.type==DataType.ARRAY_F) {
val dt = arglist.arrayvalue!!.map {it.resultingDatatype(namespace, heap)}
val dt = arglist.arrayvalue!!.map {it.resultingDatatype(program)}
if(dt.any { it!=DataType.UBYTE && it!=DataType.UWORD && it!=DataType.FLOAT}) {
throw FatalAstException("fuction $function only accepts arraysize of numeric values")
}
@ -126,7 +122,7 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
}
}
if(arglist is IdentifierReference) {
val dt = arglist.resultingDatatype(namespace, heap)
val dt = arglist.resultingDatatype(program)
return when(dt) {
in NumericDatatypes -> dt!!
in StringDatatypes -> dt!!
@ -148,7 +144,7 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
return when (function) {
"abs" -> {
val dt = args.single().resultingDatatype(namespace, heap)
val dt = args.single().resultingDatatype(program)
when(dt) {
in ByteDatatypes -> DataType.UBYTE
in WordDatatypes -> DataType.UWORD
@ -170,8 +166,7 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
}
}
"sum" -> {
val dt=datatypeFromIterableArg(args.single())
when(dt) {
when(datatypeFromIterableArg(args.single())) {
DataType.UBYTE, DataType.UWORD -> DataType.UWORD
DataType.BYTE, DataType.WORD -> DataType.WORD
DataType.FLOAT -> DataType.FLOAT
@ -195,10 +190,10 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
class NotConstArgumentException: AstException("not a const argument to a built-in function")
private fun oneDoubleArg(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues, function: (arg: Double)->Number): LiteralValue {
private fun oneDoubleArg(args: List<IExpression>, position: Position, program: Program, function: (arg: Double)->Number): LiteralValue {
if(args.size!=1)
throw SyntaxError("built-in function requires one floating point argument", position)
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
if(constval.type!=DataType.FLOAT)
throw SyntaxError("built-in function requires one floating point argument", position)
@ -206,19 +201,19 @@ private fun oneDoubleArg(args: List<IExpression>, position: Position, namespace:
return numericLiteral(function(float), args[0].position)
}
private fun oneDoubleArgOutputWord(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues, function: (arg: Double)->Number): LiteralValue {
private fun oneDoubleArgOutputWord(args: List<IExpression>, position: Position, program: Program, function: (arg: Double)->Number): LiteralValue {
if(args.size!=1)
throw SyntaxError("built-in function requires one floating point argument", position)
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
if(constval.type!=DataType.FLOAT)
throw SyntaxError("built-in function requires one floating point argument", position)
return LiteralValue(DataType.WORD, wordvalue=function(constval.asNumericValue!!.toDouble()).toInt(), position=args[0].position)
}
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues, function: (arg: Int)->Number): LiteralValue {
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position, program: Program, function: (arg: Int)->Number): LiteralValue {
if(args.size!=1)
throw SyntaxError("built-in function requires one integer argument", position)
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
if(constval.type!=DataType.UBYTE && constval.type!=DataType.UWORD)
throw SyntaxError("built-in function requires one integer argument", position)
@ -227,14 +222,14 @@ private fun oneIntArgOutputInt(args: List<IExpression>, position: Position, name
}
private fun collectionArgOutputNumber(args: List<IExpression>, position: Position,
namespace:INameScope, heap: HeapValues,
program: Program,
function: (arg: Collection<Double>)->Number): LiteralValue {
if(args.size!=1)
throw SyntaxError("builtin function requires one non-scalar argument", position)
val iterable = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val iterable = args[0].constValue(program) ?: throw NotConstArgumentException()
val result = if(iterable.arrayvalue != null) {
val constants = iterable.arrayvalue.map { it.constValue(namespace, heap)?.asNumericValue }
val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
if(null in constants)
throw NotConstArgumentException()
function(constants.map { it!!.toDouble() }).toDouble()
@ -244,7 +239,7 @@ private fun collectionArgOutputNumber(args: List<IExpression>, position: Positio
else -> {
if(iterable.heapId==null)
throw FatalAstException("iterable value should be on the heap")
val array = heap.get(iterable.heapId).array ?: throw SyntaxError("function expects an iterable type", position)
val array = program.heap.get(iterable.heapId).array ?: throw SyntaxError("function expects an iterable type", position)
function(array.map {
if(it.integer!=null)
it.integer.toDouble()
@ -258,19 +253,19 @@ private fun collectionArgOutputNumber(args: List<IExpression>, position: Positio
}
private fun collectionArgOutputBoolean(args: List<IExpression>, position: Position,
namespace:INameScope, heap: HeapValues,
program: Program,
function: (arg: Collection<Double>)->Boolean): LiteralValue {
if(args.size!=1)
throw SyntaxError("builtin function requires one non-scalar argument", position)
val iterable = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val iterable = args[0].constValue(program) ?: throw NotConstArgumentException()
val result = if(iterable.arrayvalue != null) {
val constants = iterable.arrayvalue.map { it.constValue(namespace, heap)?.asNumericValue }
val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
if(null in constants)
throw NotConstArgumentException()
function(constants.map { it!!.toDouble() })
} else {
val array = heap.get(iterable.heapId!!).array ?: throw SyntaxError("function requires array argument", position)
val array = program.heap.get(iterable.heapId!!).array ?: throw SyntaxError("function requires array argument", position)
function(array.map {
if(it.integer!=null)
it.integer.toDouble()
@ -281,32 +276,32 @@ private fun collectionArgOutputBoolean(args: List<IExpression>, position: Positi
return LiteralValue.fromBoolean(result, position)
}
private fun builtinAbs(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
private fun builtinAbs(args: List<IExpression>, position: Position, program: Program): LiteralValue {
// 1 arg, type = float or int, result type= same as argument type
if(args.size!=1)
throw SyntaxError("abs requires one numeric argument", position)
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val number = constval.asNumericValue
return when (number) {
is Int, is Byte, is Short -> numericLiteral(Math.abs(number.toInt()), args[0].position)
is Double -> numericLiteral(Math.abs(number.toDouble()), args[0].position)
is Int, is Byte, is Short -> numericLiteral(abs(number.toInt()), args[0].position)
is Double -> numericLiteral(abs(number.toDouble()), args[0].position)
else -> throw SyntaxError("abs requires one numeric argument", position)
}
}
private fun builtinAvg(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
private fun builtinAvg(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if(args.size!=1)
throw SyntaxError("avg requires array argument", position)
val iterable = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val iterable = args[0].constValue(program) ?: throw NotConstArgumentException()
val result = if(iterable.arrayvalue!=null) {
val constants = iterable.arrayvalue.map { it.constValue(namespace, heap)?.asNumericValue }
val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
if (null in constants)
throw NotConstArgumentException()
(constants.map { it!!.toDouble() }).average()
}
else {
val integerarray = heap.get(iterable.heapId!!).array
val integerarray = program.heap.get(iterable.heapId!!).array
if(integerarray!=null) {
if (integerarray.all { it.integer != null }) {
integerarray.map { it.integer!! }.average()
@ -314,20 +309,20 @@ private fun builtinAvg(args: List<IExpression>, position: Position, namespace:IN
throw ExpressionError("cannot avg() over array that does not only contain constant numerical values", position)
}
} else {
val doublearray = heap.get(iterable.heapId).doubleArray
val doublearray = program.heap.get(iterable.heapId).doubleArray
doublearray?.average() ?: throw SyntaxError("avg requires array argument", position)
}
}
return numericLiteral(result, args[0].position)
}
private fun builtinStrlen(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
private fun builtinStrlen(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 1)
throw SyntaxError("strlen requires one argument", position)
val argument = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val argument = args[0].constValue(program) ?: throw NotConstArgumentException()
if(argument.type !in StringDatatypes)
throw SyntaxError("strlen must have string argument", position)
val string = argument.strvalue(heap)
val string = argument.strvalue(program.heap)
val zeroIdx = string.indexOf('\u0000')
return if(zeroIdx>=0)
LiteralValue.optimalInteger(zeroIdx, position=position)
@ -335,38 +330,38 @@ private fun builtinStrlen(args: List<IExpression>, position: Position, namespace
LiteralValue.optimalInteger(string.length, position=position)
}
private fun builtinLen(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
private fun builtinLen(args: List<IExpression>, position: Position, program: Program): LiteralValue {
// 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(namespace, heap)
var argument = args[0].constValue(program)
if(argument==null) {
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(namespace)
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program.namespace)
val arraySize = directMemVar?.arraysize?.size()
if(arraySize != null)
return LiteralValue.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(namespace)
val target = (args[0] as IdentifierReference).targetStatement(program.namespace)
val argValue = (target as? VarDecl)?.value
argument = argValue?.constValue(namespace, heap)
argument = argValue?.constValue(program)
?: throw NotConstArgumentException()
}
return when(argument.type) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).arraysize
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 ?: heap.get(argument.heapId!!).arraysize
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(heap)
val str = argument.strvalue(program.heap)
if(str.length>255)
throw CompilerException("string length exceeds byte limit ${argument.position}")
LiteralValue.optimalInteger(str.length, args[0].position)
@ -377,75 +372,75 @@ private fun builtinLen(args: List<IExpression>, position: Position, namespace:IN
}
private fun builtinMkword(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
private fun builtinMkword(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 2)
throw SyntaxError("mkword requires lsb and msb arguments", position)
val constLsb = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val constMsb = args[1].constValue(namespace, heap) ?: throw NotConstArgumentException()
val constLsb = args[0].constValue(program) ?: throw NotConstArgumentException()
val constMsb = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = (constMsb.asIntegerValue!! shl 8) or constLsb.asIntegerValue!!
return LiteralValue(DataType.UWORD, wordvalue = result, position = position)
}
private fun builtinSin8(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
private fun builtinSin8(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 1)
throw SyntaxError("sin8 requires one argument", position)
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
return LiteralValue(DataType.BYTE, bytevalue = (127.0* sin(rad)).toShort(), position = position)
}
private fun builtinSin8u(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
private fun builtinSin8u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 1)
throw SyntaxError("sin8u requires one argument", position)
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
return LiteralValue(DataType.UBYTE, bytevalue = (128.0+127.5*sin(rad)).toShort(), position = position)
}
private fun builtinCos8(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
private fun builtinCos8(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 1)
throw SyntaxError("cos8 requires one argument", position)
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
return LiteralValue(DataType.BYTE, bytevalue = (127.0* cos(rad)).toShort(), position = position)
}
private fun builtinCos8u(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
private fun builtinCos8u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 1)
throw SyntaxError("cos8u requires one argument", position)
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
return LiteralValue(DataType.UBYTE, bytevalue = (128.0 + 127.5*cos(rad)).toShort(), position = position)
}
private fun builtinSin16(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
private fun builtinSin16(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 1)
throw SyntaxError("sin16 requires one argument", position)
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
return LiteralValue(DataType.WORD, wordvalue = (32767.0* sin(rad)).toInt(), position = position)
}
private fun builtinSin16u(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
private fun builtinSin16u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 1)
throw SyntaxError("sin16u requires one argument", position)
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
return LiteralValue(DataType.UWORD, wordvalue = (32768.0+32767.5*sin(rad)).toInt(), position = position)
}
private fun builtinCos16(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
private fun builtinCos16(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 1)
throw SyntaxError("cos16 requires one argument", position)
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
return LiteralValue(DataType.WORD, wordvalue = (32767.0* cos(rad)).toInt(), position = position)
}
private fun builtinCos16u(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
private fun builtinCos16u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 1)
throw SyntaxError("cos16u requires one argument", position)
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
return LiteralValue(DataType.UWORD, wordvalue = (32768.0+32767.5* cos(rad)).toInt(), position = position)
}
@ -453,7 +448,7 @@ private fun builtinCos16u(args: List<IExpression>, position: Position, namespace
private fun numericLiteral(value: Number, position: Position): LiteralValue {
val floatNum=value.toDouble()
val tweakedValue: Number =
if(floatNum==Math.floor(floatNum) && (floatNum>=-32768 && floatNum<=65535))
if(floatNum== floor(floatNum) && (floatNum>=-32768 && floatNum<=65535))
floatNum.toInt() // we have an integer disguised as a float.
else
floatNum

View File

@ -9,7 +9,7 @@ import prog8.compiler.target.c64.FLOAT_MAX_POSITIVE
import kotlin.math.floor
class ConstantFolding(private val namespace: GlobalNamespace, private val heap: HeapValues) : IAstProcessor {
class ConstantFolding(private val program: Program) : IAstProcessor {
var optimizationsDone: Int = 0
var errors : MutableList<AstException> = mutableListOf()
@ -49,11 +49,11 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
if(rangeExpr!=null) {
// convert the initializer range expression to an actual array (will be put on heap later)
val declArraySize = decl.arraysize?.size()
if(declArraySize!=null && declArraySize!=rangeExpr.size(heap))
if(declArraySize!=null && declArraySize!=rangeExpr.size(program.heap))
errors.add(ExpressionError("range expression size doesn't match declared array size", decl.value?.position!!))
val constRange = rangeExpr.toConstantIntegerRange(heap)
val constRange = rangeExpr.toConstantIntegerRange(program.heap)
if(constRange!=null) {
val eltType = rangeExpr.resultingDatatype(namespace, heap)!!
val eltType = rangeExpr.resultingDatatype(program)!!
if(eltType in ByteDatatypes) {
decl.value = LiteralValue(decl.datatype,
arrayvalue = constRange.map { LiteralValue(eltType, bytevalue=it.toShort(), position = decl.value!!.position ) }
@ -93,7 +93,7 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
}
else -> {}
}
val heapId = heap.addIntegerArray(decl.datatype, Array(size) { IntegerOrAddressOf(fillvalue, null) })
val heapId = program.heap.addIntegerArray(decl.datatype, Array(size) { IntegerOrAddressOf(fillvalue, null) })
decl.value = LiteralValue(decl.datatype, heapId = heapId, position = litval?.position ?: decl.position)
}
}
@ -107,7 +107,7 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
if(fillvalue< FLOAT_MAX_NEGATIVE || fillvalue> FLOAT_MAX_POSITIVE)
errors.add(ExpressionError("float value overflow", litval?.position ?: decl.position))
else {
val heapId = heap.addDoublesArray(DoubleArray(size) { fillvalue })
val heapId = program.heap.addDoublesArray(DoubleArray(size) { fillvalue })
decl.value = LiteralValue(DataType.ARRAY_F, heapId = heapId, position = litval?.position ?: decl.position)
}
}
@ -125,11 +125,11 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
if(decl.datatype==litval.type)
return // already correct datatype
val heapId = litval.heapId ?: throw FatalAstException("expected array to be on heap $litval")
val array=heap.get(heapId)
val array = program.heap.get(heapId)
when(decl.datatype) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
if(array.array!=null) {
heap.update(heapId, HeapValues.HeapValue(decl.datatype, null, array.array, null))
program.heap.update(heapId, HeapValues.HeapValue(decl.datatype, null, array.array, null))
decl.value = LiteralValue(decl.datatype, heapId=heapId, position = litval.position)
}
}
@ -137,7 +137,7 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
if(array.array!=null) {
// convert a non-float array to floats
val doubleArray = array.array.map { it.integer!!.toDouble() }.toDoubleArray()
heap.update(heapId, HeapValues.HeapValue(DataType.ARRAY_F, null, null, doubleArray))
program.heap.update(heapId, HeapValues.HeapValue(DataType.ARRAY_F, null, null, doubleArray))
decl.value = LiteralValue(decl.datatype, heapId = heapId, position = litval.position)
}
}
@ -150,7 +150,7 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
*/
override fun process(identifier: IdentifierReference): IExpression {
return try {
val cval = identifier.constValue(namespace, heap) ?: return identifier
val cval = identifier.constValue(program) ?: return identifier
return if(cval.isNumeric) {
val copy = LiteralValue(cval.type, cval.bytevalue, cval.wordvalue, cval.floatvalue, null, cval.arrayvalue, position = identifier.position)
copy.parent = identifier.parent
@ -167,7 +167,7 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
return try {
super.process(functionCall)
typeCastConstArguments(functionCall)
functionCall.constValue(namespace, heap) ?: functionCall
functionCall.constValue(program) ?: functionCall
} catch (ax: AstException) {
addError(ax)
functionCall
@ -181,12 +181,12 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
}
private fun typeCastConstArguments(functionCall: IFunctionCall) {
val subroutine = functionCall.target.targetSubroutine(namespace)
val subroutine = functionCall.target.targetSubroutine(program.namespace)
if(subroutine!=null) {
// if types differ, try to typecast constant arguments to the function call to the desired data type of the parameter
for(arg in functionCall.arglist.withIndex().zip(subroutine.parameters)) {
val expectedDt = arg.second.type
val argConst = arg.first.value.constValue(namespace, heap)
val argConst = arg.first.value.constValue(program)
if(argConst!=null && argConst.type!=expectedDt) {
val convertedValue = argConst.intoDatatype(expectedDt)
if(convertedValue!=null) {
@ -287,8 +287,8 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
override fun process(expr: BinaryExpression): IExpression {
return try {
super.process(expr)
val leftconst = expr.left.constValue(namespace, heap)
val rightconst = expr.right.constValue(namespace, heap)
val leftconst = expr.left.constValue(program)
val rightconst = expr.right.constValue(program)
val subExpr: BinaryExpression? = when {
leftconst!=null -> expr.right as? BinaryExpression
@ -296,8 +296,8 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
else -> null
}
if(subExpr!=null) {
val subleftconst = subExpr.left.constValue(namespace, heap)
val subrightconst = subExpr.right.constValue(namespace, heap)
val subleftconst = subExpr.left.constValue(program)
val subrightconst = subExpr.right.constValue(program)
if ((subleftconst != null && subrightconst == null) || (subleftconst==null && subrightconst!=null)) {
// try reordering.
return groupTwoConstsTogether(expr, subExpr,
@ -311,7 +311,7 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
return when {
leftconst != null && rightconst != null -> {
optimizationsDone++
evaluator.evaluate(leftconst, expr.operator, rightconst, heap)
evaluator.evaluate(leftconst, expr.operator, rightconst, program.heap)
}
else -> expr
}
@ -533,7 +533,7 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
val rangeTo = iterableRange.to as? LiteralValue
if(rangeFrom==null || rangeTo==null) return resultStmt
val loopvar = resultStmt.loopVar!!.targetVarDecl(namespace)
val loopvar = resultStmt.loopVar!!.targetVarDecl(program.namespace)
if(loopvar!=null) {
val stepLiteral = iterableRange.step as? LiteralValue
when(loopvar.datatype) {
@ -570,10 +570,10 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
override fun process(literalValue: LiteralValue): LiteralValue {
if(literalValue.isString) {
// intern the string; move it into the heap
if(literalValue.strvalue(heap).length !in 1..255)
if(literalValue.strvalue(program.heap).length !in 1..255)
addError(ExpressionError("string literal length must be between 1 and 255", literalValue.position))
else {
val heapId = heap.addString(literalValue.type, literalValue.strvalue(heap)) // TODO: we don't know the actual string type yet, STR != STR_S etc...
val heapId = program.heap.addString(literalValue.type, literalValue.strvalue(program.heap)) // TODO: we don't know the actual string type yet, STR != STR_S etc...
val newValue = LiteralValue(literalValue.type, heapId = heapId, position = literalValue.position)
return super.process(newValue)
}
@ -599,14 +599,14 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
else -> throw CompilerException("invalid datatype in array")
}
}
val heapId = heap.addIntegerArray(arrayDt, intArrayWithAddressOfs.toTypedArray())
val heapId = program.heap.addIntegerArray(arrayDt, intArrayWithAddressOfs.toTypedArray())
return LiteralValue(arrayDt, heapId = heapId, position = arraylit.position)
} else {
// array is only constant numerical values
val valuesInArray = array.map { it.constValue(namespace, heap)!!.asNumericValue!! }
val valuesInArray = array.map { it.constValue(program)!!.asNumericValue!! }
val integerArray = valuesInArray.map{ it.toInt() }
val doubleArray = valuesInArray.map{it.toDouble()}.toDoubleArray()
val typesInArray: Set<DataType> = array.mapNotNull { it.resultingDatatype(namespace, heap) }.toSet()
val typesInArray: Set<DataType> = array.mapNotNull { it.resultingDatatype(program) }.toSet()
// Take an educated guess about the array type.
// This may be altered (if needed & if possible) to suit an array declaration type later!
@ -638,8 +638,8 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
DataType.ARRAY_UB,
DataType.ARRAY_B,
DataType.ARRAY_UW,
DataType.ARRAY_W -> heap.addIntegerArray(arrayDt, integerArray.map { IntegerOrAddressOf(it, null) }.toTypedArray())
DataType.ARRAY_F -> heap.addDoublesArray(doubleArray)
DataType.ARRAY_W -> program.heap.addIntegerArray(arrayDt, integerArray.map { IntegerOrAddressOf(it, null) }.toTypedArray())
DataType.ARRAY_F -> program.heap.addDoublesArray(doubleArray)
else -> throw CompilerException("invalid arraysize type")
}
return LiteralValue(arrayDt, heapId = heapId, position = arraylit.position)
@ -650,9 +650,8 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
super.process(assignment)
val lv = assignment.value as? LiteralValue
if(lv!=null) {
val targetDt = assignment.singleTarget?.determineDatatype(namespace, heap, assignment)
// see if we can promote/convert a literal value to the required datatype
when(targetDt) {
when(assignment.singleTarget?.determineDatatype(program, assignment)) {
DataType.UWORD -> {
// we can convert to UWORD: any UBYTE, BYTE/WORD that are >=0, FLOAT that's an integer 0..65535,
if(lv.type==DataType.UBYTE)

View File

@ -6,7 +6,7 @@ import prog8.parser.ParsingFailedError
fun Program.constantFold() {
val optimizer = ConstantFolding(this.namespace, heap)
val optimizer = ConstantFolding(this)
try {
optimizer.process(this)
} catch (ax: AstException) {
@ -28,7 +28,7 @@ fun Program.constantFold() {
fun Program.optimizeStatements(): Int {
val optimizer = StatementOptimizer(namespace, heap)
val optimizer = StatementOptimizer(this)
optimizer.process(this)
for(stmt in optimizer.statementsToRemove) {
val scope=stmt.definingScope()
@ -40,7 +40,7 @@ fun Program.optimizeStatements(): Int {
}
fun Program.simplifyExpressions() : Int {
val optimizer = SimplifyExpressions(namespace, heap)
val optimizer = SimplifyExpressions(this)
optimizer.process(this)
return optimizer.optimizationsDone
}

View File

@ -1,7 +1,6 @@
package prog8.optimizing
import prog8.ast.*
import prog8.compiler.HeapValues
import kotlin.math.abs
import kotlin.math.log2
@ -9,7 +8,7 @@ import kotlin.math.log2
todo advanced expression optimization: common (sub) expression elimination (turn common expressions into single subroutine call + introduce variable to hold it)
*/
class SimplifyExpressions(private val namespace: INameScope, private val heap: HeapValues) : IAstProcessor {
class SimplifyExpressions(private val program: Program) : IAstProcessor {
var optimizationsDone: Int = 0
override fun process(assignment: Assignment): IStatement {
@ -83,13 +82,13 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
override fun process(expr: BinaryExpression): IExpression {
super.process(expr)
val leftVal = expr.left.constValue(namespace, heap)
val rightVal = expr.right.constValue(namespace, heap)
val leftVal = expr.left.constValue(program)
val rightVal = expr.right.constValue(program)
val constTrue = LiteralValue.fromBoolean(true, expr.position)
val constFalse = LiteralValue.fromBoolean(false, expr.position)
val leftDt = expr.left.resultingDatatype(namespace, heap)
val rightDt = expr.right.resultingDatatype(namespace, heap)
val leftDt = expr.left.resultingDatatype(program)
val rightDt = expr.right.resultingDatatype(program)
if (leftDt != null && rightDt != null && leftDt != rightDt) {
// try to convert a datatype into the other (where ddd
if (adjustDatatypes(expr, leftVal, leftDt, rightVal, rightDt)) {
@ -404,9 +403,9 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
expr.left = expr.right
expr.right = tmp
optimizationsDone++
return ReorderedAssociativeBinaryExpr(expr, expr.right.constValue(namespace, heap), leftVal)
return ReorderedAssociativeBinaryExpr(expr, expr.right.constValue(program), leftVal)
}
return ReorderedAssociativeBinaryExpr(expr, leftVal, expr.right.constValue(namespace, heap))
return ReorderedAssociativeBinaryExpr(expr, leftVal, expr.right.constValue(program))
}
private fun optimizeAdd(pexpr: BinaryExpression, pleftVal: LiteralValue?, prightVal: LiteralValue?): IExpression {
@ -550,7 +549,7 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
"%" -> {
if (cv == 1.0) {
optimizationsDone++
return LiteralValue.fromNumber(0, expr.resultingDatatype(namespace, heap)!!, expr.position)
return LiteralValue.fromNumber(0, expr.resultingDatatype(program)!!, expr.position)
} else if (cv == 2.0) {
optimizationsDone++
expr.operator = "&"
@ -573,7 +572,7 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
// right value is a constant, see if we can optimize
val rightConst: LiteralValue = rightVal
val cv = rightConst.asNumericValue?.toDouble()
val leftDt = expr.left.resultingDatatype(namespace, heap)
val leftDt = expr.left.resultingDatatype(program)
when(cv) {
-1.0 -> {
// '/' -> -left
@ -644,8 +643,7 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
// right value is a constant, see if we can optimize
val leftValue: IExpression = expr.left
val rightConst: LiteralValue = rightVal
val cv = rightConst.asNumericValue?.toDouble()
when(cv) {
when(val cv = rightConst.asNumericValue?.toDouble()) {
-1.0 -> {
// -left
optimizationsDone++
@ -662,7 +660,7 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
return expr.left
}
2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 2048.0, 4096.0, 8192.0, 16384.0, 32768.0, 65536.0 -> {
if(leftValue.resultingDatatype(namespace, heap) in IntegerDatatypes) {
if(leftValue.resultingDatatype(program) in IntegerDatatypes) {
// times a power of two => shift left
optimizationsDone++
val numshifts = log2(cv).toInt()
@ -670,7 +668,7 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
}
}
-2.0, -4.0, -8.0, -16.0, -32.0, -64.0, -128.0, -256.0, -512.0, -1024.0, -2048.0, -4096.0, -8192.0, -16384.0, -32768.0, -65536.0 -> {
if(leftValue.resultingDatatype(namespace, heap) in IntegerDatatypes) {
if(leftValue.resultingDatatype(program) in IntegerDatatypes) {
// times a negative power of two => negate, then shift left
optimizationsDone++
val numshifts = log2(-cv).toInt()

View File

@ -1,7 +1,6 @@
package prog8.optimizing
import prog8.ast.*
import prog8.compiler.HeapValues
import prog8.compiler.target.c64.Petscii
import prog8.functions.BuiltinFunctions
import kotlin.math.floor
@ -12,9 +11,8 @@ import kotlin.math.floor
todo: implement usage counters for blocks, variables, subroutines, heap variables. Then:
todo remove unused blocks
todo remove unused variables
todo remove unused subroutines
todo remove unused: subroutines, blocks, modules (in this order)
todo remove unused: variable declarations
todo remove unused strings and arrays from the heap
todo inline subroutines that are called exactly once (regardless of their size)
todo inline subroutines that are only called a few times (max 3?)
@ -23,7 +21,7 @@ import kotlin.math.floor
todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to)
*/
class StatementOptimizer(private val namespace: INameScope, private val heap: HeapValues) : IAstProcessor {
class StatementOptimizer(private val program: Program) : IAstProcessor {
var optimizationsDone: Int = 0
private set
var statementsToRemove = mutableListOf<IStatement>()
@ -101,12 +99,12 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
if(target.memoryAddress!=null)
return false
if(target.arrayindexed!=null) {
val targetStmt = target.arrayindexed.identifier.targetVarDecl(namespace)
val targetStmt = target.arrayindexed.identifier.targetVarDecl(program.namespace)
if(targetStmt!=null)
return targetStmt.type!=VarDeclType.MEMORY
}
if(target.identifier!=null) {
val targetStmt = target.identifier.targetVarDecl(namespace)
val targetStmt = target.identifier.targetVarDecl(program.namespace)
if(targetStmt!=null)
return targetStmt.type!=VarDeclType.MEMORY
}
@ -131,8 +129,8 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
throw AstException("string argument should be on heap already")
val stringVar = functionCallStatement.arglist.single() as? IdentifierReference
if(stringVar!=null) {
val heapId = stringVar.heapId(namespace)
val string = heap.get(heapId).str!!
val heapId = stringVar.heapId(program.namespace)
val string = program.heap.get(heapId).str!!
if(string.length==1) {
val petscii = Petscii.encodePetscii(string, true)[0]
functionCallStatement.arglist.clear()
@ -156,7 +154,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
// if it calls a subroutine,
// and the first instruction in the subroutine is a jump, call that jump target instead
// if the first instruction in the subroutine is a return statement, replace with a nop instruction
val subroutine = functionCallStatement.target.targetSubroutine(namespace)
val subroutine = functionCallStatement.target.targetSubroutine(program.namespace)
if(subroutine!=null) {
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
if(first is Jump && first.identifier!=null) {
@ -176,7 +174,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
// if it calls a subroutine,
// and the first instruction in the subroutine is a jump, call that jump target instead
// if the first instruction in the subroutine is a return statement with constant value, replace with the constant value
val subroutine = functionCall.target.targetSubroutine(namespace)
val subroutine = functionCall.target.targetSubroutine(program.namespace)
if(subroutine!=null) {
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
if(first is Jump && first.identifier!=null) {
@ -184,7 +182,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
return FunctionCall(first.identifier, functionCall.arglist, functionCall.position)
}
if(first is Return && first.values.size==1) {
val constval = first.values[0].constValue(namespace, heap)
val constval = first.values[0].constValue(program)
if(constval!=null)
return constval
}
@ -210,7 +208,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
return ifStatement
}
val constvalue = ifStatement.condition.constValue(namespace, heap)
val constvalue = ifStatement.condition.constValue(program)
if(constvalue!=null) {
return if(constvalue.asBooleanValue){
// always true -> keep only if-part
@ -247,7 +245,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
val range = forLoop.iterable as? RangeExpr
if(range!=null) {
if(range.size(heap)==1) {
if(range.size(program.heap)==1) {
// for loop over a (constant) range of just a single value-- optimize the loop away
// loopvar/reg = range value , follow by block
val assignment = Assignment(listOf(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, null, forLoop.position)), null, range.from, forLoop.position)
@ -261,7 +259,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
override fun process(whileLoop: WhileLoop): IStatement {
super.process(whileLoop)
val constvalue = whileLoop.condition.constValue(namespace, heap)
val constvalue = whileLoop.condition.constValue(program)
if(constvalue!=null) {
return if(constvalue.asBooleanValue){
// always true -> print a warning, and optimize into body + jump (if there are no continue and break statements)
@ -287,7 +285,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
override fun process(repeatLoop: RepeatLoop): IStatement {
super.process(repeatLoop)
val constvalue = repeatLoop.untilCondition.constValue(namespace, heap)
val constvalue = repeatLoop.untilCondition.constValue(program)
if(constvalue!=null) {
return if(constvalue.asBooleanValue){
// always true -> keep only the statement block (if there are no continue and break statements)
@ -341,7 +339,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
}
override fun process(jump: Jump): IStatement {
val subroutine = jump.identifier?.targetSubroutine(namespace)
val subroutine = jump.identifier?.targetSubroutine(program.namespace)
if(subroutine!=null) {
// if the first instruction in the subroutine is another jump, shortcut this one
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
@ -363,10 +361,10 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
optimizationsDone++
return NopStatement(assignment.position)
}
val targetDt = target.determineDatatype(namespace, heap, assignment)!!
val targetDt = target.determineDatatype(program, assignment)!!
val bexpr=assignment.value as? BinaryExpression
if(bexpr!=null) {
val cv = bexpr.right.constValue(namespace, heap)?.asNumericValue?.toDouble()
val cv = bexpr.right.constValue(program)?.asNumericValue?.toDouble()
if(cv==null) {
if(bexpr.operator=="+" && targetDt!=DataType.FLOAT) {
if (same(bexpr.left, bexpr.right) && same(target, bexpr.left)) {
@ -380,7 +378,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
if (same(target, bexpr.left)) {
// remove assignments that have no effect X=X , X+=0, X-=0, X*=1, X/=1, X//=1, A |= 0, A ^= 0, A<<=0, etc etc
// A = A <operator> B
val vardeclDt = (target.identifier?.targetVarDecl(namespace))?.type
val vardeclDt = (target.identifier?.targetVarDecl(program.namespace))?.type
when (bexpr.operator) {
"+" -> {
@ -516,14 +514,14 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
if(target1.identifier!=null && target2.identifier!=null)
return target1.identifier.nameInSource==target2.identifier.nameInSource
if(target1.memoryAddress!=null && target2.memoryAddress!=null) {
val addr1 = target1.memoryAddress!!.addressExpression.constValue(namespace, heap)
val addr2 = target2.memoryAddress!!.addressExpression.constValue(namespace, heap)
val addr1 = target1.memoryAddress!!.addressExpression.constValue(program)
val addr2 = target2.memoryAddress!!.addressExpression.constValue(program)
return addr1!=null && addr2!=null && addr1==addr2
}
if(target1.arrayindexed!=null && target2.arrayindexed!=null) {
if(target1.arrayindexed.identifier.nameInSource == target2.arrayindexed.identifier.nameInSource) {
val x1 = target1.arrayindexed.arrayspec.index.constValue(namespace, heap)
val x2 = target2.arrayindexed.arrayspec.index.constValue(namespace, heap)
val x1 = target1.arrayindexed.arrayspec.index.constValue(program)
val x2 = target2.arrayindexed.arrayspec.index.constValue(program)
return x1!=null && x2!=null && x1==x2
}
}

View File

@ -37,8 +37,7 @@ fun importModule(program: Program, filePath: Path): Module {
throw ParsingFailedError("No such file: $filePath")
val input = CharStreams.fromPath(filePath)
val module = importModule(program, input, filePath, filePath.parent==null)
return module
return importModule(program, input, filePath, filePath.parent==null)
}
fun importLibraryModule(program: Program, name: String): Module? {
@ -69,7 +68,7 @@ private fun importModule(program: Program, stream: CharStream, modulePath: Path,
moduleAst.linkParents()
program.modules.add(moduleAst)
// process imports
// process additional imports
val lines = moduleAst.statements.toMutableList()
lines.asSequence()
.mapIndexed { i, it -> Pair(i, it) }
@ -117,8 +116,9 @@ private fun executeImportDirective(program: Program, import: Directive, source:
// load the module from the embedded resource
resource.use {
if(import.args[0].int==42)
print("automatically ")
println("importing '$moduleName' (embedded library)")
println("importing '$moduleName' (library, auto)")
else
println("importing '$moduleName' (library)")
importModule(program, CharStreams.fromStream(it), Paths.get("@embedded@/$moduleName"), true)
}
} else {

View File

@ -221,8 +221,7 @@ class Program (val name: String,
val (name, typeStr, valueStr) = line.split(splitpattern, limit = 3)
if(valueStr[0] !='"' && ':' !in valueStr)
throw VmExecutionException("missing value type character")
val type = DataType.valueOf(typeStr.toUpperCase())
val value = when(type) {
val value = when(val type = DataType.valueOf(typeStr.toUpperCase())) {
DataType.UBYTE -> Value(DataType.UBYTE, valueStr.substring(3).toShort(16))
DataType.BYTE -> Value(DataType.BYTE, valueStr.substring(2).toShort(16))
DataType.UWORD -> Value(DataType.UWORD, valueStr.substring(3).toInt(16))

View File

@ -1944,8 +1944,7 @@ class StackVm(private var traceOutputFile: String?) {
private fun dispatchSyscall(ins: Instruction) {
val callId = ins.arg!!.integerValue().toShort()
val syscall = Syscall.values().first { it.callNr == callId }
when (syscall) {
when (val syscall = Syscall.values().first { it.callNr == callId }) {
Syscall.VM_WRITE_MEMCHR -> {
val address = evalstack.pop().integerValue()
print(Petscii.decodePetscii(listOf(mem.getUByte(address)), true))