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
| labeldef
| returnstmt
// @todo forloop, whileloop, repeatloop, ifelse
// @todo forloop, whileloop, repeatloop
;
labeldef : identifier ':' ;

View File

@ -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

View File

@ -137,8 +137,10 @@ fun main(args: Array<String>) {
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)
}

View File

@ -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<String>, 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<IExpression>? = 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<String>) : 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<String>) : 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)
}

View File

@ -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)
}

View File

@ -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'.")

View File

@ -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<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)
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<IExpression>, 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<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)
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<IExpression>, 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<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()
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<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArgOutputInt(args, position, namespace) { it -> Math.round(it).toInt() }
fun builtin_round(args: List<IExpression>, namespace: INameScope): LiteralValue
= oneDoubleArgOutputInt(args, namespace) { it -> Math.round(it).toInt() }
fun builtin_sin(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::sin)
fun builtin_sin(args: List<IExpression>, namespace: INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::sin)
fun builtin_cos(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::cos)
fun builtin_cos(args: List<IExpression>, namespace: INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::cos)
fun builtin_acos(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::acos)
fun builtin_acos(args: List<IExpression>, namespace: INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::acos)
fun builtin_asin(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::asin)
fun builtin_asin(args: List<IExpression>, namespace: INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::asin)
fun builtin_tan(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::tan)
fun builtin_tan(args: List<IExpression>, namespace: INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::tan)
fun builtin_atan(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::atan)
fun builtin_atan(args: List<IExpression>, namespace: INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::atan)
fun builtin_log(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::log)
fun builtin_log(args: List<IExpression>, namespace: INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::log)
fun builtin_log10(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::log10)
fun builtin_log10(args: List<IExpression>, namespace: INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::log10)
fun builtin_sqrt(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::sqrt)
fun builtin_sqrt(args: List<IExpression>, namespace: INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::sqrt)
fun builtin_rad(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::toRadians)
fun builtin_rad(args: List<IExpression>, namespace: INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::toRadians)
fun builtin_deg(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::toDegrees)
fun builtin_deg(args: List<IExpression>, namespace: INameScope): LiteralValue
= oneDoubleArg(args, namespace, Math::toDegrees)
fun builtin_abs(args: List<IExpression>, namespace: INameScope): LiteralValue {
fun builtin_abs(args: List<IExpression>, 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<IExpression>, namespace: INameScope): LiteralValue {
fun builtin_max(args: List<IExpression>, 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<IExpression>, namespace: INameScope): LiteralValue {
fun builtin_min(args: List<IExpression>, 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)
}

View File

@ -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<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
*/
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

View File

@ -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<IStatement>
return if(constvalue.asBoolean()) {
// always true -> keep only if-part
println("${ifStatement.position} Warning: condition is always true")