made position required to avoid some bugs where it was not specified

fixed some type checking bugs
This commit is contained in:
Irmen de Jong 2018-09-15 01:14:48 +02:00
parent ba81f32080
commit 28aaf38f22
15 changed files with 478 additions and 643 deletions

View File

@ -469,8 +469,8 @@ sum(x)
Sum of the values in the non-scalar (array or matrix) value x
len(x)
Number of values in the non-scalar (array or matrix) value x.
(This is different from the number of *bytes* in memory if the datatype isn't byte)
Number of values in the array or matrix value x, or the number of characters in a string (excluding the size or 0-byte).
Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte.
lsb(x)
Get the least significant byte of the word x.

View File

@ -6,6 +6,7 @@
~ not_main $d000 {
byte [100] array1 = 0
word [100] array2 = 1
word [4] array2b = [1,2,3,4]
byte [2,3] matrix1 = 2
byte [2,3] matrix2 = [1,2,3,4,5,6]
@ -113,7 +114,7 @@
equalQQ= len("abcdef")
equalQQ= len([1,2,3])
equalQQ= len(string1)
equalQQ= len(string2) ; @todo len function call on str type ALSO ADD ALL THIS TO DOCS OF FUNCTION LEN!
equalQQ= len(string2)
P_carry(1)
P_irqd(0)
@ -140,10 +141,10 @@
if(6==6) {
A=sin(X) ; @todo should give error of float loss of precision
X=max([1,2,Y]) ; @todo must be byte so should be ok
X=min([1,2,Y]) ; @todo must be byte so should be ok
X=max([1,233,Y])
X=min([1,2,Y])
X=lsl(12)
X=lsl(Y) ; @todo must be byte so should be ok
X=lsl(Y)
P_carry(0)
P_carry(1)
P_carry(1-1)

View File

@ -1,4 +1,4 @@
#/bin/env sh
#!/usr/bin/env sh
IL65_LIBDIR=../lib65
IL65CLASSPATH=out/production/il65

View File

@ -42,8 +42,7 @@ fun main(args: Array<String>) {
if(outputType==null) OutputType.PRG else OutputType.valueOf(outputType),
if(launcherType==null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
if(zpType==null) ZeropageType.KERNALSAFE else ZeropageType.valueOf(zpType),
options.contains(DirectiveArg(null, "enable_floats", null))
)
options.any{ it.name=="enable_floats"})
// perform syntax checks and optimizations

File diff suppressed because it is too large Load Diff

View File

@ -163,7 +163,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
val targetDatatype = assignment.target.determineDatatype(namespace, assignment)
val constVal = assignment.value.constValue(namespace)
if(constVal!=null) {
checkValueTypeAndRange(targetDatatype, null, assignment.value as LiteralValue, assignment.position)
checkValueTypeAndRange(targetDatatype, null, assignment.value as LiteralValue)
} else {
val sourceDatatype: DataType? = assignment.value.resultingDatatype(namespace)
if(sourceDatatype==null) {
@ -212,7 +212,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
return super.process(decl)
}
}
checkValueTypeAndRange(decl.datatype, decl.arrayspec, decl.value as LiteralValue, decl.position)
checkValueTypeAndRange(decl.datatype, decl.arrayspec, decl.value as LiteralValue)
}
VarDeclType.MEMORY -> {
if(decl.value !is LiteralValue) {
@ -310,14 +310,10 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
}
override fun process(literalValue: LiteralValue): LiteralValue {
if(!compilerOptions.floats && literalValue.isFloat) {
if(!compilerOptions.floats && literalValue.type==DataType.FLOAT) {
checkResult.add(SyntaxError("floating point value used, but floating point is not enabled via options", literalValue.position))
}
when {
literalValue.isByte -> checkValueTypeAndRange(DataType.BYTE, null, literalValue, literalValue.position)
literalValue.isWord -> checkValueTypeAndRange(DataType.WORD, null, literalValue, literalValue.position)
literalValue.isFloat -> checkValueTypeAndRange(DataType.FLOAT, null, literalValue, literalValue.position)
}
checkValueTypeAndRange(literalValue.type, null, literalValue)
return super.process(literalValue)
}
@ -389,7 +385,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
return null
}
private fun checkBuiltinFunctionCall(call: IFunctionCall, position: Position?) {
private fun checkBuiltinFunctionCall(call: IFunctionCall, position: Position) {
if(call.target.nameInSource.size==1 && BuiltinFunctionNames.contains(call.target.nameInSource[0])) {
val functionName = call.target.nameInSource[0]
if(functionName=="P_carry" || functionName=="P_irqd") {
@ -406,9 +402,9 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
}
}
private fun checkValueTypeAndRange(datatype: DataType, arrayspec: ArraySpec?, value: LiteralValue, position: Position?) : Boolean {
private fun checkValueTypeAndRange(datatype: DataType, arrayspec: ArraySpec?, value: LiteralValue) : Boolean {
fun err(msg: String) : Boolean {
checkResult.add(ExpressionError(msg, position))
checkResult.add(ExpressionError(msg, value.position))
return false
}
when (datatype) {
@ -442,17 +438,27 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
}
DataType.ARRAY -> {
// value may be either a single byte, or a byte array
if(value.isArray) {
if(value.type==DataType.ARRAY) {
for (av in value.arrayvalue!!) {
val number = (av as LiteralValue).bytevalue
?: return err("array must be all bytes")
if(arrayspec!=null) {
// arrayspec is not always known when checking
val expectedSize = arrayspec.x.constValue(namespace)?.asIntegerValue
if (value.arrayvalue.size != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got ${value.arrayvalue.size})")
}
val expectedSize = arrayspec?.x?.constValue(namespace)?.asIntegerValue
if (value.arrayvalue.size != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got ${value.arrayvalue.size})")
if(av is LiteralValue) {
val number = av.bytevalue
?: return err("array must be all bytes")
if (number < 0 || number > 255)
return err("value '$number' in byte array is out of range for unsigned byte")
} else if(av is RegisterExpr) {
if(av.register!=Register.A && av.register!=Register.X && av.register!=Register.Y)
return err("register '$av' in byte array is not a single register")
} else {
TODO("array value $av")
}
if (number < 0 || number > 255)
return err("value '$number' in byte array is out of range for unsigned byte")
}
} else {
val number = value.bytevalue ?: return if (value.floatvalue!=null)
@ -465,14 +471,17 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
}
DataType.ARRAY_W -> {
// value may be either a single word, or a word array
if(value.isArray) {
if(value.type==DataType.ARRAY || value.type==DataType.ARRAY_W) {
for (av in value.arrayvalue!!) {
val number = (av as LiteralValue).asIntegerValue // both byte and word are acceptable
?: return err("array must be all words")
val expectedSize = arrayspec!!.x.constValue(namespace)?.asIntegerValue
if (value.arrayvalue.size != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got ${value.arrayvalue.size})")
if(arrayspec!=null) {
// arrayspec is not always known when checking
val expectedSize = arrayspec.x.constValue(namespace)?.asIntegerValue
if (value.arrayvalue.size != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got ${value.arrayvalue.size})")
}
if (number < 0 || number > 65535)
return err("value '$number' in word array is out of range for unsigned word")
@ -489,7 +498,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
}
DataType.MATRIX -> {
// value can only be a single byte, or a byte array (which represents the matrix)
if(value.isArray) {
if(value.type==DataType.ARRAY) {
for (av in value.arrayvalue!!) {
val number = (av as LiteralValue).bytevalue
?: return err("array must be all bytes")
@ -514,7 +523,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
return true
}
private fun checkAssignmentCompatible(targetDatatype: DataType, sourceDatatype: DataType, position: Position?) : Boolean {
private fun checkAssignmentCompatible(targetDatatype: DataType, sourceDatatype: DataType, position: Position) : Boolean {
val result = when(targetDatatype) {
DataType.BYTE -> sourceDatatype==DataType.BYTE
DataType.WORD -> sourceDatatype==DataType.BYTE || sourceDatatype==DataType.WORD

View File

@ -32,8 +32,8 @@ class AstIdentifiersChecker : IAstProcessor {
return checkResult
}
private fun nameError(name: String, position: Position?, existing: IStatement) {
checkResult.add(NameError("name conflict '$name', first defined in ${existing.position?.file} line ${existing.position?.line}", position))
private fun nameError(name: String, position: Position, existing: IStatement) {
checkResult.add(NameError("name conflict '$name', first defined in ${existing.position.file} line ${existing.position.line}", position))
}
override fun process(block: Block): IStatement {
@ -48,12 +48,8 @@ class AstIdentifiersChecker : IAstProcessor {
}
override fun process(decl: VarDecl): IStatement {
// first, set the datatype
try {
decl.setDatatype()
} catch(ax: AstException) {
checkResult.add(ax)
}
// first, check if there are datatype errors on the vardecl
decl.datatypeErrors.forEach { checkResult.add(it) }
// now check the identifier
if(BuiltinFunctionNames.contains(decl.name))

View File

@ -96,7 +96,7 @@ class AstRecursionChecker(private val namespace: INameScope) : IAstProcessor {
if(cycle.isEmpty())
return emptyList()
val chain = cycle.joinToString(" <-- ") { "${it.name} at ${it.position}" }
return listOf(AstException("Program contains recursive subroutine calls, this is not supported. Recursive chain:\n (a subroutine call in) "+chain))
return listOf(AstException("Program contains recursive subroutine calls, this is not supported. Recursive chain:\n (a subroutine call in) $chain"))
}
override fun process(functionCall: FunctionCallStatement): IStatement {

View File

@ -109,7 +109,7 @@ class Compiler(private val options: CompilationOptions) {
}
class VarGatherer(val stackvmProg: StackVmProgram): IAstProcessor {
class VarGatherer(private val stackvmProg: StackVmProgram): IAstProcessor {
// collect all the VarDecls to make them into one global list
override fun process(decl: VarDecl): IStatement {
if(decl.type == VarDeclType.MEMORY)
@ -122,7 +122,7 @@ class Compiler(private val options: CompilationOptions) {
}
}
class StatementTranslator(val stackvmProg: StackVmProgram, val namespace: INameScope): IAstProcessor {
class StatementTranslator(private val stackvmProg: StackVmProgram, private val namespace: INameScope): IAstProcessor {
var stmtUniqueSequenceNr = 0
private set
@ -269,16 +269,16 @@ class Compiler(private val options: CompilationOptions) {
}
else -> {
val lv = expr.constValue(namespace) ?: throw CompilerException("constant expression required, not $expr")
when {
lv.isString -> stackvmProg.instruction("push \"${lv.strvalue}\"")
lv.isByte -> stackvmProg.instruction("push b:${lv.bytevalue!!.toString(16)}")
lv.isWord -> stackvmProg.instruction("push w:${lv.wordvalue!!.toString(16)}")
lv.isFloat -> stackvmProg.instruction("push f:${lv.floatvalue}")
lv.isArray -> {
when(lv.type) {
DataType.BYTE -> stackvmProg.instruction("push b:${lv.bytevalue!!.toString(16)}")
DataType.WORD -> stackvmProg.instruction("push w:${lv.wordvalue!!.toString(16)}")
DataType.FLOAT -> stackvmProg.instruction("push f:${lv.floatvalue}")
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> stackvmProg.instruction("push \"${lv.strvalue}\"")
DataType.ARRAY, DataType.ARRAY_W -> {
lv.arrayvalue?.forEach { translate(it) }
stackvmProg.instruction("array w:${lv.arrayvalue!!.size.toString(16)}")
}
else -> throw CompilerException("expression constvalue invalid type $lv")
DataType.MATRIX -> TODO("matrix type")
}
}
}
@ -448,11 +448,11 @@ class StackVmProgram(val name: String) {
throw AssertionError("Should be only VAR or CONST variables")
}
val litval = v.value.value as LiteralValue
val litvalStr = when {
litval.isByte -> litval.bytevalue!!.toString(16)
litval.isWord -> litval.wordvalue!!.toString(16)
litval.isFloat -> litval.floatvalue.toString()
litval.isString -> "\"${litval.strvalue}\""
val litvalStr = when(litval.type) {
DataType.BYTE -> litval.bytevalue!!.toString(16)
DataType.WORD -> litval.wordvalue!!.toString(16)
DataType.FLOAT -> litval.floatvalue.toString()
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> "\"${litval.strvalue}\""
else -> TODO("non-scalar value")
}
val line = "${v.key} ${v.value.datatype.toString().toLowerCase()} $litvalStr"

View File

@ -17,40 +17,40 @@ val BuiltinFunctionsWithoutSideEffects = BuiltinFunctionNames - setOf("P_carry",
class NotConstArgumentException: AstException("not a const argument to a built-in function")
private fun oneDoubleArg(args: List<IExpression>, position: Position?, namespace:INameScope, function: (arg: Double)->Number): LiteralValue {
private fun oneDoubleArg(args: List<IExpression>, position: Position, namespace:INameScope, function: (arg: Double)->Number): LiteralValue {
if(args.size!=1)
throw SyntaxError("built-in function requires one floating point argument", position)
val constval = args[0].constValue(namespace) ?: throw NotConstArgumentException()
if(!constval.isFloat)
if(constval.type!=DataType.FLOAT)
throw SyntaxError("built-in function requires one floating point argument", position)
val float = constval.asNumericValue?.toDouble()!!
return numericLiteral(function(float), args[0].position)
}
private fun oneDoubleArgOutputInt(args: List<IExpression>, position: Position?, namespace:INameScope, function: (arg: Double)->Number): LiteralValue {
private fun oneDoubleArgOutputInt(args: List<IExpression>, position: Position, namespace:INameScope, function: (arg: Double)->Number): LiteralValue {
if(args.size!=1)
throw SyntaxError("built-in function requires one floating point argument", position)
val constval = args[0].constValue(namespace) ?: throw NotConstArgumentException()
if(!constval.isFloat)
if(constval.type!=DataType.FLOAT)
throw SyntaxError("built-in function requires one floating point argument", position)
val float = constval.asNumericValue?.toDouble()!!
return numericLiteral(function(float).toInt(), args[0].position)
}
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position?, namespace:INameScope, function: (arg: Int)->Number): LiteralValue {
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position, namespace:INameScope, function: (arg: Int)->Number): LiteralValue {
if(args.size!=1)
throw SyntaxError("built-in function requires one integer argument", position)
val constval = args[0].constValue(namespace) ?: throw NotConstArgumentException()
if(!constval.isInteger)
if(constval.type!=DataType.BYTE && constval.type!=DataType.WORD)
throw SyntaxError("built-in function requires one integer argument", position)
val integer = constval.asNumericValue?.toInt()!!
return numericLiteral(function(integer).toInt(), args[0].position)
}
private fun collectionArgOutputNumber(args: List<IExpression>, position: Position?, namespace:INameScope,
private fun collectionArgOutputNumber(args: List<IExpression>, position: Position, namespace:INameScope,
function: (arg: Collection<Double>)->Number): LiteralValue {
if(args.size!=1)
throw SyntaxError("builtin function requires one non-scalar argument", position)
@ -64,7 +64,7 @@ private fun collectionArgOutputNumber(args: List<IExpression>, position: Positio
return numericLiteral(result, args[0].position)
}
private fun collectionArgOutputBoolean(args: List<IExpression>, position: Position?, namespace:INameScope,
private fun collectionArgOutputBoolean(args: List<IExpression>, position: Position, namespace:INameScope,
function: (arg: Collection<Double>)->Boolean): LiteralValue {
if(args.size!=1)
throw SyntaxError("builtin function requires one non-scalar argument", position)
@ -75,52 +75,52 @@ private fun collectionArgOutputBoolean(args: List<IExpression>, position: Positi
if(constants.contains(null))
throw NotConstArgumentException()
val result = function(constants.map { it?.toDouble()!! })
return LiteralValue(bytevalue = if(result) 1 else 0)
return LiteralValue.fromBoolean(result, position)
}
fun builtinRound(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinRound(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneDoubleArgOutputInt(args, position, namespace, Math::round)
fun builtinFloor(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinFloor(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneDoubleArgOutputInt(args, position, namespace, Math::floor)
fun builtinCeil(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinCeil(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneDoubleArgOutputInt(args, position, namespace, Math::ceil)
fun builtinSin(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinSin(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::sin)
fun builtinCos(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinCos(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::cos)
fun builtinAcos(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinAcos(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::acos)
fun builtinAsin(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinAsin(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::asin)
fun builtinTan(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinTan(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::tan)
fun builtinAtan(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinAtan(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::atan)
fun builtinLog(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinLog(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::log)
fun builtinLog10(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinLog10(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::log10)
fun builtinSqrt(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinSqrt(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::sqrt)
fun builtinRad(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinRad(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::toRadians)
fun builtinDeg(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinDeg(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneDoubleArg(args, position, namespace, Math::toDegrees)
fun builtinAbs(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue {
fun builtinAbs(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue {
// 1 arg, type = float or int, result type= same as argument type
if(args.size!=1)
throw SyntaxError("abs requires one numeric argument", position)
@ -136,28 +136,28 @@ fun builtinAbs(args: List<IExpression>, position: Position?, namespace:INameScop
// todo different functions for byte/word params/results?
fun builtinLsb(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinLsb(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x and 255 }
fun builtinMsb(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinMsb(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x ushr 8 and 255}
fun builtinLsl(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinLsl(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x shl 1 }
fun builtinLsr(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinLsr(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x ushr 1 }
fun builtinMin(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinMin(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= collectionArgOutputNumber(args, position, namespace) { it.min()!! }
fun builtinMax(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinMax(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= collectionArgOutputNumber(args, position, namespace) { it.max()!! }
fun builtinSum(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinSum(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= collectionArgOutputNumber(args, position, namespace) { it.sum() }
fun builtinAvg(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue {
fun builtinAvg(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue {
if(args.size!=1)
throw SyntaxError("avg requires one non-scalar argument", position)
val iterable = args[0].constValue(namespace)
@ -170,25 +170,25 @@ fun builtinAvg(args: List<IExpression>, position: Position?, namespace:INameScop
return numericLiteral(result, args[0].position)
}
fun builtinLen(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue {
fun builtinLen(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue {
if(args.size!=1)
throw SyntaxError("len requires one argument", position)
val argument = args[0].constValue(namespace) ?: throw NotConstArgumentException()
return when {
argument.isArray -> numericLiteral(argument.arrayvalue!!.size, args[0].position)
argument.isString -> numericLiteral(argument.strvalue!!.length, args[0].position)
return when(argument.type) {
DataType.ARRAY, DataType.ARRAY_W -> numericLiteral(argument.arrayvalue!!.size, args[0].position)
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> numericLiteral(argument.strvalue!!.length, args[0].position)
else -> throw FatalAstException("len of weird argument ${args[0]}")
}
}
fun builtinAny(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinAny(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= collectionArgOutputBoolean(args, position, namespace) { it.any { v -> v != 0.0} }
fun builtinAll(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
fun builtinAll(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
= collectionArgOutputBoolean(args, position, namespace) { it.all { v -> v != 0.0} }
private fun numericLiteral(value: Number, position: Position?): LiteralValue {
private fun numericLiteral(value: Number, position: Position): LiteralValue {
val floatNum=value.toDouble()
val tweakedValue: Number =
if(floatNum==floor(floatNum) && floatNum in -32768..65535)
@ -196,14 +196,12 @@ private fun numericLiteral(value: Number, position: Position?): LiteralValue {
else
floatNum
val result = when(tweakedValue) {
is Int -> LiteralValue.optimalNumeric(value.toInt())
is Short -> LiteralValue.optimalNumeric(value.toInt())
is Byte -> LiteralValue(bytevalue = value.toShort())
is Double -> LiteralValue(floatvalue = value.toDouble())
is Float -> LiteralValue(floatvalue = value.toDouble())
return when(tweakedValue) {
is Int -> LiteralValue.optimalNumeric(value.toInt(), position)
is Short -> LiteralValue.optimalNumeric(value.toInt(), position)
is Byte -> LiteralValue(DataType.BYTE, bytevalue = value.toShort(), position = position)
is Double -> LiteralValue(DataType.FLOAT, floatvalue = value.toDouble(), position = position)
is Float -> LiteralValue(DataType.FLOAT, floatvalue = value.toDouble(), position = position)
else -> throw FatalAstException("invalid number type ${value::class}")
}
result.position = position
return result
}

View File

@ -82,9 +82,8 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
decl.datatype == DataType.FLOAT -> {
// vardecl: for float vars, promote constant integer initialization values to floats
val literal = decl.value as? LiteralValue
if (literal != null && literal.isInteger) {
val newValue = LiteralValue(floatvalue = literal.asNumericValue!!.toDouble())
newValue.position = literal.position
if (literal != null && (literal.type == DataType.BYTE || literal.type==DataType.WORD)) {
val newValue = LiteralValue(DataType.FLOAT, floatvalue = literal.asNumericValue!!.toDouble(), position = literal.position)
decl.value = newValue
}
}
@ -93,8 +92,7 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
val literal = decl.value as? LiteralValue
if (literal != null && literal.isString && literal.strvalue?.length == 1) {
val petscii = Petscii.encodePetscii(literal.strvalue)[0]
val newValue = LiteralValue(bytevalue = petscii)
newValue.position = literal.position
val newValue = LiteralValue(DataType.BYTE, bytevalue = petscii, position = literal.position)
decl.value = newValue
}
}
@ -137,41 +135,39 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
val subexpr = expr.expression
if (subexpr is LiteralValue) {
// process prefixed literal values (such as -3, not true)
val result = when {
return when {
expr.operator == "+" -> subexpr
expr.operator == "-" -> when {
subexpr.asIntegerValue!= null -> {
optimizationsDone++
LiteralValue.optimalNumeric(-subexpr.asIntegerValue)
LiteralValue.optimalNumeric(-subexpr.asIntegerValue, subexpr.position)
}
subexpr.floatvalue != null -> {
optimizationsDone++
LiteralValue(floatvalue = -subexpr.floatvalue)
LiteralValue(DataType.FLOAT, floatvalue = -subexpr.floatvalue, position = subexpr.position)
}
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
}
expr.operator == "~" -> when {
subexpr.asIntegerValue != null -> {
optimizationsDone++
LiteralValue.optimalNumeric(subexpr.asIntegerValue.inv())
LiteralValue.optimalNumeric(subexpr.asIntegerValue.inv(), subexpr.position)
}
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
}
expr.operator == "not" -> when {
subexpr.asIntegerValue != null -> {
optimizationsDone++
LiteralValue(bytevalue = if (subexpr.asIntegerValue == 0) 1 else 0)
LiteralValue.fromBoolean(subexpr.asIntegerValue == 0, subexpr.position)
}
subexpr.floatvalue != null -> {
optimizationsDone++
LiteralValue(bytevalue = if (subexpr.floatvalue == 0.0) 1 else 0)
LiteralValue.fromBoolean(subexpr.floatvalue == 0.0, subexpr.position)
}
else -> throw ExpressionError("can not take logical not of $subexpr", subexpr.position)
}
else -> throw ExpressionError(expr.operator, subexpr.position)
}
result.position = subexpr.position
return result
}
return expr
} catch (ax: AstException) {
@ -212,31 +208,29 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
val to = range.to.constValue(globalNamespace)
if (from != null && to != null) {
when {
from.isWord || to.isWord -> {
from.type==DataType.WORD || to.type==DataType.WORD -> {
// range on word value boundaries
val rangeValue = from.asIntegerValue!!.rangeTo(to.asIntegerValue!!)
if (rangeValue.last - rangeValue.first > 65535) {
throw ExpressionError("amount of values in range exceeds 65535", range.position)
}
return LiteralValue(arrayvalue = rangeValue.map {
val v = LiteralValue(wordvalue = it)
v.position = range.position
return LiteralValue(DataType.ARRAY_W, arrayvalue = rangeValue.map {
val v = LiteralValue(DataType.WORD, wordvalue = it, position = range.position)
v.parent = range.parent
v
}.toMutableList())
}.toMutableList(), position = from.position)
}
from.isByte && to.isByte -> {
from.type==DataType.BYTE && to.type==DataType.BYTE -> {
// range on byte value boundaries
val rangeValue = from.bytevalue!!.rangeTo(to.bytevalue!!)
if (rangeValue.last - rangeValue.first > 65535) {
throw ExpressionError("amount of values in range exceeds 65535", range.position)
}
return LiteralValue(arrayvalue = rangeValue.map {
val v = LiteralValue(bytevalue = it.toShort())
v.position = range.position
return LiteralValue(DataType.ARRAY, arrayvalue = rangeValue.map {
val v = LiteralValue(DataType.BYTE, bytevalue = it.toShort(), position = range.position)
v.parent = range.parent
v
}.toMutableList())
}.toMutableList(), position = from.position)
}
from.strvalue != null && to.strvalue != null -> {
// char range
@ -244,8 +238,7 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
if (rangevalue.last - rangevalue.first > 65535) {
throw ExpressionError("amount of characters in range exceeds 65535", range.position)
}
val newval = LiteralValue(strvalue = rangevalue.toList().joinToString(""))
newval.position = range.position
val newval = LiteralValue(DataType.STR, strvalue = rangevalue.toList().joinToString(""), position = range.position)
newval.parent = range.parent
return newval
}
@ -297,9 +290,7 @@ class ConstExprEvaluator {
private fun comparenotequal(left: LiteralValue, right: LiteralValue): LiteralValue {
val leq = compareequal(left, right)
val litval = LiteralValue(bytevalue = if (leq.bytevalue == 1.toShort()) 0 else 1)
litval.position = left.position
return litval
return LiteralValue.fromBoolean(leq.bytevalue == 1.toShort(), left.position)
}
private fun compareequal(left: LiteralValue, right: LiteralValue): LiteralValue {
@ -319,186 +310,138 @@ class ConstExprEvaluator {
right.arrayvalue!=null -> right.arrayvalue
else -> throw FatalAstException("missing literal value")
}
val litval = LiteralValue(bytevalue = if (leftvalue == rightvalue) 1 else 0)
litval.position = left.position
return litval
return LiteralValue.fromBoolean(leftvalue == rightvalue, left.position)
}
private fun comparegreaterequal(left: LiteralValue, right: LiteralValue): LiteralValue {
val error = "cannot compute $left >= $right"
val litval = when {
return when {
left.asIntegerValue!=null -> when {
right.asIntegerValue!=null -> LiteralValue(
bytevalue = if (left.asIntegerValue >= right.asIntegerValue) 1 else 0)
right.floatvalue!=null -> LiteralValue(
bytevalue = if (left.asIntegerValue >= right.floatvalue) 1 else 0)
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.asIntegerValue >= right.asIntegerValue, left.position)
right.floatvalue!=null -> LiteralValue.fromBoolean(left.asIntegerValue >= right.floatvalue, left.position)
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
right.asIntegerValue!=null -> LiteralValue(
bytevalue = if (left.floatvalue >= right.asIntegerValue) 1 else 0)
right.floatvalue!=null -> LiteralValue(
bytevalue = if (left.floatvalue >= right.floatvalue) 1 else 0)
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.floatvalue >= right.asIntegerValue, left.position)
right.floatvalue!=null -> LiteralValue.fromBoolean(left.floatvalue >= right.floatvalue, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
litval.position = left.position
return litval
}
private fun comparelessequal(left: LiteralValue, right: LiteralValue): LiteralValue {
val error = "cannot compute $left >= $right"
val litval = when {
return when {
left.asIntegerValue!=null -> when {
right.asIntegerValue!=null -> LiteralValue(
bytevalue = if (left.asIntegerValue <= right.asIntegerValue) 1 else 0)
right.floatvalue!=null -> LiteralValue(
bytevalue = if (left.asIntegerValue <= right.floatvalue) 1 else 0)
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.asIntegerValue <= right.asIntegerValue, left.position)
right.floatvalue!=null -> LiteralValue.fromBoolean(left.asIntegerValue <= right.floatvalue, left.position)
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
right.asIntegerValue!=null -> LiteralValue(
bytevalue = if (left.floatvalue <= right.asIntegerValue) 1 else 0)
right.floatvalue!=null -> LiteralValue(
bytevalue = if (left.floatvalue <= right.floatvalue) 1 else 0)
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.floatvalue <= right.asIntegerValue, left.position)
right.floatvalue!=null -> LiteralValue.fromBoolean(left.floatvalue <= right.floatvalue, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
litval.position = left.position
return litval
}
private fun comparegreater(left: LiteralValue, right: LiteralValue): LiteralValue {
val leq = comparelessequal(left, right)
val litval = LiteralValue(bytevalue = if (leq.bytevalue == 1.toShort()) 0 else 1)
litval.position = left.position
return litval
return LiteralValue.fromBoolean(leq.bytevalue == 1.toShort(), left.position)
}
private fun compareless(left: LiteralValue, right: LiteralValue): LiteralValue {
val leq = comparegreaterequal(left, right)
val litval = LiteralValue(bytevalue = if (leq.bytevalue == 1.toShort()) 0 else 1)
litval.position = left.position
return litval
return LiteralValue.fromBoolean(leq.bytevalue == 1.toShort(), left.position)
}
private fun logicalxor(left: LiteralValue, right: LiteralValue): LiteralValue {
val error = "cannot compute $left locical-bitxor $right"
val litval = when {
return when {
left.asIntegerValue!=null -> when {
right.asIntegerValue!=null -> LiteralValue(
bytevalue = if ((left.asIntegerValue != 0).xor(right.asIntegerValue != 0)) 1 else 0)
right.floatvalue!=null -> LiteralValue(
bytevalue = if ((left.asIntegerValue != 0).xor(right.floatvalue != 0.0)) 1 else 0)
right.asIntegerValue!=null -> LiteralValue.fromBoolean((left.asIntegerValue != 0) xor (right.asIntegerValue != 0), left.position)
right.floatvalue!=null -> LiteralValue.fromBoolean((left.asIntegerValue != 0) xor (right.floatvalue != 0.0), left.position)
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
right.asIntegerValue!=null -> LiteralValue(
bytevalue = if ((left.floatvalue != 0.0).xor(right.asIntegerValue != 0)) 1 else 0)
right.floatvalue!=null -> LiteralValue(
bytevalue = if ((left.floatvalue != 0.0).xor(right.floatvalue != 0.0)) 1 else 0)
right.asIntegerValue!=null -> LiteralValue.fromBoolean((left.floatvalue != 0.0) xor (right.asIntegerValue != 0), left.position)
right.floatvalue!=null -> LiteralValue.fromBoolean((left.floatvalue != 0.0) xor (right.floatvalue != 0.0), left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
litval.position = left.position
return litval
}
private fun logicalor(left: LiteralValue, right: LiteralValue): LiteralValue {
val error = "cannot compute $left locical-or $right"
val litval = when {
return when {
left.asIntegerValue!=null -> when {
right.asIntegerValue!=null -> LiteralValue(
bytevalue = if (left.asIntegerValue != 0 || right.asIntegerValue != 0) 1 else 0)
right.floatvalue!=null -> LiteralValue(
bytevalue = if (left.asIntegerValue != 0 || right.floatvalue != 0.0) 1 else 0)
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.asIntegerValue != 0 || right.asIntegerValue != 0, left.position)
right.floatvalue!=null -> LiteralValue.fromBoolean(left.asIntegerValue != 0 || right.floatvalue != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
right.asIntegerValue!=null -> LiteralValue(
bytevalue = if (left.floatvalue != 0.0 || right.asIntegerValue != 0) 1 else 0)
right.floatvalue!=null -> LiteralValue(
bytevalue = if (left.floatvalue != 0.0 || right.floatvalue != 0.0) 1 else 0)
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.floatvalue != 0.0 || right.asIntegerValue != 0, left.position)
right.floatvalue!=null -> LiteralValue.fromBoolean(left.floatvalue != 0.0 || right.floatvalue != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
litval.position = left.position
return litval
}
private fun logicaland(left: LiteralValue, right: LiteralValue): LiteralValue {
val error = "cannot compute $left locical-and $right"
val litval = when {
return when {
left.asIntegerValue!=null -> when {
right.asIntegerValue!=null -> LiteralValue(
bytevalue = if (left.asIntegerValue != 0 && right.asIntegerValue != 0) 1 else 0)
right.floatvalue!=null -> LiteralValue(
bytevalue = if (left.asIntegerValue != 0 && right.floatvalue != 0.0) 1 else 0)
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.asIntegerValue != 0 && right.asIntegerValue != 0, left.position)
right.floatvalue!=null -> LiteralValue.fromBoolean(left.asIntegerValue != 0 && right.floatvalue != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
right.asIntegerValue!=null -> LiteralValue(
bytevalue = if (left.floatvalue != 0.0 && right.asIntegerValue != 0) 1 else 0)
right.floatvalue!=null -> LiteralValue(
bytevalue = if (left.floatvalue != 0.0 && right.floatvalue != 0.0) 1 else 0)
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.floatvalue != 0.0 && right.asIntegerValue != 0, left.position)
right.floatvalue!=null -> LiteralValue.fromBoolean(left.floatvalue != 0.0 && right.floatvalue != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
litval.position = left.position
return litval
}
private fun bitwisexor(left: LiteralValue, right: LiteralValue): LiteralValue {
if(left.isByte) {
if(left.type==DataType.BYTE) {
if(right.asIntegerValue!=null) {
val litval = LiteralValue(bytevalue = (left.bytevalue!!.toInt() xor (right.asIntegerValue and 255)).toShort())
litval.position = left.position
return litval
return LiteralValue(DataType.BYTE, bytevalue = (left.bytevalue!!.toInt() xor (right.asIntegerValue and 255)).toShort(), position = left.position)
}
} else if(left.isWord) {
} else if(left.type==DataType.WORD) {
if(right.asIntegerValue!=null) {
val litval = LiteralValue(wordvalue = left.wordvalue!! xor right.asIntegerValue)
litval.position = left.position
return litval
return LiteralValue(DataType.WORD, wordvalue = left.wordvalue!! xor right.asIntegerValue, position = left.position)
}
}
throw ExpressionError("cannot calculate $left ^ $right", left.position)
}
private fun bitwiseor(left: LiteralValue, right: LiteralValue): LiteralValue {
if(left.isByte) {
if(left.type==DataType.BYTE) {
if(right.asIntegerValue!=null) {
val litval = LiteralValue(bytevalue = (left.bytevalue!!.toInt() or (right.asIntegerValue and 255)).toShort())
litval.position = left.position
return litval
return LiteralValue(DataType.BYTE, bytevalue = (left.bytevalue!!.toInt() or (right.asIntegerValue and 255)).toShort(), position = left.position)
}
} else if(left.isWord) {
} else if(left.type==DataType.WORD) {
if(right.asIntegerValue!=null) {
val litval = LiteralValue(wordvalue = left.wordvalue!! or right.asIntegerValue)
litval.position = left.position
return litval
return LiteralValue(DataType.WORD, wordvalue = left.wordvalue!! or right.asIntegerValue, position = left.position)
}
}
throw ExpressionError("cannot calculate $left | $right", left.position)
}
private fun bitwiseand(left: LiteralValue, right: LiteralValue): LiteralValue {
if(left.isByte) {
if(left.type==DataType.BYTE) {
if(right.asIntegerValue!=null) {
val litval = LiteralValue(bytevalue = (left.bytevalue!!.toInt() or (right.asIntegerValue and 255)).toShort())
litval.position = left.position
return litval
return LiteralValue(DataType.BYTE, bytevalue = (left.bytevalue!!.toInt() or (right.asIntegerValue and 255)).toShort(), position = left.position)
}
} else if(left.isWord) {
} else if(left.type==DataType.WORD) {
if(right.asIntegerValue!=null) {
val litval = LiteralValue(wordvalue = left.wordvalue!! or right.asIntegerValue)
litval.position = left.position
return litval
return LiteralValue(DataType.WORD, wordvalue = left.wordvalue!! or right.asIntegerValue, position = left.position)
}
}
throw ExpressionError("cannot calculate $left & $right", left.position)
@ -506,119 +449,110 @@ class ConstExprEvaluator {
private fun power(left: LiteralValue, right: LiteralValue): LiteralValue {
val error = "cannot calculate $left ** $right"
val litval = when {
return when {
left.asIntegerValue!=null -> when {
right.asIntegerValue!=null -> LiteralValue(wordvalue = left.asIntegerValue.toDouble().pow(right.asIntegerValue).toInt())
right.floatvalue!=null -> LiteralValue(floatvalue = left.asIntegerValue.toDouble().pow(right.floatvalue))
// @todo BYTE?
right.asIntegerValue!=null -> LiteralValue(DataType.WORD, wordvalue = left.asIntegerValue.toDouble().pow(right.asIntegerValue).toInt(), position = left.position)
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue.toDouble().pow(right.floatvalue), position = left.position)
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
right.asIntegerValue!=null -> LiteralValue(floatvalue = left.floatvalue.pow(right.asIntegerValue))
right.floatvalue!=null -> LiteralValue(floatvalue = left.floatvalue.pow(right.floatvalue))
right.asIntegerValue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue.pow(right.asIntegerValue), position = left.position)
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue.pow(right.floatvalue), position = left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
litval.position = left.position
return litval
}
private fun plus(left: LiteralValue, right: LiteralValue): LiteralValue {
val error = "cannot add $left and $right"
val litval = when {
return when {
left.asIntegerValue!=null -> when {
right.asIntegerValue!=null -> LiteralValue.optimalNumeric(left.asIntegerValue + right.asIntegerValue)
right.floatvalue!=null -> LiteralValue(floatvalue = left.asIntegerValue + right.floatvalue)
right.asIntegerValue!=null -> LiteralValue.optimalNumeric(left.asIntegerValue + right.asIntegerValue, left.position)
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue + right.floatvalue, position = left.position)
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
right.asIntegerValue!=null -> LiteralValue(floatvalue = left.floatvalue + right.asIntegerValue)
right.floatvalue!=null -> LiteralValue(floatvalue = left.floatvalue + right.floatvalue)
right.asIntegerValue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue + right.asIntegerValue, position = left.position)
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue + right.floatvalue, position = left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
litval.position = left.position
return litval
}
private fun minus(left: LiteralValue, right: LiteralValue): LiteralValue {
val error = "cannot subtract $left and $right"
val litval = when {
return when {
left.asIntegerValue!=null -> when {
right.asIntegerValue!=null -> LiteralValue.optimalNumeric(left.asIntegerValue - right.asIntegerValue)
right.floatvalue!=null -> LiteralValue(floatvalue = left.asIntegerValue - right.floatvalue)
right.asIntegerValue!=null -> LiteralValue.optimalNumeric(left.asIntegerValue - right.asIntegerValue, left.position)
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue - right.floatvalue, position = left.position)
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
right.asIntegerValue!=null -> LiteralValue(floatvalue = left.floatvalue - right.asIntegerValue)
right.floatvalue!=null -> LiteralValue(floatvalue = left.floatvalue - right.floatvalue)
right.asIntegerValue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue - right.asIntegerValue, position = left.position)
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue - right.floatvalue, position = left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
litval.position = left.position
return litval
}
private fun multiply(left: LiteralValue, right: LiteralValue): LiteralValue {
val error = "cannot multiply $left and $right"
val litval = when {
return when {
left.asIntegerValue!=null -> when {
right.asIntegerValue!=null -> LiteralValue.optimalNumeric(left.asIntegerValue * right.asIntegerValue)
right.floatvalue!=null -> LiteralValue(floatvalue = left.asIntegerValue * right.floatvalue)
right.asIntegerValue!=null -> LiteralValue.optimalNumeric(left.asIntegerValue * right.asIntegerValue, left.position)
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue * right.floatvalue, position = left.position)
right.strvalue!=null -> {
if(right.strvalue.length * left.asIntegerValue > 65535) throw ExpressionError("string too large", left.position)
LiteralValue(strvalue = right.strvalue.repeat(left.asIntegerValue))
LiteralValue(DataType.STR, strvalue = right.strvalue.repeat(left.asIntegerValue), position = left.position)
}
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
right.asIntegerValue!=null -> LiteralValue(floatvalue = left.floatvalue * right.asIntegerValue)
right.floatvalue!=null -> LiteralValue(floatvalue = left.floatvalue * right.floatvalue)
right.asIntegerValue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue * right.asIntegerValue, position = left.position)
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue * right.floatvalue, position = left.position)
else -> throw ExpressionError(error, left.position)
}
left.strvalue!=null -> when {
right.asIntegerValue!=null -> {
if(left.strvalue.length * right.asIntegerValue > 65535) throw ExpressionError("string too large", left.position)
LiteralValue(strvalue = left.strvalue.repeat(right.asIntegerValue))
LiteralValue(DataType.STR, strvalue = left.strvalue.repeat(right.asIntegerValue), position = left.position)
}
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
litval.position = left.position
return litval
}
private fun divide(left: LiteralValue, right: LiteralValue): LiteralValue {
val error = "cannot divide $left by $right"
val litval = when {
return when {
left.asIntegerValue!=null -> when {
right.asIntegerValue!=null -> {
if(right.asIntegerValue==0) throw ExpressionError("attempt to divide by zero", left.position)
LiteralValue.optimalNumeric(left.asIntegerValue / right.asIntegerValue)
LiteralValue.optimalNumeric(left.asIntegerValue / right.asIntegerValue, left.position)
}
right.floatvalue!=null -> {
if(right.floatvalue==0.0) throw ExpressionError("attempt to divide by zero", left.position)
LiteralValue(floatvalue = left.asIntegerValue / right.floatvalue)
LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue / right.floatvalue, position = left.position)
}
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
right.asIntegerValue!=null -> {
if(right.asIntegerValue==0) throw ExpressionError("attempt to divide by zero", left.position)
LiteralValue(floatvalue = left.floatvalue / right.asIntegerValue)
LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue / right.asIntegerValue, position = left.position)
}
right.floatvalue!=null -> {
if(right.floatvalue==0.0) throw ExpressionError("attempt to divide by zero", left.position)
LiteralValue(floatvalue = left.floatvalue / right.floatvalue)
LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue / right.floatvalue, position = left.position)
}
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
litval.position = left.position
return litval
}
}

View File

@ -80,11 +80,11 @@ class StatementOptimizer(private val globalNamespace: INameScope) : IAstProcesso
return if(constvalue.asBooleanValue){
// always true -> keep only if-part
println("${ifStatement.position} Warning: condition is always true")
AnonymousStatementList(ifStatement.parent, ifStatement.statements)
AnonymousStatementList(ifStatement.parent, ifStatement.statements, ifStatement.position)
} else {
// always false -> keep only else-part
println("${ifStatement.position} Warning: condition is always false")
AnonymousStatementList(ifStatement.parent, ifStatement.elsepart)
AnonymousStatementList(ifStatement.parent, ifStatement.elsepart, ifStatement.position)
}
}
return ifStatement

View File

@ -32,7 +32,7 @@ fun importModule(filePath: Path) : Module {
// tokens.commentTokens().forEach { println(it) }
// convert to Ast
val moduleAst = parseTree.toAst(moduleName,true)
val moduleAst = parseTree.toAst(moduleName)
importedModules[moduleAst.name] = moduleAst
// process imports
@ -60,7 +60,7 @@ fun importModule(filePath: Path) : Module {
fun discoverImportedModule(name: String, importedFrom: Path, position: Position?): Path {
val fileName = name + ".ill"
val fileName = "$name.ill"
val locations = mutableListOf(Paths.get(importedFrom.parent.toString()))
val propPath = System.getProperty("il65.libdir")

View File

@ -1,4 +1,4 @@
#/bin/env sh
#!/usr/bin/env sh
IL65_LIBDIR=../lib65
IL65CLASSPATH=out/production/il65

View File

@ -1,6 +1,7 @@
package il65tests
import il65.ast.DataType
import il65.ast.Position
import il65.ast.VarDecl
import il65.ast.VarDeclType
import il65.compiler.*
@ -106,6 +107,9 @@ class TestCompiler {
}
private val dummypos = Position("test", 0, 0, 0)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestZeropage {
@Test
@ -113,26 +117,26 @@ class TestZeropage {
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, false))
assertFailsWith<AssertionError> {
zp.allocate(VarDecl(VarDeclType.MEMORY, DataType.BYTE, null, "", null))
zp.allocate(VarDecl(VarDeclType.MEMORY, DataType.BYTE, null, "", null, dummypos))
}
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "varname", null))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null, dummypos))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null, dummypos))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "varname", null, dummypos))
assertFailsWith<AssertionError> {
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "varname", null))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "varname", null, dummypos))
}
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "varname2", null))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "varname2", null, dummypos))
}
@Test
fun testZpFloatEnable() {
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, false))
assertFailsWith<CompilerException> {
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos))
}
val zp2 = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, true))
zp2.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
zp2.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos))
}
@Test
@ -150,22 +154,22 @@ class TestZeropage {
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, true))
assertEquals(23, zp.available())
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos))
assertFailsWith<CompilerException> {
// in regular zp there aren't 5 sequential bytes free after we take the first sequence
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos))
}
for (i in 0 until zp.available()) {
val loc = zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
val loc = zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null, dummypos))
assertTrue(loc > 0)
}
assertEquals(0, zp.available())
assertFailsWith<CompilerException> {
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null, dummypos))
}
assertFailsWith<CompilerException> {
zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null, dummypos))
}
}
@ -173,33 +177,33 @@ class TestZeropage {
fun testFullAllocation() {
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, true))
assertEquals(239, zp.available())
val loc = zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
val loc = zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos))
assertTrue(loc > 3)
assertFalse(zp.free.contains(loc))
val num = zp.available() / 5
val rest = zp.available() % 5
for(i in 0..num-4) {
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos))
}
assertEquals(19,zp.available())
assertFailsWith<CompilerException> {
// can't allocate because no more sequential bytes, only fragmented
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos))
}
for(i in 0..13) {
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null, dummypos))
}
zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null, dummypos))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null, dummypos))
assertEquals(1, zp.available())
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null, dummypos))
assertFailsWith<CompilerException> {
// no more space
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null, dummypos))
}
}
@ -210,18 +214,18 @@ class TestZeropage {
// 0xb5, 0xb6, 0xf7, 0xf8, 0xf9, 0xfa))
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, true))
assertEquals(23, zp.available())
assertEquals(0x04, zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null)))
assertEquals(0x09, zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null)))
assertEquals(0x12, zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null)))
assertEquals(0x0d, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null)))
assertEquals(0x94, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null)))
assertEquals(0x2a, zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null)))
assertEquals(0xa7, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null)))
assertEquals(0xa9, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null)))
assertEquals(0xb5, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null)))
assertEquals(0xf7, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null)))
assertEquals(0xf9, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null)))
assertEquals(0x52, zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null)))
assertEquals(0x04, zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos)))
assertEquals(0x09, zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null, dummypos)))
assertEquals(0x12, zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null, dummypos)))
assertEquals(0x0d, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null, dummypos)))
assertEquals(0x94, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null, dummypos)))
assertEquals(0x2a, zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null, dummypos)))
assertEquals(0xa7, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null, dummypos)))
assertEquals(0xa9, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null, dummypos)))
assertEquals(0xb5, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null, dummypos)))
assertEquals(0xf7, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null, dummypos)))
assertEquals(0xf9, zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null, dummypos)))
assertEquals(0x52, zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null, dummypos)))
assertEquals(0, zp.available())
}
}