diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index 2452f4633..cbaec7b69 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -832,7 +832,7 @@ data class AssignTarget(val register: Register?, } } - fun determineDatatype(program: Program, stmt: IStatement): DataType? { + fun inferType(program: Program, stmt: IStatement): DataType? { if(register!=null) return DataType.UBYTE @@ -842,7 +842,7 @@ data class AssignTarget(val register: Register?, } if(arrayindexed!=null) { - val dt = arrayindexed.resultingDatatype(program) + val dt = arrayindexed.inferType(program) if(dt!=null) return dt } @@ -929,7 +929,7 @@ interface IExpression: Node { fun constValue(program: Program): LiteralValue? fun process(processor: IAstProcessor): IExpression fun referencesIdentifier(name: String): Boolean - fun resultingDatatype(program: Program): DataType? + fun inferType(program: Program): DataType? infix fun isSameAs(other: IExpression): Boolean { if(this===other) @@ -969,7 +969,7 @@ class PrefixExpression(val operator: String, var expression: IExpression, overri 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(program: Program): DataType? = expression.resultingDatatype(program) + override fun inferType(program: Program): DataType? = expression.inferType(program) override fun toString(): String { return "Prefix($operator $expression)" @@ -994,9 +994,9 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I override fun constValue(program: Program): LiteralValue? = null override fun process(processor: IAstProcessor) = processor.process(this) override fun referencesIdentifier(name: String) = left.referencesIdentifier(name) || right.referencesIdentifier(name) - override fun resultingDatatype(program: Program): DataType? { - val leftDt = left.resultingDatatype(program) - val rightDt = right.resultingDatatype(program) + override fun inferType(program: Program): DataType? { + val leftDt = left.inferType(program) + val rightDt = right.inferType(program) return when(operator) { "+", "-", "*", "**", "%" -> if(leftDt==null || rightDt==null) null else { try { @@ -1101,7 +1101,7 @@ class ArrayIndexedExpression(val identifier: IdentifierReference, override fun process(processor: IAstProcessor): IExpression = processor.process(this) override fun referencesIdentifier(name: String) = identifier.referencesIdentifier(name) - override fun resultingDatatype(program: Program): DataType? { + override fun inferType(program: Program): DataType? { val target = identifier.targetStatement(program.namespace) if (target is VarDecl) { return when (target.datatype) { @@ -1134,7 +1134,7 @@ 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(program: Program): DataType? = type + override fun inferType(program: Program): DataType? = type override fun constValue(program: Program): LiteralValue? { val cv = expression.constValue(program) ?: return null val value = RuntimeValue(cv.type, cv.asNumericValue!!).cast(type) @@ -1158,7 +1158,7 @@ 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 constValue(program: Program): LiteralValue? = null override fun referencesIdentifier(name: String) = false - override fun resultingDatatype(program: Program) = DataType.UWORD + override fun inferType(program: Program) = DataType.UWORD override fun process(processor: IAstProcessor) = processor.process(this) } @@ -1173,7 +1173,7 @@ 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(program: Program): DataType? = DataType.UBYTE + override fun inferType(program: Program): DataType? = DataType.UBYTE override fun constValue(program: Program): LiteralValue? = null override fun toString(): String { @@ -1192,7 +1192,7 @@ 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(program: Program): DataType? = DataType.UBYTE + override fun inferType(program: Program): DataType? = DataType.UBYTE override fun constValue(program: Program): LiteralValue? = null override fun toString(): String { @@ -1326,7 +1326,7 @@ open class LiteralValue(val type: DataType, return "LiteralValue($vstr)" } - override fun resultingDatatype(program: Program) = type + override fun inferType(program: Program) = type override fun hashCode(): Int { val bh = bytevalue?.hashCode() ?: 0x10001234 @@ -1458,9 +1458,9 @@ class RangeExpr(var from: IExpression, override fun constValue(program: Program): LiteralValue? = null override fun process(processor: IAstProcessor) = processor.process(this) override fun referencesIdentifier(name: String): Boolean = from.referencesIdentifier(name) || to.referencesIdentifier(name) - override fun resultingDatatype(program: Program): DataType? { - val fromDt=from.resultingDatatype(program) - val toDt=to.resultingDatatype(program) + override fun inferType(program: Program): DataType? { + val fromDt=from.inferType(program) + val toDt=to.inferType(program) return when { fromDt==null || toDt==null -> null fromDt==DataType.UBYTE && toDt==DataType.UBYTE -> DataType.UBYTE @@ -1531,7 +1531,7 @@ class RegisterExpr(val register: Register, override val position: Position) : IE return "RegisterExpr(register=$register, pos=$position)" } - override fun resultingDatatype(program: Program) = DataType.UBYTE + override fun inferType(program: Program) = DataType.UBYTE } @@ -1570,7 +1570,7 @@ data class IdentifierReference(val nameInSource: List, override val posi override fun process(processor: IAstProcessor) = processor.process(this) override fun referencesIdentifier(name: String): Boolean = nameInSource.last() == name // @todo is this correct all the time? - override fun resultingDatatype(program: Program): DataType? { + override fun inferType(program: Program): DataType? { val targetStmt = targetStatement(program.namespace) if(targetStmt is VarDecl) { return targetStmt.datatype @@ -1652,7 +1652,7 @@ class FunctionCall(override var target: IdentifierReference, } if(withDatatypeCheck) { - val resultDt = this.resultingDatatype(program) + val resultDt = this.inferType(program) if(resultValue==null || resultDt == resultValue.type) return resultValue throw FatalAstException("evaluated const expression result value doesn't match expected datatype $resultDt, pos=$position") @@ -1673,7 +1673,7 @@ 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(program: Program): DataType? { + override fun inferType(program: Program): DataType? { val constVal = constValue(program ,false) if(constVal!=null) return constVal.type diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index a2e92f34d..6fe735155 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -131,7 +131,7 @@ private class AstChecker(private val program: Program, 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(program) + val dt = (returnStmt.values[0] as FunctionCall).inferType(program) if(dt!=null && expectedReturnValues.isEmpty()) checkResult.add(SyntaxError("invalid number of return values", returnStmt.position)) } else @@ -139,7 +139,7 @@ private class AstChecker(private val program: Program, } for (rv in expectedReturnValues.withIndex().zip(returnStmt.values)) { - val valueDt=rv.second.resultingDatatype(program) + val valueDt=rv.second.inferType(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)) } @@ -150,7 +150,7 @@ private class AstChecker(private val program: Program, if(forLoop.body.containsNoCodeNorVars()) printWarning("for loop body is empty", forLoop.position) - val iterableDt = forLoop.iterable.resultingDatatype(program) + val iterableDt = forLoop.iterable.inferType(program) if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) { checkResult.add(ExpressionError("can only loop over an iterable type", forLoop.position)) } else { @@ -372,7 +372,7 @@ private class AstChecker(private val program: Program, 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(program, assignment) != thing.first) + if (thing.second.inferType(program, assignment) != thing.first) checkResult.add(ExpressionError("return type mismatch for target ${thing.second.shortString()}", assignment.value.position)) } } @@ -435,7 +435,7 @@ private class AstChecker(private val program: Program, return assignment2 } - val targetDatatype = target.determineDatatype(program, assignment) + val targetDatatype = target.inferType(program, assignment) if(targetDatatype!=null) { val constVal = assignment.value.constValue(program) if(constVal!=null) { @@ -447,7 +447,7 @@ private class AstChecker(private val program: Program, arrayspec ?: ArrayIndex(LiteralValue.optimalInteger(-1, assignment.position), assignment.position), constVal, program.heap) } else { - val sourceDatatype: DataType? = assignment.value.resultingDatatype(program) + val sourceDatatype: DataType? = assignment.value.inferType(program) if(sourceDatatype==null) { if(assignment.targets.size<=1) { if (assignment.value is FunctionCall) { @@ -708,7 +708,7 @@ private class AstChecker(private val program: Program, override fun process(expr: PrefixExpression): IExpression { if(expr.operator=="-") { - val dt = expr.resultingDatatype(program) + val dt = expr.inferType(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)) } @@ -717,8 +717,8 @@ private class AstChecker(private val program: Program, } override fun process(expr: BinaryExpression): IExpression { - val leftDt = expr.left.resultingDatatype(program) - val rightDt = expr.right.resultingDatatype(program) + val leftDt = expr.left.inferType(program) + val rightDt = expr.right.inferType(program) when(expr.operator){ "/", "%" -> { @@ -836,15 +836,15 @@ private class AstChecker(private val program: Program, checkResult.add(SyntaxError("invalid number of arguments", position)) else { for (arg in args.withIndex().zip(func.parameters)) { - val argDt=arg.first.value.resultingDatatype(program) + val argDt=arg.first.value.inferType(program) if(argDt!=null && !(argDt isAssignableTo 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(program)!! - val dt2 = args[1].resultingDatatype(program)!! + val dt1 = args[0].inferType(program)!! + val dt2 = args[1].inferType(program)!! if (dt1 != dt2) checkResult.add(ExpressionError("swap requires 2 args of identical type", position)) else if (args[0].constValue(program) != null || args[1].constValue(program) != null) @@ -860,7 +860,7 @@ private class AstChecker(private val program: Program, checkResult.add(SyntaxError("invalid number of arguments", position)) else { for (arg in args.withIndex().zip(target.parameters)) { - val argDt = arg.first.value.resultingDatatype(program) + val argDt = arg.first.value.inferType(program) if(argDt!=null && !(argDt isAssignableTo 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)) @@ -943,7 +943,7 @@ private class AstChecker(private val program: Program, checkResult.add(SyntaxError("indexing requires a variable to act upon", arrayIndexedExpression.position)) // check index value 0..255 - val dtx = arrayIndexedExpression.arrayspec.index.resultingDatatype(program) + val dtx = arrayIndexedExpression.arrayspec.index.inferType(program) if(dtx!=DataType.UBYTE && dtx!=DataType.BYTE) checkResult.add(SyntaxError("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)) diff --git a/compiler/src/prog8/ast/StmtReorderer.kt b/compiler/src/prog8/ast/StmtReorderer.kt index 1f867e483..dd067f2c3 100644 --- a/compiler/src/prog8/ast/StmtReorderer.kt +++ b/compiler/src/prog8/ast/StmtReorderer.kt @@ -199,11 +199,12 @@ private class StatementReorderer(private val program: Program): IAstProcessor { val target=assignment.singleTarget if(target!=null) { // see if a typecast is needed to convert the value's type into the proper target type - val valuetype = assignment.value.resultingDatatype(program) - val targettype = target.determineDatatype(program, assignment) + val valuetype = assignment.value.inferType(program) + val targettype = target.inferType(program, assignment) if(targettype!=null && valuetype!=null && valuetype!=targettype) { if(valuetype isAssignableTo targettype) { assignment.value = TypecastExpression(assignment.value, targettype, assignment.value.position) + assignment.value.linkParents(assignment) } // if they're not assignable, we'll get a proper error later from the AstChecker } @@ -228,12 +229,13 @@ private class StatementReorderer(private val program: Program): IAstProcessor { when(sub) { is Subroutine -> { for(arg in sub.parameters.zip(call.arglist.withIndex())) { - val argtype = arg.second.value.resultingDatatype(program) + val argtype = arg.second.value.inferType(program) if(argtype!=null) { val requiredType = arg.first.type if (requiredType != argtype) { if (argtype isAssignableTo requiredType) { val typecasted = TypecastExpression(arg.second.value, requiredType, arg.second.value.position) + typecasted.linkParents(arg.second.value.parent) call.arglist[arg.second.index] = typecasted } // if they're not assignable, we'll get a proper error later from the AstChecker @@ -244,13 +246,14 @@ private class StatementReorderer(private val program: Program): IAstProcessor { is BuiltinFunctionStatementPlaceholder -> { val func = BuiltinFunctions.getValue(sub.name) for(arg in func.parameters.zip(call.arglist.withIndex())) { - val argtype = arg.second.value.resultingDatatype(program) + val argtype = arg.second.value.inferType(program) if(argtype!=null) { if(arg.first.possibleDatatypes.any{ argtype == it}) continue for(possibleType in arg.first.possibleDatatypes) { if(argtype isAssignableTo possibleType) { val typecasted = TypecastExpression(arg.second.value, possibleType, arg.second.value.position) + typecasted.linkParents(arg.second.value.parent) call.arglist[arg.second.index] = typecasted break } @@ -258,7 +261,8 @@ private class StatementReorderer(private val program: Program): IAstProcessor { } } } - else -> TODO("call to something weird $sub") + null -> {} + else -> TODO("call to something weird $sub ${call.target}") } } @@ -280,7 +284,7 @@ private class StatementReorderer(private val program: Program): IAstProcessor { break } } - val sorted = sequence.sortedWith(compareBy({it.value.resultingDatatype(program)}, {it.singleTarget?.shortString(true)})) + val sorted = sequence.sortedWith(compareBy({it.value.inferType(program)}, {it.singleTarget?.shortString(true)})) return Pair(sorted, trailing) } diff --git a/compiler/src/prog8/astvm/AstVm.kt b/compiler/src/prog8/astvm/AstVm.kt index 82c6697fb..0898813bf 100644 --- a/compiler/src/prog8/astvm/AstVm.kt +++ b/compiler/src/prog8/astvm/AstVm.kt @@ -109,9 +109,6 @@ class AstVm(val program: Program) { } println("PROGRAM EXITED!") dialog.title = "PROGRAM EXITED" - } catch (bp: VmBreakpointException) { - println("Breakpoint: execution halted. Press enter to resume.") - readLine() } catch (tx: VmTerminationException) { println("Execution halted: ${tx.message}") } catch (xx: VmExecutionException) { @@ -143,7 +140,13 @@ class AstVm(val program: Program) { try { for (s in sub.statements) { - executeStatement(sub, s) + try { + executeStatement(sub, s) + } + catch (b: VmBreakpointException) { + print("BREAKPOINT HIT at ${s.position} - Press enter to continue:") + readLine() + } } } catch (r: LoopControlReturn) { return r.returnvalues @@ -269,7 +272,7 @@ class AstVm(val program: Program) { loopvarDt = DataType.UBYTE loopvar = IdentifierReference(listOf(stmt.loopRegister.name), stmt.position) } else { - loopvarDt = stmt.loopVar!!.resultingDatatype(program)!! + loopvarDt = stmt.loopVar!!.inferType(program)!! loopvar = stmt.loopVar } val iterator = iterable.iterator() diff --git a/compiler/src/prog8/astvm/Expressions.kt b/compiler/src/prog8/astvm/Expressions.kt index 1e085f1d8..49d259690 100644 --- a/compiler/src/prog8/astvm/Expressions.kt +++ b/compiler/src/prog8/astvm/Expressions.kt @@ -124,7 +124,7 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue { is RangeExpr -> { val cRange = expr.toConstantIntegerRange(ctx.program.heap) if(cRange!=null) - return RuntimeValueRange(expr.resultingDatatype(ctx.program)!!, cRange) + return RuntimeValueRange(expr.inferType(ctx.program)!!, cRange) val fromVal = evaluate(expr.from, ctx).integerValue() val toVal = evaluate(expr.to, ctx).integerValue() val stepVal = evaluate(expr.step, ctx).integerValue() @@ -140,7 +140,7 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue { else -> fromVal downTo toVal step abs(stepVal) } } - return RuntimeValueRange(expr.resultingDatatype(ctx.program)!!, range) + return RuntimeValueRange(expr.inferType(ctx.program)!!, range) } else -> { TODO("implement eval $expr") diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index cb3b31926..cdec2a27f 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -514,7 +514,7 @@ internal class Compiler(private val program: Program): IAstProcessor { val trueGoto = stmt.truepart.statements.singleOrNull() as? Jump if(trueGoto!=null) { // optimization for if (condition) goto .... - val conditionJumpOpcode = when(stmt.condition.resultingDatatype(program)) { + val conditionJumpOpcode = when(stmt.condition.inferType(program)) { in ByteDatatypes -> Opcode.JNZ in WordDatatypes -> Opcode.JNZW else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt") @@ -524,7 +524,7 @@ internal class Compiler(private val program: Program): IAstProcessor { return } - val conditionJumpOpcode = when(stmt.condition.resultingDatatype(program)) { + val conditionJumpOpcode = when(stmt.condition.inferType(program)) { in ByteDatatypes -> Opcode.JZ in WordDatatypes -> Opcode.JZW else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt") @@ -617,11 +617,11 @@ internal class Compiler(private val program: Program): IAstProcessor { } is PrefixExpression -> { translate(expr.expression) - translatePrefixOperator(expr.operator, expr.expression.resultingDatatype(program)) + translatePrefixOperator(expr.operator, expr.expression.inferType(program)) } is BinaryExpression -> { - val leftDt = expr.left.resultingDatatype(program)!! - val rightDt = expr.right.resultingDatatype(program)!! + val leftDt = expr.left.inferType(program)!! + val rightDt = expr.right.inferType(program)!! val commonDt = if(expr.operator=="/") BinaryExpression.divisionOpDt(leftDt, rightDt) @@ -793,7 +793,7 @@ internal class Compiler(private val program: Program): IAstProcessor { // cast type if needed if(builtinFuncParams!=null) { val paramDts = builtinFuncParams[index].possibleDatatypes - val argDt = arg.resultingDatatype(program)!! + val argDt = arg.inferType(program)!! if(argDt !in paramDts) { for(paramDt in paramDts.sorted()) if(tryConvertType(argDt, paramDt)) @@ -806,7 +806,7 @@ internal class Compiler(private val program: Program): IAstProcessor { "len" -> { // 1 argument, type determines the exact syscall to use val arg=args.single() - when (arg.resultingDatatype(program)) { + when (arg.inferType(program)) { DataType.STR, DataType.STR_S -> createSyscall("${funcname}_str") else -> throw CompilerException("wrong datatype for len()") } @@ -817,7 +817,7 @@ internal class Compiler(private val program: Program): IAstProcessor { val target=arg.targetVarDecl(program.namespace)!! val length= RuntimeValue(DataType.UBYTE, target.arraysize!!.size()!!) prog.instr(Opcode.PUSH_BYTE, length) - when (arg.resultingDatatype(program)) { + when (arg.inferType(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") @@ -829,7 +829,7 @@ internal class Compiler(private val program: Program): IAstProcessor { val arg=args.single() as IdentifierReference val target=arg.targetVarDecl(program.namespace)!! val length= RuntimeValue(DataType.UBYTE, target.arraysize!!.size()!!) - val arrayDt=arg.resultingDatatype(program) + val arrayDt=arg.inferType(program) prog.instr(Opcode.PUSH_BYTE, length) when (arrayDt) { DataType.ARRAY_UB -> { @@ -861,7 +861,7 @@ internal class Compiler(private val program: Program): IAstProcessor { val target=arg.targetVarDecl(program.namespace)!! val length= RuntimeValue(DataType.UBYTE, target.arraysize!!.size()!!) prog.instr(Opcode.PUSH_BYTE, length) - when (arg.resultingDatatype(program)) { + when (arg.inferType(program)) { DataType.ARRAY_UB -> createSyscall("${funcname}_ub") DataType.ARRAY_B -> createSyscall("${funcname}_b") DataType.ARRAY_UW -> createSyscall("${funcname}_uw") @@ -873,7 +873,7 @@ internal class Compiler(private val program: Program): IAstProcessor { "abs" -> { // 1 argument, type determines the exact opcode to use val arg = args.single() - when (arg.resultingDatatype(program)) { + when (arg.inferType(program)) { DataType.UBYTE, DataType.UWORD -> {} DataType.BYTE -> prog.instr(Opcode.ABS_B) DataType.WORD -> prog.instr(Opcode.ABS_W) @@ -885,7 +885,7 @@ internal class Compiler(private val program: Program): IAstProcessor { "mkword" -> prog.instr(Opcode.MKWORD) "lsl" -> { val arg = args.single() - val dt = arg.resultingDatatype(program) + val dt = arg.inferType(program) when (dt) { in ByteDatatypes -> prog.instr(Opcode.SHL_BYTE) in WordDatatypes -> prog.instr(Opcode.SHL_WORD) @@ -896,7 +896,7 @@ internal class Compiler(private val program: Program): IAstProcessor { } "lsr" -> { val arg = args.single() - val dt = arg.resultingDatatype(program) + val dt = arg.inferType(program) when (dt) { DataType.UBYTE -> prog.instr(Opcode.SHR_UBYTE) DataType.BYTE -> prog.instr(Opcode.SHR_SBYTE) @@ -909,7 +909,7 @@ internal class Compiler(private val program: Program): IAstProcessor { } "rol" -> { val arg = args.single() - val dt = arg.resultingDatatype(program) + val dt = arg.inferType(program) when (dt) { DataType.UBYTE -> prog.instr(Opcode.ROL_BYTE) DataType.UWORD -> prog.instr(Opcode.ROL_WORD) @@ -920,7 +920,7 @@ internal class Compiler(private val program: Program): IAstProcessor { } "ror" -> { val arg = args.single() - val dt = arg.resultingDatatype(program) + val dt = arg.inferType(program) when (dt) { in ByteDatatypes -> prog.instr(Opcode.ROR_BYTE) in WordDatatypes -> prog.instr(Opcode.ROR_WORD) @@ -931,7 +931,7 @@ internal class Compiler(private val program: Program): IAstProcessor { } "rol2" -> { val arg = args.single() - val dt = arg.resultingDatatype(program) + val dt = arg.inferType(program) when (dt) { in ByteDatatypes -> prog.instr(Opcode.ROL2_BYTE) in WordDatatypes -> prog.instr(Opcode.ROL2_WORD) @@ -942,7 +942,7 @@ internal class Compiler(private val program: Program): IAstProcessor { } "ror2" -> { val arg = args.single() - val dt = arg.resultingDatatype(program) + val dt = arg.inferType(program) when (dt) { in ByteDatatypes -> prog.instr(Opcode.ROR2_BYTE) in WordDatatypes -> prog.instr(Opcode.ROR2_WORD) @@ -965,8 +965,8 @@ internal class Compiler(private val program: 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(program)!! - val dt2 = args[1].resultingDatatype(program)!! + val dt1 = args[0].inferType(program)!! + val dt2 = args[1].inferType(program)!! if (dt1 != dt2) throw AstException("swap requires 2 args of identical type") if (args[0].constValue(program) != null || args[1].constValue(program) != null) @@ -999,7 +999,7 @@ internal class Compiler(private val program: Program): IAstProcessor { // (subroutine arguments are not passed via the stack!) for (arg in arguments.zip(subroutine.parameters)) { translate(arg.first) - convertType(arg.first.resultingDatatype(program)!!, arg.second.type) // convert types of arguments to required parameter type + convertType(arg.first.inferType(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) } @@ -1072,7 +1072,7 @@ internal class Compiler(private val program: Program): IAstProcessor { } val valueA: IExpression val valueX: IExpression - val paramDt = arg.first.resultingDatatype(program) + val paramDt = arg.first.inferType(program) when (paramDt) { DataType.UBYTE -> { valueA = arg.first @@ -1095,7 +1095,7 @@ internal class Compiler(private val program: Program): IAstProcessor { AY -> { val valueA: IExpression val valueY: IExpression - val paramDt = arg.first.resultingDatatype(program) + val paramDt = arg.first.inferType(program) when (paramDt) { DataType.UBYTE -> { valueA = arg.first @@ -1122,7 +1122,7 @@ internal class Compiler(private val program: Program): IAstProcessor { } val valueX: IExpression val valueY: IExpression - val paramDt = arg.first.resultingDatatype(program) + val paramDt = arg.first.inferType(program) when (paramDt) { DataType.UBYTE -> { valueX = arg.first @@ -1474,8 +1474,8 @@ internal class Compiler(private val program: Program): IAstProcessor { return } - val valueDt = stmt.value.resultingDatatype(program) - val targetDt = assignTarget.determineDatatype(program, stmt) + val valueDt = stmt.value.inferType(program) + val targetDt = assignTarget.inferType(program, stmt) if(valueDt!=targetDt) { // convert value to target datatype if possible // @todo use convertType()???? @@ -1526,7 +1526,7 @@ internal class Compiler(private val program: 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(program, stmt)!! + val datatype = assignTarget.inferType(program, stmt)!! popValueIntoTarget(assignTarget, datatype) } @@ -1561,7 +1561,7 @@ internal class Compiler(private val program: 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(program, stmt) + val dt = target.inferType(program, stmt) popValueIntoTarget(target, dt!!) } } else throw CompilerException("can only use multiple assignment targets on an asmsub call") @@ -2011,7 +2011,7 @@ internal class Compiler(private val program: Program): IAstProcessor { translate(stmt.body) prog.label(continueLabel) translate(stmt.condition) - val conditionJumpOpcode = when(stmt.condition.resultingDatatype(program)) { + val conditionJumpOpcode = when(stmt.condition.inferType(program)) { in ByteDatatypes -> Opcode.JNZ in WordDatatypes -> Opcode.JNZW else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt") @@ -2048,7 +2048,7 @@ internal class Compiler(private val program: Program): IAstProcessor { translate(stmt.body) prog.label(continueLabel) translate(stmt.untilCondition) - val conditionJumpOpcode = when(stmt.untilCondition.resultingDatatype(program)) { + val conditionJumpOpcode = when(stmt.untilCondition.inferType(program)) { in ByteDatatypes -> Opcode.JZ in WordDatatypes -> Opcode.JZW else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt") @@ -2062,7 +2062,7 @@ internal class Compiler(private val program: Program): IAstProcessor { private fun translate(expr: TypecastExpression) { translate(expr.expression) - val sourceDt = expr.expression.resultingDatatype(program) ?: throw CompilerException("don't know what type to cast") + val sourceDt = expr.expression.inferType(program) ?: throw CompilerException("don't know what type to cast") if(sourceDt==expr.type) return diff --git a/compiler/src/prog8/compiler/RuntimeValue.kt b/compiler/src/prog8/compiler/RuntimeValue.kt index 7a224fc8d..d0b976b61 100644 --- a/compiler/src/prog8/compiler/RuntimeValue.kt +++ b/compiler/src/prog8/compiler/RuntimeValue.kt @@ -444,12 +444,7 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?= DataType.UBYTE -> { when (targetType) { DataType.UBYTE -> this - DataType.BYTE -> { - if(byteval!!<=127) - RuntimeValue(DataType.BYTE, byteval) - else - RuntimeValue(DataType.BYTE, -(256 - byteval)) - } + DataType.BYTE -> RuntimeValue(DataType.BYTE, byteval) DataType.UWORD -> RuntimeValue(DataType.UWORD, numericValue()) DataType.WORD -> RuntimeValue(DataType.WORD, numericValue()) DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue()) @@ -468,21 +463,18 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?= } DataType.UWORD -> { when (targetType) { - in ByteDatatypes -> RuntimeValue(DataType.UBYTE, integerValue()) + DataType.BYTE -> RuntimeValue(DataType.BYTE, integerValue()) + DataType.UBYTE -> RuntimeValue(DataType.UBYTE, integerValue()) DataType.UWORD -> this - DataType.WORD -> { - if(integerValue()<=32767) - RuntimeValue(DataType.WORD, integerValue()) - else - RuntimeValue(DataType.WORD, -(65536 - integerValue())) - } + DataType.WORD -> RuntimeValue(DataType.WORD, integerValue()) DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue()) else -> throw ArithmeticException("invalid type cast from $type to $targetType") } } DataType.WORD -> { when (targetType) { - in ByteDatatypes -> RuntimeValue(DataType.UBYTE, integerValue()) + DataType.BYTE -> RuntimeValue(DataType.BYTE, integerValue()) + DataType.UBYTE -> RuntimeValue(DataType.UBYTE, integerValue()) DataType.UWORD -> RuntimeValue(DataType.UWORD, integerValue()) DataType.WORD -> this DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue()) diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index 9fac6e136..599d6d182 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -112,7 +112,7 @@ fun builtinFunctionReturnType(function: String, args: List, program 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(program)} + val dt = arglist.arrayvalue!!.map {it.inferType(program)} if(dt.any { it!=DataType.UBYTE && it!=DataType.UWORD && it!=DataType.FLOAT}) { throw FatalAstException("fuction $function only accepts arraysize of numeric values") } @@ -122,7 +122,7 @@ fun builtinFunctionReturnType(function: String, args: List, program } } if(arglist is IdentifierReference) { - val dt = arglist.resultingDatatype(program) + val dt = arglist.inferType(program) return when(dt) { in NumericDatatypes -> dt!! in StringDatatypes -> dt!! @@ -144,7 +144,7 @@ fun builtinFunctionReturnType(function: String, args: List, program return when (function) { "abs" -> { - val dt = args.single().resultingDatatype(program) + val dt = args.single().inferType(program) when(dt) { in ByteDatatypes -> DataType.UBYTE in WordDatatypes -> DataType.UWORD diff --git a/compiler/src/prog8/optimizing/ConstantFolding.kt b/compiler/src/prog8/optimizing/ConstantFolding.kt index 9cc618170..db0ead88c 100644 --- a/compiler/src/prog8/optimizing/ConstantFolding.kt +++ b/compiler/src/prog8/optimizing/ConstantFolding.kt @@ -53,7 +53,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor { errors.add(ExpressionError("range expression size doesn't match declared array size", decl.value?.position!!)) val constRange = rangeExpr.toConstantIntegerRange(program.heap) if(constRange!=null) { - val eltType = rangeExpr.resultingDatatype(program)!! + val eltType = rangeExpr.inferType(program)!! if(eltType in ByteDatatypes) { decl.value = LiteralValue(decl.datatype, arrayvalue = constRange.map { LiteralValue(eltType, bytevalue=it.toShort(), position = decl.value!!.position ) } @@ -606,7 +606,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor { val valuesInArray = array.map { it.constValue(program)!!.asNumericValue!! } val integerArray = valuesInArray.map{ it.toInt() } val doubleArray = valuesInArray.map{it.toDouble()}.toDoubleArray() - val typesInArray: Set = array.mapNotNull { it.resultingDatatype(program) }.toSet() + val typesInArray: Set = array.mapNotNull { it.inferType(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! @@ -651,7 +651,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor { val lv = assignment.value as? LiteralValue if(lv!=null) { // see if we can promote/convert a literal value to the required datatype - when(assignment.singleTarget?.determineDatatype(program, assignment)) { + when(assignment.singleTarget?.inferType(program, assignment)) { DataType.UWORD -> { // we can convert to UWORD: any UBYTE, BYTE/WORD that are >=0, FLOAT that's an integer 0..65535, if(lv.type==DataType.UBYTE) diff --git a/compiler/src/prog8/optimizing/SimplifyExpressions.kt b/compiler/src/prog8/optimizing/SimplifyExpressions.kt index f8debf585..6527cc873 100644 --- a/compiler/src/prog8/optimizing/SimplifyExpressions.kt +++ b/compiler/src/prog8/optimizing/SimplifyExpressions.kt @@ -33,6 +33,27 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor return super.process(memwrite) } + override fun process(typecast: TypecastExpression): IExpression { + // remove redundant typecasts + var tc = typecast + while(true) { + val expr = tc.expression + if(expr !is TypecastExpression || expr.type!=tc.type) { + val assignment = typecast.parent as? Assignment + if(assignment!=null) { + val targetDt = assignment.singleTarget?.inferType(program, assignment) + if(tc.expression.inferType(program)==targetDt) { + optimizationsDone++ + return tc.expression + } + } + return super.process(tc) + } + optimizationsDone++ + tc = expr + } + } + override fun process(expr: PrefixExpression): IExpression { if (expr.operator == "+") { // +X --> X @@ -87,8 +108,8 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor val constTrue = LiteralValue.fromBoolean(true, expr.position) val constFalse = LiteralValue.fromBoolean(false, expr.position) - val leftDt = expr.left.resultingDatatype(program) - val rightDt = expr.right.resultingDatatype(program) + val leftDt = expr.left.inferType(program) + val rightDt = expr.right.inferType(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)) { @@ -541,7 +562,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor "%" -> { if (cv == 1.0) { optimizationsDone++ - return LiteralValue.fromNumber(0, expr.resultingDatatype(program)!!, expr.position) + return LiteralValue.fromNumber(0, expr.inferType(program)!!, expr.position) } else if (cv == 2.0) { optimizationsDone++ expr.operator = "&" @@ -564,7 +585,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor // right value is a constant, see if we can optimize val rightConst: LiteralValue = rightVal val cv = rightConst.asNumericValue?.toDouble() - val leftDt = expr.left.resultingDatatype(program) + val leftDt = expr.left.inferType(program) when(cv) { -1.0 -> { // '/' -> -left @@ -652,7 +673,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor 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(program) in IntegerDatatypes) { + if(leftValue.inferType(program) in IntegerDatatypes) { // times a power of two => shift left optimizationsDone++ val numshifts = log2(cv).toInt() @@ -660,7 +681,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor } } -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(program) in IntegerDatatypes) { + if(leftValue.inferType(program) in IntegerDatatypes) { // times a negative power of two => negate, then shift left optimizationsDone++ val numshifts = log2(-cv).toInt() diff --git a/compiler/src/prog8/optimizing/StatementOptimizer.kt b/compiler/src/prog8/optimizing/StatementOptimizer.kt index f1a8cc1ed..526f53731 100644 --- a/compiler/src/prog8/optimizing/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizing/StatementOptimizer.kt @@ -452,7 +452,7 @@ internal class StatementOptimizer(private val program: Program, private val opti optimizationsDone++ return NopStatement(assignment.position) } - val targetDt = target.determineDatatype(program, assignment) + val targetDt = target.inferType(program, assignment) val bexpr=assignment.value as? BinaryExpression if(bexpr!=null) { val cv = bexpr.right.constValue(program)?.asNumericValue?.toDouble() diff --git a/examples/test.p8 b/examples/test.p8 index 16c7410ea..35757d989 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -8,37 +8,15 @@ sub start() { ubyte u1 = 100 ubyte u2 = 30 - byte ub = -30 + byte bb = -30 + byte bb2 = -30 + float ff = -3.3 + word ww + + bb = (u2 as word) as byte + bb = (((u2 as word) as byte) as word) as byte - abs(ub) - word r = moo(u1,u2) - c64scr.print_w(r) } - sub moo(ubyte p1, word p2) -> word { - c64scr.print_ub(p1) - c64.CHROUT(',') - c64scr.print_w(p2) - c64.CHROUT('\n') - c64.CHROUT('\n') - - for word ww in 200 to 300 step 13 { - c64scr.print_w(ww) - c64.CHROUT('\n') - } - - ubyte u1 = 100 - ubyte u2 = 30 - - c64scr.print_ub(u1 % u2) - c64.CHROUT('\n') - c64scr.print_ub(u1 / u2) - c64.CHROUT('\n') - c64scr.print_ub(u2 * 2) - c64.CHROUT('\n') - c64scr.print_ub(u2 * 7) - c64.CHROUT('\n') - return 12345 - } }