From b6ea33efa3f5cf8709ba4c017f1029cd59160792 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 16 Aug 2018 15:09:24 +0200 Subject: [PATCH] error reporting improved --- il65/antlr/il65.g4 | 2 +- il65/examples/test.ill | 9 +- il65/src/il65/Main.kt | 6 +- il65/src/il65/ast/AST.kt | 83 ++--- il65/src/il65/ast/AstChecker.kt | 14 +- il65/src/il65/ast/ImportedAstChecker.kt | 2 +- il65/src/il65/functions/BuiltinFunctions.kt | 97 +++--- .../il65/optimizing/ExpressionOptimizer.kt | 303 ++++++++++-------- .../il65/optimizing/StatementsOptimizer.kt | 2 - 9 files changed, 283 insertions(+), 235 deletions(-) diff --git a/il65/antlr/il65.g4 b/il65/antlr/il65.g4 index 86f726b36..5428f7d48 100644 --- a/il65/antlr/il65.g4 +++ b/il65/antlr/il65.g4 @@ -64,7 +64,7 @@ statement : | inlineasm | labeldef | returnstmt - // @todo forloop, whileloop, repeatloop, ifelse + // @todo forloop, whileloop, repeatloop ; labeldef : identifier ':' ; diff --git a/il65/examples/test.ill b/il65/examples/test.ill index cd3b543b7..2f0a1746b 100644 --- a/il65/examples/test.ill +++ b/il65/examples/test.ill @@ -8,13 +8,16 @@ memory byte derp = max($ffdd) memory byte derpA = abs(-2.5-0.5) memory byte derpB = max(1, 2.2, 4.4, 100) - memory byte cderp = min($ffdd) + memory byte cderp = min($ffdd)+ (1/1) + memory byte cderp1 = foobar + memory byte cderp2 = boo.bar.booz + memory byte cderp3 = main.doesnt_exist memory byte cderpA = min($ffdd, 10, 20, 30) memory byte cderpB = min(1, 2.2, 4.4, 100) memory byte derp2 = 2+$ffdd+round(10*sin(3)) - memory byte derp3 = round(100*sin(3)) + memory byte derp3 = round2(100*sin(3)) const byte hopla=55-33 - const byte hopla2=100+(-main.hopla) + const byte hopla2=100+(-main.hopla2) const byte hopla3=100+(-hopla) const byte hopla4 = 100-hopla const byte hopla1=main.hopla diff --git a/il65/src/il65/Main.kt b/il65/src/il65/Main.kt index d58fb6f1e..d014bb876 100644 --- a/il65/src/il65/Main.kt +++ b/il65/src/il65/Main.kt @@ -137,8 +137,10 @@ fun main(args: Array) { moduleAst.statements.forEach { println(it) } - } catch(sx: SyntaxError) { - sx.printError() +// } catch(sx: SyntaxError) { +// System.err.println(sx) +// } catch(ex: ExpressionException) { +// System.err.println(ex) } catch (px: ParsingFailedError) { System.err.println(px.message) } diff --git a/il65/src/il65/ast/AST.kt b/il65/src/il65/ast/AST.kt index 9603ab923..a5bdb9732 100644 --- a/il65/src/il65/ast/AST.kt +++ b/il65/src/il65/ast/AST.kt @@ -34,13 +34,21 @@ enum class Register { } -open class AstException(override var message: String) : Exception(message) -class ExpressionException(override var message: String) : AstException(message) +class FatalAstException (override var message: String) : Exception(message) -class SyntaxError(override var message: String, val position: Position?) : AstException(message) { - fun printError() { +open class AstException (override var message: String) : Exception(message) + +open class SyntaxError(override var message: String, val position: Position?) : AstException(message) { + override fun toString(): String { val location = position?.toString() ?: "" - System.err.println("$location $message") + return "$location Syntax error: $message" + } +} + +class ExpressionException(message: String, val position: Position?) : AstException(message) { + override fun toString(): String { + val location = position?.toString() ?: "" + return "$location Error: $message" } } @@ -97,7 +105,7 @@ interface IAstProcessor { fun process(ifStatement: IfStatement): IStatement { ifStatement.condition = ifStatement.condition.process(this) ifStatement.statements = ifStatement.statements.map { it.process(this) } - ifStatement.elsepart = ifStatement.elsepart?.map { it.process(this) } + ifStatement.elsepart = ifStatement.elsepart.map { it.process(this) } return ifStatement } @@ -134,13 +142,7 @@ interface INameScope { fun subScopes() = statements.filter { it is INameScope } .map { it as INameScope }.associate { it.name to it } fun definedNames() = statements.filter { it is Label || it is VarDecl } - .associate { - when(it) { - is Label -> it.name to it - is VarDecl -> it.name to it - else -> throw AstException("expected label or vardecl") - } - } + .associate {((it as? Label)?.name ?: (it as? VarDecl)?.name) to it } fun lookup(scopedName: List, statement: Node) : IStatement? { if(scopedName.size>1) { @@ -391,6 +393,7 @@ data class AssignTarget(val register: Register?, val identifier: Identifier?) : interface IExpression: Node { fun constValue(namespace: INameScope): LiteralValue? fun process(processor: IAstProcessor): IExpression + fun referencesIdentifier(name: String): Boolean } @@ -407,6 +410,7 @@ data class PrefixExpression(val operator: String, var expression: IExpression) : override fun constValue(namespace: INameScope): LiteralValue? = null override fun process(processor: IAstProcessor) = processor.process(this) + override fun referencesIdentifier(name: String) = expression.referencesIdentifier(name) } @@ -421,10 +425,11 @@ data class BinaryExpression(var left: IExpression, val operator: String, var rig } override fun constValue(namespace: INameScope): LiteralValue? { - throw ExpressionException("should have been optimized away before const value was asked") + throw ExpressionException("expression should have been optimized away into a single value, before const value was requested (this error is often caused by another)", position) } override fun process(processor: IAstProcessor) = processor.process(this) + override fun referencesIdentifier(name: String) = left.referencesIdentifier(name) || right.referencesIdentifier(name) } data class LiteralValue(val intvalue: Int? = null, @@ -433,6 +438,7 @@ data class LiteralValue(val intvalue: Int? = null, val arrayvalue: List? = null) : IExpression { override var position: Position? = null override var parent: Node? = null + override fun referencesIdentifier(name: String) = arrayvalue?.any { it.referencesIdentifier(name) } ?: false fun asInt(errorIfNotNumeric: Boolean=true): Int? { return when { @@ -486,6 +492,7 @@ data class RangeExpr(var from: IExpression, var to: IExpression) : IExpression { override fun constValue(namespace: INameScope): LiteralValue? = null override fun process(processor: IAstProcessor) = processor.process(this) + override fun referencesIdentifier(name: String): Boolean = from.referencesIdentifier(name) || to.referencesIdentifier(name) } @@ -499,6 +506,7 @@ data class RegisterExpr(val register: Register) : IExpression { override fun constValue(namespace: INameScope): LiteralValue? = null override fun process(processor: IAstProcessor) = this + override fun referencesIdentifier(name: String): Boolean = false } @@ -513,11 +521,10 @@ data class Identifier(val scopedName: List) : IExpression { override fun constValue(namespace: INameScope): LiteralValue? { val node = namespace.lookup(scopedName, this) ?: - throw SyntaxError("undefined symbol: ${scopedName.joinToString(".")}", position) // todo add to a list of errors instead + throw ExpressionException("undefined symbol: ${scopedName.joinToString(".")}", position) val vardecl = node as? VarDecl if(vardecl==null) { - // todo add to a list of errors instead - throw SyntaxError("name should be a constant, instead of: ${node::class.simpleName}", position) + throw ExpressionException("name should be a constant, instead of: ${node::class.simpleName}", position) } else if(vardecl.type!=VarDeclType.CONST) { return null } @@ -525,6 +532,7 @@ data class Identifier(val scopedName: List) : IExpression { } override fun process(processor: IAstProcessor) = processor.process(this) + override fun referencesIdentifier(name: String): Boolean = scopedName.last() == name // @todo is this correct all the time? } @@ -571,26 +579,27 @@ data class FunctionCall(override var location: Identifier, override var arglist: // if the function is a built-in function and the args are consts, should evaluate! if(location.scopedName.size>1) return null return when(location.scopedName[0]){ - "sin" -> builtin_sin(arglist, namespace) - "cos" -> builtin_cos(arglist, namespace) - "abs" -> builtin_abs(arglist, namespace) - "acos" -> builtin_acos(arglist, namespace) - "asin" -> builtin_asin(arglist, namespace) - "tan" -> builtin_tan(arglist, namespace) - "atan" -> builtin_atan(arglist, namespace) - "log" -> builtin_log(arglist, namespace) - "log10" -> builtin_log10(arglist, namespace) - "sqrt" -> builtin_sqrt(arglist, namespace) - "max" -> builtin_max(arglist, namespace) - "min" -> builtin_min(arglist, namespace) - "round" -> builtin_round(arglist, namespace) - "rad" -> builtin_rad(arglist, namespace) - "deg" -> builtin_deg(arglist, namespace) + "sin" -> builtin_sin(arglist, position, namespace) + "cos" -> builtin_cos(arglist, position, namespace) + "abs" -> builtin_abs(arglist, position, namespace) + "acos" -> builtin_acos(arglist, position, namespace) + "asin" -> builtin_asin(arglist, position, namespace) + "tan" -> builtin_tan(arglist, position, namespace) + "atan" -> builtin_atan(arglist, position, namespace) + "log" -> builtin_log(arglist, position, namespace) + "log10" -> builtin_log10(arglist, position, namespace) + "sqrt" -> builtin_sqrt(arglist, position, namespace) + "max" -> builtin_max(arglist, position, namespace) + "min" -> builtin_min(arglist, position, namespace) + "round" -> builtin_round(arglist, position, namespace) + "rad" -> builtin_rad(arglist, position, namespace) + "deg" -> builtin_deg(arglist, position, namespace) else -> null } } override fun process(processor: IAstProcessor) = processor.process(this) + override fun referencesIdentifier(name: String): Boolean = location.referencesIdentifier(name) || arglist.any{it.referencesIdentifier(name)} } @@ -708,7 +717,7 @@ private fun il65Parser.ModulestatementContext.toAst(withPosition: Boolean) : ISt val block = block()?.toAst(withPosition) if(block!=null) return block - throw UnsupportedOperationException(text) + throw FatalAstException(text) } @@ -820,7 +829,7 @@ private fun il65Parser.StatementContext.toAst(withPosition: Boolean) : IStatemen val asm = inlineasm()?.toAst(withPosition) if(asm!=null) return asm - throw UnsupportedOperationException(text) + throw FatalAstException(text) } private fun il65Parser.Functioncall_stmtContext.toAst(withPosition: Boolean): IStatement { @@ -952,7 +961,7 @@ private fun il65Parser.IntegerliteralContext.toAst(): Int { il65Parser.DEC_INTEGER -> text.toInt() il65Parser.HEX_INTEGER -> text.substring(1).toInt(16) il65Parser.BIN_INTEGER -> text.substring(1).toInt(2) - else -> throw UnsupportedOperationException(text) + else -> throw FatalAstException(terminal.text) } } @@ -1013,7 +1022,7 @@ private fun il65Parser.ExpressionContext.toAst(withPosition: Boolean) : IExpress if(childCount==3 && children[0].text=="(" && children[2].text==")") return expression(0).toAst(withPosition) // expression within ( ) - throw UnsupportedOperationException(text) + throw FatalAstException(text) } @@ -1040,7 +1049,7 @@ private fun il65Parser.FloatliteralContext.toAst() = text.toDouble() private fun il65Parser.BooleanliteralContext.toAst() = when(text) { "true" -> true "false" -> false - else -> throw UnsupportedOperationException(text) + else -> throw FatalAstException(text) } diff --git a/il65/src/il65/ast/AstChecker.kt b/il65/src/il65/ast/AstChecker.kt index 24f3c473a..9d135fb40 100644 --- a/il65/src/il65/ast/AstChecker.kt +++ b/il65/src/il65/ast/AstChecker.kt @@ -11,7 +11,7 @@ fun Module.checkValid(globalNamespace: INameScope) { this.process(checker) val checkResult = checker.result() checkResult.forEach { - it.printError() + System.err.println(it) } if(checkResult.isNotEmpty()) throw ParsingFailedError("There are ${checkResult.size} errors in module '$name'.") @@ -157,6 +157,14 @@ class AstChecker(private val globalNamespace: INameScope) : IAstProcessor { fun err(msg: String) { checkResult.add(SyntaxError(msg, decl.position)) } + + // the initializer value can't refer to the variable itself (recursive definition) + if(decl.value?.referencesIdentifier(decl.name) == true|| + decl.arrayspec?.x?.referencesIdentifier(decl.name) == true || + decl.arrayspec?.y?.referencesIdentifier(decl.name) == true) { + err("recursive var declaration") + } + when(decl.type) { VarDeclType.VAR, VarDeclType.CONST -> { when { @@ -176,7 +184,7 @@ class AstChecker(private val globalNamespace: INameScope) : IAstProcessor { VarDeclType.MEMORY -> { if(decl.value !is LiteralValue) // @todo normal error reporting - throw AstException("${decl.value?.position} value of memory var decl is not a literal (it is a ${decl.value!!::class.simpleName}).") + throw SyntaxError("value of memory var decl is not a literal (it is a ${decl.value!!::class.simpleName}).", decl.value?.position) val value = decl.value as LiteralValue if(value.intvalue==null || value.intvalue<0 || value.intvalue>65535) { @@ -263,7 +271,7 @@ class AstChecker(private val globalNamespace: INameScope) : IAstProcessor { if(directive.args.size!=1 || directive.args[0].name != "enable_floats") err("invalid option directive argument, expected enable_floats") } - else -> throw AstException("invalid directive ${directive.directive}") + else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position) } return super.process(directive) } diff --git a/il65/src/il65/ast/ImportedAstChecker.kt b/il65/src/il65/ast/ImportedAstChecker.kt index 2427cb1b9..330c7f3a0 100644 --- a/il65/src/il65/ast/ImportedAstChecker.kt +++ b/il65/src/il65/ast/ImportedAstChecker.kt @@ -11,7 +11,7 @@ fun Module.checkImportedValid() { this.process(checker) val result = checker.result() result.forEach { - it.printError() + System.err.println(it) } if(result.isNotEmpty()) throw ParsingFailedError("There are ${result.size} errors in imported module '$name'.") diff --git a/il65/src/il65/functions/BuiltinFunctions.kt b/il65/src/il65/functions/BuiltinFunctions.kt index 756139027..34fc2c9af 100644 --- a/il65/src/il65/functions/BuiltinFunctions.kt +++ b/il65/src/il65/functions/BuiltinFunctions.kt @@ -1,13 +1,10 @@ package il65.functions -import il65.ast.IExpression -import il65.ast.INameScope -import il65.ast.LiteralValue -import il65.ast.Position +import il65.ast.* -private fun oneDoubleArg(args: List, namespace: INameScope, function: (arg: Double)->Double): LiteralValue { +private fun oneDoubleArg(args: List, position: Position?, namespace:INameScope, function: (arg: Double)->Double): LiteralValue { if(args.size!=1) - throw UnsupportedOperationException("built-in function requires one floating point argument") + throw SyntaxError("built-in function requires one floating point argument", position) val float = args[0].constValue(namespace)?.asFloat() if(float!=null) { @@ -16,12 +13,12 @@ private fun oneDoubleArg(args: List, namespace: INameScope, functio return result } else - throw UnsupportedOperationException("built-in function requires floating point value as argument") + throw SyntaxError("built-in function requires floating point value as argument", position) } -private fun oneDoubleArgOutputInt(args: List, namespace: INameScope, function: (arg: Double)->Int): LiteralValue { +private fun oneDoubleArgOutputInt(args: List, position: Position?, namespace:INameScope, function: (arg: Double)->Int): LiteralValue { if(args.size!=1) - throw UnsupportedOperationException("built-in function requires one floating point argument") + throw SyntaxError("built-in function requires one floating point argument", position) val float = args[0].constValue(namespace)?.asFloat() if(float!=null) { @@ -30,86 +27,72 @@ private fun oneDoubleArgOutputInt(args: List, namespace: INameScope return result } else - throw UnsupportedOperationException("built-in function requires floating point value as argument") + throw SyntaxError("built-in function requires floating point value as argument", position) } -private fun twoDoubleArg(args: List, namespace: INameScope, function: (arg1: Double, arg2: Double)->Double): LiteralValue { - if(args.size!=2) - throw UnsupportedOperationException("built-in function requires two floating point arguments") - val float1 = args[0].constValue(namespace)?.asFloat() - val float2 = args[1].constValue(namespace)?.asFloat() - if(float1!=null && float2!=null) { - val result = LiteralValue(floatvalue = function(float1, float2)) - result.position = args[0].position - return result - } - else - throw UnsupportedOperationException("built-in function requires two floating point values as argument") -} +fun builtin_round(args: List, position: Position?, namespace:INameScope): LiteralValue + = oneDoubleArgOutputInt(args, position, namespace) { it -> Math.round(it).toInt() } -fun builtin_round(args: List, namespace: INameScope): LiteralValue - = oneDoubleArgOutputInt(args, namespace) { it -> Math.round(it).toInt() } +fun builtin_sin(args: List, position: Position?, namespace:INameScope): LiteralValue + = oneDoubleArg(args, position, namespace, Math::sin) -fun builtin_sin(args: List, namespace: INameScope): LiteralValue - = oneDoubleArg(args, namespace, Math::sin) +fun builtin_cos(args: List, position: Position?, namespace:INameScope): LiteralValue + = oneDoubleArg(args, position, namespace, Math::cos) -fun builtin_cos(args: List, namespace: INameScope): LiteralValue - = oneDoubleArg(args, namespace, Math::cos) +fun builtin_acos(args: List, position: Position?, namespace:INameScope): LiteralValue + = oneDoubleArg(args, position, namespace, Math::acos) -fun builtin_acos(args: List, namespace: INameScope): LiteralValue - = oneDoubleArg(args, namespace, Math::acos) +fun builtin_asin(args: List, position: Position?, namespace:INameScope): LiteralValue + = oneDoubleArg(args, position, namespace, Math::asin) -fun builtin_asin(args: List, namespace: INameScope): LiteralValue - = oneDoubleArg(args, namespace, Math::asin) +fun builtin_tan(args: List, position: Position?, namespace:INameScope): LiteralValue + = oneDoubleArg(args, position, namespace, Math::tan) -fun builtin_tan(args: List, namespace: INameScope): LiteralValue - = oneDoubleArg(args, namespace, Math::tan) +fun builtin_atan(args: List, position: Position?, namespace:INameScope): LiteralValue + = oneDoubleArg(args, position, namespace, Math::atan) -fun builtin_atan(args: List, namespace: INameScope): LiteralValue - = oneDoubleArg(args, namespace, Math::atan) +fun builtin_log(args: List, position: Position?, namespace:INameScope): LiteralValue + = oneDoubleArg(args, position, namespace, Math::log) -fun builtin_log(args: List, namespace: INameScope): LiteralValue - = oneDoubleArg(args, namespace, Math::log) +fun builtin_log10(args: List, position: Position?, namespace:INameScope): LiteralValue + = oneDoubleArg(args, position, namespace, Math::log10) -fun builtin_log10(args: List, namespace: INameScope): LiteralValue - = oneDoubleArg(args, namespace, Math::log10) +fun builtin_sqrt(args: List, position: Position?, namespace:INameScope): LiteralValue + = oneDoubleArg(args, position, namespace, Math::sqrt) -fun builtin_sqrt(args: List, namespace: INameScope): LiteralValue - = oneDoubleArg(args, namespace, Math::sqrt) +fun builtin_rad(args: List, position: Position?, namespace:INameScope): LiteralValue + = oneDoubleArg(args, position, namespace, Math::toRadians) -fun builtin_rad(args: List, namespace: INameScope): LiteralValue - = oneDoubleArg(args, namespace, Math::toRadians) +fun builtin_deg(args: List, position: Position?, namespace:INameScope): LiteralValue + = oneDoubleArg(args, position, namespace, Math::toDegrees) -fun builtin_deg(args: List, namespace: INameScope): LiteralValue - = oneDoubleArg(args, namespace, Math::toDegrees) - -fun builtin_abs(args: List, namespace: INameScope): LiteralValue { +fun builtin_abs(args: List, position: Position?, namespace:INameScope): LiteralValue { if(args.size!=1) - throw UnsupportedOperationException("built-in function abs requires one numeric argument") + throw SyntaxError("built-in function abs requires one numeric argument", position) val float = args[0].constValue(namespace)?.asFloat() if(float!=null) return IntOrFloatLiteral(Math.abs(float), args[0].position) else - throw UnsupportedOperationException("built-in function abs requires floating point value as argument") + throw SyntaxError("built-in function abs requires floating point value as argument", position) } -fun builtin_max(args: List, namespace: INameScope): LiteralValue { +fun builtin_max(args: List, position: Position?, namespace:INameScope): LiteralValue { if(args.isEmpty()) - throw UnsupportedOperationException("max requires at least one argument") + throw SyntaxError("max requires at least one argument", position) val constants = args.map { it.constValue(namespace) } if(constants.contains(null)) - throw UnsupportedOperationException("not all arguments to max are a constant value") + throw SyntaxError("not all arguments to max are a constant value", position) val result = constants.map { it?.asFloat()!! }.max() return IntOrFloatLiteral(result!!, args[0].position) } -fun builtin_min(args: List, namespace: INameScope): LiteralValue { +fun builtin_min(args: List, position: Position?, namespace:INameScope): LiteralValue { if(args.isEmpty()) - throw UnsupportedOperationException("min requires at least one argument") + throw SyntaxError("min requires at least one argument", position) val constants = args.map { it.constValue(namespace) } if(constants.contains(null)) - throw UnsupportedOperationException("not all arguments to min are a constant value") + throw SyntaxError("not all arguments to min are a constant value", position) val result = constants.map { it?.asFloat()!! }.min() return IntOrFloatLiteral(result!!, args[0].position) } diff --git a/il65/src/il65/optimizing/ExpressionOptimizer.kt b/il65/src/il65/optimizing/ExpressionOptimizer.kt index 7165b0316..3c264ac80 100644 --- a/il65/src/il65/optimizing/ExpressionOptimizer.kt +++ b/il65/src/il65/optimizing/ExpressionOptimizer.kt @@ -1,42 +1,72 @@ package il65.optimizing +import il65.ParsingFailedError import il65.ast.* import kotlin.math.pow fun Module.optimizeExpressions(globalNamespace: INameScope) { val optimizer = ExpressionOptimizer(globalNamespace) - this.process(optimizer) + try { + this.process(optimizer) + } catch (ax: AstException) { + optimizer.errors.add(ax) + } + if(optimizer.optimizationsDone==0) println("[${this.name}] 0 optimizations performed") - while(optimizer.optimizationsDone>0) { + while(optimizer.errors.isEmpty() && optimizer.optimizationsDone>0) { println("[${this.name}] ${optimizer.optimizationsDone} optimizations performed") - optimizer.reset() + optimizer.optimizationsDone = 0 this.process(optimizer) } - this.linkParents() // re-link in final configuration + + if(optimizer.errors.isNotEmpty()) { + optimizer.errors.forEach { System.err.println(it) } + throw ParsingFailedError("There are ${optimizer.errors.size} errors.") + } else { + this.linkParents() // re-link in final configuration + } } class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcessor { var optimizationsDone: Int = 0 - private set + var errors : MutableList = mutableListOf() - fun reset() { - optimizationsDone = 0 + + override fun process(decl: VarDecl): IStatement { + // the initializer value can't refer to the variable itself (recursive definition) + if(decl.value?.referencesIdentifier(decl.name) == true|| + decl.arrayspec?.x?.referencesIdentifier(decl.name) == true || + decl.arrayspec?.y?.referencesIdentifier(decl.name) == true) { + errors.add(ExpressionException("recursive var declaration", decl.position)) + return decl + } + return super.process(decl) } /** * replace identifiers that refer to const value, with the value itself */ override fun process(identifier: Identifier): IExpression { - return identifier.constValue(globalNamespace) ?: identifier + return try { + identifier.constValue(globalNamespace) ?: identifier + } catch (ax: AstException) { + errors.add(ax) + identifier + } } override fun process(functionCall: FunctionCall): IExpression { - super.process(functionCall) - return functionCall.constValue(globalNamespace) ?: functionCall + return try { + super.process(functionCall) + functionCall.constValue(globalNamespace) ?: functionCall + } catch (ax: AstException) { + errors.add(ax) + functionCall + } } /** @@ -45,48 +75,53 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess * For instance, the expression for "- 4.5" will be optimized into the float literal -4.5 */ override fun process(expr: PrefixExpression): IExpression { - super.process(expr) + return try { + super.process(expr) - val subexpr = expr.expression - if (subexpr is LiteralValue) { - // process prefixed literal values (such as -3, not true) - val result = when { - expr.operator == "+" -> subexpr - expr.operator == "-" -> when { - subexpr.intvalue != null -> { - optimizationsDone++ - LiteralValue(intvalue = -subexpr.intvalue) + val subexpr = expr.expression + if (subexpr is LiteralValue) { + // process prefixed literal values (such as -3, not true) + val result = when { + expr.operator == "+" -> subexpr + expr.operator == "-" -> when { + subexpr.intvalue != null -> { + optimizationsDone++ + LiteralValue(intvalue = -subexpr.intvalue) + } + subexpr.floatvalue != null -> { + optimizationsDone++ + LiteralValue(floatvalue = -subexpr.floatvalue) + } + else -> throw ExpressionException("can only take negative of int or float", subexpr.position) } - subexpr.floatvalue != null -> { - optimizationsDone++ - LiteralValue(floatvalue = -subexpr.floatvalue) + expr.operator == "~" -> when { + subexpr.intvalue != null -> { + optimizationsDone++ + LiteralValue(intvalue = subexpr.intvalue.inv()) + } + else -> throw ExpressionException("can only take bitwise inversion of int", subexpr.position) } - else -> throw UnsupportedOperationException("can only take negative of int or float") + expr.operator == "not" -> when { + subexpr.intvalue != null -> { + optimizationsDone++ + LiteralValue(intvalue = if (subexpr.intvalue == 0) 1 else 0) + } + subexpr.floatvalue != null -> { + optimizationsDone++ + LiteralValue(intvalue = if (subexpr.floatvalue == 0.0) 1 else 0) + } + else -> throw ExpressionException("can not take logical not of $subexpr", subexpr.position) + } + else -> throw ExpressionException(expr.operator, subexpr.position) } - expr.operator == "~" -> when { - subexpr.intvalue != null -> { - optimizationsDone++ - LiteralValue(intvalue = subexpr.intvalue.inv()) - } - else -> throw UnsupportedOperationException("can only take bitwise inversion of int") - } - expr.operator == "not" -> when { - subexpr.intvalue != null -> { - optimizationsDone++ - LiteralValue(intvalue = if (subexpr.intvalue == 0) 1 else 0) - } - subexpr.floatvalue != null -> { - optimizationsDone++ - LiteralValue(intvalue = if (subexpr.floatvalue == 0.0) 1 else 0) - } - else -> throw UnsupportedOperationException("can not take logical not of $subexpr") - } - else -> throw UnsupportedOperationException(expr.operator) + result.position = subexpr.position + return result } - result.position = subexpr.position - return result + return expr + } catch (ax: AstException) { + errors.add(ax) + expr } - return expr } /** @@ -95,53 +130,63 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess * For instance, "9 * (4 + 2)" will be optimized into the integer literal 54. */ override fun process(expr: BinaryExpression): IExpression { - super.process(expr) + return try { + super.process(expr) - val evaluator = ConstExprEvaluator() - val leftconst = expr.left.constValue(globalNamespace) - val rightconst = expr.right.constValue(globalNamespace) - return when { - leftconst != null && rightconst != null -> { - optimizationsDone++ - evaluator.evaluate(leftconst, expr.operator, rightconst) + val evaluator = ConstExprEvaluator() + val leftconst = expr.left.constValue(globalNamespace) + val rightconst = expr.right.constValue(globalNamespace) + return when { + leftconst != null && rightconst != null -> { + optimizationsDone++ + evaluator.evaluate(leftconst, expr.operator, rightconst) + } + else -> expr } - else -> expr + } catch (ax: AstException) { + errors.add(ax) + expr } } override fun process(range: RangeExpr): IExpression { - super.process(range) - val from = range.from.constValue(globalNamespace) - val to = range.to.constValue(globalNamespace) - if(from!=null && to != null) { - when { - from.intvalue!=null && to.intvalue!=null -> { - // int range - val rangevalue = from.intvalue.rangeTo(to.intvalue) - if(rangevalue.last-rangevalue.first > 65535) { - throw AstException("amount of values in range exceeds 65535, at ${range.position}") + return try { + super.process(range) + val from = range.from.constValue(globalNamespace) + val to = range.to.constValue(globalNamespace) + if (from != null && to != null) { + when { + from.intvalue != null && to.intvalue != null -> { + // int range + val rangevalue = from.intvalue.rangeTo(to.intvalue) + if (rangevalue.last - rangevalue.first > 65535) { + throw ExpressionException("amount of values in range exceeds 65535", range.position) + } + return LiteralValue(arrayvalue = rangevalue.map { + val v = LiteralValue(intvalue = it) + v.position = range.position + v.parent = range.parent + v + }) } - return LiteralValue(arrayvalue = rangevalue.map { - val v = LiteralValue(intvalue=it) - v.position=range.position - v.parent=range.parent - v - }) - } - from.strvalue!=null && to.strvalue!=null -> { - // char range - val rangevalue = from.strvalue[0].rangeTo(to.strvalue[0]) - if(rangevalue.last-rangevalue.first > 65535) { - throw AstException("amount of characters in range exceeds 65535, at ${range.position}") + from.strvalue != null && to.strvalue != null -> { + // char range + val rangevalue = from.strvalue[0].rangeTo(to.strvalue[0]) + if (rangevalue.last - rangevalue.first > 65535) { + throw ExpressionException("amount of characters in range exceeds 65535", range.position) + } + val newval = LiteralValue(strvalue = rangevalue.toList().joinToString("")) + newval.position = range.position + newval.parent = range.parent + return newval } - val newval = LiteralValue(strvalue = rangevalue.toList().joinToString("")) - newval.position = range.position - newval.parent = range.parent - return newval } } + return range + } catch (ax: AstException) { + errors.add(ax) + range } - return range } } @@ -171,7 +216,7 @@ class ConstExprEvaluator { ">=" -> comparegreaterequal(left, right) "==" -> compareequal(left, right) "!=" -> comparenotequal(left, right) - else -> throw AstException("const evaluation for invalid operator $operator") + else -> throw FatalAstException("const evaluation for invalid operator $operator") } } @@ -188,14 +233,14 @@ class ConstExprEvaluator { left.floatvalue!=null -> left.floatvalue left.strvalue!=null -> left.strvalue left.arrayvalue!=null -> left.arrayvalue - else -> throw AstException("missing literal value") + else -> throw FatalAstException("missing literal value") } val rightvalue: Any = when { right.intvalue!=null -> right.intvalue right.floatvalue!=null -> right.floatvalue right.strvalue!=null -> right.strvalue right.arrayvalue!=null -> right.arrayvalue - else -> throw AstException("missing literal value") + else -> throw FatalAstException("missing literal value") } val litval = LiteralValue(intvalue = if (leftvalue == rightvalue) 1 else 0) litval.position = left.position @@ -210,16 +255,16 @@ class ConstExprEvaluator { intvalue = if (left.intvalue >= right.intvalue) 1 else 0) right.floatvalue!=null -> LiteralValue( intvalue = if (left.intvalue >= right.floatvalue) 1 else 0) - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } left.floatvalue!=null -> when { right.intvalue!=null -> LiteralValue( intvalue = if (left.floatvalue >= right.intvalue) 1 else 0) right.floatvalue!=null -> LiteralValue( intvalue = if (left.floatvalue >= right.floatvalue) 1 else 0) - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } litval.position = left.position return litval @@ -233,16 +278,16 @@ class ConstExprEvaluator { intvalue = if (left.intvalue <= right.intvalue) 1 else 0) right.floatvalue!=null -> LiteralValue( intvalue = if (left.intvalue <= right.floatvalue) 1 else 0) - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } left.floatvalue!=null -> when { right.intvalue!=null -> LiteralValue( intvalue = if (left.floatvalue <= right.intvalue) 1 else 0) right.floatvalue!=null -> LiteralValue( intvalue = if (left.floatvalue <= right.floatvalue) 1 else 0) - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } litval.position = left.position return litval @@ -270,16 +315,16 @@ class ConstExprEvaluator { intvalue = if ((left.intvalue != 0).xor(right.intvalue != 0)) 1 else 0) right.floatvalue!=null -> LiteralValue( intvalue = if ((left.intvalue != 0).xor(right.floatvalue != 0.0)) 1 else 0) - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } left.floatvalue!=null -> when { right.intvalue!=null -> LiteralValue( intvalue = if ((left.floatvalue != 0.0).xor(right.intvalue != 0)) 1 else 0) right.floatvalue!=null -> LiteralValue( intvalue = if ((left.floatvalue != 0.0).xor(right.floatvalue != 0.0)) 1 else 0) - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } litval.position = left.position return litval @@ -293,16 +338,16 @@ class ConstExprEvaluator { intvalue = if (left.intvalue != 0 || right.intvalue != 0) 1 else 0) right.floatvalue!=null -> LiteralValue( intvalue = if (left.intvalue != 0 || right.floatvalue != 0.0) 1 else 0) - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } left.floatvalue!=null -> when { right.intvalue!=null -> LiteralValue( intvalue = if (left.floatvalue != 0.0 || right.intvalue != 0) 1 else 0) right.floatvalue!=null -> LiteralValue( intvalue = if (left.floatvalue != 0.0 || right.floatvalue != 0.0) 1 else 0) - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } litval.position = left.position return litval @@ -316,16 +361,16 @@ class ConstExprEvaluator { intvalue = if (left.intvalue != 0 && right.intvalue != 0) 1 else 0) right.floatvalue!=null -> LiteralValue( intvalue = if (left.intvalue != 0 && right.floatvalue != 0.0) 1 else 0) - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } left.floatvalue!=null -> when { right.intvalue!=null -> LiteralValue( intvalue = if (left.floatvalue != 0.0 && right.intvalue != 0) 1 else 0) right.floatvalue!=null -> LiteralValue( intvalue = if (left.floatvalue != 0.0 && right.floatvalue != 0.0) 1 else 0) - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } litval.position = left.position return litval @@ -337,7 +382,7 @@ class ConstExprEvaluator { litval.position = left.position return litval } - throw ExpressionException("cannot calculate $left ^ $right") + throw ExpressionException("cannot calculate $left ^ $right", left.position) } private fun bitwiseor(left: LiteralValue, right: LiteralValue): LiteralValue { @@ -346,7 +391,7 @@ class ConstExprEvaluator { litval.position = left.position return litval } - throw ExpressionException("cannot calculate $left | $right") + throw ExpressionException("cannot calculate $left | $right", left.position) } private fun bitwiseand(left: LiteralValue, right: LiteralValue): LiteralValue { @@ -355,15 +400,15 @@ class ConstExprEvaluator { litval.position = left.position return litval } - throw ExpressionException("cannot calculate $left & $right") + throw ExpressionException("cannot calculate $left & $right", left.position) } private fun rotateright(left: LiteralValue, right: LiteralValue): LiteralValue { - throw ExpressionException("ror not possible on literal values") + throw ExpressionException("ror not possible on literal values", left.position) } private fun rotateleft(left: LiteralValue, right: LiteralValue): LiteralValue { - throw ExpressionException("rol not possible on literal values") + throw ExpressionException("rol not possible on literal values", left.position) } private fun shiftright(left: LiteralValue, right: LiteralValue): LiteralValue { @@ -372,7 +417,7 @@ class ConstExprEvaluator { litval.position = left.position return litval } - throw ExpressionException("cannot calculate $left >> $right") + throw ExpressionException("cannot calculate $left >> $right", left.position) } private fun shiftleft(left: LiteralValue, right: LiteralValue): LiteralValue { @@ -381,7 +426,7 @@ class ConstExprEvaluator { litval.position = left.position return litval } - throw ExpressionException("cannot calculate $left << $right") + throw ExpressionException("cannot calculate $left << $right", left.position) } private fun power(left: LiteralValue, right: LiteralValue): LiteralValue { @@ -390,14 +435,14 @@ class ConstExprEvaluator { left.intvalue!=null -> when { right.intvalue!=null -> LiteralValue(intvalue = left.intvalue.toDouble().pow(right.intvalue).toInt()) right.floatvalue!=null -> LiteralValue(floatvalue = left.intvalue.toDouble().pow(right.floatvalue)) - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } left.floatvalue!=null -> when { right.intvalue!=null -> LiteralValue(floatvalue = left.floatvalue.pow(right.intvalue)) right.floatvalue!=null -> LiteralValue(floatvalue = left.floatvalue.pow(right.floatvalue)) - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } litval.position = left.position return litval @@ -409,14 +454,14 @@ class ConstExprEvaluator { left.intvalue!=null -> when { right.intvalue!=null -> LiteralValue(intvalue = left.intvalue + right.intvalue) right.floatvalue!=null -> LiteralValue(floatvalue = left.intvalue + right.floatvalue) - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } left.floatvalue!=null -> when { right.intvalue!=null -> LiteralValue(floatvalue = left.floatvalue + right.intvalue) right.floatvalue!=null -> LiteralValue(floatvalue = left.floatvalue + right.floatvalue) - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } litval.position = left.position return litval @@ -428,14 +473,14 @@ class ConstExprEvaluator { left.intvalue!=null -> when { right.intvalue!=null -> LiteralValue(intvalue = left.intvalue - right.intvalue) right.floatvalue!=null -> LiteralValue(floatvalue = left.intvalue - right.floatvalue) - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } left.floatvalue!=null -> when { right.intvalue!=null -> LiteralValue(floatvalue = left.floatvalue - right.intvalue) right.floatvalue!=null -> LiteralValue(floatvalue = left.floatvalue - right.floatvalue) - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } litval.position = left.position return litval @@ -448,24 +493,24 @@ class ConstExprEvaluator { right.intvalue!=null -> LiteralValue(intvalue = left.intvalue * right.intvalue) right.floatvalue!=null -> LiteralValue(floatvalue = left.intvalue * right.floatvalue) right.strvalue!=null -> { - if(right.strvalue.length * left.intvalue > 65535) throw ExpressionException("string too large") + if(right.strvalue.length * left.intvalue > 65535) throw ExpressionException("string too large", left.position) LiteralValue(strvalue = right.strvalue.repeat(left.intvalue)) } - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } left.floatvalue!=null -> when { right.intvalue!=null -> LiteralValue(floatvalue = left.floatvalue * right.intvalue) right.floatvalue!=null -> LiteralValue(floatvalue = left.floatvalue * right.floatvalue) - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } left.strvalue!=null -> when { right.intvalue!=null -> { - if(left.strvalue.length * right.intvalue > 65535) throw ExpressionException("string too large") + if(left.strvalue.length * right.intvalue > 65535) throw ExpressionException("string too large", left.position) LiteralValue(strvalue = left.strvalue.repeat(right.intvalue)) } - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } litval.position = left.position return litval @@ -476,27 +521,27 @@ class ConstExprEvaluator { val litval = when { left.intvalue!=null -> when { right.intvalue!=null -> { - if(right.intvalue==0) throw ExpressionException("attempt to divide by zero") + if(right.intvalue==0) throw ExpressionException("attempt to divide by zero", left.position) LiteralValue(intvalue = left.intvalue / right.intvalue) } right.floatvalue!=null -> { - if(right.floatvalue==0.0) throw ExpressionException("attempt to divide by zero") + if(right.floatvalue==0.0) throw ExpressionException("attempt to divide by zero", left.position) LiteralValue(floatvalue = left.intvalue / right.floatvalue) } - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } left.floatvalue!=null -> when { right.intvalue!=null -> { - if(right.intvalue==0) throw ExpressionException("attempt to divide by zero") + if(right.intvalue==0) throw ExpressionException("attempt to divide by zero", left.position) LiteralValue(floatvalue = left.floatvalue / right.intvalue) } right.floatvalue!=null -> { - if(right.floatvalue==0.0) throw ExpressionException("attempt to divide by zero") + if(right.floatvalue==0.0) throw ExpressionException("attempt to divide by zero", left.position) LiteralValue(floatvalue = left.floatvalue / right.floatvalue) } - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } - else -> throw ExpressionException(error) + else -> throw ExpressionException(error, left.position) } litval.position = left.position return litval diff --git a/il65/src/il65/optimizing/StatementsOptimizer.kt b/il65/src/il65/optimizing/StatementsOptimizer.kt index bcafcac93..c387e6fc1 100644 --- a/il65/src/il65/optimizing/StatementsOptimizer.kt +++ b/il65/src/il65/optimizing/StatementsOptimizer.kt @@ -1,7 +1,6 @@ package il65.optimizing import il65.ast.* -import kotlin.math.pow fun Module.optimizeStatements(globalNamespace: INameScope) { @@ -31,7 +30,6 @@ class StatementOptimizer(private val globalNamespace: INameScope) : IAstProcesso super.process(ifStatement) val constvalue = ifStatement.condition.constValue(globalNamespace) if(constvalue!=null) { - val statements: List return if(constvalue.asBoolean()) { // always true -> keep only if-part println("${ifStatement.position} Warning: condition is always true")