diff --git a/il65/examples/imported2.ill b/il65/examples/imported2.ill index 013aa18db..6b0c70c81 100644 --- a/il65/examples/imported2.ill +++ b/il65/examples/imported2.ill @@ -22,6 +22,9 @@ return A+snerp2 } + sub thingy()->() { + return 99 + } } diff --git a/il65/examples/test.ill b/il65/examples/test.ill index 4f40c76ee..a93d3f732 100644 --- a/il65/examples/test.ill +++ b/il65/examples/test.ill @@ -6,9 +6,10 @@ ~ main $c003 { const word len1 = len([1,2,3,wa1, wa2, ws1, all1]) - const word wa1 = abs(-999) + const word wa1 = ceil(abs(-999.22)) const byte wa2 = abs(-99) const float wa3 = abs(-1.23456) + const float wa4 = abs(-133) const float avg1 = avg([-1.23456, 99999]) const float sum1 = sum([-1.23456, 99999]) const word ws1 = floor(sum([1,2,3,4.9])) @@ -28,14 +29,15 @@ A = X>2 X = Y>Y + byte myByteChar = "A" + word myWordChar = "B" word[1000] ascending = 10 to 1009 - word[100] ascending2 = "a" to "z" str ascending3 = "a" to "z" str ascending5 = "z" to "z" const byte cc = 4 + (2==9) byte cc2 = 4 - (2==10) memory byte derp = max([$ffdd]) - memory byte derpA = abs(-2.5-0.5) + memory byte derpA = abs(-20000) memory byte derpB = max([1, 2.2, 4.4, 100]) memory byte cderp = min([$ffdd])+ (1/1) memory byte cderpA = min([$ffdd, 10, 20, 30]) @@ -92,18 +94,21 @@ if(6==6) { A=sin(X) + A=sin([X]) X=max([1,2,Y]) X=min([1,2,Y]) - X=lsl(1) + X=lsl(1.2) X=lsl(Y) P_carry(0) - P_carry(Y) ; TODO error - P_carry(9.99) ; TODO error + P_carry(1) + P_carry(1-1) P_irqd(0) - P_irqd(Y) ; TODO error - P_irqd(9.99) ; TODO error + P_irqd(1) } else X=33 + X= extra233.thingy() ; TODO EHHHH, should be counted as used? + + if(6>36) { A=99 } else { @@ -126,6 +131,7 @@ cool: mega: cool: sub ultrafoo() -> () { + X= extra233.thingy() ; TODO EHHHH, should be counted as used? return 33 goto main.mega } diff --git a/il65/src/il65/Main.kt b/il65/src/il65/Main.kt index c02df9d43..56aaea91a 100644 --- a/il65/src/il65/Main.kt +++ b/il65/src/il65/Main.kt @@ -64,12 +64,13 @@ fun main(args: Array) { val intermediate = compiler.compile(moduleAst) intermediate.optimize() -// val assembler = intermediate.compileToAssembly() -// assembler.assemble(compilerOptions, "input", "output") -// val monitorfile = assembler.generateBreakpointList() +// val assembly = intermediate.compileToAssembly() +// +// assembly.assemble(compilerOptions, "input", "output") +// val monitorfile = assembly.generateBreakpointList() val endTime = System.currentTimeMillis() - println("Compilation time: ${(endTime-startTime)/1000.0} sec.") + println("\nTotal compilation time: ${(endTime-startTime)/1000.0} sec.") // // start the vice emulator // val program = "foo" diff --git a/il65/src/il65/ast/AST.kt b/il65/src/il65/ast/AST.kt index 5c513db20..db1395675 100644 --- a/il65/src/il65/ast/AST.kt +++ b/il65/src/il65/ast/AST.kt @@ -518,6 +518,10 @@ class VarDecl(val type: VarDeclType, fun arraySizeY(namespace: INameScope) : Int? { return arrayspec?.y?.constValue(namespace)?.intvalue } + + override fun toString(): String { + return "VarDecl(name=$name, vartype=$type, datatype=$datatype, value=$value, pos=$position)" + } } @@ -601,31 +605,19 @@ class LiteralValue(val intvalue: Int? = null, override lateinit var parent: Node override fun referencesIdentifier(name: String) = arrayvalue?.any { it.referencesIdentifier(name) } ?: false - fun asInt(errorIfNotNumeric: Boolean=true): Int? { - return when { - intvalue!=null -> intvalue - floatvalue!=null -> floatvalue.toInt() - else -> { - if((strvalue!=null || arrayvalue!=null) && errorIfNotNumeric) - throw AstException("attempt to get int value from non-numeric $this") - else null - } - } + val isInteger = intvalue!=null + val isFloat = floatvalue!=null + val isNumeric = intvalue!=null || floatvalue!=null + val isArray = arrayvalue!=null + val isString = strvalue!=null + + val asNumericValue: Number? = when { + intvalue!=null -> intvalue + floatvalue!=null -> floatvalue + else -> null } - fun asFloat(errorIfNotNumeric: Boolean=true): Double? { - return when { - floatvalue!=null -> floatvalue - intvalue!=null -> intvalue.toDouble() - else -> { - if((strvalue!=null || arrayvalue!=null) && errorIfNotNumeric) - throw AstException("attempt to get float value from non-numeric $this") - else null - } - } - } - - fun asBoolean(): Boolean = + val asBooleanValue: Boolean = (floatvalue!=null && floatvalue != 0.0) || (intvalue!=null && intvalue != 0) || (strvalue!=null && strvalue.isNotEmpty()) || @@ -775,12 +767,12 @@ class FunctionCall(override var target: IdentifierReference, override var arglis "ceil" -> builtinCeil(arglist, position, namespace) "lsl" -> builtinLsl(arglist, position, namespace) "lsr" -> builtinLsr(arglist, position, namespace) - "rol" -> throw ExpressionException("builtin function _rol can't be used in expressions because it doesn't return a value", position) - "rol2" -> throw ExpressionException("builtin function _rol2 can't be used in expressions because it doesn't return a value", position) - "ror" -> throw ExpressionException("builtin function _ror can't be used in expressions because it doesn't return a value", position) - "ror2" -> throw ExpressionException("builtin function _ror2 can't be used in expressions because it doesn't return a value", position) - "P_carry" -> throw ExpressionException("builtin function _P_carry can't be used in expressions because it doesn't return a value", position) - "P_irqd" -> throw ExpressionException("builtin function _P_irqd can't be used in expressions because it doesn't return a value", position) + "rol" -> throw ExpressionException("builtin function rol can't be used in expressions because it doesn't return a value", position) + "rol2" -> throw ExpressionException("builtin function rol2 can't be used in expressions because it doesn't return a value", position) + "ror" -> throw ExpressionException("builtin function ror can't be used in expressions because it doesn't return a value", position) + "ror2" -> throw ExpressionException("builtin function ror2 can't be used in expressions because it doesn't return a value", position) + "P_carry" -> throw ExpressionException("builtin function P_carry can't be used in expressions because it doesn't return a value", position) + "P_irqd" -> throw ExpressionException("builtin function P_irqd can't be used in expressions because it doesn't return a value", position) else -> null } } @@ -791,7 +783,7 @@ class FunctionCall(override var target: IdentifierReference, override var arglis } override fun toString(): String { - return "FunctionCall(target=$target, targetStmt=$targetStatement, pos=$position)" + return "FunctionCall(target=$target, pos=$position)" } override fun process(processor: IAstProcessor) = processor.process(this) diff --git a/il65/src/il65/ast/AstChecker.kt b/il65/src/il65/ast/AstChecker.kt index b8c731ae8..8eb9cd669 100644 --- a/il65/src/il65/ast/AstChecker.kt +++ b/il65/src/il65/ast/AstChecker.kt @@ -129,9 +129,9 @@ class AstChecker(private val globalNamespace: INameScope) : IAstProcessor { decl.value !is LiteralValue -> err("var/const declaration needs a compile-time constant initializer value, found: ${decl.value!!::class.simpleName}") decl.isScalar -> { - checkConstInitializerValueScalar(decl) - checkValueType(decl, decl.value as LiteralValue, decl.position) - checkValueRange(decl.datatype, decl.value as LiteralValue, decl.position) + if(checkValueType(decl, decl.value as LiteralValue, decl.position)) { + checkValueRange(decl.datatype, decl.value as LiteralValue, decl.position) + } } decl.isArray || decl.isMatrix -> { checkConstInitializerValueArray(decl) @@ -159,7 +159,7 @@ class AstChecker(private val globalNamespace: INameScope) : IAstProcessor { override fun process(ifStatement: IfStatement): IStatement { val constvalue = ifStatement.condition.constValue(globalNamespace) if(constvalue!=null) { - val msg = if (constvalue.asBoolean()) "condition is always true" else "condition is always false" + val msg = if (constvalue.asBooleanValue) "condition is always true" else "condition is always false" println("${ifStatement.position} Warning: $msg") } @@ -303,21 +303,26 @@ class AstChecker(private val globalNamespace: INameScope) : IAstProcessor { ?: throw FatalAstException("cannot determine statement scope of function call expression at ${functionCall.position}") val targetStatement = checkFunctionOrLabelExists(functionCall.target, stmtOfExpression) - if(targetStatement!=null) + if(targetStatement!=null) { functionCall.targetStatement = targetStatement // link to the actual target statement + checkBuiltinFunctionCall(functionCall, functionCall.position) + } return super.process(functionCall) } override fun process(functionCall: FunctionCallStatement): IStatement { val targetStatement = checkFunctionOrLabelExists(functionCall.target, functionCall) - if(targetStatement!=null) + if(targetStatement!=null) { functionCall.targetStatement = targetStatement // link to the actual target statement + checkBuiltinFunctionCall(functionCall, functionCall.position) + } return super.process(functionCall) } private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: IStatement): IStatement? { - if(target.nameInSource.size==1 && BuiltinFunctionNames.contains(target.nameInSource[0])) - return BuiltinFunctionStatementPlaceholder + if(target.nameInSource.size==1 && BuiltinFunctionNames.contains(target.nameInSource[0])) { + return BuiltinFunctionStatementPlaceholder + } val targetStatement = globalNamespace.lookup(target.nameInSource, statement) if(targetStatement is Label || targetStatement is Subroutine) return targetStatement @@ -325,6 +330,23 @@ class AstChecker(private val globalNamespace: INameScope) : IAstProcessor { return null } + private fun checkBuiltinFunctionCall(call: IFunctionCall, position: Position?) { + if(call.target.nameInSource.size==1 && BuiltinFunctionNames.contains(call.target.nameInSource[0])) { + val functionName = call.target.nameInSource[0] + if(functionName=="P_carry" || functionName=="P_irqd") { + // these functions allow only 0 or 1 as argument + if(call.arglist.size!=1 || call.arglist[0] !is LiteralValue) { + checkResult.add(SyntaxError("$functionName requires one argument, 0 or 1", position)) + } else { + val value = call.arglist[0] as LiteralValue + if(value.intvalue==null || value.intvalue < 0 || value.intvalue > 1) { + checkResult.add(SyntaxError("$functionName requires one argument, 0 or 1", position)) + } + } + } + } + } + private fun checkValueRange(datatype: DataType, value: LiteralValue, position: Position?) : Boolean { fun err(msg: String) : Boolean { checkResult.add(SyntaxError(msg, position)) @@ -332,23 +354,27 @@ class AstChecker(private val globalNamespace: INameScope) : IAstProcessor { } when (datatype) { DataType.FLOAT -> { - val number = value.asFloat(false) - if (number!=null && (number > 1.7014118345e+38 || number < -1.7014118345e+38)) + val number = value.floatvalue + ?: return err("floating point value expected") + if (number > 1.7014118345e+38 || number < -1.7014118345e+38) return err("floating point value '$number' out of range for MFLPT format") } DataType.BYTE -> { - val number = value.asInt(false) - if (number!=null && (number < 0 || number > 255)) + val number = value.intvalue + ?: return err("byte integer value expected") + if (number < 0 || number > 255) return err("value '$number' out of range for unsigned byte") } DataType.WORD -> { - val number = value.asInt(false) - if (number!=null && (number < 0 || number > 65535)) + val number = value.intvalue + ?: return err("word integer value expected") + if (number < 0 || number > 65535) return err("value '$number' out of range for unsigned word") } DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> { val str = value.strvalue - if (str!=null && (str.isEmpty() || str.length > 65535)) + ?: return err("string value expected") + if (str.isEmpty() || str.length > 65535) return err("string length must be 1..65535") } } @@ -385,33 +411,4 @@ class AstChecker(private val globalNamespace: INameScope) : IAstProcessor { } return true } - - private fun checkConstInitializerValueScalar(decl: VarDecl) { - fun err(msg: String) { - checkResult.add(SyntaxError(msg, decl.position)) - } - val value = decl.value as LiteralValue - when (decl.datatype) { - DataType.FLOAT -> { - val number = value.asFloat(false) - if (number == null) - err("need a const float initializer value") - } - DataType.BYTE -> { - val number = value.asInt(false) - if (number == null) - err("need a const integer initializer value") - } - DataType.WORD -> { - val number = value.asInt(false) - if (number == null) - err("need a const integer initializer value") - } - DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> { - val str = value.strvalue - if (str == null) - err("need a const string initializer value") - } - } - } } diff --git a/il65/src/il65/compiler/Compiler.kt b/il65/src/il65/compiler/Compiler.kt index 0fd18f56c..1d697420f 100644 --- a/il65/src/il65/compiler/Compiler.kt +++ b/il65/src/il65/compiler/Compiler.kt @@ -1,5 +1,6 @@ package il65.compiler +import il65.ast.Block import il65.ast.INameScope import il65.ast.Module import kotlin.experimental.and @@ -93,7 +94,16 @@ class Compiler(private val options: CompilationOptions, val namespace: INameScop } fun compile(module: Module) : IntermediateForm { - println("......@TODO compile Ast into Intermediate result......") // todo + println("\nCompiling parsed source code to intermediate code...") + // todo + + namespace.debugPrint() + + module.statements.filter { it is Block }.map { + with(it as Block) { + "$address $scopedname" + } + }.forEach { println(it) } return IntermediateForm(module.name) } } @@ -101,11 +111,13 @@ class Compiler(private val options: CompilationOptions, val namespace: INameScop class IntermediateForm(val name: String) { fun optimize() { - println("......@TODO optimize intermediate result......") // todo + println("\nOptimizing intermediate code...") + // todo } fun compileToAssembly(): AssemblyResult { - println("......@TODO compile intermediate result to assembly code......") // todo + println("\nGenerating assembly code from intermediate code... ") + // todo return AssemblyResult(name) } @@ -136,10 +148,10 @@ data class CompilationOptions(val output: OutputType, class AssemblyResult(val name: String) { fun assemble(options: CompilationOptions, inputfilename: String, outputfilename: String) { - println("......@TODO assemble with 64tass......") // todo + println("\nGenerating machine code program...") val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "-Wall", "-Wno-strict-bool", - "--dump-labels", "--vice-labels", "-l", outputfilename+".vice-mon-list", + "--dump-labels", "--vice-labels", "-l", "$outputfilename.vice-mon-list", "--no-monitor", "--output", outputfilename, inputfilename) when(options.output) { diff --git a/il65/src/il65/compiler/Petscii.kt b/il65/src/il65/compiler/Petscii.kt index ed6a31396..6d8e58a4d 100644 --- a/il65/src/il65/compiler/Petscii.kt +++ b/il65/src/il65/compiler/Petscii.kt @@ -1052,7 +1052,11 @@ class Petscii { fun encodePetscii(text: String, lowercase: Boolean = false): List { val lookup = if(lowercase) encodingPetsciiLowercase else encodingPetsciiUppercase return text.map { - val petscii = lookup[it] ?: throw CompilerException("no Petscii character for '$it'") + val petscii = lookup[it] + if(petscii==null) { + val case = if(lowercase) "lower" else "upper" + throw CompilerException("no ${case}case Petscii character for '$it'") + } petscii.toShort() } } @@ -1065,7 +1069,11 @@ class Petscii { fun encodeScreencode(text: String, lowercase: Boolean = false): List { val lookup = if(lowercase) encodingScreencodeLowercase else encodingScreencodeUppercase return text.map{ - val screencode = lookup[it] ?: throw CompilerException("no Screencode character for '$it'") + val screencode = lookup[it] + if(screencode==null) { + val case = if(lowercase) "lower" else "upper" + throw CompilerException("no ${case}Screencode character for '$it'") + } screencode.toShort() } } diff --git a/il65/src/il65/functions/BuiltinFunctions.kt b/il65/src/il65/functions/BuiltinFunctions.kt index 788735ebd..a20899c3f 100644 --- a/il65/src/il65/functions/BuiltinFunctions.kt +++ b/il65/src/il65/functions/BuiltinFunctions.kt @@ -1,6 +1,8 @@ package il65.functions import il65.ast.* +import kotlin.math.abs +import kotlin.math.floor val BuiltIns = listOf( @@ -16,9 +18,9 @@ private fun oneDoubleArg(args: List, position: Position?, namespace if(args.size!=1) throw SyntaxError("built-in function requires one floating point argument", position) - val float = args[0].constValue(namespace)?.asFloat() + val float = args[0].constValue(namespace)?.asNumericValue?.toDouble() if(float!=null) { - val result = intOrFloatLiteral(function(float).toDouble(), args[0].position) + val result = numericLiteral(function(float), args[0].position) result.position = args[0].position return result } @@ -30,7 +32,7 @@ private fun oneDoubleArgOutputInt(args: List, position: Position?, if(args.size!=1) throw SyntaxError("built-in function requires one floating point argument", position) - val float = args[0].constValue(namespace)?.asFloat() + val float = args[0].constValue(namespace)?.asNumericValue?.toDouble() if(float!=null) { val result = LiteralValue(function(float).toInt()) result.position = args[0].position @@ -44,7 +46,7 @@ private fun oneIntArgOutputInt(args: List, position: Position?, nam if(args.size!=1) throw SyntaxError("built-in function requires one integer argument", position) - val integer = args[0].constValue(namespace)?.asInt() + val integer = args[0].constValue(namespace)?.asNumericValue?.toInt() if(integer!=null) { val result = LiteralValue(function(integer).toInt()) result.position = args[0].position @@ -54,31 +56,38 @@ private fun oneIntArgOutputInt(args: List, position: Position?, nam throw NotConstArgumentException() } -private fun nonScalarArgOutputNumber(args: List, position: Position?, namespace:INameScope, - function: (arg: Collection)->Number): LiteralValue { +private fun collectionArgOutputNumber(args: List, position: Position?, namespace:INameScope, + function: (arg: Collection)->Number): LiteralValue { if(args.size!=1) throw SyntaxError("builtin function requires one non-scalar argument", position) val iterable = args[0].constValue(namespace) if(iterable?.arrayvalue == null) throw SyntaxError("builtin function requires one non-scalar argument", position) - val constants = iterable.arrayvalue.map { it.constValue(namespace) } + val constants = iterable.arrayvalue.map { it.constValue(namespace)?.asNumericValue } if(constants.contains(null)) throw NotConstArgumentException() - val result = function(constants.map { it?.asFloat()!! }).toDouble() - return intOrFloatLiteral(result, args[0].position) + val result = function(constants.map { it!!.toDouble() }).toDouble() + val value = + if(result-floor(result) == 0.0) { + LiteralValue(result.toInt()) + } else { + LiteralValue(floatvalue = result) + } + value.position = args[0].position + return value } -private fun nonScalarArgOutputBoolean(args: List, position: Position?, namespace:INameScope, - function: (arg: Collection)->Boolean): LiteralValue { +private fun collectionArgOutputBoolean(args: List, position: Position?, namespace:INameScope, + function: (arg: Collection)->Boolean): LiteralValue { if(args.size!=1) throw SyntaxError("builtin function requires one non-scalar argument", position) val iterable = args[0].constValue(namespace) if(iterable?.arrayvalue == null) throw SyntaxError("builtin function requires one non-scalar argument", position) - val constants = iterable.arrayvalue.map { it.constValue(namespace) } + val constants = iterable.arrayvalue.map { it.constValue(namespace)?.asNumericValue } if(constants.contains(null)) throw NotConstArgumentException() - val result = function(constants.map { it?.asFloat()!! }) + val result = function(constants.map { it?.toDouble()!! }) return LiteralValue(if(result) 1 else 0) } @@ -124,8 +133,25 @@ fun builtinRad(args: List, position: Position?, namespace:INameScop fun builtinDeg(args: List, position: Position?, namespace:INameScope): LiteralValue = oneDoubleArg(args, position, namespace, Math::toDegrees) -fun builtinAbs(args: List, position: Position?, namespace:INameScope): LiteralValue - = oneDoubleArg(args, position, namespace, Math::abs) +fun builtinAbs(args: List, position: Position?, namespace:INameScope): 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) ?: throw NotConstArgumentException() + var number = constval.asNumericValue + val result = + if(number is Int || number is Byte || number is Short) { + number = number.toInt() + LiteralValue(intvalue = abs(number)) + } else if(number is Double) { + LiteralValue(floatvalue = abs(number.toDouble())) + } else { + throw SyntaxError("abs requires one numeric argument", position) + } + result.position = args[0].position + return result +} fun builtinLsl(args: List, position: Position?, namespace:INameScope): LiteralValue = oneIntArgOutputInt(args, position, namespace) { x: Int -> x shl 1 } @@ -134,33 +160,60 @@ fun builtinLsr(args: List, position: Position?, namespace:INameScop = oneIntArgOutputInt(args, position, namespace) { x: Int -> x ushr 1 } fun builtinMin(args: List, position: Position?, namespace:INameScope): LiteralValue - = nonScalarArgOutputNumber(args, position, namespace) { it.min()!! } + = collectionArgOutputNumber(args, position, namespace) { it.min()!! } fun builtinMax(args: List, position: Position?, namespace:INameScope): LiteralValue - = nonScalarArgOutputNumber(args, position, namespace) { it.max()!! } + = collectionArgOutputNumber(args, position, namespace) { it.max()!! } fun builtinSum(args: List, position: Position?, namespace:INameScope): LiteralValue - = nonScalarArgOutputNumber(args, position, namespace) { it.sum() } + = collectionArgOutputNumber(args, position, namespace) { it.sum() } -fun builtinAvg(args: List, position: Position?, namespace:INameScope): LiteralValue - = nonScalarArgOutputNumber(args, position, namespace) { it.average() } +fun builtinAvg(args: List, position: Position?, namespace:INameScope): LiteralValue { + if(args.size!=1) + throw SyntaxError("avg requires one non-scalar argument", position) + val iterable = args[0].constValue(namespace) + if(iterable?.arrayvalue == null) + throw SyntaxError("avg requires one non-scalar argument", position) + val constants = iterable.arrayvalue.map { it.constValue(namespace)?.asNumericValue } + if(constants.contains(null)) + throw NotConstArgumentException() + val result = (constants.map { it!!.toDouble() }).average() + val value = LiteralValue(floatvalue = result) + value.position = args[0].position + return value +} -fun builtinLen(args: List, position: Position?, namespace:INameScope): LiteralValue - = nonScalarArgOutputNumber(args, position, namespace) { it.size } +fun builtinLen(args: List, position: Position?, namespace:INameScope): LiteralValue { + if(args.size!=1) + throw SyntaxError("len requires one non-scalar argument", position) + val iterable = args[0].constValue(namespace) + if(iterable?.arrayvalue == null) + throw SyntaxError("len requires one non-scalar argument", position) + val constants = iterable.arrayvalue.map { it.constValue(namespace)?.asNumericValue } + if(constants.contains(null)) + throw NotConstArgumentException() + val result = (constants.map { it!!.toDouble() }).size + val value = LiteralValue(intvalue = result) + value.position = args[0].position + return value +} fun builtinAny(args: List, position: Position?, namespace:INameScope): LiteralValue - = nonScalarArgOutputBoolean(args, position, namespace) { it.any { v -> v != 0.0} } + = collectionArgOutputBoolean(args, position, namespace) { it.any { v -> v != 0.0} } fun builtinAll(args: List, position: Position?, namespace:INameScope): LiteralValue - = nonScalarArgOutputBoolean(args, position, namespace) { it.all { v -> v != 0.0} } + = collectionArgOutputBoolean(args, position, namespace) { it.all { v -> v != 0.0} } -private fun intOrFloatLiteral(value: Double, position: Position?): LiteralValue { - val intresult = value.toInt() - val result = if(value-intresult==0.0) - LiteralValue(intvalue = intresult) - else - LiteralValue(floatvalue = value) +private fun numericLiteral(value: Number, position: Position?): LiteralValue { + val result = when(value) { + is Int -> LiteralValue(intvalue = value.toInt()) + is Short -> LiteralValue(intvalue = value.toInt()) + is Byte -> LiteralValue(intvalue = value.toInt()) + is Double -> LiteralValue(floatvalue = value.toDouble()) + is Float -> LiteralValue(floatvalue = value.toDouble()) + else -> throw FatalAstException("invalid number type ${value::class}") + } result.position = position return result -} \ No newline at end of file +} diff --git a/il65/src/il65/optimizing/ExpressionOptimizer.kt b/il65/src/il65/optimizing/ExpressionOptimizer.kt index 037976293..9d1bb56d0 100644 --- a/il65/src/il65/optimizing/ExpressionOptimizer.kt +++ b/il65/src/il65/optimizing/ExpressionOptimizer.kt @@ -2,6 +2,7 @@ package il65.optimizing import il65.parser.ParsingFailedError import il65.ast.* +import il65.compiler.Petscii import kotlin.math.pow @@ -64,7 +65,33 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess errors.add(ExpressionException("recursive var declaration", decl.position)) return decl } - return super.process(decl) + + val result = super.process(decl) + + if(decl.type==VarDeclType.CONST || decl.type==VarDeclType.VAR) { + when { + decl.datatype == DataType.FLOAT -> { + // vardecl: for float vars, promote constant integer initialization values to floats + val literal = decl.value as? LiteralValue + if (literal != null && literal.isInteger) { + val newValue = LiteralValue(floatvalue = literal.intvalue!!.toDouble()) + newValue.position = literal.position + decl.value = newValue + } + } + decl.datatype == DataType.BYTE || decl.datatype == DataType.WORD -> { + // vardecl: for byte/word vars, convert char/string of length 1 initialization values to integer + val literal = decl.value as? LiteralValue + if (literal != null && literal.isString && literal.strvalue?.length == 1) { + val petscii = Petscii.encodePetscii(literal.strvalue)[0] + val newValue = LiteralValue(petscii.toInt()) + newValue.position = literal.position + decl.value = newValue + } + } + } + } + return result } /** diff --git a/il65/src/il65/optimizing/StatementsOptimizer.kt b/il65/src/il65/optimizing/StatementsOptimizer.kt index 0a57c78ad..df109dedc 100644 --- a/il65/src/il65/optimizing/StatementsOptimizer.kt +++ b/il65/src/il65/optimizing/StatementsOptimizer.kt @@ -64,7 +64,7 @@ class StatementOptimizer(private val globalNamespace: INameScope) : IAstProcesso super.process(ifStatement) val constvalue = ifStatement.condition.constValue(globalNamespace) if(constvalue!=null) { - return if(constvalue.asBoolean()) { + return if(constvalue.asBooleanValue){ // always true -> keep only if-part println("${ifStatement.position} Warning: condition is always true") AnonymousStatementList(ifStatement.parent, ifStatement.statements)