mirror of
https://github.com/irmen/prog8.git
synced 2024-08-19 14:29:17 +00:00
some more program node cleanups
This commit is contained in:
parent
e96d3d4455
commit
b35430214b
@ -36,8 +36,7 @@ fun main(args: Array<String>) {
|
||||
|
||||
fun printSoftwareHeader(what: String) {
|
||||
val buildVersion = object {}.javaClass.getResource("/version.txt").readText().trim()
|
||||
println("\nProg8 $what by Irmen de Jong (irmen@razorvine.net)")
|
||||
println("Version: $buildVersion")
|
||||
println("\nProg8 $what v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
|
||||
println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
|
||||
}
|
||||
|
||||
|
@ -806,17 +806,17 @@ data class AssignTarget(val register: Register?,
|
||||
}
|
||||
}
|
||||
|
||||
fun determineDatatype(namespace: INameScope, heap: HeapValues, stmt: IStatement): DataType? {
|
||||
fun determineDatatype(program: Program, stmt: IStatement): DataType? {
|
||||
if(register!=null)
|
||||
return DataType.UBYTE
|
||||
|
||||
if(identifier!=null) {
|
||||
val symbol = namespace.lookup(identifier.nameInSource, stmt) ?: return null
|
||||
val symbol = program.namespace.lookup(identifier.nameInSource, stmt) ?: return null
|
||||
if (symbol is VarDecl) return symbol.datatype
|
||||
}
|
||||
|
||||
if(arrayindexed!=null) {
|
||||
val dt = arrayindexed.resultingDatatype(namespace, heap)
|
||||
val dt = arrayindexed.resultingDatatype(program)
|
||||
if(dt!=null)
|
||||
return dt
|
||||
}
|
||||
@ -846,12 +846,11 @@ data class AssignTarget(val register: Register?,
|
||||
|
||||
|
||||
interface IExpression: Node {
|
||||
// TODO pass programAst instead of namespace + heap?
|
||||
fun isIterable(namespace: INameScope, heap: HeapValues): Boolean
|
||||
fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue?
|
||||
fun isIterable(program: Program): Boolean
|
||||
fun constValue(program: Program): LiteralValue?
|
||||
fun process(processor: IAstProcessor): IExpression
|
||||
fun referencesIdentifier(name: String): Boolean
|
||||
fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType?
|
||||
fun resultingDatatype(program: Program): DataType?
|
||||
}
|
||||
|
||||
|
||||
@ -865,11 +864,11 @@ class PrefixExpression(val operator: String, var expression: IExpression, overri
|
||||
expression.linkParents(this)
|
||||
}
|
||||
|
||||
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = null
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
override fun referencesIdentifier(name: String) = expression.referencesIdentifier(name)
|
||||
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? = expression.resultingDatatype(namespace, heap)
|
||||
override fun isIterable(namespace: INameScope, heap: HeapValues) = false
|
||||
override fun resultingDatatype(program: Program): DataType? = expression.resultingDatatype(program)
|
||||
override fun isIterable(program: Program) = false
|
||||
|
||||
override fun toString(): String {
|
||||
return "Prefix($operator $expression)"
|
||||
@ -891,13 +890,13 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
|
||||
}
|
||||
|
||||
// binary expression should actually have been optimized away into a single value, before const value was requested...
|
||||
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = null
|
||||
override fun isIterable(namespace: INameScope, heap: HeapValues) = false
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
override fun isIterable(program: Program) = false
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
override fun referencesIdentifier(name: String) = left.referencesIdentifier(name) || right.referencesIdentifier(name)
|
||||
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? {
|
||||
val leftDt = left.resultingDatatype(namespace, heap)
|
||||
val rightDt = right.resultingDatatype(namespace, heap)
|
||||
override fun resultingDatatype(program: Program): DataType? {
|
||||
val leftDt = left.resultingDatatype(program)
|
||||
val rightDt = right.resultingDatatype(program)
|
||||
return when(operator) {
|
||||
"+", "-", "*", "**", "%" -> if(leftDt==null || rightDt==null) null else {
|
||||
try {
|
||||
@ -998,13 +997,13 @@ class ArrayIndexedExpression(val identifier: IdentifierReference,
|
||||
arrayspec.linkParents(this)
|
||||
}
|
||||
|
||||
override fun isIterable(namespace: INameScope, heap: HeapValues) = false
|
||||
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = null
|
||||
override fun isIterable(program: Program) = false
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
override fun process(processor: IAstProcessor): IExpression = processor.process(this)
|
||||
override fun referencesIdentifier(name: String) = identifier.referencesIdentifier(name)
|
||||
|
||||
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? {
|
||||
val target = identifier.targetStatement(namespace)
|
||||
override fun resultingDatatype(program: Program): DataType? {
|
||||
val target = identifier.targetStatement(program.namespace)
|
||||
if (target is VarDecl) {
|
||||
return when (target.datatype) {
|
||||
in NumericDatatypes -> null
|
||||
@ -1036,10 +1035,10 @@ class TypecastExpression(var expression: IExpression, var type: DataType, overri
|
||||
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
override fun referencesIdentifier(name: String) = expression.referencesIdentifier(name)
|
||||
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? = type
|
||||
override fun isIterable(namespace: INameScope, heap: HeapValues) = type in IterableDatatypes
|
||||
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? {
|
||||
val cv = expression.constValue(namespace, heap) ?: return null
|
||||
override fun resultingDatatype(program: Program): DataType? = type
|
||||
override fun isIterable(program: Program) = type in IterableDatatypes
|
||||
override fun constValue(program: Program): LiteralValue? {
|
||||
val cv = expression.constValue(program) ?: return null
|
||||
val value = Value(cv.type, cv.asNumericValue!!).cast(type)
|
||||
return LiteralValue.fromNumber(value.numericValue(), value.type, position)
|
||||
}
|
||||
@ -1059,10 +1058,10 @@ data class AddressOf(val identifier: IdentifierReference, override val position:
|
||||
}
|
||||
|
||||
var scopedname: String? = null // will be set in a later state by the compiler
|
||||
override fun isIterable(namespace: INameScope, heap: HeapValues) = false
|
||||
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = null
|
||||
override fun isIterable(program: Program) = false
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
override fun referencesIdentifier(name: String) = false
|
||||
override fun resultingDatatype(namespace: INameScope, heap: HeapValues) = DataType.UWORD
|
||||
override fun resultingDatatype(program: Program) = DataType.UWORD
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
}
|
||||
|
||||
@ -1077,9 +1076,9 @@ class DirectMemoryRead(var addressExpression: IExpression, override val position
|
||||
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
override fun referencesIdentifier(name: String) = false
|
||||
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? = DataType.UBYTE
|
||||
override fun isIterable(namespace: INameScope, heap: HeapValues) = false
|
||||
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = null
|
||||
override fun resultingDatatype(program: Program): DataType? = DataType.UBYTE
|
||||
override fun isIterable(program: Program) = false
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
|
||||
override fun toString(): String {
|
||||
return "DirectMemoryRead($addressExpression)"
|
||||
@ -1097,9 +1096,9 @@ class DirectMemoryWrite(var addressExpression: IExpression, override val positio
|
||||
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
override fun referencesIdentifier(name: String) = false
|
||||
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? = DataType.UBYTE
|
||||
override fun isIterable(namespace: INameScope, heap: HeapValues) = false
|
||||
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = null
|
||||
override fun resultingDatatype(program: Program): DataType? = DataType.UBYTE
|
||||
override fun isIterable(program: Program) = false
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
|
||||
override fun toString(): String {
|
||||
return "DirectMemoryWrite($addressExpression)"
|
||||
@ -1209,7 +1208,7 @@ class LiteralValue(val type: DataType,
|
||||
arrayvalue?.forEach {it.linkParents(this)}
|
||||
}
|
||||
|
||||
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = this
|
||||
override fun constValue(program: Program): LiteralValue? = this
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
|
||||
override fun toString(): String {
|
||||
@ -1232,9 +1231,9 @@ class LiteralValue(val type: DataType,
|
||||
return "LiteralValue($vstr)"
|
||||
}
|
||||
|
||||
override fun resultingDatatype(namespace: INameScope, heap: HeapValues) = type
|
||||
override fun resultingDatatype(program: Program) = type
|
||||
|
||||
override fun isIterable(namespace: INameScope, heap: HeapValues): Boolean = type in IterableDatatypes
|
||||
override fun isIterable(program: Program): Boolean = type in IterableDatatypes
|
||||
|
||||
override fun hashCode(): Int {
|
||||
val bh = bytevalue?.hashCode() ?: 0x10001234
|
||||
@ -1363,13 +1362,13 @@ class RangeExpr(var from: IExpression,
|
||||
step.linkParents(this)
|
||||
}
|
||||
|
||||
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = null
|
||||
override fun isIterable(namespace: INameScope, heap: HeapValues) = true
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
override fun isIterable(program: Program) = true
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
override fun referencesIdentifier(name: String): Boolean = from.referencesIdentifier(name) || to.referencesIdentifier(name)
|
||||
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? {
|
||||
val fromDt=from.resultingDatatype(namespace, heap)
|
||||
val toDt=to.resultingDatatype(namespace, heap)
|
||||
override fun resultingDatatype(program: Program): DataType? {
|
||||
val fromDt=from.resultingDatatype(program)
|
||||
val toDt=to.resultingDatatype(program)
|
||||
return when {
|
||||
fromDt==null || toDt==null -> null
|
||||
fromDt==DataType.UBYTE && toDt==DataType.UBYTE -> DataType.UBYTE
|
||||
@ -1433,15 +1432,15 @@ class RegisterExpr(val register: Register, override val position: Position) : IE
|
||||
this.parent = parent
|
||||
}
|
||||
|
||||
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? = null
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
override fun process(processor: IAstProcessor) = this
|
||||
override fun referencesIdentifier(name: String): Boolean = false
|
||||
override fun isIterable(namespace: INameScope, heap: HeapValues) = false
|
||||
override fun isIterable(program: Program) = false
|
||||
override fun toString(): String {
|
||||
return "RegisterExpr(register=$register, pos=$position)"
|
||||
}
|
||||
|
||||
override fun resultingDatatype(namespace: INameScope, heap: HeapValues) = DataType.UBYTE
|
||||
override fun resultingDatatype(program: Program) = DataType.UBYTE
|
||||
}
|
||||
|
||||
|
||||
@ -1461,8 +1460,8 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
this.parent = parent
|
||||
}
|
||||
|
||||
override fun constValue(namespace: INameScope, heap: HeapValues): LiteralValue? {
|
||||
val node = namespace.lookup(nameInSource, this)
|
||||
override fun constValue(program: Program): LiteralValue? {
|
||||
val node = program.namespace.lookup(nameInSource, this)
|
||||
?: throw UndefinedSymbolError(this)
|
||||
val vardecl = node as? VarDecl
|
||||
if(vardecl==null) {
|
||||
@ -1470,7 +1469,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
} else if(vardecl.type!=VarDeclType.CONST) {
|
||||
return null
|
||||
}
|
||||
return vardecl.value?.constValue(namespace, heap)
|
||||
return vardecl.value?.constValue(program)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@ -1480,8 +1479,8 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
override fun referencesIdentifier(name: String): Boolean = nameInSource.last() == name // @todo is this correct all the time?
|
||||
|
||||
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? {
|
||||
val targetStmt = targetStatement(namespace)
|
||||
override fun resultingDatatype(program: Program): DataType? {
|
||||
val targetStmt = targetStatement(program.namespace)
|
||||
if(targetStmt is VarDecl) {
|
||||
return targetStmt.datatype
|
||||
} else {
|
||||
@ -1489,7 +1488,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
}
|
||||
}
|
||||
|
||||
override fun isIterable(namespace: INameScope, heap: HeapValues): Boolean = resultingDatatype(namespace, heap) in IterableDatatypes
|
||||
override fun isIterable(program: Program): Boolean = resultingDatatype(program) in IterableDatatypes
|
||||
|
||||
fun heapId(namespace: INameScope): Int {
|
||||
val node = namespace.lookup(nameInSource, this) ?: throw UndefinedSymbolError(this)
|
||||
@ -1544,9 +1543,9 @@ class FunctionCall(override var target: IdentifierReference,
|
||||
arglist.forEach { it.linkParents(this) }
|
||||
}
|
||||
|
||||
override fun constValue(namespace: INameScope, heap: HeapValues) = constValue(namespace, heap, true)
|
||||
override fun constValue(program: Program) = constValue(program, true)
|
||||
|
||||
private fun constValue(namespace: INameScope, heap: HeapValues, withDatatypeCheck: Boolean): LiteralValue? {
|
||||
private fun constValue(program: Program, withDatatypeCheck: Boolean): LiteralValue? {
|
||||
// if the function is a built-in function and the args are consts, should try to const-evaluate!
|
||||
// lenghts of arrays and strings are constants that are determined at compile time!
|
||||
if(target.nameInSource.size>1) return null
|
||||
@ -1556,13 +1555,13 @@ class FunctionCall(override var target: IdentifierReference,
|
||||
if(func!=null) {
|
||||
val exprfunc = func.constExpressionFunc
|
||||
if(exprfunc!=null)
|
||||
resultValue = exprfunc(arglist, position, namespace, heap)
|
||||
resultValue = exprfunc(arglist, position, program)
|
||||
else if(func.returntype==null)
|
||||
throw ExpressionError("builtin function ${target.nameInSource[0]} can't be used here because it doesn't return a value", position)
|
||||
}
|
||||
|
||||
if(withDatatypeCheck) {
|
||||
val resultDt = this.resultingDatatype(namespace, heap)
|
||||
val resultDt = this.resultingDatatype(program)
|
||||
if(resultValue==null || resultDt == resultValue.type)
|
||||
return resultValue
|
||||
throw FatalAstException("evaluated const expression result value doesn't match expected datatype $resultDt, pos=$position")
|
||||
@ -1583,18 +1582,18 @@ class FunctionCall(override var target: IdentifierReference,
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
override fun referencesIdentifier(name: String): Boolean = target.referencesIdentifier(name) || arglist.any{it.referencesIdentifier(name)}
|
||||
|
||||
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? {
|
||||
val constVal = constValue(namespace, heap,false)
|
||||
override fun resultingDatatype(program: Program): DataType? {
|
||||
val constVal = constValue(program ,false)
|
||||
if(constVal!=null)
|
||||
return constVal.type
|
||||
val stmt = target.targetStatement(namespace) ?: return null
|
||||
val stmt = target.targetStatement(program.namespace) ?: return null
|
||||
when (stmt) {
|
||||
is BuiltinFunctionStatementPlaceholder -> {
|
||||
if(target.nameInSource[0] == "set_carry" || target.nameInSource[0]=="set_irqd" ||
|
||||
target.nameInSource[0] == "clear_carry" || target.nameInSource[0]=="clear_irqd") {
|
||||
return null // these have no return value
|
||||
}
|
||||
return builtinFunctionReturnType(target.nameInSource[0], this.arglist, namespace, heap)
|
||||
return builtinFunctionReturnType(target.nameInSource[0], this.arglist, program)
|
||||
}
|
||||
is Subroutine -> {
|
||||
if(stmt.returntypes.isEmpty())
|
||||
@ -1608,7 +1607,7 @@ class FunctionCall(override var target: IdentifierReference,
|
||||
return null // calling something we don't recognise...
|
||||
}
|
||||
|
||||
override fun isIterable(namespace: INameScope, heap: HeapValues) = resultingDatatype(namespace, heap) in IterableDatatypes
|
||||
override fun isIterable(program: Program) = resultingDatatype(program) in IterableDatatypes
|
||||
}
|
||||
|
||||
|
||||
|
@ -14,7 +14,7 @@ import java.io.File
|
||||
*/
|
||||
|
||||
fun Program.checkValid(compilerOptions: CompilationOptions) {
|
||||
val checker = AstChecker(namespace, compilerOptions, heap)
|
||||
val checker = AstChecker(this, compilerOptions)
|
||||
checker.process(this)
|
||||
printErrors(checker.result(), name)
|
||||
}
|
||||
@ -52,14 +52,13 @@ fun printWarning(msg: String) {
|
||||
print("\u001b[0m\n") // normal
|
||||
}
|
||||
|
||||
private class AstChecker(private val namespace: INameScope,
|
||||
private val compilerOptions: CompilationOptions,
|
||||
private val heap: HeapValues) : IAstProcessor {
|
||||
private class AstChecker(private val program: Program,
|
||||
private val compilerOptions: CompilationOptions) : IAstProcessor {
|
||||
private val checkResult: MutableList<AstException> = mutableListOf()
|
||||
private val heapStringSentinel: Int
|
||||
init {
|
||||
val stringSentinel = heap.allEntries().firstOrNull {it.value.str==""}
|
||||
heapStringSentinel = stringSentinel?.key ?: heap.addString(DataType.STR, "")
|
||||
val stringSentinel = program.heap.allEntries().firstOrNull {it.value.str==""}
|
||||
heapStringSentinel = stringSentinel?.key ?: program.heap.addString(DataType.STR, "")
|
||||
}
|
||||
|
||||
fun result(): List<AstException> {
|
||||
@ -67,13 +66,14 @@ private class AstChecker(private val namespace: INameScope,
|
||||
}
|
||||
|
||||
override fun process(program: Program) {
|
||||
assert(program === this.program)
|
||||
// there must be a single 'main' block with a 'start' subroutine for the program entry point.
|
||||
val mainBlocks = program.modules.flatMap { it.statements }.filter { b -> b is Block && b.name=="main" }.map { it as Block }
|
||||
if(mainBlocks.size>1)
|
||||
checkResult.add(SyntaxError("more than one 'main' block", mainBlocks[0].position))
|
||||
|
||||
for(mainBlock in mainBlocks) {
|
||||
val startSub = mainBlock.subScopes().get("start") as? Subroutine
|
||||
val startSub = mainBlock.subScopes()["start"] as? Subroutine
|
||||
if (startSub == null) {
|
||||
checkResult.add(SyntaxError("missing program entrypoint ('start' subroutine in 'main' block)", mainBlock.position))
|
||||
} else {
|
||||
@ -106,7 +106,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
if(irqBlocks.size>1)
|
||||
checkResult.add(SyntaxError("more than one 'irq' block", irqBlocks[0].position))
|
||||
for(irqBlock in irqBlocks) {
|
||||
val irqSub = irqBlock?.subScopes()?.get("irq") as? Subroutine
|
||||
val irqSub = irqBlock.subScopes()["irq"] as? Subroutine
|
||||
if (irqSub != null) {
|
||||
if (irqSub.parameters.isNotEmpty() || irqSub.returntypes.isNotEmpty())
|
||||
checkResult.add(SyntaxError("irq entrypoint subroutine can't have parameters and/or return values", irqSub.position))
|
||||
@ -132,7 +132,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
if(expectedReturnValues.size != returnStmt.values.size) {
|
||||
// if the return value is a function call, check the result of that call instead
|
||||
if(returnStmt.values.size==1 && returnStmt.values[0] is FunctionCall) {
|
||||
val dt = (returnStmt.values[0] as FunctionCall).resultingDatatype(namespace, heap)
|
||||
val dt = (returnStmt.values[0] as FunctionCall).resultingDatatype(program)
|
||||
if(dt!=null && expectedReturnValues.isEmpty())
|
||||
checkResult.add(SyntaxError("invalid number of return values", returnStmt.position))
|
||||
} else
|
||||
@ -140,7 +140,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
}
|
||||
|
||||
for (rv in expectedReturnValues.withIndex().zip(returnStmt.values)) {
|
||||
val valueDt=rv.second.resultingDatatype(namespace, heap)
|
||||
val valueDt=rv.second.resultingDatatype(program)
|
||||
if(rv.first.value!=valueDt)
|
||||
checkResult.add(ExpressionError("type $valueDt of return value #${rv.first.index+1} doesn't match subroutine return type ${rv.first.value}", rv.second.position))
|
||||
}
|
||||
@ -151,10 +151,10 @@ private class AstChecker(private val namespace: INameScope,
|
||||
if(forLoop.body.isEmpty())
|
||||
printWarning("for loop body is empty", forLoop.position)
|
||||
|
||||
if(!forLoop.iterable.isIterable(namespace, heap)) {
|
||||
if(!forLoop.iterable.isIterable(program)) {
|
||||
checkResult.add(ExpressionError("can only loop over an iterable type", forLoop.position))
|
||||
} else {
|
||||
val iterableDt = forLoop.iterable.resultingDatatype(namespace, heap)
|
||||
val iterableDt = forLoop.iterable.resultingDatatype(program)
|
||||
if (forLoop.loopRegister != null) {
|
||||
printWarning("using a register as loop variable is risky (it could get clobbered in the body)", forLoop.position)
|
||||
// loop register
|
||||
@ -162,7 +162,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
checkResult.add(ExpressionError("register can only loop over bytes", forLoop.position))
|
||||
} else {
|
||||
// loop variable
|
||||
val loopvar = forLoop.loopVar!!.targetVarDecl(namespace)
|
||||
val loopvar = forLoop.loopVar!!.targetVarDecl(program.namespace)
|
||||
if(loopvar==null || loopvar.type==VarDeclType.CONST) {
|
||||
checkResult.add(SyntaxError("for loop requires a variable to loop with", forLoop.position))
|
||||
|
||||
@ -366,14 +366,14 @@ private class AstChecker(private val namespace: INameScope,
|
||||
|
||||
// assigning from a functioncall COULD return multiple values (from an asm subroutine)
|
||||
if(assignment.value is FunctionCall) {
|
||||
val stmt = (assignment.value as FunctionCall).target.targetStatement(namespace)
|
||||
val stmt = (assignment.value as FunctionCall).target.targetStatement(program.namespace)
|
||||
if (stmt is Subroutine) {
|
||||
if (stmt.isAsmSubroutine) {
|
||||
if (stmt.returntypes.size != assignment.targets.size)
|
||||
checkResult.add(ExpressionError("number of return values doesn't match number of assignment targets", assignment.value.position))
|
||||
else {
|
||||
for (thing in stmt.returntypes.zip(assignment.targets)) {
|
||||
if (thing.second.determineDatatype(namespace, heap, assignment) != thing.first)
|
||||
if (thing.second.determineDatatype(program, assignment) != thing.first)
|
||||
checkResult.add(ExpressionError("return type mismatch for target ${thing.second.shortString()}", assignment.value.position))
|
||||
}
|
||||
}
|
||||
@ -390,7 +390,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
}
|
||||
|
||||
private fun processAssignmentTarget(assignment: Assignment, target: AssignTarget): Assignment {
|
||||
val memAddr = target.memoryAddress?.addressExpression?.constValue(namespace, heap)?.asIntegerValue
|
||||
val memAddr = target.memoryAddress?.addressExpression?.constValue(program)?.asIntegerValue
|
||||
if(memAddr!=null) {
|
||||
if(memAddr<0 || memAddr>=65536)
|
||||
checkResult.add(ExpressionError("address out of range", target.position))
|
||||
@ -398,7 +398,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
|
||||
if(target.identifier!=null) {
|
||||
val targetName = target.identifier.nameInSource
|
||||
val targetSymbol = namespace.lookup(targetName, assignment)
|
||||
val targetSymbol = program.namespace.lookup(targetName, assignment)
|
||||
when (targetSymbol) {
|
||||
null -> {
|
||||
checkResult.add(ExpressionError("undefined symbol: ${targetName.joinToString(".")}", assignment.position))
|
||||
@ -436,23 +436,23 @@ private class AstChecker(private val namespace: INameScope,
|
||||
return assignment2
|
||||
}
|
||||
|
||||
val targetDatatype = target.determineDatatype(namespace, heap, assignment)
|
||||
val targetDatatype = target.determineDatatype(program, assignment)
|
||||
if(targetDatatype!=null) {
|
||||
val constVal = assignment.value.constValue(namespace, heap)
|
||||
val constVal = assignment.value.constValue(program)
|
||||
if(constVal!=null) {
|
||||
val arrayspec = if(target.identifier!=null) {
|
||||
val targetVar = namespace.lookup(target.identifier.nameInSource, assignment) as? VarDecl
|
||||
val targetVar = program.namespace.lookup(target.identifier.nameInSource, assignment) as? VarDecl
|
||||
targetVar?.arraysize
|
||||
} else null
|
||||
checkValueTypeAndRange(targetDatatype,
|
||||
arrayspec ?: ArrayIndex(LiteralValue.optimalInteger(-1, assignment.position), assignment.position),
|
||||
constVal, heap)
|
||||
constVal, program.heap)
|
||||
} else {
|
||||
val sourceDatatype: DataType? = assignment.value.resultingDatatype(namespace, heap)
|
||||
val sourceDatatype: DataType? = assignment.value.resultingDatatype(program)
|
||||
if(sourceDatatype==null) {
|
||||
if(assignment.targets.size<=1) {
|
||||
if (assignment.value is FunctionCall) {
|
||||
val targetStmt = (assignment.value as FunctionCall).target.targetStatement(namespace)
|
||||
val targetStmt = (assignment.value as FunctionCall).target.targetStatement(program.namespace)
|
||||
if(targetStmt!=null)
|
||||
checkResult.add(ExpressionError("function call doesn't return a suitable value to use in assignment", assignment.value.position))
|
||||
}
|
||||
@ -468,7 +468,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
}
|
||||
|
||||
override fun process(addressOf: AddressOf): IExpression {
|
||||
val variable=addressOf.identifier.targetVarDecl(namespace)
|
||||
val variable=addressOf.identifier.targetVarDecl(program.namespace)
|
||||
if(variable==null)
|
||||
checkResult.add(ExpressionError("pointer-of operand must be the name of a heap variable", addressOf.position))
|
||||
else {
|
||||
@ -548,11 +548,11 @@ private class AstChecker(private val namespace: INameScope,
|
||||
decl.value is LiteralValue -> {
|
||||
val arraySpec = decl.arraysize ?: (
|
||||
if((decl.value as LiteralValue).isArray)
|
||||
ArrayIndex.forArray(decl.value as LiteralValue, heap)
|
||||
ArrayIndex.forArray(decl.value as LiteralValue, program.heap)
|
||||
else
|
||||
ArrayIndex(LiteralValue.optimalInteger(-2, decl.position), decl.position)
|
||||
)
|
||||
checkValueTypeAndRange(decl.datatype, arraySpec, decl.value as LiteralValue, heap)
|
||||
checkValueTypeAndRange(decl.datatype, arraySpec, decl.value as LiteralValue, program.heap)
|
||||
}
|
||||
else -> {
|
||||
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!::class.simpleName}")
|
||||
@ -682,10 +682,10 @@ private class AstChecker(private val namespace: INameScope,
|
||||
}
|
||||
val arrayspec =
|
||||
if(literalValue.isArray)
|
||||
ArrayIndex.forArray(literalValue, heap)
|
||||
ArrayIndex.forArray(literalValue, program.heap)
|
||||
else
|
||||
ArrayIndex(LiteralValue.optimalInteger(-3, literalValue.position), literalValue.position)
|
||||
checkValueTypeAndRange(literalValue.type, arrayspec, literalValue, heap)
|
||||
checkValueTypeAndRange(literalValue.type, arrayspec, literalValue, program.heap)
|
||||
|
||||
val lv = super.process(literalValue)
|
||||
when(lv.type) {
|
||||
@ -704,7 +704,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
|
||||
override fun process(expr: PrefixExpression): IExpression {
|
||||
if(expr.operator=="-") {
|
||||
val dt = expr.resultingDatatype(namespace, heap)
|
||||
val dt = expr.resultingDatatype(program)
|
||||
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
|
||||
checkResult.add(ExpressionError("can only take negative of a signed number type", expr.position))
|
||||
}
|
||||
@ -713,12 +713,12 @@ private class AstChecker(private val namespace: INameScope,
|
||||
}
|
||||
|
||||
override fun process(expr: BinaryExpression): IExpression {
|
||||
val leftDt = expr.left.resultingDatatype(namespace, heap)
|
||||
val rightDt = expr.right.resultingDatatype(namespace, heap)
|
||||
val leftDt = expr.left.resultingDatatype(program)
|
||||
val rightDt = expr.right.resultingDatatype(program)
|
||||
|
||||
when(expr.operator){
|
||||
"/", "%" -> {
|
||||
val constvalRight = expr.right.constValue(namespace, heap)
|
||||
val constvalRight = expr.right.constValue(program)
|
||||
val divisor = constvalRight?.asNumericValue?.toDouble()
|
||||
if(divisor==0.0)
|
||||
checkResult.add(ExpressionError("division by zero", expr.right.position))
|
||||
@ -735,8 +735,8 @@ private class AstChecker(private val namespace: INameScope,
|
||||
// only integer numeric operands accepted, and if literal constants, only boolean values accepted (0 or 1)
|
||||
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
|
||||
checkResult.add(ExpressionError("logical operator can only be used on boolean operands", expr.right.position))
|
||||
val constLeft = expr.left.constValue(namespace, heap)
|
||||
val constRight = expr.right.constValue(namespace, heap)
|
||||
val constLeft = expr.left.constValue(program)
|
||||
val constRight = expr.right.constValue(program)
|
||||
if(constLeft!=null && constLeft.asIntegerValue !in 0..1 || constRight!=null && constRight.asIntegerValue !in 0..1)
|
||||
checkResult.add(ExpressionError("const literal argument of logical operator must be boolean (0 or 1)", expr.position))
|
||||
}
|
||||
@ -765,9 +765,9 @@ private class AstChecker(private val namespace: INameScope,
|
||||
checkResult.add(SyntaxError(msg, range.position))
|
||||
}
|
||||
super.process(range)
|
||||
val from = range.from.constValue(namespace, heap)
|
||||
val to = range.to.constValue(namespace, heap)
|
||||
val stepLv = range.step.constValue(namespace, heap) ?: LiteralValue(DataType.UBYTE, 1, position = range.position)
|
||||
val from = range.from.constValue(program)
|
||||
val to = range.to.constValue(program)
|
||||
val stepLv = range.step.constValue(program) ?: LiteralValue(DataType.UBYTE, 1, position = range.position)
|
||||
if (stepLv.asIntegerValue == null || stepLv.asIntegerValue == 0) {
|
||||
err("range step must be an integer != 0")
|
||||
return range
|
||||
@ -784,8 +784,8 @@ private class AstChecker(private val namespace: INameScope,
|
||||
err("descending range requires step < 0")
|
||||
}
|
||||
from.isString && to.isString -> {
|
||||
val fromString = from.strvalue(heap)
|
||||
val toString = to.strvalue(heap)
|
||||
val fromString = from.strvalue(program.heap)
|
||||
val toString = to.strvalue(program.heap)
|
||||
if(fromString.length!=1 || toString.length!=1)
|
||||
err("range from and to must be a single character")
|
||||
if(fromString[0] == toString[0])
|
||||
@ -832,18 +832,18 @@ private class AstChecker(private val namespace: INameScope,
|
||||
checkResult.add(SyntaxError("invalid number of arguments", position))
|
||||
else {
|
||||
for (arg in args.withIndex().zip(func.parameters)) {
|
||||
val argDt=arg.first.value.resultingDatatype(namespace, heap)
|
||||
val argDt=arg.first.value.resultingDatatype(program)
|
||||
if(argDt!=null && !argDt.assignableTo(arg.second.possibleDatatypes)) {
|
||||
checkResult.add(ExpressionError("builtin function '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.possibleDatatypes}", position))
|
||||
}
|
||||
}
|
||||
if(target.name=="swap") {
|
||||
// swap() is a bit weird because this one is translated into a sequence of bytecodes, instead of being an actual function call
|
||||
val dt1 = args[0].resultingDatatype(namespace, heap)!!
|
||||
val dt2 = args[1].resultingDatatype(namespace, heap)!!
|
||||
val dt1 = args[0].resultingDatatype(program)!!
|
||||
val dt2 = args[1].resultingDatatype(program)!!
|
||||
if (dt1 != dt2)
|
||||
checkResult.add(ExpressionError("swap requires 2 args of identical type", position))
|
||||
else if (args[0].constValue(namespace, heap) != null || args[1].constValue(namespace, heap) != null)
|
||||
else if (args[0].constValue(program) != null || args[1].constValue(program) != null)
|
||||
checkResult.add(ExpressionError("swap requires 2 variables, not constant value(s)", position))
|
||||
else if(same(args[0], args[1]))
|
||||
checkResult.add(ExpressionError("swap should have 2 different args", position))
|
||||
@ -856,7 +856,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
checkResult.add(SyntaxError("invalid number of arguments", position))
|
||||
else {
|
||||
for (arg in args.withIndex().zip(target.parameters)) {
|
||||
val argDt = arg.first.value.resultingDatatype(namespace, heap)
|
||||
val argDt = arg.first.value.resultingDatatype(program)
|
||||
if(argDt!=null && !argDt.assignableTo(arg.second.type)) {
|
||||
// for asm subroutines having STR param it's okay to provide a UWORD too (pointer value)
|
||||
if(!(target.isAsmSubroutine && arg.second.type in StringDatatypes && argDt==DataType.UWORD))
|
||||
@ -890,7 +890,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
override fun process(postIncrDecr: PostIncrDecr): IStatement {
|
||||
if(postIncrDecr.target.identifier != null) {
|
||||
val targetName = postIncrDecr.target.identifier!!.nameInSource
|
||||
val target = namespace.lookup(targetName, postIncrDecr)
|
||||
val target = program.namespace.lookup(targetName, postIncrDecr)
|
||||
if(target==null) {
|
||||
checkResult.add(SyntaxError("undefined symbol: ${targetName.joinToString(".")}", postIncrDecr.position))
|
||||
} else {
|
||||
@ -901,7 +901,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
}
|
||||
}
|
||||
} else if(postIncrDecr.target.arrayindexed != null) {
|
||||
val target = postIncrDecr.target.arrayindexed?.identifier?.targetStatement(namespace)
|
||||
val target = postIncrDecr.target.arrayindexed?.identifier?.targetStatement(program.namespace)
|
||||
if(target==null) {
|
||||
checkResult.add(SyntaxError("undefined symbol", postIncrDecr.position))
|
||||
}
|
||||
@ -917,7 +917,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
}
|
||||
|
||||
override fun process(arrayIndexedExpression: ArrayIndexedExpression): IExpression {
|
||||
val target = arrayIndexedExpression.identifier.targetStatement(namespace)
|
||||
val target = arrayIndexedExpression.identifier.targetStatement(program.namespace)
|
||||
if(target is VarDecl) {
|
||||
if(target.datatype !in IterableDatatypes)
|
||||
checkResult.add(SyntaxError("indexing requires an iterable variable", arrayIndexedExpression.position))
|
||||
@ -930,7 +930,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
} else if(target.datatype in StringDatatypes) {
|
||||
// check string lengths
|
||||
val heapId = (target.value as LiteralValue).heapId!!
|
||||
val stringLen = heap.get(heapId).str!!.length
|
||||
val stringLen = program.heap.get(heapId).str!!.length
|
||||
val index = (arrayIndexedExpression.arrayspec.index as? LiteralValue)?.asIntegerValue
|
||||
if(index!=null && (index<0 || index>=stringLen))
|
||||
checkResult.add(ExpressionError("index out of bounds", arrayIndexedExpression.arrayspec.position))
|
||||
@ -939,7 +939,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
checkResult.add(SyntaxError("indexing requires a variable to act upon", arrayIndexedExpression.position))
|
||||
|
||||
// check index value 0..255
|
||||
val dtx = arrayIndexedExpression.arrayspec.index.resultingDatatype(namespace, heap)
|
||||
val dtx = arrayIndexedExpression.arrayspec.index.resultingDatatype(program)
|
||||
if(dtx!=DataType.UBYTE && dtx!=DataType.BYTE)
|
||||
checkResult.add(SyntaxError("array indexing is limited to byte size 0..255", arrayIndexedExpression.position))
|
||||
|
||||
@ -947,7 +947,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
}
|
||||
|
||||
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: IStatement): IStatement? {
|
||||
val targetStatement = target.targetStatement(namespace)
|
||||
val targetStatement = target.targetStatement(program.namespace)
|
||||
if(targetStatement is Label || targetStatement is Subroutine || targetStatement is BuiltinFunctionStatementPlaceholder)
|
||||
return targetStatement
|
||||
checkResult.add(NameError("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", statement.position))
|
||||
@ -955,8 +955,8 @@ private class AstChecker(private val namespace: INameScope,
|
||||
}
|
||||
|
||||
private fun checkValueTypeAndRange(targetDt: DataType, arrayspec: ArrayIndex, range: RangeExpr) : Boolean {
|
||||
val from = range.from.constValue(namespace, heap)
|
||||
val to = range.to.constValue(namespace, heap)
|
||||
val from = range.from.constValue(program)
|
||||
val to = range.to.constValue(program)
|
||||
if(from==null || to==null) {
|
||||
checkResult.add(SyntaxError("range from and to values must be constants", range.position))
|
||||
return false
|
||||
@ -973,7 +973,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
checkResult.add(ExpressionError("range for string must have single characters from and to values", range.position))
|
||||
return false
|
||||
}
|
||||
val rangeSize=range.size(heap)
|
||||
val rangeSize=range.size(program.heap)
|
||||
if(rangeSize!=null && (rangeSize<0 || rangeSize>255)) {
|
||||
checkResult.add(ExpressionError("size of range for string must be 0..255, instead of $rangeSize", range.position))
|
||||
return false
|
||||
@ -983,7 +983,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
in ArrayDatatypes -> {
|
||||
// range and length check bytes
|
||||
val expectedSize = arrayspec.size()
|
||||
val rangeSize=range.size(heap)
|
||||
val rangeSize=range.size(program.heap)
|
||||
if(rangeSize!=null && rangeSize != expectedSize) {
|
||||
checkResult.add(ExpressionError("range size doesn't match array size, expected $expectedSize found $rangeSize", range.position))
|
||||
return false
|
||||
@ -1059,7 +1059,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||
if(arraySpecSize<1 || arraySpecSize>256)
|
||||
return err("byte array length must be 1-256")
|
||||
val constX = arrayspec.index.constValue(namespace, heap)
|
||||
val constX = arrayspec.index.constValue(program)
|
||||
if(constX?.asIntegerValue==null)
|
||||
return err("array size specifier must be constant integer value")
|
||||
val expectedSize = constX.asIntegerValue
|
||||
@ -1081,7 +1081,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||
if(arraySpecSize<1 || arraySpecSize>128)
|
||||
return err("word array length must be 1-128")
|
||||
val constX = arrayspec.index.constValue(namespace, heap)
|
||||
val constX = arrayspec.index.constValue(program)
|
||||
if(constX?.asIntegerValue==null)
|
||||
return err("array size specifier must be constant integer value")
|
||||
val expectedSize = constX.asIntegerValue
|
||||
@ -1103,7 +1103,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||
if(arraySpecSize < 1 || arraySpecSize>51)
|
||||
return err("float array length must be 1-51")
|
||||
val constX = arrayspec.index.constValue(namespace, heap)
|
||||
val constX = arrayspec.index.constValue(program)
|
||||
if(constX?.asIntegerValue==null)
|
||||
return err("array size specifier must be constant integer value")
|
||||
val expectedSize = constX.asIntegerValue
|
||||
@ -1114,7 +1114,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
|
||||
// check if the floating point values are all within range
|
||||
val doubles = if(value.arrayvalue!=null)
|
||||
value.arrayvalue.map {it.constValue(namespace, heap)?.asNumericValue!!.toDouble()}.toDoubleArray()
|
||||
value.arrayvalue.map {it.constValue(program)?.asNumericValue!!.toDouble()}.toDoubleArray()
|
||||
else
|
||||
heap.get(value.heapId!!).doubleArray!!
|
||||
if(doubles.any { it < FLOAT_MAX_NEGATIVE || it> FLOAT_MAX_POSITIVE})
|
||||
@ -1128,7 +1128,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
}
|
||||
|
||||
private fun checkArrayValues(value: LiteralValue, type: DataType): Boolean {
|
||||
val array = heap.get(value.heapId!!)
|
||||
val array = program.heap.get(value.heapId!!)
|
||||
val correct: Boolean
|
||||
when(type) {
|
||||
DataType.ARRAY_UB -> {
|
||||
|
@ -30,7 +30,7 @@ private class DirectedGraph<VT> {
|
||||
|
||||
fun print() {
|
||||
println("#vertices: $numVertices")
|
||||
graph.forEach { from, to ->
|
||||
graph.forEach { (from, to) ->
|
||||
println("$from CALLS:")
|
||||
to.forEach { println(" $it") }
|
||||
}
|
||||
@ -41,8 +41,8 @@ private class DirectedGraph<VT> {
|
||||
}
|
||||
|
||||
fun checkForCycle(): MutableList<VT> {
|
||||
val visited = uniqueVertices.associate { it to false }.toMutableMap()
|
||||
val recStack = uniqueVertices.associate { it to false }.toMutableMap()
|
||||
val visited = uniqueVertices.associateWith { false }.toMutableMap()
|
||||
val recStack = uniqueVertices.associateWith { false }.toMutableMap()
|
||||
val cycle = mutableListOf<VT>()
|
||||
for(node in uniqueVertices) {
|
||||
if(isCyclicUntil(node, visited, recStack, cycle))
|
||||
|
@ -1,19 +1,17 @@
|
||||
package prog8.ast
|
||||
|
||||
import prog8.compiler.HeapValues
|
||||
|
||||
fun Program.reorderStatements() {
|
||||
val initvalueCreator = VarInitValueAndAddressOfCreator(namespace)
|
||||
initvalueCreator.process(this)
|
||||
|
||||
val checker = StatementReorderer(namespace, heap)
|
||||
val checker = StatementReorderer(this)
|
||||
checker.process(this)
|
||||
}
|
||||
|
||||
const val initvarsSubName="prog8_init_vars" // the name of the subroutine that should be called for every block to initialize its variables
|
||||
|
||||
|
||||
private class StatementReorderer(private val namespace: INameScope, private val heap: HeapValues): IAstProcessor {
|
||||
private class StatementReorderer(private val program: Program): IAstProcessor {
|
||||
// Reorders the statements in a way the compiler needs.
|
||||
// - 'main' block must be the very first statement UNLESS it has an address set.
|
||||
// - blocks are ordered by address, where blocks without address are put at the end.
|
||||
@ -97,7 +95,7 @@ private class StatementReorderer(private val namespace: INameScope, private val
|
||||
}
|
||||
}
|
||||
|
||||
val varDecls = block.statements.filter { it is VarDecl }
|
||||
val varDecls = block.statements.filterIsInstance<VarDecl>()
|
||||
block.statements.removeAll(varDecls)
|
||||
block.statements.addAll(0, varDecls)
|
||||
val directives = block.statements.filter {it is Directive && it.directive in directivesToMove}
|
||||
@ -160,7 +158,7 @@ private class StatementReorderer(private val namespace: INameScope, private val
|
||||
if(decl.arraysize==null) {
|
||||
val array = decl.value as? LiteralValue
|
||||
if(array!=null && array.isArray) {
|
||||
val size = heap.get(array.heapId!!).arraysize
|
||||
val size = program.heap.get(array.heapId!!).arraysize
|
||||
decl.arraysize = ArrayIndex(LiteralValue.optimalInteger(size, decl.position), decl.position)
|
||||
}
|
||||
}
|
||||
@ -172,8 +170,8 @@ private class StatementReorderer(private val namespace: INameScope, private val
|
||||
val result = mutableListOf<IStatement>()
|
||||
val stmtIter = statements.iterator()
|
||||
for(stmt in stmtIter) {
|
||||
if(stmt is Assignment && !stmt.targets.any { it.isMemoryMapped(namespace) }) {
|
||||
val constval = stmt.value.constValue(namespace, heap)
|
||||
if(stmt is Assignment && !stmt.targets.any { it.isMemoryMapped(program.namespace) }) {
|
||||
val constval = stmt.value.constValue(program)
|
||||
if(constval!=null) {
|
||||
val (sorted, trailing) = sortConstantAssignmentSequence(stmt, stmtIter)
|
||||
result.addAll(sorted)
|
||||
@ -196,7 +194,7 @@ private class StatementReorderer(private val namespace: INameScope, private val
|
||||
while(stmtIter.hasNext()) {
|
||||
val next = stmtIter.next()
|
||||
if(next is Assignment) {
|
||||
val constValue = next.value.constValue(namespace, heap)
|
||||
val constValue = next.value.constValue(program)
|
||||
if(constValue==null) {
|
||||
trailing = next
|
||||
break
|
||||
@ -208,7 +206,7 @@ private class StatementReorderer(private val namespace: INameScope, private val
|
||||
break
|
||||
}
|
||||
}
|
||||
val sorted = sequence.sortedWith(compareBy({it.value.resultingDatatype(namespace, heap)}, {it.singleTarget?.shortString(true)}))
|
||||
val sorted = sequence.sortedWith(compareBy({it.value.resultingDatatype(program)}, {it.singleTarget?.shortString(true)}))
|
||||
return Pair(sorted, trailing)
|
||||
}
|
||||
|
||||
|
@ -145,11 +145,11 @@ data class CompilationOptions(val output: OutputType,
|
||||
val floats: Boolean)
|
||||
|
||||
|
||||
internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
internal class Compiler(private val program: Program): IAstProcessor {
|
||||
|
||||
val prog: IntermediateProgram = IntermediateProgram(programAst2.name, programAst2.loadAddress, programAst2.heap, programAst2.modules.first().source)
|
||||
val namespace = programAst2.namespace
|
||||
val heap = programAst2.heap
|
||||
val prog: IntermediateProgram = IntermediateProgram(program.name, program.loadAddress, program.heap, program.modules.first().source)
|
||||
val namespace = program.namespace
|
||||
val heap = program.heap
|
||||
|
||||
private var generatedLabelSequenceNumber = 0
|
||||
private val breakStmtLabelStack : Stack<String> = Stack()
|
||||
@ -157,7 +157,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
|
||||
fun compile(options: CompilationOptions) : IntermediateProgram {
|
||||
println("Creating stackVM code...")
|
||||
programAst2.modules.forEach {
|
||||
program.modules.forEach {
|
||||
process(it)
|
||||
}
|
||||
return prog
|
||||
@ -519,7 +519,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
val trueGoto = stmt.truepart.statements.singleOrNull() as? Jump
|
||||
if(trueGoto!=null) {
|
||||
// optimization for if (condition) goto ....
|
||||
val conditionJumpOpcode = when(stmt.condition.resultingDatatype(namespace, heap)) {
|
||||
val conditionJumpOpcode = when(stmt.condition.resultingDatatype(program)) {
|
||||
in ByteDatatypes -> Opcode.JNZ
|
||||
in WordDatatypes -> Opcode.JNZW
|
||||
else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt")
|
||||
@ -529,7 +529,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
return
|
||||
}
|
||||
|
||||
val conditionJumpOpcode = when(stmt.condition.resultingDatatype(namespace, heap)) {
|
||||
val conditionJumpOpcode = when(stmt.condition.resultingDatatype(program)) {
|
||||
in ByteDatatypes -> Opcode.JZ
|
||||
in WordDatatypes -> Opcode.JZW
|
||||
else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt")
|
||||
@ -622,11 +622,11 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
}
|
||||
is PrefixExpression -> {
|
||||
translate(expr.expression)
|
||||
translatePrefixOperator(expr.operator, expr.expression.resultingDatatype(namespace, heap))
|
||||
translatePrefixOperator(expr.operator, expr.expression.resultingDatatype(program))
|
||||
}
|
||||
is BinaryExpression -> {
|
||||
val leftDt = expr.left.resultingDatatype(namespace, heap)!!
|
||||
val rightDt = expr.right.resultingDatatype(namespace, heap)!!
|
||||
val leftDt = expr.left.resultingDatatype(program)!!
|
||||
val rightDt = expr.right.resultingDatatype(program)!!
|
||||
val commonDt =
|
||||
if(expr.operator=="/")
|
||||
BinaryExpression.divisionOpDt(leftDt, rightDt)
|
||||
@ -639,7 +639,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
if(rightDt!=commonDt)
|
||||
convertType(rightDt, commonDt)
|
||||
if(expr.operator=="<<" || expr.operator==">>")
|
||||
translateBitshiftedOperator(expr.operator, leftDt, expr.right.constValue(namespace, heap))
|
||||
translateBitshiftedOperator(expr.operator, leftDt, expr.right.constValue(program))
|
||||
else
|
||||
translateBinaryOperator(expr.operator, commonDt)
|
||||
}
|
||||
@ -662,7 +662,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
is DirectMemoryWrite -> translate(expr)
|
||||
is AddressOf -> translate(expr)
|
||||
else -> {
|
||||
val lv = expr.constValue(namespace, heap) ?: throw CompilerException("constant expression required, not $expr")
|
||||
val lv = expr.constValue(program) ?: throw CompilerException("constant expression required, not $expr")
|
||||
when(lv.type) {
|
||||
in ByteDatatypes -> prog.instr(Opcode.PUSH_BYTE, Value(lv.type, lv.bytevalue!!))
|
||||
in WordDatatypes -> prog.instr(Opcode.PUSH_WORD, Value(lv.type, lv.wordvalue!!))
|
||||
@ -798,7 +798,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
// cast type if needed
|
||||
if(builtinFuncParams!=null) {
|
||||
val paramDts = builtinFuncParams[index].possibleDatatypes
|
||||
val argDt = arg.resultingDatatype(namespace, heap)!!
|
||||
val argDt = arg.resultingDatatype(program)!!
|
||||
if(argDt !in paramDts) {
|
||||
for(paramDt in paramDts.sorted())
|
||||
if(tryConvertType(argDt, paramDt))
|
||||
@ -811,7 +811,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
"len" -> {
|
||||
// 1 argument, type determines the exact syscall to use
|
||||
val arg=args.single()
|
||||
when (arg.resultingDatatype(namespace, heap)) {
|
||||
when (arg.resultingDatatype(program)) {
|
||||
DataType.STR, DataType.STR_S -> createSyscall("${funcname}_str")
|
||||
else -> throw CompilerException("wrong datatype for len()")
|
||||
}
|
||||
@ -822,7 +822,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
val target=arg.targetVarDecl(namespace)!!
|
||||
val length=Value(DataType.UBYTE, target.arraysize!!.size()!!)
|
||||
prog.instr(Opcode.PUSH_BYTE, length)
|
||||
when (arg.resultingDatatype(namespace, heap)) {
|
||||
when (arg.resultingDatatype(program)) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB -> createSyscall("${funcname}_b")
|
||||
DataType.ARRAY_W, DataType.ARRAY_UW -> createSyscall("${funcname}_w")
|
||||
DataType.ARRAY_F -> createSyscall("${funcname}_f")
|
||||
@ -834,7 +834,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
val arg=args.single() as IdentifierReference
|
||||
val target=arg.targetVarDecl(namespace)!!
|
||||
val length=Value(DataType.UBYTE, target.arraysize!!.size()!!)
|
||||
val arrayDt=arg.resultingDatatype(namespace, heap)
|
||||
val arrayDt=arg.resultingDatatype(program)
|
||||
prog.instr(Opcode.PUSH_BYTE, length)
|
||||
when (arrayDt) {
|
||||
DataType.ARRAY_UB -> {
|
||||
@ -866,7 +866,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
val target=arg.targetVarDecl(namespace)!!
|
||||
val length=Value(DataType.UBYTE, target.arraysize!!.size()!!)
|
||||
prog.instr(Opcode.PUSH_BYTE, length)
|
||||
when (arg.resultingDatatype(namespace, heap)) {
|
||||
when (arg.resultingDatatype(program)) {
|
||||
DataType.ARRAY_UB -> createSyscall("${funcname}_ub")
|
||||
DataType.ARRAY_B -> createSyscall("${funcname}_b")
|
||||
DataType.ARRAY_UW -> createSyscall("${funcname}_uw")
|
||||
@ -878,7 +878,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
"abs" -> {
|
||||
// 1 argument, type determines the exact opcode to use
|
||||
val arg = args.single()
|
||||
when (arg.resultingDatatype(namespace, heap)) {
|
||||
when (arg.resultingDatatype(program)) {
|
||||
DataType.UBYTE, DataType.UWORD -> {}
|
||||
DataType.BYTE -> prog.instr(Opcode.ABS_B)
|
||||
DataType.WORD -> prog.instr(Opcode.ABS_W)
|
||||
@ -890,7 +890,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
"mkword" -> prog.instr(Opcode.MKWORD)
|
||||
"lsl" -> {
|
||||
val arg = args.single()
|
||||
val dt = arg.resultingDatatype(namespace, heap)
|
||||
val dt = arg.resultingDatatype(program)
|
||||
when (dt) {
|
||||
in ByteDatatypes -> prog.instr(Opcode.SHL_BYTE)
|
||||
in WordDatatypes -> prog.instr(Opcode.SHL_WORD)
|
||||
@ -901,7 +901,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
}
|
||||
"lsr" -> {
|
||||
val arg = args.single()
|
||||
val dt = arg.resultingDatatype(namespace, heap)
|
||||
val dt = arg.resultingDatatype(program)
|
||||
when (dt) {
|
||||
DataType.UBYTE -> prog.instr(Opcode.SHR_UBYTE)
|
||||
DataType.BYTE -> prog.instr(Opcode.SHR_SBYTE)
|
||||
@ -914,7 +914,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
}
|
||||
"rol" -> {
|
||||
val arg = args.single()
|
||||
val dt = arg.resultingDatatype(namespace, heap)
|
||||
val dt = arg.resultingDatatype(program)
|
||||
when (dt) {
|
||||
DataType.UBYTE -> prog.instr(Opcode.ROL_BYTE)
|
||||
DataType.UWORD -> prog.instr(Opcode.ROL_WORD)
|
||||
@ -925,7 +925,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
}
|
||||
"ror" -> {
|
||||
val arg = args.single()
|
||||
val dt = arg.resultingDatatype(namespace, heap)
|
||||
val dt = arg.resultingDatatype(program)
|
||||
when (dt) {
|
||||
in ByteDatatypes -> prog.instr(Opcode.ROR_BYTE)
|
||||
in WordDatatypes -> prog.instr(Opcode.ROR_WORD)
|
||||
@ -936,7 +936,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
}
|
||||
"rol2" -> {
|
||||
val arg = args.single()
|
||||
val dt = arg.resultingDatatype(namespace, heap)
|
||||
val dt = arg.resultingDatatype(program)
|
||||
when (dt) {
|
||||
in ByteDatatypes -> prog.instr(Opcode.ROL2_BYTE)
|
||||
in WordDatatypes -> prog.instr(Opcode.ROL2_WORD)
|
||||
@ -947,7 +947,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
}
|
||||
"ror2" -> {
|
||||
val arg = args.single()
|
||||
val dt = arg.resultingDatatype(namespace, heap)
|
||||
val dt = arg.resultingDatatype(program)
|
||||
when (dt) {
|
||||
in ByteDatatypes -> prog.instr(Opcode.ROR2_BYTE)
|
||||
in WordDatatypes -> prog.instr(Opcode.ROR2_WORD)
|
||||
@ -970,11 +970,11 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
// swap(x,y) is treated differently, it's not a normal function call
|
||||
if (args.size != 2)
|
||||
throw AstException("swap requires 2 arguments")
|
||||
val dt1 = args[0].resultingDatatype(namespace, heap)!!
|
||||
val dt2 = args[1].resultingDatatype(namespace, heap)!!
|
||||
val dt1 = args[0].resultingDatatype(program)!!
|
||||
val dt2 = args[1].resultingDatatype(program)!!
|
||||
if (dt1 != dt2)
|
||||
throw AstException("swap requires 2 args of identical type")
|
||||
if (args[0].constValue(namespace, heap) != null || args[1].constValue(namespace, heap) != null)
|
||||
if (args[0].constValue(program) != null || args[1].constValue(program) != null)
|
||||
throw AstException("swap requires 2 variables, not constant value(s)")
|
||||
if(same(args[0], args[1]))
|
||||
throw AstException("swap should have 2 different args")
|
||||
@ -1004,7 +1004,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
// (subroutine arguments are not passed via the stack!)
|
||||
for (arg in arguments.zip(subroutine.parameters)) {
|
||||
translate(arg.first)
|
||||
convertType(arg.first.resultingDatatype(namespace, heap)!!, arg.second.type) // convert types of arguments to required parameter type
|
||||
convertType(arg.first.resultingDatatype(program)!!, arg.second.type) // convert types of arguments to required parameter type
|
||||
val opcode = opcodePopvar(arg.second.type)
|
||||
prog.instr(opcode, callLabel = subroutine.scopedname + "." + arg.second.name)
|
||||
}
|
||||
@ -1046,7 +1046,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
for (arg in arguments.zip(subroutine.asmParameterRegisters)) {
|
||||
if (arg.second.statusflag != null) {
|
||||
if (arg.second.statusflag == Statusflag.Pc)
|
||||
carryParam = arg.first.constValue(namespace, heap)!!.asBooleanValue
|
||||
carryParam = arg.first.constValue(program)!!.asBooleanValue
|
||||
else
|
||||
throw CompilerException("no support for status flag parameter: ${arg.second.statusflag}")
|
||||
} else {
|
||||
@ -1077,7 +1077,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
}
|
||||
val valueA: IExpression
|
||||
val valueX: IExpression
|
||||
val paramDt = arg.first.resultingDatatype(namespace, heap)
|
||||
val paramDt = arg.first.resultingDatatype(program)
|
||||
when (paramDt) {
|
||||
DataType.UBYTE -> {
|
||||
valueA = arg.first
|
||||
@ -1100,7 +1100,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
AY -> {
|
||||
val valueA: IExpression
|
||||
val valueY: IExpression
|
||||
val paramDt = arg.first.resultingDatatype(namespace, heap)
|
||||
val paramDt = arg.first.resultingDatatype(program)
|
||||
when (paramDt) {
|
||||
DataType.UBYTE -> {
|
||||
valueA = arg.first
|
||||
@ -1127,7 +1127,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
}
|
||||
val valueX: IExpression
|
||||
val valueY: IExpression
|
||||
val paramDt = arg.first.resultingDatatype(namespace, heap)
|
||||
val paramDt = arg.first.resultingDatatype(program)
|
||||
when (paramDt) {
|
||||
DataType.UBYTE -> {
|
||||
valueX = arg.first
|
||||
@ -1450,7 +1450,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
}
|
||||
}
|
||||
stmt.target.memoryAddress != null -> {
|
||||
val address = stmt.target.memoryAddress?.addressExpression?.constValue(namespace, heap)?.asIntegerValue
|
||||
val address = stmt.target.memoryAddress?.addressExpression?.constValue(program)?.asIntegerValue
|
||||
if(address!=null) {
|
||||
when(stmt.operator) {
|
||||
"++" -> prog.instr(Opcode.INC_MEMORY, Value(DataType.UWORD, address))
|
||||
@ -1479,8 +1479,8 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
return
|
||||
}
|
||||
|
||||
val valueDt = stmt.value.resultingDatatype(namespace, heap)
|
||||
val targetDt = assignTarget.determineDatatype(namespace, heap, stmt)
|
||||
val valueDt = stmt.value.resultingDatatype(program)
|
||||
val targetDt = assignTarget.determineDatatype(program, stmt)
|
||||
if(valueDt!=targetDt) {
|
||||
// convert value to target datatype if possible
|
||||
// @todo use convertType()????
|
||||
@ -1531,7 +1531,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
throw CompilerException("augmented assignment should have been converted to regular assignment already")
|
||||
|
||||
// pop the result value back into the assignment target
|
||||
val datatype = assignTarget.determineDatatype(namespace, heap, stmt)!!
|
||||
val datatype = assignTarget.determineDatatype(program, stmt)!!
|
||||
popValueIntoTarget(assignTarget, datatype)
|
||||
}
|
||||
|
||||
@ -1566,7 +1566,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
if(stmt.targets.size!=targetStmt.asmReturnvaluesRegisters.size)
|
||||
throw CompilerException("asmsub number of return values doesn't match number of assignment targets ${stmt.position}")
|
||||
for(target in stmt.targets) {
|
||||
val dt = target.determineDatatype(namespace, heap, stmt)
|
||||
val dt = target.determineDatatype(program, stmt)
|
||||
popValueIntoTarget(target, dt!!)
|
||||
}
|
||||
} else throw CompilerException("can only use multiple assignment targets on an asmsub call")
|
||||
@ -1584,7 +1584,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
}
|
||||
VarDeclType.MEMORY -> {
|
||||
val opcode = opcodePopmem(datatype)
|
||||
val address = target.value?.constValue(namespace, heap)!!.asIntegerValue!!
|
||||
val address = target.value?.constValue(program)!!.asIntegerValue!!
|
||||
prog.instr(opcode, Value(DataType.UWORD, address))
|
||||
}
|
||||
VarDeclType.CONST -> throw CompilerException("cannot assign to const")
|
||||
@ -1594,7 +1594,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
assignTarget.register != null -> prog.instr(Opcode.POP_VAR_BYTE, callLabel = assignTarget.register.name)
|
||||
assignTarget.arrayindexed != null -> translate(assignTarget.arrayindexed, true) // write value to it
|
||||
assignTarget.memoryAddress != null -> {
|
||||
val address = assignTarget.memoryAddress?.addressExpression?.constValue(namespace, heap)?.asIntegerValue
|
||||
val address = assignTarget.memoryAddress?.addressExpression?.constValue(program)?.asIntegerValue
|
||||
if(address!=null) {
|
||||
// const integer address given
|
||||
prog.instr(Opcode.POP_MEM_BYTE, arg=Value(DataType.UWORD, address))
|
||||
@ -1681,7 +1681,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
val idRef = loop.iterable as IdentifierReference
|
||||
val vardecl = idRef.targetVarDecl(namespace)!!
|
||||
val iterableValue = vardecl.value as LiteralValue
|
||||
if(!iterableValue.isIterable(namespace, heap))
|
||||
if(!iterableValue.isIterable(program))
|
||||
throw CompilerException("loop over something that isn't iterable ${loop.iterable}")
|
||||
translateForOverIterableVar(loop, loopVarDt, iterableValue)
|
||||
}
|
||||
@ -2016,7 +2016,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
translate(stmt.body)
|
||||
prog.label(continueLabel)
|
||||
translate(stmt.condition)
|
||||
val conditionJumpOpcode = when(stmt.condition.resultingDatatype(namespace, heap)) {
|
||||
val conditionJumpOpcode = when(stmt.condition.resultingDatatype(program)) {
|
||||
in ByteDatatypes -> Opcode.JNZ
|
||||
in WordDatatypes -> Opcode.JNZW
|
||||
else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt")
|
||||
@ -2053,7 +2053,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
translate(stmt.body)
|
||||
prog.label(continueLabel)
|
||||
translate(stmt.untilCondition)
|
||||
val conditionJumpOpcode = when(stmt.untilCondition.resultingDatatype(namespace, heap)) {
|
||||
val conditionJumpOpcode = when(stmt.untilCondition.resultingDatatype(program)) {
|
||||
in ByteDatatypes -> Opcode.JZ
|
||||
in WordDatatypes -> Opcode.JZW
|
||||
else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt")
|
||||
@ -2067,7 +2067,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
|
||||
private fun translate(expr: TypecastExpression) {
|
||||
translate(expr.expression)
|
||||
val sourceDt = expr.expression.resultingDatatype(namespace, heap) ?: throw CompilerException("don't know what type to cast")
|
||||
val sourceDt = expr.expression.resultingDatatype(program) ?: throw CompilerException("don't know what type to cast")
|
||||
if(sourceDt==expr.type)
|
||||
return
|
||||
|
||||
@ -2118,7 +2118,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
|
||||
private fun translate(memread: DirectMemoryRead) {
|
||||
// for now, only a single memory location (ubyte) is read at a time.
|
||||
val address = memread.addressExpression.constValue(namespace, heap)?.asIntegerValue
|
||||
val address = memread.addressExpression.constValue(program)?.asIntegerValue
|
||||
if(address!=null) {
|
||||
prog.instr(Opcode.PUSH_MEM_UB, arg = Value(DataType.UWORD, address))
|
||||
} else {
|
||||
@ -2129,7 +2129,7 @@ internal class Compiler(private val programAst2: Program): IAstProcessor {
|
||||
|
||||
private fun translate(memwrite: DirectMemoryWrite) {
|
||||
// for now, only a single memory location (ubyte) is written at a time.
|
||||
val address = memwrite.addressExpression.constValue(namespace, heap)?.asIntegerValue
|
||||
val address = memwrite.addressExpression.constValue(program)?.asIntegerValue
|
||||
if(address!=null) {
|
||||
prog.instr(Opcode.POP_MEM_BYTE, arg = Value(DataType.UWORD, address))
|
||||
} else {
|
||||
|
@ -2,11 +2,7 @@ package prog8.functions
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.compiler.CompilerException
|
||||
import prog8.compiler.HeapValues
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.log2
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.*
|
||||
|
||||
|
||||
class BuiltinFunctionParam(val name: String, val possibleDatatypes: Set<DataType>)
|
||||
@ -14,7 +10,7 @@ class BuiltinFunctionParam(val name: String, val possibleDatatypes: Set<DataType
|
||||
class FunctionSignature(val pure: Boolean, // does it have side effects?
|
||||
val parameters: List<BuiltinFunctionParam>,
|
||||
val returntype: DataType?,
|
||||
val constExpressionFunc: ((args: List<IExpression>, position: Position, namespace: INameScope, heap: HeapValues) -> LiteralValue)? = null)
|
||||
val constExpressionFunc: ((args: List<IExpression>, position: Position, program: Program) -> LiteralValue)? = null)
|
||||
|
||||
|
||||
val BuiltinFunctions = mapOf(
|
||||
@ -26,38 +22,38 @@ val BuiltinFunctions = mapOf(
|
||||
"lsl" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null),
|
||||
"lsr" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null),
|
||||
// these few have a return value depending on the argument(s):
|
||||
"max" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.max()!! }}, // type depends on args
|
||||
"min" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.min()!! }}, // type depends on args
|
||||
"sum" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.sum() }}, // type depends on args
|
||||
"max" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArgOutputNumber(a, p, prg) { it.max()!! }}, // type depends on args
|
||||
"min" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArgOutputNumber(a, p, prg) { it.min()!! }}, // type depends on args
|
||||
"sum" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArgOutputNumber(a, p, prg) { it.sum() }}, // type depends on args
|
||||
"abs" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
||||
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
||||
// normal functions follow:
|
||||
"sin" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::sin) },
|
||||
"sin" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
|
||||
"sin8" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ),
|
||||
"sin8u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ),
|
||||
"sin16" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinSin16 ),
|
||||
"sin16u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinSin16u ),
|
||||
"cos" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::cos) },
|
||||
"cos" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::cos) },
|
||||
"cos8" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinCos8 ),
|
||||
"cos8u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCos8u ),
|
||||
"cos16" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinCos16 ),
|
||||
"cos16u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinCos16u ),
|
||||
"tan" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::tan) },
|
||||
"atan" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::atan) },
|
||||
"ln" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::log) },
|
||||
"log2" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, ::log2) },
|
||||
"sqrt16" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD))), DataType.UBYTE) { a, p, n, h -> oneIntArgOutputInt(a, p, n, h) { Math.sqrt(it.toDouble()).toInt() } },
|
||||
"sqrt" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::sqrt) },
|
||||
"rad" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::toRadians) },
|
||||
"deg" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::toDegrees) },
|
||||
"tan" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::tan) },
|
||||
"atan" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::atan) },
|
||||
"ln" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::log) },
|
||||
"log2" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, ::log2) },
|
||||
"sqrt16" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()).toInt() } },
|
||||
"sqrt" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) },
|
||||
"rad" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) },
|
||||
"deg" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) },
|
||||
"avg" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.FLOAT, ::builtinAvg),
|
||||
"round" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::round) },
|
||||
"floor" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::floor) },
|
||||
"ceil" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::ceil) },
|
||||
"any" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.any { v -> v != 0.0} }},
|
||||
"all" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.all { v -> v != 0.0} }},
|
||||
"lsb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, n, h -> oneIntArgOutputInt(a, p, n, h) { x: Int -> x and 255 }},
|
||||
"msb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, n, h -> oneIntArgOutputInt(a, p, n, h) { x: Int -> x ushr 8 and 255}},
|
||||
"round" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::round) },
|
||||
"floor" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
|
||||
"ceil" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
|
||||
"any" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArgOutputBoolean(a, p, prg) { it.any { v -> v != 0.0} }},
|
||||
"all" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArgOutputBoolean(a, p, prg) { it.all { v -> v != 0.0} }},
|
||||
"lsb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 }},
|
||||
"msb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255}},
|
||||
"mkword" to FunctionSignature(true, listOf(
|
||||
BuiltinFunctionParam("lsb", setOf(DataType.UBYTE)),
|
||||
BuiltinFunctionParam("msb", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
|
||||
@ -111,12 +107,12 @@ val BuiltinFunctions = mapOf(
|
||||
)
|
||||
|
||||
|
||||
fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespace: INameScope, heap: HeapValues): DataType? {
|
||||
fun builtinFunctionReturnType(function: String, args: List<IExpression>, program: Program): DataType? {
|
||||
|
||||
fun datatypeFromIterableArg(arglist: IExpression): DataType {
|
||||
if(arglist is LiteralValue) {
|
||||
if(arglist.type==DataType.ARRAY_UB || arglist.type==DataType.ARRAY_UW || arglist.type==DataType.ARRAY_F) {
|
||||
val dt = arglist.arrayvalue!!.map {it.resultingDatatype(namespace, heap)}
|
||||
val dt = arglist.arrayvalue!!.map {it.resultingDatatype(program)}
|
||||
if(dt.any { it!=DataType.UBYTE && it!=DataType.UWORD && it!=DataType.FLOAT}) {
|
||||
throw FatalAstException("fuction $function only accepts arraysize of numeric values")
|
||||
}
|
||||
@ -126,7 +122,7 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
|
||||
}
|
||||
}
|
||||
if(arglist is IdentifierReference) {
|
||||
val dt = arglist.resultingDatatype(namespace, heap)
|
||||
val dt = arglist.resultingDatatype(program)
|
||||
return when(dt) {
|
||||
in NumericDatatypes -> dt!!
|
||||
in StringDatatypes -> dt!!
|
||||
@ -148,7 +144,7 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
|
||||
|
||||
return when (function) {
|
||||
"abs" -> {
|
||||
val dt = args.single().resultingDatatype(namespace, heap)
|
||||
val dt = args.single().resultingDatatype(program)
|
||||
when(dt) {
|
||||
in ByteDatatypes -> DataType.UBYTE
|
||||
in WordDatatypes -> DataType.UWORD
|
||||
@ -170,8 +166,7 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
|
||||
}
|
||||
}
|
||||
"sum" -> {
|
||||
val dt=datatypeFromIterableArg(args.single())
|
||||
when(dt) {
|
||||
when(datatypeFromIterableArg(args.single())) {
|
||||
DataType.UBYTE, DataType.UWORD -> DataType.UWORD
|
||||
DataType.BYTE, DataType.WORD -> DataType.WORD
|
||||
DataType.FLOAT -> DataType.FLOAT
|
||||
@ -195,10 +190,10 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
|
||||
class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
||||
|
||||
|
||||
private fun oneDoubleArg(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues, function: (arg: Double)->Number): LiteralValue {
|
||||
private fun oneDoubleArg(args: List<IExpression>, position: Position, program: Program, function: (arg: Double)->Number): LiteralValue {
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
if(constval.type!=DataType.FLOAT)
|
||||
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||
|
||||
@ -206,19 +201,19 @@ private fun oneDoubleArg(args: List<IExpression>, position: Position, namespace:
|
||||
return numericLiteral(function(float), args[0].position)
|
||||
}
|
||||
|
||||
private fun oneDoubleArgOutputWord(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues, function: (arg: Double)->Number): LiteralValue {
|
||||
private fun oneDoubleArgOutputWord(args: List<IExpression>, position: Position, program: Program, function: (arg: Double)->Number): LiteralValue {
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
if(constval.type!=DataType.FLOAT)
|
||||
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||
return LiteralValue(DataType.WORD, wordvalue=function(constval.asNumericValue!!.toDouble()).toInt(), position=args[0].position)
|
||||
}
|
||||
|
||||
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues, function: (arg: Int)->Number): LiteralValue {
|
||||
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position, program: Program, function: (arg: Int)->Number): LiteralValue {
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("built-in function requires one integer argument", position)
|
||||
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
if(constval.type!=DataType.UBYTE && constval.type!=DataType.UWORD)
|
||||
throw SyntaxError("built-in function requires one integer argument", position)
|
||||
|
||||
@ -227,14 +222,14 @@ private fun oneIntArgOutputInt(args: List<IExpression>, position: Position, name
|
||||
}
|
||||
|
||||
private fun collectionArgOutputNumber(args: List<IExpression>, position: Position,
|
||||
namespace:INameScope, heap: HeapValues,
|
||||
program: Program,
|
||||
function: (arg: Collection<Double>)->Number): LiteralValue {
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
||||
val iterable = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val iterable = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
|
||||
val result = if(iterable.arrayvalue != null) {
|
||||
val constants = iterable.arrayvalue.map { it.constValue(namespace, heap)?.asNumericValue }
|
||||
val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
|
||||
if(null in constants)
|
||||
throw NotConstArgumentException()
|
||||
function(constants.map { it!!.toDouble() }).toDouble()
|
||||
@ -244,7 +239,7 @@ private fun collectionArgOutputNumber(args: List<IExpression>, position: Positio
|
||||
else -> {
|
||||
if(iterable.heapId==null)
|
||||
throw FatalAstException("iterable value should be on the heap")
|
||||
val array = heap.get(iterable.heapId).array ?: throw SyntaxError("function expects an iterable type", position)
|
||||
val array = program.heap.get(iterable.heapId).array ?: throw SyntaxError("function expects an iterable type", position)
|
||||
function(array.map {
|
||||
if(it.integer!=null)
|
||||
it.integer.toDouble()
|
||||
@ -258,19 +253,19 @@ private fun collectionArgOutputNumber(args: List<IExpression>, position: Positio
|
||||
}
|
||||
|
||||
private fun collectionArgOutputBoolean(args: List<IExpression>, position: Position,
|
||||
namespace:INameScope, heap: HeapValues,
|
||||
program: Program,
|
||||
function: (arg: Collection<Double>)->Boolean): LiteralValue {
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
||||
val iterable = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val iterable = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
|
||||
val result = if(iterable.arrayvalue != null) {
|
||||
val constants = iterable.arrayvalue.map { it.constValue(namespace, heap)?.asNumericValue }
|
||||
val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
|
||||
if(null in constants)
|
||||
throw NotConstArgumentException()
|
||||
function(constants.map { it!!.toDouble() })
|
||||
} else {
|
||||
val array = heap.get(iterable.heapId!!).array ?: throw SyntaxError("function requires array argument", position)
|
||||
val array = program.heap.get(iterable.heapId!!).array ?: throw SyntaxError("function requires array argument", position)
|
||||
function(array.map {
|
||||
if(it.integer!=null)
|
||||
it.integer.toDouble()
|
||||
@ -281,32 +276,32 @@ private fun collectionArgOutputBoolean(args: List<IExpression>, position: Positi
|
||||
return LiteralValue.fromBoolean(result, position)
|
||||
}
|
||||
|
||||
private fun builtinAbs(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
|
||||
private fun builtinAbs(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
// 1 arg, type = float or int, result type= same as argument type
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("abs requires one numeric argument", position)
|
||||
|
||||
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val number = constval.asNumericValue
|
||||
return when (number) {
|
||||
is Int, is Byte, is Short -> numericLiteral(Math.abs(number.toInt()), args[0].position)
|
||||
is Double -> numericLiteral(Math.abs(number.toDouble()), args[0].position)
|
||||
is Int, is Byte, is Short -> numericLiteral(abs(number.toInt()), args[0].position)
|
||||
is Double -> numericLiteral(abs(number.toDouble()), args[0].position)
|
||||
else -> throw SyntaxError("abs requires one numeric argument", position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun builtinAvg(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
|
||||
private fun builtinAvg(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("avg requires array argument", position)
|
||||
val iterable = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val iterable = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = if(iterable.arrayvalue!=null) {
|
||||
val constants = iterable.arrayvalue.map { it.constValue(namespace, heap)?.asNumericValue }
|
||||
val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
|
||||
if (null in constants)
|
||||
throw NotConstArgumentException()
|
||||
(constants.map { it!!.toDouble() }).average()
|
||||
}
|
||||
else {
|
||||
val integerarray = heap.get(iterable.heapId!!).array
|
||||
val integerarray = program.heap.get(iterable.heapId!!).array
|
||||
if(integerarray!=null) {
|
||||
if (integerarray.all { it.integer != null }) {
|
||||
integerarray.map { it.integer!! }.average()
|
||||
@ -314,20 +309,20 @@ private fun builtinAvg(args: List<IExpression>, position: Position, namespace:IN
|
||||
throw ExpressionError("cannot avg() over array that does not only contain constant numerical values", position)
|
||||
}
|
||||
} else {
|
||||
val doublearray = heap.get(iterable.heapId).doubleArray
|
||||
val doublearray = program.heap.get(iterable.heapId).doubleArray
|
||||
doublearray?.average() ?: throw SyntaxError("avg requires array argument", position)
|
||||
}
|
||||
}
|
||||
return numericLiteral(result, args[0].position)
|
||||
}
|
||||
|
||||
private fun builtinStrlen(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
|
||||
private fun builtinStrlen(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("strlen requires one argument", position)
|
||||
val argument = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val argument = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
if(argument.type !in StringDatatypes)
|
||||
throw SyntaxError("strlen must have string argument", position)
|
||||
val string = argument.strvalue(heap)
|
||||
val string = argument.strvalue(program.heap)
|
||||
val zeroIdx = string.indexOf('\u0000')
|
||||
return if(zeroIdx>=0)
|
||||
LiteralValue.optimalInteger(zeroIdx, position=position)
|
||||
@ -335,38 +330,38 @@ private fun builtinStrlen(args: List<IExpression>, position: Position, namespace
|
||||
LiteralValue.optimalInteger(string.length, position=position)
|
||||
}
|
||||
|
||||
private fun builtinLen(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
|
||||
private fun builtinLen(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
// note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE.
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("len requires one argument", position)
|
||||
var argument = args[0].constValue(namespace, heap)
|
||||
var argument = args[0].constValue(program)
|
||||
if(argument==null) {
|
||||
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(namespace)
|
||||
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program.namespace)
|
||||
val arraySize = directMemVar?.arraysize?.size()
|
||||
if(arraySize != null)
|
||||
return LiteralValue.optimalInteger(arraySize, position)
|
||||
if(args[0] !is IdentifierReference)
|
||||
throw SyntaxError("len argument should be an identifier, but is ${args[0]}", position)
|
||||
val target = (args[0] as IdentifierReference).targetStatement(namespace)
|
||||
val target = (args[0] as IdentifierReference).targetStatement(program.namespace)
|
||||
val argValue = (target as? VarDecl)?.value
|
||||
argument = argValue?.constValue(namespace, heap)
|
||||
argument = argValue?.constValue(program)
|
||||
?: throw NotConstArgumentException()
|
||||
}
|
||||
return when(argument.type) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).arraysize
|
||||
val arraySize = argument.arrayvalue?.size ?: program.heap.get(argument.heapId!!).arraysize
|
||||
if(arraySize>256)
|
||||
throw CompilerException("array length exceeds byte limit ${argument.position}")
|
||||
LiteralValue.optimalInteger(arraySize, args[0].position)
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).arraysize
|
||||
val arraySize = argument.arrayvalue?.size ?: program.heap.get(argument.heapId!!).arraysize
|
||||
if(arraySize>256)
|
||||
throw CompilerException("array length exceeds byte limit ${argument.position}")
|
||||
LiteralValue.optimalInteger(arraySize, args[0].position)
|
||||
}
|
||||
in StringDatatypes -> {
|
||||
val str = argument.strvalue(heap)
|
||||
val str = argument.strvalue(program.heap)
|
||||
if(str.length>255)
|
||||
throw CompilerException("string length exceeds byte limit ${argument.position}")
|
||||
LiteralValue.optimalInteger(str.length, args[0].position)
|
||||
@ -377,75 +372,75 @@ private fun builtinLen(args: List<IExpression>, position: Position, namespace:IN
|
||||
}
|
||||
|
||||
|
||||
private fun builtinMkword(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
|
||||
private fun builtinMkword(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("mkword requires lsb and msb arguments", position)
|
||||
val constLsb = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val constMsb = args[1].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val constLsb = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val constMsb = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = (constMsb.asIntegerValue!! shl 8) or constLsb.asIntegerValue!!
|
||||
return LiteralValue(DataType.UWORD, wordvalue = result, position = position)
|
||||
}
|
||||
|
||||
private fun builtinSin8(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
|
||||
private fun builtinSin8(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("sin8 requires one argument", position)
|
||||
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
||||
return LiteralValue(DataType.BYTE, bytevalue = (127.0* sin(rad)).toShort(), position = position)
|
||||
}
|
||||
|
||||
private fun builtinSin8u(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
|
||||
private fun builtinSin8u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("sin8u requires one argument", position)
|
||||
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
||||
return LiteralValue(DataType.UBYTE, bytevalue = (128.0+127.5*sin(rad)).toShort(), position = position)
|
||||
}
|
||||
|
||||
private fun builtinCos8(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
|
||||
private fun builtinCos8(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("cos8 requires one argument", position)
|
||||
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
||||
return LiteralValue(DataType.BYTE, bytevalue = (127.0* cos(rad)).toShort(), position = position)
|
||||
}
|
||||
|
||||
private fun builtinCos8u(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
|
||||
private fun builtinCos8u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("cos8u requires one argument", position)
|
||||
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
||||
return LiteralValue(DataType.UBYTE, bytevalue = (128.0 + 127.5*cos(rad)).toShort(), position = position)
|
||||
}
|
||||
|
||||
private fun builtinSin16(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
|
||||
private fun builtinSin16(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("sin16 requires one argument", position)
|
||||
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
||||
return LiteralValue(DataType.WORD, wordvalue = (32767.0* sin(rad)).toInt(), position = position)
|
||||
}
|
||||
|
||||
private fun builtinSin16u(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
|
||||
private fun builtinSin16u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("sin16u requires one argument", position)
|
||||
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
||||
return LiteralValue(DataType.UWORD, wordvalue = (32768.0+32767.5*sin(rad)).toInt(), position = position)
|
||||
}
|
||||
|
||||
private fun builtinCos16(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
|
||||
private fun builtinCos16(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("cos16 requires one argument", position)
|
||||
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
||||
return LiteralValue(DataType.WORD, wordvalue = (32767.0* cos(rad)).toInt(), position = position)
|
||||
}
|
||||
|
||||
private fun builtinCos16u(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
|
||||
private fun builtinCos16u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("cos16u requires one argument", position)
|
||||
val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
||||
return LiteralValue(DataType.UWORD, wordvalue = (32768.0+32767.5* cos(rad)).toInt(), position = position)
|
||||
}
|
||||
@ -453,7 +448,7 @@ private fun builtinCos16u(args: List<IExpression>, position: Position, namespace
|
||||
private fun numericLiteral(value: Number, position: Position): LiteralValue {
|
||||
val floatNum=value.toDouble()
|
||||
val tweakedValue: Number =
|
||||
if(floatNum==Math.floor(floatNum) && (floatNum>=-32768 && floatNum<=65535))
|
||||
if(floatNum== floor(floatNum) && (floatNum>=-32768 && floatNum<=65535))
|
||||
floatNum.toInt() // we have an integer disguised as a float.
|
||||
else
|
||||
floatNum
|
||||
|
@ -9,7 +9,7 @@ import prog8.compiler.target.c64.FLOAT_MAX_POSITIVE
|
||||
import kotlin.math.floor
|
||||
|
||||
|
||||
class ConstantFolding(private val namespace: GlobalNamespace, private val heap: HeapValues) : IAstProcessor {
|
||||
class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
var optimizationsDone: Int = 0
|
||||
var errors : MutableList<AstException> = mutableListOf()
|
||||
|
||||
@ -49,11 +49,11 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
|
||||
if(rangeExpr!=null) {
|
||||
// convert the initializer range expression to an actual array (will be put on heap later)
|
||||
val declArraySize = decl.arraysize?.size()
|
||||
if(declArraySize!=null && declArraySize!=rangeExpr.size(heap))
|
||||
if(declArraySize!=null && declArraySize!=rangeExpr.size(program.heap))
|
||||
errors.add(ExpressionError("range expression size doesn't match declared array size", decl.value?.position!!))
|
||||
val constRange = rangeExpr.toConstantIntegerRange(heap)
|
||||
val constRange = rangeExpr.toConstantIntegerRange(program.heap)
|
||||
if(constRange!=null) {
|
||||
val eltType = rangeExpr.resultingDatatype(namespace, heap)!!
|
||||
val eltType = rangeExpr.resultingDatatype(program)!!
|
||||
if(eltType in ByteDatatypes) {
|
||||
decl.value = LiteralValue(decl.datatype,
|
||||
arrayvalue = constRange.map { LiteralValue(eltType, bytevalue=it.toShort(), position = decl.value!!.position ) }
|
||||
@ -93,7 +93,7 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
val heapId = heap.addIntegerArray(decl.datatype, Array(size) { IntegerOrAddressOf(fillvalue, null) })
|
||||
val heapId = program.heap.addIntegerArray(decl.datatype, Array(size) { IntegerOrAddressOf(fillvalue, null) })
|
||||
decl.value = LiteralValue(decl.datatype, heapId = heapId, position = litval?.position ?: decl.position)
|
||||
}
|
||||
}
|
||||
@ -107,7 +107,7 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
|
||||
if(fillvalue< FLOAT_MAX_NEGATIVE || fillvalue> FLOAT_MAX_POSITIVE)
|
||||
errors.add(ExpressionError("float value overflow", litval?.position ?: decl.position))
|
||||
else {
|
||||
val heapId = heap.addDoublesArray(DoubleArray(size) { fillvalue })
|
||||
val heapId = program.heap.addDoublesArray(DoubleArray(size) { fillvalue })
|
||||
decl.value = LiteralValue(DataType.ARRAY_F, heapId = heapId, position = litval?.position ?: decl.position)
|
||||
}
|
||||
}
|
||||
@ -125,11 +125,11 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
|
||||
if(decl.datatype==litval.type)
|
||||
return // already correct datatype
|
||||
val heapId = litval.heapId ?: throw FatalAstException("expected array to be on heap $litval")
|
||||
val array=heap.get(heapId)
|
||||
val array = program.heap.get(heapId)
|
||||
when(decl.datatype) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
if(array.array!=null) {
|
||||
heap.update(heapId, HeapValues.HeapValue(decl.datatype, null, array.array, null))
|
||||
program.heap.update(heapId, HeapValues.HeapValue(decl.datatype, null, array.array, null))
|
||||
decl.value = LiteralValue(decl.datatype, heapId=heapId, position = litval.position)
|
||||
}
|
||||
}
|
||||
@ -137,7 +137,7 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
|
||||
if(array.array!=null) {
|
||||
// convert a non-float array to floats
|
||||
val doubleArray = array.array.map { it.integer!!.toDouble() }.toDoubleArray()
|
||||
heap.update(heapId, HeapValues.HeapValue(DataType.ARRAY_F, null, null, doubleArray))
|
||||
program.heap.update(heapId, HeapValues.HeapValue(DataType.ARRAY_F, null, null, doubleArray))
|
||||
decl.value = LiteralValue(decl.datatype, heapId = heapId, position = litval.position)
|
||||
}
|
||||
}
|
||||
@ -150,7 +150,7 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
|
||||
*/
|
||||
override fun process(identifier: IdentifierReference): IExpression {
|
||||
return try {
|
||||
val cval = identifier.constValue(namespace, heap) ?: return identifier
|
||||
val cval = identifier.constValue(program) ?: return identifier
|
||||
return if(cval.isNumeric) {
|
||||
val copy = LiteralValue(cval.type, cval.bytevalue, cval.wordvalue, cval.floatvalue, null, cval.arrayvalue, position = identifier.position)
|
||||
copy.parent = identifier.parent
|
||||
@ -167,7 +167,7 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
|
||||
return try {
|
||||
super.process(functionCall)
|
||||
typeCastConstArguments(functionCall)
|
||||
functionCall.constValue(namespace, heap) ?: functionCall
|
||||
functionCall.constValue(program) ?: functionCall
|
||||
} catch (ax: AstException) {
|
||||
addError(ax)
|
||||
functionCall
|
||||
@ -181,12 +181,12 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
|
||||
}
|
||||
|
||||
private fun typeCastConstArguments(functionCall: IFunctionCall) {
|
||||
val subroutine = functionCall.target.targetSubroutine(namespace)
|
||||
val subroutine = functionCall.target.targetSubroutine(program.namespace)
|
||||
if(subroutine!=null) {
|
||||
// if types differ, try to typecast constant arguments to the function call to the desired data type of the parameter
|
||||
for(arg in functionCall.arglist.withIndex().zip(subroutine.parameters)) {
|
||||
val expectedDt = arg.second.type
|
||||
val argConst = arg.first.value.constValue(namespace, heap)
|
||||
val argConst = arg.first.value.constValue(program)
|
||||
if(argConst!=null && argConst.type!=expectedDt) {
|
||||
val convertedValue = argConst.intoDatatype(expectedDt)
|
||||
if(convertedValue!=null) {
|
||||
@ -287,8 +287,8 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
|
||||
override fun process(expr: BinaryExpression): IExpression {
|
||||
return try {
|
||||
super.process(expr)
|
||||
val leftconst = expr.left.constValue(namespace, heap)
|
||||
val rightconst = expr.right.constValue(namespace, heap)
|
||||
val leftconst = expr.left.constValue(program)
|
||||
val rightconst = expr.right.constValue(program)
|
||||
|
||||
val subExpr: BinaryExpression? = when {
|
||||
leftconst!=null -> expr.right as? BinaryExpression
|
||||
@ -296,8 +296,8 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
|
||||
else -> null
|
||||
}
|
||||
if(subExpr!=null) {
|
||||
val subleftconst = subExpr.left.constValue(namespace, heap)
|
||||
val subrightconst = subExpr.right.constValue(namespace, heap)
|
||||
val subleftconst = subExpr.left.constValue(program)
|
||||
val subrightconst = subExpr.right.constValue(program)
|
||||
if ((subleftconst != null && subrightconst == null) || (subleftconst==null && subrightconst!=null)) {
|
||||
// try reordering.
|
||||
return groupTwoConstsTogether(expr, subExpr,
|
||||
@ -311,7 +311,7 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
|
||||
return when {
|
||||
leftconst != null && rightconst != null -> {
|
||||
optimizationsDone++
|
||||
evaluator.evaluate(leftconst, expr.operator, rightconst, heap)
|
||||
evaluator.evaluate(leftconst, expr.operator, rightconst, program.heap)
|
||||
}
|
||||
else -> expr
|
||||
}
|
||||
@ -533,7 +533,7 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
|
||||
val rangeTo = iterableRange.to as? LiteralValue
|
||||
if(rangeFrom==null || rangeTo==null) return resultStmt
|
||||
|
||||
val loopvar = resultStmt.loopVar!!.targetVarDecl(namespace)
|
||||
val loopvar = resultStmt.loopVar!!.targetVarDecl(program.namespace)
|
||||
if(loopvar!=null) {
|
||||
val stepLiteral = iterableRange.step as? LiteralValue
|
||||
when(loopvar.datatype) {
|
||||
@ -570,10 +570,10 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
|
||||
override fun process(literalValue: LiteralValue): LiteralValue {
|
||||
if(literalValue.isString) {
|
||||
// intern the string; move it into the heap
|
||||
if(literalValue.strvalue(heap).length !in 1..255)
|
||||
if(literalValue.strvalue(program.heap).length !in 1..255)
|
||||
addError(ExpressionError("string literal length must be between 1 and 255", literalValue.position))
|
||||
else {
|
||||
val heapId = heap.addString(literalValue.type, literalValue.strvalue(heap)) // TODO: we don't know the actual string type yet, STR != STR_S etc...
|
||||
val heapId = program.heap.addString(literalValue.type, literalValue.strvalue(program.heap)) // TODO: we don't know the actual string type yet, STR != STR_S etc...
|
||||
val newValue = LiteralValue(literalValue.type, heapId = heapId, position = literalValue.position)
|
||||
return super.process(newValue)
|
||||
}
|
||||
@ -599,14 +599,14 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
|
||||
else -> throw CompilerException("invalid datatype in array")
|
||||
}
|
||||
}
|
||||
val heapId = heap.addIntegerArray(arrayDt, intArrayWithAddressOfs.toTypedArray())
|
||||
val heapId = program.heap.addIntegerArray(arrayDt, intArrayWithAddressOfs.toTypedArray())
|
||||
return LiteralValue(arrayDt, heapId = heapId, position = arraylit.position)
|
||||
} else {
|
||||
// array is only constant numerical values
|
||||
val valuesInArray = array.map { it.constValue(namespace, heap)!!.asNumericValue!! }
|
||||
val valuesInArray = array.map { it.constValue(program)!!.asNumericValue!! }
|
||||
val integerArray = valuesInArray.map{ it.toInt() }
|
||||
val doubleArray = valuesInArray.map{it.toDouble()}.toDoubleArray()
|
||||
val typesInArray: Set<DataType> = array.mapNotNull { it.resultingDatatype(namespace, heap) }.toSet()
|
||||
val typesInArray: Set<DataType> = array.mapNotNull { it.resultingDatatype(program) }.toSet()
|
||||
|
||||
// Take an educated guess about the array type.
|
||||
// This may be altered (if needed & if possible) to suit an array declaration type later!
|
||||
@ -638,8 +638,8 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
|
||||
DataType.ARRAY_UB,
|
||||
DataType.ARRAY_B,
|
||||
DataType.ARRAY_UW,
|
||||
DataType.ARRAY_W -> heap.addIntegerArray(arrayDt, integerArray.map { IntegerOrAddressOf(it, null) }.toTypedArray())
|
||||
DataType.ARRAY_F -> heap.addDoublesArray(doubleArray)
|
||||
DataType.ARRAY_W -> program.heap.addIntegerArray(arrayDt, integerArray.map { IntegerOrAddressOf(it, null) }.toTypedArray())
|
||||
DataType.ARRAY_F -> program.heap.addDoublesArray(doubleArray)
|
||||
else -> throw CompilerException("invalid arraysize type")
|
||||
}
|
||||
return LiteralValue(arrayDt, heapId = heapId, position = arraylit.position)
|
||||
@ -650,9 +650,8 @@ class ConstantFolding(private val namespace: GlobalNamespace, private val heap:
|
||||
super.process(assignment)
|
||||
val lv = assignment.value as? LiteralValue
|
||||
if(lv!=null) {
|
||||
val targetDt = assignment.singleTarget?.determineDatatype(namespace, heap, assignment)
|
||||
// see if we can promote/convert a literal value to the required datatype
|
||||
when(targetDt) {
|
||||
when(assignment.singleTarget?.determineDatatype(program, assignment)) {
|
||||
DataType.UWORD -> {
|
||||
// we can convert to UWORD: any UBYTE, BYTE/WORD that are >=0, FLOAT that's an integer 0..65535,
|
||||
if(lv.type==DataType.UBYTE)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -1,7 +1,6 @@
|
||||
package prog8.optimizing
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.compiler.target.c64.Petscii
|
||||
import prog8.functions.BuiltinFunctions
|
||||
import kotlin.math.floor
|
||||
@ -12,9 +11,8 @@ import kotlin.math.floor
|
||||
|
||||
|
||||
todo: implement usage counters for blocks, variables, subroutines, heap variables. Then:
|
||||
todo remove unused blocks
|
||||
todo remove unused variables
|
||||
todo remove unused subroutines
|
||||
todo remove unused: subroutines, blocks, modules (in this order)
|
||||
todo remove unused: variable declarations
|
||||
todo remove unused strings and arrays from the heap
|
||||
todo inline subroutines that are called exactly once (regardless of their size)
|
||||
todo inline subroutines that are only called a few times (max 3?)
|
||||
@ -23,7 +21,7 @@ import kotlin.math.floor
|
||||
todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to)
|
||||
*/
|
||||
|
||||
class StatementOptimizer(private val namespace: INameScope, private val heap: HeapValues) : IAstProcessor {
|
||||
class StatementOptimizer(private val program: Program) : IAstProcessor {
|
||||
var optimizationsDone: Int = 0
|
||||
private set
|
||||
var statementsToRemove = mutableListOf<IStatement>()
|
||||
@ -101,12 +99,12 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
if(target.memoryAddress!=null)
|
||||
return false
|
||||
if(target.arrayindexed!=null) {
|
||||
val targetStmt = target.arrayindexed.identifier.targetVarDecl(namespace)
|
||||
val targetStmt = target.arrayindexed.identifier.targetVarDecl(program.namespace)
|
||||
if(targetStmt!=null)
|
||||
return targetStmt.type!=VarDeclType.MEMORY
|
||||
}
|
||||
if(target.identifier!=null) {
|
||||
val targetStmt = target.identifier.targetVarDecl(namespace)
|
||||
val targetStmt = target.identifier.targetVarDecl(program.namespace)
|
||||
if(targetStmt!=null)
|
||||
return targetStmt.type!=VarDeclType.MEMORY
|
||||
}
|
||||
@ -131,8 +129,8 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
throw AstException("string argument should be on heap already")
|
||||
val stringVar = functionCallStatement.arglist.single() as? IdentifierReference
|
||||
if(stringVar!=null) {
|
||||
val heapId = stringVar.heapId(namespace)
|
||||
val string = heap.get(heapId).str!!
|
||||
val heapId = stringVar.heapId(program.namespace)
|
||||
val string = program.heap.get(heapId).str!!
|
||||
if(string.length==1) {
|
||||
val petscii = Petscii.encodePetscii(string, true)[0]
|
||||
functionCallStatement.arglist.clear()
|
||||
@ -156,7 +154,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
// if it calls a subroutine,
|
||||
// and the first instruction in the subroutine is a jump, call that jump target instead
|
||||
// if the first instruction in the subroutine is a return statement, replace with a nop instruction
|
||||
val subroutine = functionCallStatement.target.targetSubroutine(namespace)
|
||||
val subroutine = functionCallStatement.target.targetSubroutine(program.namespace)
|
||||
if(subroutine!=null) {
|
||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||
if(first is Jump && first.identifier!=null) {
|
||||
@ -176,7 +174,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
// if it calls a subroutine,
|
||||
// and the first instruction in the subroutine is a jump, call that jump target instead
|
||||
// if the first instruction in the subroutine is a return statement with constant value, replace with the constant value
|
||||
val subroutine = functionCall.target.targetSubroutine(namespace)
|
||||
val subroutine = functionCall.target.targetSubroutine(program.namespace)
|
||||
if(subroutine!=null) {
|
||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||
if(first is Jump && first.identifier!=null) {
|
||||
@ -184,7 +182,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
return FunctionCall(first.identifier, functionCall.arglist, functionCall.position)
|
||||
}
|
||||
if(first is Return && first.values.size==1) {
|
||||
val constval = first.values[0].constValue(namespace, heap)
|
||||
val constval = first.values[0].constValue(program)
|
||||
if(constval!=null)
|
||||
return constval
|
||||
}
|
||||
@ -210,7 +208,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
return ifStatement
|
||||
}
|
||||
|
||||
val constvalue = ifStatement.condition.constValue(namespace, heap)
|
||||
val constvalue = ifStatement.condition.constValue(program)
|
||||
if(constvalue!=null) {
|
||||
return if(constvalue.asBooleanValue){
|
||||
// always true -> keep only if-part
|
||||
@ -247,7 +245,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
|
||||
val range = forLoop.iterable as? RangeExpr
|
||||
if(range!=null) {
|
||||
if(range.size(heap)==1) {
|
||||
if(range.size(program.heap)==1) {
|
||||
// for loop over a (constant) range of just a single value-- optimize the loop away
|
||||
// loopvar/reg = range value , follow by block
|
||||
val assignment = Assignment(listOf(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, null, forLoop.position)), null, range.from, forLoop.position)
|
||||
@ -261,7 +259,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
|
||||
override fun process(whileLoop: WhileLoop): IStatement {
|
||||
super.process(whileLoop)
|
||||
val constvalue = whileLoop.condition.constValue(namespace, heap)
|
||||
val constvalue = whileLoop.condition.constValue(program)
|
||||
if(constvalue!=null) {
|
||||
return if(constvalue.asBooleanValue){
|
||||
// always true -> print a warning, and optimize into body + jump (if there are no continue and break statements)
|
||||
@ -287,7 +285,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
|
||||
override fun process(repeatLoop: RepeatLoop): IStatement {
|
||||
super.process(repeatLoop)
|
||||
val constvalue = repeatLoop.untilCondition.constValue(namespace, heap)
|
||||
val constvalue = repeatLoop.untilCondition.constValue(program)
|
||||
if(constvalue!=null) {
|
||||
return if(constvalue.asBooleanValue){
|
||||
// always true -> keep only the statement block (if there are no continue and break statements)
|
||||
@ -341,7 +339,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
}
|
||||
|
||||
override fun process(jump: Jump): IStatement {
|
||||
val subroutine = jump.identifier?.targetSubroutine(namespace)
|
||||
val subroutine = jump.identifier?.targetSubroutine(program.namespace)
|
||||
if(subroutine!=null) {
|
||||
// if the first instruction in the subroutine is another jump, shortcut this one
|
||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||
@ -363,10 +361,10 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
optimizationsDone++
|
||||
return NopStatement(assignment.position)
|
||||
}
|
||||
val targetDt = target.determineDatatype(namespace, heap, assignment)!!
|
||||
val targetDt = target.determineDatatype(program, assignment)!!
|
||||
val bexpr=assignment.value as? BinaryExpression
|
||||
if(bexpr!=null) {
|
||||
val cv = bexpr.right.constValue(namespace, heap)?.asNumericValue?.toDouble()
|
||||
val cv = bexpr.right.constValue(program)?.asNumericValue?.toDouble()
|
||||
if(cv==null) {
|
||||
if(bexpr.operator=="+" && targetDt!=DataType.FLOAT) {
|
||||
if (same(bexpr.left, bexpr.right) && same(target, bexpr.left)) {
|
||||
@ -380,7 +378,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
if (same(target, bexpr.left)) {
|
||||
// remove assignments that have no effect X=X , X+=0, X-=0, X*=1, X/=1, X//=1, A |= 0, A ^= 0, A<<=0, etc etc
|
||||
// A = A <operator> B
|
||||
val vardeclDt = (target.identifier?.targetVarDecl(namespace))?.type
|
||||
val vardeclDt = (target.identifier?.targetVarDecl(program.namespace))?.type
|
||||
|
||||
when (bexpr.operator) {
|
||||
"+" -> {
|
||||
@ -516,14 +514,14 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
if(target1.identifier!=null && target2.identifier!=null)
|
||||
return target1.identifier.nameInSource==target2.identifier.nameInSource
|
||||
if(target1.memoryAddress!=null && target2.memoryAddress!=null) {
|
||||
val addr1 = target1.memoryAddress!!.addressExpression.constValue(namespace, heap)
|
||||
val addr2 = target2.memoryAddress!!.addressExpression.constValue(namespace, heap)
|
||||
val addr1 = target1.memoryAddress!!.addressExpression.constValue(program)
|
||||
val addr2 = target2.memoryAddress!!.addressExpression.constValue(program)
|
||||
return addr1!=null && addr2!=null && addr1==addr2
|
||||
}
|
||||
if(target1.arrayindexed!=null && target2.arrayindexed!=null) {
|
||||
if(target1.arrayindexed.identifier.nameInSource == target2.arrayindexed.identifier.nameInSource) {
|
||||
val x1 = target1.arrayindexed.arrayspec.index.constValue(namespace, heap)
|
||||
val x2 = target2.arrayindexed.arrayspec.index.constValue(namespace, heap)
|
||||
val x1 = target1.arrayindexed.arrayspec.index.constValue(program)
|
||||
val x2 = target2.arrayindexed.arrayspec.index.constValue(program)
|
||||
return x1!=null && x2!=null && x1==x2
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
|
@ -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))
|
||||
|
Loading…
Reference in New Issue
Block a user