From b35430214bf78cf17647bb1e471ec8cd61a4dacf Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 20 Jun 2019 21:06:51 +0200 Subject: [PATCH] some more program node cleanups --- compiler/src/prog8/CompilerMain.kt | 3 +- compiler/src/prog8/ast/AST.kt | 115 ++++++------ compiler/src/prog8/ast/AstChecker.kt | 118 ++++++------- compiler/src/prog8/ast/AstRecursionChecker.kt | 6 +- compiler/src/prog8/ast/StmtReorderer.kt | 18 +- compiler/src/prog8/compiler/Compiler.kt | 90 +++++----- .../src/prog8/functions/BuiltinFunctions.kt | 167 +++++++++--------- .../src/prog8/optimizing/ConstantFolding.kt | 55 +++--- compiler/src/prog8/optimizing/Extensions.kt | 6 +- .../prog8/optimizing/SimplifyExpressions.kt | 26 ++- .../prog8/optimizing/StatementOptimizer.kt | 46 +++-- compiler/src/prog8/parser/ModuleParsing.kt | 10 +- compiler/src/prog8/stackvm/Program.kt | 3 +- compiler/src/prog8/stackvm/StackVm.kt | 3 +- 14 files changed, 325 insertions(+), 341 deletions(-) diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt index 8bf5a3959..275485368 100644 --- a/compiler/src/prog8/CompilerMain.kt +++ b/compiler/src/prog8/CompilerMain.kt @@ -36,8 +36,7 @@ fun main(args: Array) { 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") } diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index 796b6badb..4d2225b3c 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -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, 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, 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, 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, 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 } diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index 4604fb6c0..9960b32da 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -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 = 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 { @@ -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 -> { diff --git a/compiler/src/prog8/ast/AstRecursionChecker.kt b/compiler/src/prog8/ast/AstRecursionChecker.kt index 195e67b3d..e6f210883 100644 --- a/compiler/src/prog8/ast/AstRecursionChecker.kt +++ b/compiler/src/prog8/ast/AstRecursionChecker.kt @@ -30,7 +30,7 @@ private class DirectedGraph { 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 { } fun checkForCycle(): MutableList { - 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() for(node in uniqueVertices) { if(isCyclicUntil(node, visited, recStack, cycle)) diff --git a/compiler/src/prog8/ast/StmtReorderer.kt b/compiler/src/prog8/ast/StmtReorderer.kt index 9b8396ccf..d0732f744 100644 --- a/compiler/src/prog8/ast/StmtReorderer.kt +++ b/compiler/src/prog8/ast/StmtReorderer.kt @@ -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() 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() 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) } diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 6812cda3f..12a44d613 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -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 = 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 { diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index 01a3669b1..29f098ffb 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -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) @@ -14,7 +10,7 @@ class BuiltinFunctionParam(val name: String, val possibleDatatypes: Set, val returntype: DataType?, - val constExpressionFunc: ((args: List, position: Position, namespace: INameScope, heap: HeapValues) -> LiteralValue)? = null) + val constExpressionFunc: ((args: List, 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, namespace: INameScope, heap: HeapValues): DataType? { +fun builtinFunctionReturnType(function: String, args: List, 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, 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, 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, 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, namespa class NotConstArgumentException: AstException("not a const argument to a built-in function") -private fun oneDoubleArg(args: List, position: Position, namespace:INameScope, heap: HeapValues, function: (arg: Double)->Number): LiteralValue { +private fun oneDoubleArg(args: List, 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, position: Position, namespace: return numericLiteral(function(float), args[0].position) } -private fun oneDoubleArgOutputWord(args: List, position: Position, namespace:INameScope, heap: HeapValues, function: (arg: Double)->Number): LiteralValue { +private fun oneDoubleArgOutputWord(args: List, 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, position: Position, namespace:INameScope, heap: HeapValues, function: (arg: Int)->Number): LiteralValue { +private fun oneIntArgOutputInt(args: List, 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, position: Position, name } private fun collectionArgOutputNumber(args: List, position: Position, - namespace:INameScope, heap: HeapValues, + program: Program, function: (arg: Collection)->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, 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, position: Positio } private fun collectionArgOutputBoolean(args: List, position: Position, - namespace:INameScope, heap: HeapValues, + program: Program, function: (arg: Collection)->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, position: Positi return LiteralValue.fromBoolean(result, position) } -private fun builtinAbs(args: List, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue { +private fun builtinAbs(args: List, 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, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue { +private fun builtinAvg(args: List, 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, 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, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue { +private fun builtinStrlen(args: List, 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, position: Position, namespace LiteralValue.optimalInteger(string.length, position=position) } -private fun builtinLen(args: List, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue { +private fun builtinLen(args: List, 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, position: Position, namespace:IN } -private fun builtinMkword(args: List, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue { +private fun builtinMkword(args: List, 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, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue { +private fun builtinSin8(args: List, 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, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue { +private fun builtinSin8u(args: List, 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, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue { +private fun builtinCos8(args: List, 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, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue { +private fun builtinCos8u(args: List, 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, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue { +private fun builtinSin16(args: List, 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, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue { +private fun builtinSin16u(args: List, 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, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue { +private fun builtinCos16(args: List, 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, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue { +private fun builtinCos16u(args: List, 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, 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 diff --git a/compiler/src/prog8/optimizing/ConstantFolding.kt b/compiler/src/prog8/optimizing/ConstantFolding.kt index e0b73aaad..15c7157bf 100644 --- a/compiler/src/prog8/optimizing/ConstantFolding.kt +++ b/compiler/src/prog8/optimizing/ConstantFolding.kt @@ -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 = 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 = array.mapNotNull { it.resultingDatatype(namespace, heap) }.toSet() + val typesInArray: Set = 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) diff --git a/compiler/src/prog8/optimizing/Extensions.kt b/compiler/src/prog8/optimizing/Extensions.kt index 4c18ea14a..b8fe4649d 100644 --- a/compiler/src/prog8/optimizing/Extensions.kt +++ b/compiler/src/prog8/optimizing/Extensions.kt @@ -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 } diff --git a/compiler/src/prog8/optimizing/SimplifyExpressions.kt b/compiler/src/prog8/optimizing/SimplifyExpressions.kt index 1a8c3056d..819015677 100644 --- a/compiler/src/prog8/optimizing/SimplifyExpressions.kt +++ b/compiler/src/prog8/optimizing/SimplifyExpressions.kt @@ -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() diff --git a/compiler/src/prog8/optimizing/StatementOptimizer.kt b/compiler/src/prog8/optimizing/StatementOptimizer.kt index 4d8132617..519032a67 100644 --- a/compiler/src/prog8/optimizing/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizing/StatementOptimizer.kt @@ -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() @@ -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 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 } } diff --git a/compiler/src/prog8/parser/ModuleParsing.kt b/compiler/src/prog8/parser/ModuleParsing.kt index a7a2d2f7a..5966e18d3 100644 --- a/compiler/src/prog8/parser/ModuleParsing.kt +++ b/compiler/src/prog8/parser/ModuleParsing.kt @@ -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 { diff --git a/compiler/src/prog8/stackvm/Program.kt b/compiler/src/prog8/stackvm/Program.kt index 9cb65bc08..0c4a4bb8f 100644 --- a/compiler/src/prog8/stackvm/Program.kt +++ b/compiler/src/prog8/stackvm/Program.kt @@ -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)) diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index a19980c21..5934aea8b 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -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))