error reporting improved

This commit is contained in:
Irmen de Jong 2018-08-16 15:09:24 +02:00
parent 50bbbc67e2
commit b6ea33efa3
9 changed files with 283 additions and 235 deletions

View File

@ -64,7 +64,7 @@ statement :
| inlineasm | inlineasm
| labeldef | labeldef
| returnstmt | returnstmt
// @todo forloop, whileloop, repeatloop, ifelse // @todo forloop, whileloop, repeatloop
; ;
labeldef : identifier ':' ; labeldef : identifier ':' ;

View File

@ -8,13 +8,16 @@
memory byte derp = max($ffdd) memory byte derp = max($ffdd)
memory byte derpA = abs(-2.5-0.5) memory byte derpA = abs(-2.5-0.5)
memory byte derpB = max(1, 2.2, 4.4, 100) 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 cderpA = min($ffdd, 10, 20, 30)
memory byte cderpB = min(1, 2.2, 4.4, 100) memory byte cderpB = min(1, 2.2, 4.4, 100)
memory byte derp2 = 2+$ffdd+round(10*sin(3)) 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 hopla=55-33
const byte hopla2=100+(-main.hopla) const byte hopla2=100+(-main.hopla2)
const byte hopla3=100+(-hopla) const byte hopla3=100+(-hopla)
const byte hopla4 = 100-hopla const byte hopla4 = 100-hopla
const byte hopla1=main.hopla const byte hopla1=main.hopla

View File

@ -137,8 +137,10 @@ fun main(args: Array<String>) {
moduleAst.statements.forEach { moduleAst.statements.forEach {
println(it) println(it)
} }
} catch(sx: SyntaxError) { // } catch(sx: SyntaxError) {
sx.printError() // System.err.println(sx)
// } catch(ex: ExpressionException) {
// System.err.println(ex)
} catch (px: ParsingFailedError) { } catch (px: ParsingFailedError) {
System.err.println(px.message) System.err.println(px.message)
} }

View File

@ -34,13 +34,21 @@ enum class Register {
} }
open class AstException(override var message: String) : Exception(message) class FatalAstException (override var message: String) : Exception(message)
class ExpressionException(override var message: String) : AstException(message)
class SyntaxError(override var message: String, val position: Position?) : AstException(message) { open class AstException (override var message: String) : Exception(message)
fun printError() {
open class SyntaxError(override var message: String, val position: Position?) : AstException(message) {
override fun toString(): String {
val location = position?.toString() ?: "" 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 { fun process(ifStatement: IfStatement): IStatement {
ifStatement.condition = ifStatement.condition.process(this) ifStatement.condition = ifStatement.condition.process(this)
ifStatement.statements = ifStatement.statements.map { it.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 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 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 } fun definedNames() = statements.filter { it is Label || it is VarDecl }
.associate { .associate {((it as? Label)?.name ?: (it as? VarDecl)?.name) to it }
when(it) {
is Label -> it.name to it
is VarDecl -> it.name to it
else -> throw AstException("expected label or vardecl")
}
}
fun lookup(scopedName: List<String>, statement: Node) : IStatement? { fun lookup(scopedName: List<String>, statement: Node) : IStatement? {
if(scopedName.size>1) { if(scopedName.size>1) {
@ -391,6 +393,7 @@ data class AssignTarget(val register: Register?, val identifier: Identifier?) :
interface IExpression: Node { interface IExpression: Node {
fun constValue(namespace: INameScope): LiteralValue? fun constValue(namespace: INameScope): LiteralValue?
fun process(processor: IAstProcessor): IExpression 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 constValue(namespace: INameScope): LiteralValue? = null
override fun process(processor: IAstProcessor) = processor.process(this) 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? { 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 process(processor: IAstProcessor) = processor.process(this)
override fun referencesIdentifier(name: String) = left.referencesIdentifier(name) || right.referencesIdentifier(name)
} }
data class LiteralValue(val intvalue: Int? = null, data class LiteralValue(val intvalue: Int? = null,
@ -433,6 +438,7 @@ data class LiteralValue(val intvalue: Int? = null,
val arrayvalue: List<IExpression>? = null) : IExpression { val arrayvalue: List<IExpression>? = null) : IExpression {
override var position: Position? = null override var position: Position? = null
override var parent: Node? = null override var parent: Node? = null
override fun referencesIdentifier(name: String) = arrayvalue?.any { it.referencesIdentifier(name) } ?: false
fun asInt(errorIfNotNumeric: Boolean=true): Int? { fun asInt(errorIfNotNumeric: Boolean=true): Int? {
return when { 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 constValue(namespace: INameScope): LiteralValue? = null
override fun process(processor: IAstProcessor) = processor.process(this) 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 constValue(namespace: INameScope): LiteralValue? = null
override fun process(processor: IAstProcessor) = this override fun process(processor: IAstProcessor) = this
override fun referencesIdentifier(name: String): Boolean = false
} }
@ -513,11 +521,10 @@ data class Identifier(val scopedName: List<String>) : IExpression {
override fun constValue(namespace: INameScope): LiteralValue? { override fun constValue(namespace: INameScope): LiteralValue? {
val node = namespace.lookup(scopedName, this) 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 val vardecl = node as? VarDecl
if(vardecl==null) { if(vardecl==null) {
// todo add to a list of errors instead throw ExpressionException("name should be a constant, instead of: ${node::class.simpleName}", position)
throw SyntaxError("name should be a constant, instead of: ${node::class.simpleName}", position)
} else if(vardecl.type!=VarDeclType.CONST) { } else if(vardecl.type!=VarDeclType.CONST) {
return null return null
} }
@ -525,6 +532,7 @@ data class Identifier(val scopedName: List<String>) : IExpression {
} }
override fun process(processor: IAstProcessor) = processor.process(this) 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 the function is a built-in function and the args are consts, should evaluate!
if(location.scopedName.size>1) return null if(location.scopedName.size>1) return null
return when(location.scopedName[0]){ return when(location.scopedName[0]){
"sin" -> builtin_sin(arglist, namespace) "sin" -> builtin_sin(arglist, position, namespace)
"cos" -> builtin_cos(arglist, namespace) "cos" -> builtin_cos(arglist, position, namespace)
"abs" -> builtin_abs(arglist, namespace) "abs" -> builtin_abs(arglist, position, namespace)
"acos" -> builtin_acos(arglist, namespace) "acos" -> builtin_acos(arglist, position, namespace)
"asin" -> builtin_asin(arglist, namespace) "asin" -> builtin_asin(arglist, position, namespace)
"tan" -> builtin_tan(arglist, namespace) "tan" -> builtin_tan(arglist, position, namespace)
"atan" -> builtin_atan(arglist, namespace) "atan" -> builtin_atan(arglist, position, namespace)
"log" -> builtin_log(arglist, namespace) "log" -> builtin_log(arglist, position, namespace)
"log10" -> builtin_log10(arglist, namespace) "log10" -> builtin_log10(arglist, position, namespace)
"sqrt" -> builtin_sqrt(arglist, namespace) "sqrt" -> builtin_sqrt(arglist, position, namespace)
"max" -> builtin_max(arglist, namespace) "max" -> builtin_max(arglist, position, namespace)
"min" -> builtin_min(arglist, namespace) "min" -> builtin_min(arglist, position, namespace)
"round" -> builtin_round(arglist, namespace) "round" -> builtin_round(arglist, position, namespace)
"rad" -> builtin_rad(arglist, namespace) "rad" -> builtin_rad(arglist, position, namespace)
"deg" -> builtin_deg(arglist, namespace) "deg" -> builtin_deg(arglist, position, namespace)
else -> null else -> null
} }
} }
override fun process(processor: IAstProcessor) = processor.process(this) 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) val block = block()?.toAst(withPosition)
if(block!=null) return block 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) val asm = inlineasm()?.toAst(withPosition)
if(asm!=null) return asm if(asm!=null) return asm
throw UnsupportedOperationException(text) throw FatalAstException(text)
} }
private fun il65Parser.Functioncall_stmtContext.toAst(withPosition: Boolean): IStatement { 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.DEC_INTEGER -> text.toInt()
il65Parser.HEX_INTEGER -> text.substring(1).toInt(16) il65Parser.HEX_INTEGER -> text.substring(1).toInt(16)
il65Parser.BIN_INTEGER -> text.substring(1).toInt(2) 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==")") if(childCount==3 && children[0].text=="(" && children[2].text==")")
return expression(0).toAst(withPosition) // expression within ( ) 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) { private fun il65Parser.BooleanliteralContext.toAst() = when(text) {
"true" -> true "true" -> true
"false" -> false "false" -> false
else -> throw UnsupportedOperationException(text) else -> throw FatalAstException(text)
} }

View File

@ -11,7 +11,7 @@ fun Module.checkValid(globalNamespace: INameScope) {
this.process(checker) this.process(checker)
val checkResult = checker.result() val checkResult = checker.result()
checkResult.forEach { checkResult.forEach {
it.printError() System.err.println(it)
} }
if(checkResult.isNotEmpty()) if(checkResult.isNotEmpty())
throw ParsingFailedError("There are ${checkResult.size} errors in module '$name'.") 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) { fun err(msg: String) {
checkResult.add(SyntaxError(msg, decl.position)) 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) { when(decl.type) {
VarDeclType.VAR, VarDeclType.CONST -> { VarDeclType.VAR, VarDeclType.CONST -> {
when { when {
@ -176,7 +184,7 @@ class AstChecker(private val globalNamespace: INameScope) : IAstProcessor {
VarDeclType.MEMORY -> { VarDeclType.MEMORY -> {
if(decl.value !is LiteralValue) if(decl.value !is LiteralValue)
// @todo normal error reporting // @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 val value = decl.value as LiteralValue
if(value.intvalue==null || value.intvalue<0 || value.intvalue>65535) { 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") if(directive.args.size!=1 || directive.args[0].name != "enable_floats")
err("invalid option directive argument, expected 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) return super.process(directive)
} }

View File

@ -11,7 +11,7 @@ fun Module.checkImportedValid() {
this.process(checker) this.process(checker)
val result = checker.result() val result = checker.result()
result.forEach { result.forEach {
it.printError() System.err.println(it)
} }
if(result.isNotEmpty()) if(result.isNotEmpty())
throw ParsingFailedError("There are ${result.size} errors in imported module '$name'.") throw ParsingFailedError("There are ${result.size} errors in imported module '$name'.")

View File

@ -1,13 +1,10 @@
package il65.functions package il65.functions
import il65.ast.IExpression import il65.ast.*
import il65.ast.INameScope
import il65.ast.LiteralValue
import il65.ast.Position
private fun oneDoubleArg(args: List<IExpression>, namespace: INameScope, function: (arg: Double)->Double): LiteralValue { private fun oneDoubleArg(args: List<IExpression>, position: Position?, namespace:INameScope, function: (arg: Double)->Double): LiteralValue {
if(args.size!=1) 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() val float = args[0].constValue(namespace)?.asFloat()
if(float!=null) { if(float!=null) {
@ -16,12 +13,12 @@ private fun oneDoubleArg(args: List<IExpression>, namespace: INameScope, functio
return result return result
} }
else 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<IExpression>, namespace: INameScope, function: (arg: Double)->Int): LiteralValue { private fun oneDoubleArgOutputInt(args: List<IExpression>, position: Position?, namespace:INameScope, function: (arg: Double)->Int): LiteralValue {
if(args.size!=1) 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() val float = args[0].constValue(namespace)?.asFloat()
if(float!=null) { if(float!=null) {
@ -30,86 +27,72 @@ private fun oneDoubleArgOutputInt(args: List<IExpression>, namespace: INameScope
return result return result
} }
else 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<IExpression>, 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() fun builtin_round(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
val float2 = args[1].constValue(namespace)?.asFloat() = oneDoubleArgOutputInt(args, position, namespace) { it -> Math.round(it).toInt() }
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<IExpression>, namespace: INameScope): LiteralValue fun builtin_sin(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArgOutputInt(args, namespace) { it -> Math.round(it).toInt() } = oneDoubleArg(args, position, namespace, Math::sin)
fun builtin_sin(args: List<IExpression>, namespace: INameScope): LiteralValue fun builtin_cos(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::sin) = oneDoubleArg(args, position, namespace, Math::cos)
fun builtin_cos(args: List<IExpression>, namespace: INameScope): LiteralValue fun builtin_acos(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::cos) = oneDoubleArg(args, position, namespace, Math::acos)
fun builtin_acos(args: List<IExpression>, namespace: INameScope): LiteralValue fun builtin_asin(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::acos) = oneDoubleArg(args, position, namespace, Math::asin)
fun builtin_asin(args: List<IExpression>, namespace: INameScope): LiteralValue fun builtin_tan(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::asin) = oneDoubleArg(args, position, namespace, Math::tan)
fun builtin_tan(args: List<IExpression>, namespace: INameScope): LiteralValue fun builtin_atan(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::tan) = oneDoubleArg(args, position, namespace, Math::atan)
fun builtin_atan(args: List<IExpression>, namespace: INameScope): LiteralValue fun builtin_log(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::atan) = oneDoubleArg(args, position, namespace, Math::log)
fun builtin_log(args: List<IExpression>, namespace: INameScope): LiteralValue fun builtin_log10(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::log) = oneDoubleArg(args, position, namespace, Math::log10)
fun builtin_log10(args: List<IExpression>, namespace: INameScope): LiteralValue fun builtin_sqrt(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::log10) = oneDoubleArg(args, position, namespace, Math::sqrt)
fun builtin_sqrt(args: List<IExpression>, namespace: INameScope): LiteralValue fun builtin_rad(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::sqrt) = oneDoubleArg(args, position, namespace, Math::toRadians)
fun builtin_rad(args: List<IExpression>, namespace: INameScope): LiteralValue fun builtin_deg(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::toRadians) = oneDoubleArg(args, position, namespace, Math::toDegrees)
fun builtin_deg(args: List<IExpression>, namespace: INameScope): LiteralValue fun builtin_abs(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue {
= oneDoubleArg(args, namespace, Math::toDegrees)
fun builtin_abs(args: List<IExpression>, namespace: INameScope): LiteralValue {
if(args.size!=1) 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() val float = args[0].constValue(namespace)?.asFloat()
if(float!=null) if(float!=null)
return IntOrFloatLiteral(Math.abs(float), args[0].position) return IntOrFloatLiteral(Math.abs(float), args[0].position)
else 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<IExpression>, namespace: INameScope): LiteralValue { fun builtin_max(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue {
if(args.isEmpty()) 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) } val constants = args.map { it.constValue(namespace) }
if(constants.contains(null)) 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() val result = constants.map { it?.asFloat()!! }.max()
return IntOrFloatLiteral(result!!, args[0].position) return IntOrFloatLiteral(result!!, args[0].position)
} }
fun builtin_min(args: List<IExpression>, namespace: INameScope): LiteralValue { fun builtin_min(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue {
if(args.isEmpty()) 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) } val constants = args.map { it.constValue(namespace) }
if(constants.contains(null)) 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() val result = constants.map { it?.asFloat()!! }.min()
return IntOrFloatLiteral(result!!, args[0].position) return IntOrFloatLiteral(result!!, args[0].position)
} }

View File

@ -1,42 +1,72 @@
package il65.optimizing package il65.optimizing
import il65.ParsingFailedError
import il65.ast.* import il65.ast.*
import kotlin.math.pow import kotlin.math.pow
fun Module.optimizeExpressions(globalNamespace: INameScope) { fun Module.optimizeExpressions(globalNamespace: INameScope) {
val optimizer = ExpressionOptimizer(globalNamespace) val optimizer = ExpressionOptimizer(globalNamespace)
this.process(optimizer) try {
this.process(optimizer)
} catch (ax: AstException) {
optimizer.errors.add(ax)
}
if(optimizer.optimizationsDone==0) if(optimizer.optimizationsDone==0)
println("[${this.name}] 0 optimizations performed") println("[${this.name}] 0 optimizations performed")
while(optimizer.optimizationsDone>0) { while(optimizer.errors.isEmpty() && optimizer.optimizationsDone>0) {
println("[${this.name}] ${optimizer.optimizationsDone} optimizations performed") println("[${this.name}] ${optimizer.optimizationsDone} optimizations performed")
optimizer.reset() optimizer.optimizationsDone = 0
this.process(optimizer) 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 { class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcessor {
var optimizationsDone: Int = 0 var optimizationsDone: Int = 0
private set var errors : MutableList<AstException> = 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 * replace identifiers that refer to const value, with the value itself
*/ */
override fun process(identifier: Identifier): IExpression { 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 { override fun process(functionCall: FunctionCall): IExpression {
super.process(functionCall) return try {
return functionCall.constValue(globalNamespace) ?: functionCall 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 * For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
*/ */
override fun process(expr: PrefixExpression): IExpression { override fun process(expr: PrefixExpression): IExpression {
super.process(expr) return try {
super.process(expr)
val subexpr = expr.expression val subexpr = expr.expression
if (subexpr is LiteralValue) { if (subexpr is LiteralValue) {
// process prefixed literal values (such as -3, not true) // process prefixed literal values (such as -3, not true)
val result = when { val result = when {
expr.operator == "+" -> subexpr expr.operator == "+" -> subexpr
expr.operator == "-" -> when { expr.operator == "-" -> when {
subexpr.intvalue != null -> { subexpr.intvalue != null -> {
optimizationsDone++ optimizationsDone++
LiteralValue(intvalue = -subexpr.intvalue) 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 -> { expr.operator == "~" -> when {
optimizationsDone++ subexpr.intvalue != null -> {
LiteralValue(floatvalue = -subexpr.floatvalue) 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 { result.position = subexpr.position
subexpr.intvalue != null -> { return result
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 expr
return result } 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. * For instance, "9 * (4 + 2)" will be optimized into the integer literal 54.
*/ */
override fun process(expr: BinaryExpression): IExpression { override fun process(expr: BinaryExpression): IExpression {
super.process(expr) return try {
super.process(expr)
val evaluator = ConstExprEvaluator() val evaluator = ConstExprEvaluator()
val leftconst = expr.left.constValue(globalNamespace) val leftconst = expr.left.constValue(globalNamespace)
val rightconst = expr.right.constValue(globalNamespace) val rightconst = expr.right.constValue(globalNamespace)
return when { return when {
leftconst != null && rightconst != null -> { leftconst != null && rightconst != null -> {
optimizationsDone++ optimizationsDone++
evaluator.evaluate(leftconst, expr.operator, rightconst) evaluator.evaluate(leftconst, expr.operator, rightconst)
}
else -> expr
} }
else -> expr } catch (ax: AstException) {
errors.add(ax)
expr
} }
} }
override fun process(range: RangeExpr): IExpression { override fun process(range: RangeExpr): IExpression {
super.process(range) return try {
val from = range.from.constValue(globalNamespace) super.process(range)
val to = range.to.constValue(globalNamespace) val from = range.from.constValue(globalNamespace)
if(from!=null && to != null) { val to = range.to.constValue(globalNamespace)
when { if (from != null && to != null) {
from.intvalue!=null && to.intvalue!=null -> { when {
// int range from.intvalue != null && to.intvalue != null -> {
val rangevalue = from.intvalue.rangeTo(to.intvalue) // int range
if(rangevalue.last-rangevalue.first > 65535) { val rangevalue = from.intvalue.rangeTo(to.intvalue)
throw AstException("amount of values in range exceeds 65535, at ${range.position}") 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 { from.strvalue != null && to.strvalue != null -> {
val v = LiteralValue(intvalue=it) // char range
v.position=range.position val rangevalue = from.strvalue[0].rangeTo(to.strvalue[0])
v.parent=range.parent if (rangevalue.last - rangevalue.first > 65535) {
v throw ExpressionException("amount of characters in range exceeds 65535", range.position)
}) }
} val newval = LiteralValue(strvalue = rangevalue.toList().joinToString(""))
from.strvalue!=null && to.strvalue!=null -> { newval.position = range.position
// char range newval.parent = range.parent
val rangevalue = from.strvalue[0].rangeTo(to.strvalue[0]) return newval
if(rangevalue.last-rangevalue.first > 65535) {
throw AstException("amount of characters in range exceeds 65535, at ${range.position}")
} }
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) ">=" -> comparegreaterequal(left, right)
"==" -> compareequal(left, right) "==" -> compareequal(left, right)
"!=" -> comparenotequal(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.floatvalue!=null -> left.floatvalue
left.strvalue!=null -> left.strvalue left.strvalue!=null -> left.strvalue
left.arrayvalue!=null -> left.arrayvalue left.arrayvalue!=null -> left.arrayvalue
else -> throw AstException("missing literal value") else -> throw FatalAstException("missing literal value")
} }
val rightvalue: Any = when { val rightvalue: Any = when {
right.intvalue!=null -> right.intvalue right.intvalue!=null -> right.intvalue
right.floatvalue!=null -> right.floatvalue right.floatvalue!=null -> right.floatvalue
right.strvalue!=null -> right.strvalue right.strvalue!=null -> right.strvalue
right.arrayvalue!=null -> right.arrayvalue 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) val litval = LiteralValue(intvalue = if (leftvalue == rightvalue) 1 else 0)
litval.position = left.position litval.position = left.position
@ -210,16 +255,16 @@ class ConstExprEvaluator {
intvalue = if (left.intvalue >= right.intvalue) 1 else 0) intvalue = if (left.intvalue >= right.intvalue) 1 else 0)
right.floatvalue!=null -> LiteralValue( right.floatvalue!=null -> LiteralValue(
intvalue = if (left.intvalue >= right.floatvalue) 1 else 0) intvalue = if (left.intvalue >= right.floatvalue) 1 else 0)
else -> throw ExpressionException(error) else -> throw ExpressionException(error, left.position)
} }
left.floatvalue!=null -> when { left.floatvalue!=null -> when {
right.intvalue!=null -> LiteralValue( right.intvalue!=null -> LiteralValue(
intvalue = if (left.floatvalue >= right.intvalue) 1 else 0) intvalue = if (left.floatvalue >= right.intvalue) 1 else 0)
right.floatvalue!=null -> LiteralValue( right.floatvalue!=null -> LiteralValue(
intvalue = if (left.floatvalue >= right.floatvalue) 1 else 0) 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 litval.position = left.position
return litval return litval
@ -233,16 +278,16 @@ class ConstExprEvaluator {
intvalue = if (left.intvalue <= right.intvalue) 1 else 0) intvalue = if (left.intvalue <= right.intvalue) 1 else 0)
right.floatvalue!=null -> LiteralValue( right.floatvalue!=null -> LiteralValue(
intvalue = if (left.intvalue <= right.floatvalue) 1 else 0) intvalue = if (left.intvalue <= right.floatvalue) 1 else 0)
else -> throw ExpressionException(error) else -> throw ExpressionException(error, left.position)
} }
left.floatvalue!=null -> when { left.floatvalue!=null -> when {
right.intvalue!=null -> LiteralValue( right.intvalue!=null -> LiteralValue(
intvalue = if (left.floatvalue <= right.intvalue) 1 else 0) intvalue = if (left.floatvalue <= right.intvalue) 1 else 0)
right.floatvalue!=null -> LiteralValue( right.floatvalue!=null -> LiteralValue(
intvalue = if (left.floatvalue <= right.floatvalue) 1 else 0) 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 litval.position = left.position
return litval return litval
@ -270,16 +315,16 @@ class ConstExprEvaluator {
intvalue = if ((left.intvalue != 0).xor(right.intvalue != 0)) 1 else 0) intvalue = if ((left.intvalue != 0).xor(right.intvalue != 0)) 1 else 0)
right.floatvalue!=null -> LiteralValue( right.floatvalue!=null -> LiteralValue(
intvalue = if ((left.intvalue != 0).xor(right.floatvalue != 0.0)) 1 else 0) 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 { left.floatvalue!=null -> when {
right.intvalue!=null -> LiteralValue( right.intvalue!=null -> LiteralValue(
intvalue = if ((left.floatvalue != 0.0).xor(right.intvalue != 0)) 1 else 0) intvalue = if ((left.floatvalue != 0.0).xor(right.intvalue != 0)) 1 else 0)
right.floatvalue!=null -> LiteralValue( right.floatvalue!=null -> LiteralValue(
intvalue = if ((left.floatvalue != 0.0).xor(right.floatvalue != 0.0)) 1 else 0) 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 litval.position = left.position
return litval return litval
@ -293,16 +338,16 @@ class ConstExprEvaluator {
intvalue = if (left.intvalue != 0 || right.intvalue != 0) 1 else 0) intvalue = if (left.intvalue != 0 || right.intvalue != 0) 1 else 0)
right.floatvalue!=null -> LiteralValue( right.floatvalue!=null -> LiteralValue(
intvalue = if (left.intvalue != 0 || right.floatvalue != 0.0) 1 else 0) 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 { left.floatvalue!=null -> when {
right.intvalue!=null -> LiteralValue( right.intvalue!=null -> LiteralValue(
intvalue = if (left.floatvalue != 0.0 || right.intvalue != 0) 1 else 0) intvalue = if (left.floatvalue != 0.0 || right.intvalue != 0) 1 else 0)
right.floatvalue!=null -> LiteralValue( right.floatvalue!=null -> LiteralValue(
intvalue = if (left.floatvalue != 0.0 || right.floatvalue != 0.0) 1 else 0) 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 litval.position = left.position
return litval return litval
@ -316,16 +361,16 @@ class ConstExprEvaluator {
intvalue = if (left.intvalue != 0 && right.intvalue != 0) 1 else 0) intvalue = if (left.intvalue != 0 && right.intvalue != 0) 1 else 0)
right.floatvalue!=null -> LiteralValue( right.floatvalue!=null -> LiteralValue(
intvalue = if (left.intvalue != 0 && right.floatvalue != 0.0) 1 else 0) 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 { left.floatvalue!=null -> when {
right.intvalue!=null -> LiteralValue( right.intvalue!=null -> LiteralValue(
intvalue = if (left.floatvalue != 0.0 && right.intvalue != 0) 1 else 0) intvalue = if (left.floatvalue != 0.0 && right.intvalue != 0) 1 else 0)
right.floatvalue!=null -> LiteralValue( right.floatvalue!=null -> LiteralValue(
intvalue = if (left.floatvalue != 0.0 && right.floatvalue != 0.0) 1 else 0) 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 litval.position = left.position
return litval return litval
@ -337,7 +382,7 @@ class ConstExprEvaluator {
litval.position = left.position litval.position = left.position
return litval return litval
} }
throw ExpressionException("cannot calculate $left ^ $right") throw ExpressionException("cannot calculate $left ^ $right", left.position)
} }
private fun bitwiseor(left: LiteralValue, right: LiteralValue): LiteralValue { private fun bitwiseor(left: LiteralValue, right: LiteralValue): LiteralValue {
@ -346,7 +391,7 @@ class ConstExprEvaluator {
litval.position = left.position litval.position = left.position
return litval return litval
} }
throw ExpressionException("cannot calculate $left | $right") throw ExpressionException("cannot calculate $left | $right", left.position)
} }
private fun bitwiseand(left: LiteralValue, right: LiteralValue): LiteralValue { private fun bitwiseand(left: LiteralValue, right: LiteralValue): LiteralValue {
@ -355,15 +400,15 @@ class ConstExprEvaluator {
litval.position = left.position litval.position = left.position
return litval return litval
} }
throw ExpressionException("cannot calculate $left & $right") throw ExpressionException("cannot calculate $left & $right", left.position)
} }
private fun rotateright(left: LiteralValue, right: LiteralValue): LiteralValue { 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 { 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 { private fun shiftright(left: LiteralValue, right: LiteralValue): LiteralValue {
@ -372,7 +417,7 @@ class ConstExprEvaluator {
litval.position = left.position litval.position = left.position
return litval return litval
} }
throw ExpressionException("cannot calculate $left >> $right") throw ExpressionException("cannot calculate $left >> $right", left.position)
} }
private fun shiftleft(left: LiteralValue, right: LiteralValue): LiteralValue { private fun shiftleft(left: LiteralValue, right: LiteralValue): LiteralValue {
@ -381,7 +426,7 @@ class ConstExprEvaluator {
litval.position = left.position litval.position = left.position
return litval return litval
} }
throw ExpressionException("cannot calculate $left << $right") throw ExpressionException("cannot calculate $left << $right", left.position)
} }
private fun power(left: LiteralValue, right: LiteralValue): LiteralValue { private fun power(left: LiteralValue, right: LiteralValue): LiteralValue {
@ -390,14 +435,14 @@ class ConstExprEvaluator {
left.intvalue!=null -> when { left.intvalue!=null -> when {
right.intvalue!=null -> LiteralValue(intvalue = left.intvalue.toDouble().pow(right.intvalue).toInt()) right.intvalue!=null -> LiteralValue(intvalue = left.intvalue.toDouble().pow(right.intvalue).toInt())
right.floatvalue!=null -> LiteralValue(floatvalue = left.intvalue.toDouble().pow(right.floatvalue)) 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 { left.floatvalue!=null -> when {
right.intvalue!=null -> LiteralValue(floatvalue = left.floatvalue.pow(right.intvalue)) right.intvalue!=null -> LiteralValue(floatvalue = left.floatvalue.pow(right.intvalue))
right.floatvalue!=null -> LiteralValue(floatvalue = left.floatvalue.pow(right.floatvalue)) 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 litval.position = left.position
return litval return litval
@ -409,14 +454,14 @@ class ConstExprEvaluator {
left.intvalue!=null -> when { left.intvalue!=null -> when {
right.intvalue!=null -> LiteralValue(intvalue = left.intvalue + right.intvalue) right.intvalue!=null -> LiteralValue(intvalue = left.intvalue + right.intvalue)
right.floatvalue!=null -> LiteralValue(floatvalue = left.intvalue + right.floatvalue) right.floatvalue!=null -> LiteralValue(floatvalue = left.intvalue + right.floatvalue)
else -> throw ExpressionException(error) else -> throw ExpressionException(error, left.position)
} }
left.floatvalue!=null -> when { left.floatvalue!=null -> when {
right.intvalue!=null -> LiteralValue(floatvalue = left.floatvalue + right.intvalue) right.intvalue!=null -> LiteralValue(floatvalue = left.floatvalue + right.intvalue)
right.floatvalue!=null -> LiteralValue(floatvalue = left.floatvalue + right.floatvalue) 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 litval.position = left.position
return litval return litval
@ -428,14 +473,14 @@ class ConstExprEvaluator {
left.intvalue!=null -> when { left.intvalue!=null -> when {
right.intvalue!=null -> LiteralValue(intvalue = left.intvalue - right.intvalue) right.intvalue!=null -> LiteralValue(intvalue = left.intvalue - right.intvalue)
right.floatvalue!=null -> LiteralValue(floatvalue = left.intvalue - right.floatvalue) right.floatvalue!=null -> LiteralValue(floatvalue = left.intvalue - right.floatvalue)
else -> throw ExpressionException(error) else -> throw ExpressionException(error, left.position)
} }
left.floatvalue!=null -> when { left.floatvalue!=null -> when {
right.intvalue!=null -> LiteralValue(floatvalue = left.floatvalue - right.intvalue) right.intvalue!=null -> LiteralValue(floatvalue = left.floatvalue - right.intvalue)
right.floatvalue!=null -> LiteralValue(floatvalue = left.floatvalue - right.floatvalue) 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 litval.position = left.position
return litval return litval
@ -448,24 +493,24 @@ class ConstExprEvaluator {
right.intvalue!=null -> LiteralValue(intvalue = left.intvalue * right.intvalue) right.intvalue!=null -> LiteralValue(intvalue = left.intvalue * right.intvalue)
right.floatvalue!=null -> LiteralValue(floatvalue = left.intvalue * right.floatvalue) right.floatvalue!=null -> LiteralValue(floatvalue = left.intvalue * right.floatvalue)
right.strvalue!=null -> { 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)) LiteralValue(strvalue = right.strvalue.repeat(left.intvalue))
} }
else -> throw ExpressionException(error) else -> throw ExpressionException(error, left.position)
} }
left.floatvalue!=null -> when { left.floatvalue!=null -> when {
right.intvalue!=null -> LiteralValue(floatvalue = left.floatvalue * right.intvalue) right.intvalue!=null -> LiteralValue(floatvalue = left.floatvalue * right.intvalue)
right.floatvalue!=null -> LiteralValue(floatvalue = left.floatvalue * right.floatvalue) right.floatvalue!=null -> LiteralValue(floatvalue = left.floatvalue * right.floatvalue)
else -> throw ExpressionException(error) else -> throw ExpressionException(error, left.position)
} }
left.strvalue!=null -> when { left.strvalue!=null -> when {
right.intvalue!=null -> { 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)) 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 litval.position = left.position
return litval return litval
@ -476,27 +521,27 @@ class ConstExprEvaluator {
val litval = when { val litval = when {
left.intvalue!=null -> when { left.intvalue!=null -> when {
right.intvalue!=null -> { 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) LiteralValue(intvalue = left.intvalue / right.intvalue)
} }
right.floatvalue!=null -> { 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) LiteralValue(floatvalue = left.intvalue / right.floatvalue)
} }
else -> throw ExpressionException(error) else -> throw ExpressionException(error, left.position)
} }
left.floatvalue!=null -> when { left.floatvalue!=null -> when {
right.intvalue!=null -> { 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) LiteralValue(floatvalue = left.floatvalue / right.intvalue)
} }
right.floatvalue!=null -> { 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) 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 litval.position = left.position
return litval return litval

View File

@ -1,7 +1,6 @@
package il65.optimizing package il65.optimizing
import il65.ast.* import il65.ast.*
import kotlin.math.pow
fun Module.optimizeStatements(globalNamespace: INameScope) { fun Module.optimizeStatements(globalNamespace: INameScope) {
@ -31,7 +30,6 @@ class StatementOptimizer(private val globalNamespace: INameScope) : IAstProcesso
super.process(ifStatement) super.process(ifStatement)
val constvalue = ifStatement.condition.constValue(globalNamespace) val constvalue = ifStatement.condition.constValue(globalNamespace)
if(constvalue!=null) { if(constvalue!=null) {
val statements: List<IStatement>
return if(constvalue.asBoolean()) { return if(constvalue.asBoolean()) {
// always true -> keep only if-part // always true -> keep only if-part
println("${ifStatement.position} Warning: condition is always true") println("${ifStatement.position} Warning: condition is always true")