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