split up Literalvalue into numeric and reference ones

This commit is contained in:
Irmen de Jong 2019-07-15 00:26:06 +02:00
parent 8a26b7b248
commit 31f4e378aa
26 changed files with 1128 additions and 1093 deletions

View File

@ -1 +1 @@
1.11
1.20-dev

View File

@ -189,10 +189,10 @@ interface INameScope {
}
interface IExpression: Node {
fun constValue(program: Program): LiteralValue?
fun constValue(program: Program): NumericLiteralValue?
fun accept(visitor: IAstModifyingVisitor): IExpression
fun accept(visitor: IAstVisitor)
fun referencesIdentifiers(vararg name: String): Boolean
fun referencesIdentifiers(vararg name: String): Boolean // todo: remove and use calltree instead
fun inferType(program: Program): DataType?
infix fun isSameAs(other: IExpression): Boolean {
@ -213,7 +213,8 @@ interface IExpression: Node {
return (other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource
&& other.arrayspec.index isSameAs arrayspec.index)
}
is LiteralValue -> return (other is LiteralValue && other==this)
is NumericLiteralValue -> return other==this
is ReferenceLiteralValue -> return other==this
}
return false
}

View File

@ -384,24 +384,24 @@ private fun prog8Parser.ExpressionContext.toAst() : IExpression {
if(litval!=null) {
val booleanlit = litval.booleanliteral()?.toAst()
return if(booleanlit!=null) {
LiteralValue.fromBoolean(booleanlit, litval.toPosition())
NumericLiteralValue.fromBoolean(booleanlit, litval.toPosition())
}
else {
val intLit = litval.integerliteral()?.toAst()
when {
intLit!=null -> when(intLit.datatype) {
DataType.UBYTE -> LiteralValue(DataType.UBYTE, bytevalue = intLit.number.toShort(), position = litval.toPosition())
DataType.BYTE -> LiteralValue(DataType.BYTE, bytevalue = intLit.number.toShort(), position = litval.toPosition())
DataType.UWORD -> LiteralValue(DataType.UWORD, wordvalue = intLit.number.toInt(), position = litval.toPosition())
DataType.WORD -> LiteralValue(DataType.WORD, wordvalue = intLit.number.toInt(), position = litval.toPosition())
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue = intLit.number.toDouble(), position = litval.toPosition())
DataType.UBYTE -> NumericLiteralValue(DataType.UBYTE, intLit.number.toShort(), litval.toPosition())
DataType.BYTE -> NumericLiteralValue(DataType.BYTE, intLit.number.toShort(), litval.toPosition())
DataType.UWORD -> NumericLiteralValue(DataType.UWORD, intLit.number.toInt(), litval.toPosition())
DataType.WORD -> NumericLiteralValue(DataType.WORD, intLit.number.toInt(), litval.toPosition())
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, intLit.number.toDouble(), litval.toPosition())
else -> throw FatalAstException("invalid datatype for numeric literal")
}
litval.floatliteral()!=null -> LiteralValue(DataType.FLOAT, floatvalue = litval.floatliteral().toAst(), position = litval.toPosition())
litval.stringliteral()!=null -> LiteralValue(DataType.STR, strvalue = unescape(litval.stringliteral().text, litval.toPosition()), position = litval.toPosition())
litval.floatliteral()!=null -> NumericLiteralValue(DataType.FLOAT, litval.floatliteral().toAst(), litval.toPosition())
litval.stringliteral()!=null -> ReferenceLiteralValue(DataType.STR, unescape(litval.stringliteral().text, litval.toPosition()), position = litval.toPosition())
litval.charliteral()!=null -> {
try {
LiteralValue(DataType.UBYTE, bytevalue = Petscii.encodePetscii(unescape(litval.charliteral().text, litval.toPosition()), true)[0], position = litval.toPosition())
NumericLiteralValue(DataType.UBYTE, Petscii.encodePetscii(unescape(litval.charliteral().text, litval.toPosition()), true)[0], litval.toPosition())
} catch (ce: CharConversionException) {
throw SyntaxError(ce.message ?: ce.toString(), litval.toPosition())
}
@ -410,7 +410,7 @@ private fun prog8Parser.ExpressionContext.toAst() : IExpression {
val array = litval.arrayliteral()?.toAst()
// the actual type of the arraysize can not yet be determined here (missing namespace & heap)
// the ConstantFolder takes care of that and converts the type if needed.
LiteralValue(DataType.ARRAY_UB, arrayvalue = array, position = litval.toPosition())
ReferenceLiteralValue(DataType.ARRAY_UB, array = array, position = litval.toPosition())
}
else -> throw FatalAstException("invalid parsed literal")
}
@ -433,7 +433,7 @@ private fun prog8Parser.ExpressionContext.toAst() : IExpression {
if(funcall!=null) return funcall
if (rangefrom!=null && rangeto!=null) {
val step = rangestep?.toAst() ?: LiteralValue(DataType.UBYTE, 1, position = toPosition())
val step = rangestep?.toAst() ?: NumericLiteralValue(DataType.UBYTE, 1, toPosition())
return RangeExpr(rangefrom.toAst(), rangeto.toAst(), step, toPosition())
}

View File

@ -26,7 +26,7 @@ class PrefixExpression(val operator: String, var expression: IExpression, overri
expression.linkParents(this)
}
override fun constValue(program: Program): LiteralValue? = null
override fun constValue(program: Program): NumericLiteralValue? = null
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name)
@ -51,7 +51,7 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
}
// binary expression should actually have been optimized away into a single value, before const value was requested...
override fun constValue(program: Program): LiteralValue? = null
override fun constValue(program: Program): NumericLiteralValue? = null
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
@ -220,7 +220,7 @@ class ArrayIndexedExpression(val identifier: IdentifierReference,
arrayspec.linkParents(this)
}
override fun constValue(program: Program): LiteralValue? = null
override fun constValue(program: Program): NumericLiteralValue? = null
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun referencesIdentifiers(vararg name: String) = identifier.referencesIdentifiers(*name)
@ -255,7 +255,7 @@ class TypecastExpression(var expression: IExpression, var type: DataType, val im
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name)
override fun inferType(program: Program): DataType? = type
override fun constValue(program: Program): LiteralValue? {
override fun constValue(program: Program): NumericLiteralValue? {
val cv = expression.constValue(program) ?: return null
return cv.cast(type)
// val value = RuntimeValue(cv.type, cv.asNumericValue!!).cast(type)
@ -276,7 +276,7 @@ data class AddressOf(val identifier: IdentifierReference, override val position:
}
var scopedname: String? = null // will be set in a later state by the compiler
override fun constValue(program: Program): LiteralValue? = null
override fun constValue(program: Program): NumericLiteralValue? = null
override fun referencesIdentifiers(vararg name: String) = false
override fun inferType(program: Program) = DataType.UWORD
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
@ -295,163 +295,200 @@ class DirectMemoryRead(var addressExpression: IExpression, override val position
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun referencesIdentifiers(vararg name: String) = false
override fun inferType(program: Program): DataType? = DataType.UBYTE
override fun constValue(program: Program): LiteralValue? = null
override fun constValue(program: Program): NumericLiteralValue? = null
override fun toString(): String {
return "DirectMemoryRead($addressExpression)"
}
}
open class LiteralValue(val type: DataType,
val bytevalue: Short? = null,
val wordvalue: Int? = null,
val floatvalue: Double? = null,
val strvalue: String? = null,
val arrayvalue: Array<IExpression>? = null,
initHeapId: Int? =null,
override val position: Position) : IExpression {
class NumericLiteralValue(val type: DataType, // only numerical types allowed
val number: Number, // can be byte, word or float depending on the type
override val position: Position) : IExpression {
override lateinit var parent: Node
override fun referencesIdentifiers(vararg name: String) = arrayvalue?.any { it.referencesIdentifiers(*name) } ?: false
val isString = type in StringDatatypes
val isNumeric = type in NumericDatatypes
val isArray = type in ArrayDatatypes
var heapId = initHeapId
private set
companion object {
fun fromBoolean(bool: Boolean, position: Position) =
LiteralValue(DataType.UBYTE, bytevalue = if (bool) 1 else 0, position = position)
NumericLiteralValue(DataType.UBYTE, if (bool) 1 else 0, position)
fun fromNumber(value: Number, type: DataType, position: Position) : LiteralValue {
return when(type) {
in ByteDatatypes -> LiteralValue(type, bytevalue = value.toShort(), position = position)
in WordDatatypes -> LiteralValue(type, wordvalue = value.toInt(), position = position)
DataType.FLOAT -> LiteralValue(type, floatvalue = value.toDouble(), position = position)
else -> throw FatalAstException("non numeric datatype")
}
}
fun optimalNumeric(value: Number, position: Position): LiteralValue {
fun optimalNumeric(value: Number, position: Position): NumericLiteralValue {
return if(value is Double) {
LiteralValue(DataType.FLOAT, floatvalue = value, position = position)
NumericLiteralValue(DataType.FLOAT, value, position)
} else {
when (val intval = value.toInt()) {
in 0..255 -> LiteralValue(DataType.UBYTE, bytevalue = intval.toShort(), position = position)
in -128..127 -> LiteralValue(DataType.BYTE, bytevalue = intval.toShort(), position = position)
in 0..65535 -> LiteralValue(DataType.UWORD, wordvalue = intval, position = position)
in -32768..32767 -> LiteralValue(DataType.WORD, wordvalue = intval, position = position)
else -> LiteralValue(DataType.FLOAT, floatvalue = intval.toDouble(), position = position)
in 0..255 -> NumericLiteralValue(DataType.UBYTE, intval, position)
in -128..127 -> NumericLiteralValue(DataType.BYTE, intval, position)
in 0..65535 -> NumericLiteralValue(DataType.UWORD, intval, position)
in -32768..32767 -> NumericLiteralValue(DataType.WORD, intval, position)
else -> NumericLiteralValue(DataType.FLOAT, intval.toDouble(), position)
}
}
}
fun optimalInteger(value: Number, position: Position): LiteralValue {
val intval = value.toInt()
if(intval.toDouble() != value.toDouble())
throw FatalAstException("value is not an integer: $value")
return when (intval) {
in 0..255 -> LiteralValue(DataType.UBYTE, bytevalue = value.toShort(), position = position)
in -128..127 -> LiteralValue(DataType.BYTE, bytevalue = value.toShort(), position = position)
in 0..65535 -> LiteralValue(DataType.UWORD, wordvalue = value.toInt(), position = position)
fun optimalInteger(value: Int, position: Position): NumericLiteralValue {
return when (value) {
in 0..255 -> NumericLiteralValue(DataType.UBYTE, value, position)
in -128..127 -> NumericLiteralValue(DataType.BYTE, value, position)
in 0..65535 -> NumericLiteralValue(DataType.UWORD, value, position)
else -> throw FatalAstException("integer overflow: $value")
}
}
}
init {
when(type){
in ByteDatatypes -> if(bytevalue==null) throw FatalAstException("literal value missing bytevalue")
in WordDatatypes -> if(wordvalue==null) throw FatalAstException("literal value missing wordvalue")
DataType.FLOAT -> if(floatvalue==null) throw FatalAstException("literal value missing floatvalue")
in StringDatatypes ->
if(strvalue==null && heapId==null) throw FatalAstException("literal value missing strvalue/heapId")
in ArrayDatatypes ->
if(arrayvalue==null && heapId==null) throw FatalAstException("literal value missing arrayvalue/heapId")
else -> throw FatalAstException("invalid type $type")
}
if(bytevalue==null && wordvalue==null && floatvalue==null && arrayvalue==null && strvalue==null && heapId==null)
throw FatalAstException("literal value without actual value")
}
val asNumericValue: Number? = when {
bytevalue!=null -> bytevalue
wordvalue!=null -> wordvalue
floatvalue!=null -> floatvalue
else -> null
}
val asIntegerValue: Int? = when {
bytevalue!=null -> bytevalue.toInt()
wordvalue!=null -> wordvalue
// don't round a float value, otherwise code will not detect that it's not an integer
else -> null
}
val asBooleanValue: Boolean =
(floatvalue!=null && floatvalue != 0.0) ||
(bytevalue!=null && bytevalue != 0.toShort()) ||
(wordvalue!=null && wordvalue != 0) ||
(strvalue!=null && strvalue.isNotEmpty()) ||
(arrayvalue != null && arrayvalue.isNotEmpty())
val asBooleanValue: Boolean = number!=0
override fun linkParents(parent: Node) {
this.parent = parent
arrayvalue?.forEach {it.linkParents(this)}
}
override fun constValue(program: Program): LiteralValue? {
if(arrayvalue!=null) {
for(v in arrayvalue) {
if(v.constValue(program)==null) return null
override fun referencesIdentifiers(vararg name: String) = false
override fun constValue(program: Program) = this
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun toString(): String = "NumericLiteral(${type.name}:$number)"
override fun inferType(program: Program) = type
override fun hashCode(): Int = type.hashCode() * 31 xor number.hashCode()
override fun equals(other: Any?): Boolean {
if(other==null || other !is NumericLiteralValue)
return false
return number.toDouble()==other.number.toDouble()
}
operator fun compareTo(other: NumericLiteralValue): Int = number.toDouble().compareTo(other.number.toDouble())
fun cast(targettype: DataType): NumericLiteralValue? {
if(type==targettype)
return this
val numval = number.toDouble()
when(type) {
DataType.UBYTE -> {
if(targettype== DataType.BYTE && numval <= 127)
return NumericLiteralValue(targettype, number.toShort(), position)
if(targettype== DataType.WORD || targettype== DataType.UWORD)
return NumericLiteralValue(targettype, number.toInt(), position)
if(targettype== DataType.FLOAT)
return NumericLiteralValue(targettype, number.toDouble(), position)
}
DataType.BYTE -> {
if(targettype== DataType.UBYTE && numval >= 0)
return NumericLiteralValue(targettype, number.toShort(), position)
if(targettype== DataType.UWORD && numval >= 0)
return NumericLiteralValue(targettype, number.toInt(), position)
if(targettype== DataType.WORD)
return NumericLiteralValue(targettype, number.toInt(), position)
if(targettype== DataType.FLOAT)
return NumericLiteralValue(targettype, number.toDouble(), position)
}
DataType.UWORD -> {
if(targettype== DataType.BYTE && numval <= 127)
return NumericLiteralValue(targettype, number.toShort(), position)
if(targettype== DataType.UBYTE && numval <= 255)
return NumericLiteralValue(targettype, number.toShort(), position)
if(targettype== DataType.WORD && numval <= 32767)
return NumericLiteralValue(targettype, number.toInt(), position)
if(targettype== DataType.FLOAT)
return NumericLiteralValue(targettype, number.toDouble(), position)
}
DataType.WORD -> {
if(targettype== DataType.BYTE && numval >= -128 && numval <=127)
return NumericLiteralValue(targettype, number.toShort(), position)
if(targettype== DataType.UBYTE && numval >= 0 && numval <= 255)
return NumericLiteralValue(targettype, number.toShort(), position)
if(targettype== DataType.UWORD && numval >=0)
return NumericLiteralValue(targettype, number.toInt(), position)
if(targettype== DataType.FLOAT)
return NumericLiteralValue(targettype, number.toDouble(), position)
}
DataType.FLOAT -> {
if (targettype == DataType.BYTE && numval >= -128 && numval <=127)
return NumericLiteralValue(targettype, number.toShort(), position)
if (targettype == DataType.UBYTE && numval >=0 && numval <= 255)
return NumericLiteralValue(targettype, number.toShort(), position)
if (targettype == DataType.WORD && numval >= -32768 && numval <= 32767)
return NumericLiteralValue(targettype, number.toInt(), position)
if (targettype == DataType.UWORD && numval >=0 && numval <= 65535)
return NumericLiteralValue(targettype, number.toInt(), position)
}
else -> {}
}
return this
return null // invalid type conversion from $this to $targettype
}
}
class ReferenceLiteralValue(val type: DataType, // only reference types allowed here
val str: String? = null,
val array: Array<IExpression>? = null,
// actually, at the moment, we don't have struct literals in the language
initHeapId: Int? =null,
override val position: Position) : IExpression {
override lateinit var parent: Node
override fun referencesIdentifiers(vararg name: String): Boolean {
return array?.any { it.referencesIdentifiers(*name) } ?: false
}
val isString = type in StringDatatypes
val isArray = type in ArrayDatatypes
var heapId = initHeapId
private set
init {
when(type){
in StringDatatypes ->
if(str==null && heapId==null) throw FatalAstException("literal value missing strvalue/heapId")
in ArrayDatatypes ->
if(array==null && heapId==null) throw FatalAstException("literal value missing arrayvalue/heapId")
// DataType.STRUCT ->
// if(struct==null && heapId==null) throw FatalAstException("literal value missing structvalue/heapId")
else -> throw FatalAstException("invalid type $type")
}
if(array==null && str==null && heapId==null)
throw FatalAstException("literal ref value without actual value")
}
override fun linkParents(parent: Node) {
this.parent = parent
array?.forEach {it.linkParents(this)}
}
override fun constValue(program: Program): NumericLiteralValue? {
// TODO: what about arrays that only contain constant numbers?
return null
}
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun toString(): String {
val vstr = when(type) {
DataType.UBYTE -> "ubyte:$bytevalue"
DataType.BYTE -> "byte:$bytevalue"
DataType.UWORD -> "uword:$wordvalue"
DataType.WORD -> "word:$wordvalue"
DataType.FLOAT -> "float:$floatvalue"
in StringDatatypes -> "str:'${escape(strvalue?:"")}'"
in ArrayDatatypes -> "array:$arrayvalue"
else -> throw FatalAstException("weird datatype")
val valueStr = when(type) {
in StringDatatypes -> "'${escape(str!!)}'"
in ArrayDatatypes -> "$array"
DataType.STRUCT -> TODO("struct literals")
else -> throw FatalAstException("weird ref type")
}
return "LiteralValue($vstr)"
return "ReferenceValueLiteral($type, $valueStr)"
}
override fun inferType(program: Program) = type
override fun hashCode(): Int {
val bh = bytevalue?.hashCode() ?: 0x10001234
val wh = wordvalue?.hashCode() ?: 0x01002345
val fh = floatvalue?.hashCode() ?: 0x00103456
val sh = strvalue?.hashCode() ?: 0x00014567
val ah = arrayvalue?.hashCode() ?: 0x11119876
var hash = bh * 31 xor wh
hash = hash*31 xor fh
hash = hash*31 xor sh
hash = hash*31 xor ah
hash = hash*31 xor type.hashCode()
return hash
val sh = str?.hashCode() ?: 0x00014567
val ah = array?.hashCode() ?: 0x11119876
return sh * 31 xor ah xor type.hashCode()
}
override fun equals(other: Any?): Boolean {
if(other==null || other !is LiteralValue)
if(other==null || other !is ReferenceLiteralValue)
return false
if(isNumeric && other.isNumeric)
return asNumericValue?.toDouble()==other.asNumericValue?.toDouble()
if(isArray && other.isArray)
return arrayvalue!!.contentEquals(other.arrayvalue!!) && heapId==other.heapId
return array!!.contentEquals(other.array!!) && heapId==other.heapId
if(isString && other.isString)
return strvalue==other.strvalue && heapId==other.heapId
return str==other.str && heapId==other.heapId
if(type!=other.type)
return false
@ -459,74 +496,17 @@ open class LiteralValue(val type: DataType,
return compareTo(other) == 0
}
operator fun compareTo(other: LiteralValue): Int {
val numLeft = asNumericValue?.toDouble()
val numRight = other.asNumericValue?.toDouble()
if(numLeft!=null && numRight!=null)
return numLeft.compareTo(numRight)
if(strvalue!=null && other.strvalue!=null)
return strvalue.compareTo(other.strvalue)
operator fun compareTo(other: ReferenceLiteralValue): Int {
throw ExpressionError("cannot order compare type $type with ${other.type}", other.position)
}
fun cast(targettype: DataType): LiteralValue? {
fun cast(targettype: DataType): ReferenceLiteralValue? {
if(type==targettype)
return this
when(type) {
DataType.UBYTE -> {
if(targettype== DataType.BYTE && bytevalue!! <= 127)
return LiteralValue(targettype, bytevalue = bytevalue, position = position)
if(targettype== DataType.WORD || targettype== DataType.UWORD)
return LiteralValue(targettype, wordvalue = bytevalue!!.toInt(), position = position)
if(targettype== DataType.FLOAT)
return LiteralValue(targettype, floatvalue = bytevalue!!.toDouble(), position = position)
}
DataType.BYTE -> {
if(targettype== DataType.UBYTE && bytevalue!! >= 0)
return LiteralValue(targettype, bytevalue = bytevalue, position = position)
if(targettype== DataType.UWORD && bytevalue!! >= 0)
return LiteralValue(targettype, wordvalue = bytevalue.toInt(), position = position)
if(targettype== DataType.WORD)
return LiteralValue(targettype, wordvalue = bytevalue!!.toInt(), position = position)
if(targettype== DataType.FLOAT)
return LiteralValue(targettype, floatvalue = bytevalue!!.toDouble(), position = position)
}
DataType.UWORD -> {
if(targettype== DataType.BYTE && wordvalue!! <= 127)
return LiteralValue(targettype, bytevalue = wordvalue.toShort(), position = position)
if(targettype== DataType.UBYTE && wordvalue!! <= 255)
return LiteralValue(targettype, bytevalue = wordvalue.toShort(), position = position)
if(targettype== DataType.WORD && wordvalue!! <= 32767)
return LiteralValue(targettype, wordvalue = wordvalue, position = position)
if(targettype== DataType.FLOAT)
return LiteralValue(targettype, floatvalue = wordvalue!!.toDouble(), position = position)
}
DataType.WORD -> {
if(targettype== DataType.BYTE && wordvalue!! in -128..127)
return LiteralValue(targettype, bytevalue = wordvalue.toShort(), position = position)
if(targettype== DataType.UBYTE && wordvalue!! in 0..255)
return LiteralValue(targettype, bytevalue = wordvalue.toShort(), position = position)
if(targettype== DataType.UWORD && wordvalue!! >=0)
return LiteralValue(targettype, wordvalue = wordvalue, position = position)
if(targettype== DataType.FLOAT)
return LiteralValue(targettype, floatvalue = wordvalue!!.toDouble(), position = position)
}
DataType.FLOAT -> {
val value = floatvalue!!.toInt()
if (targettype == DataType.BYTE && value in -128..127)
return LiteralValue(targettype, bytevalue = value.toShort(), position = position)
if (targettype == DataType.UBYTE && value in 0..255)
return LiteralValue(targettype, bytevalue = value.toShort(), position = position)
if (targettype == DataType.WORD && value in -32768..32767)
return LiteralValue(targettype, wordvalue = value, position = position)
if (targettype == DataType.UWORD && value in 0..65535)
return LiteralValue(targettype, wordvalue = value, position = position)
}
in StringDatatypes -> {
if(targettype in StringDatatypes)
return this
return ReferenceLiteralValue(targettype, str, initHeapId = heapId, position = position)
}
else -> {}
}
@ -534,22 +514,23 @@ open class LiteralValue(val type: DataType,
}
fun addToHeap(heap: HeapValues) {
println("-->adding to HEAP: $this ${this.position}") // TODO
if(heapId==null) {
if (strvalue != null) {
heapId = heap.addString(type, strvalue)
if (str != null) {
heapId = heap.addString(type, str)
}
else if (arrayvalue!=null) {
if(arrayvalue.any {it is AddressOf }) {
val intArrayWithAddressOfs = arrayvalue.map {
else if (array!=null) {
if(array.any {it is AddressOf }) {
val intArrayWithAddressOfs = array.map {
when (it) {
is AddressOf -> IntegerOrAddressOf(null, it)
is LiteralValue -> IntegerOrAddressOf(it.asIntegerValue, null)
is NumericLiteralValue -> IntegerOrAddressOf(it.number.toInt(), null)
else -> throw FatalAstException("invalid datatype in array")
}
}
heapId = heap.addIntegerArray(type, intArrayWithAddressOfs.toTypedArray())
} else {
val valuesInArray = arrayvalue.map { (it as LiteralValue).asNumericValue!! }
val valuesInArray = array.map { (it as NumericLiteralValue).number }
heapId = if(type== DataType.ARRAY_F) {
val doubleArray = valuesInArray.map { it.toDouble() }.toDoubleArray()
heap.addDoublesArray(doubleArray)
@ -576,7 +557,7 @@ class RangeExpr(var from: IExpression,
step.linkParents(this)
}
override fun constValue(program: Program): LiteralValue? = null
override fun constValue(program: Program): NumericLiteralValue? = null
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun referencesIdentifiers(vararg name: String): Boolean = from.referencesIdentifiers(*name) || to.referencesIdentifiers(*name)
@ -599,30 +580,32 @@ class RangeExpr(var from: IExpression,
}
fun size(): Int? {
val fromLv = (from as? LiteralValue)
val toLv = (to as? LiteralValue)
val fromLv = (from as? NumericLiteralValue)
val toLv = (to as? NumericLiteralValue)
if(fromLv==null || toLv==null)
return null
return toConstantIntegerRange()?.count()
}
fun toConstantIntegerRange(): IntProgression? {
val fromLv = from as? LiteralValue
val toLv = to as? LiteralValue
if(fromLv==null || toLv==null)
return null // non-constant range
val fromVal: Int
val toVal: Int
if(fromLv.isString && toLv.isString) {
val fromRlv = from as? ReferenceLiteralValue
val toRlv = to as? ReferenceLiteralValue
if(fromRlv!=null && fromRlv.isString && toRlv!=null && toRlv.isString) {
// string range -> int range over petscii values
fromVal = Petscii.encodePetscii(fromLv.strvalue!!, true)[0].toInt()
toVal = Petscii.encodePetscii(toLv.strvalue!!, true)[0].toInt()
fromVal = Petscii.encodePetscii(fromRlv.str!!, true)[0].toInt()
toVal = Petscii.encodePetscii(toRlv.str!!, true)[0].toInt()
} else {
val fromLv = from as? NumericLiteralValue
val toLv = to as? NumericLiteralValue
if(fromLv==null || toLv==null)
return null // non-constant range
// integer range
fromVal = (from as LiteralValue).asIntegerValue!!
toVal = (to as LiteralValue).asIntegerValue!!
fromVal = fromLv.number.toInt()
toVal = toLv.number.toInt()
}
val stepVal = (step as? LiteralValue)?.asIntegerValue ?: 1
val stepVal = (step as? NumericLiteralValue)?.number?.toInt() ?: 1
return when {
fromVal <= toVal -> when {
stepVal <= 0 -> IntRange.EMPTY
@ -645,7 +628,7 @@ class RegisterExpr(val register: Register, override val position: Position) : IE
this.parent = parent
}
override fun constValue(program: Program): LiteralValue? = null
override fun constValue(program: Program): NumericLiteralValue? = null
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun referencesIdentifiers(vararg name: String): Boolean = register.name in name
@ -672,7 +655,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
this.parent = parent
}
override fun constValue(program: Program): LiteralValue? {
override fun constValue(program: Program): NumericLiteralValue? {
val node = program.namespace.lookup(nameInSource, this)
?: throw UndefinedSymbolError(this)
val vardecl = node as? VarDecl
@ -703,7 +686,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
fun heapId(namespace: INameScope): Int {
val node = namespace.lookup(nameInSource, this) ?: throw UndefinedSymbolError(this)
return ((node as? VarDecl)?.value as? LiteralValue)?.heapId ?: throw FatalAstException("identifier is not on the heap: $this")
return ((node as? VarDecl)?.value as? ReferenceLiteralValue)?.heapId ?: throw FatalAstException("identifier is not on the heap: $this")
}
}
@ -720,12 +703,12 @@ class FunctionCall(override var target: IdentifierReference,
override fun constValue(program: Program) = constValue(program, true)
private fun constValue(program: Program, withDatatypeCheck: Boolean): LiteralValue? {
private fun constValue(program: Program, withDatatypeCheck: Boolean): NumericLiteralValue? {
// if the function is a built-in function and the args are consts, should try to const-evaluate!
// lenghts of arrays and strings are constants that are determined at compile time!
if(target.nameInSource.size>1) return null
try {
var resultValue: LiteralValue? = null
var resultValue: NumericLiteralValue? = null
val func = BuiltinFunctions[target.nameInSource[0]]
if(func!=null) {
val exprfunc = func.constExpressionFunc

View File

@ -15,10 +15,10 @@ import java.io.File
internal class AstChecker(private val program: Program,
private val compilerOptions: CompilationOptions) : IAstModifyingVisitor {
private val checkResult: MutableList<AstException> = mutableListOf()
private val heapStringSentinel: Int
private val heapIdSentinel: Int
init {
val stringSentinel = program.heap.allEntries().firstOrNull {it.value.str==""}
heapStringSentinel = stringSentinel?.key ?: program.heap.addString(DataType.STR, "")
heapIdSentinel = stringSentinel?.key ?: program.heap.addString(DataType.STR, "")
}
fun result(): List<AstException> {
@ -367,7 +367,7 @@ internal class AstChecker(private val program: Program,
}
private fun processAssignmentTarget(assignment: Assignment, target: AssignTarget): Assignment {
val memAddr = target.memoryAddress?.addressExpression?.constValue(program)?.asIntegerValue
val memAddr = target.memoryAddress?.addressExpression?.constValue(program)?.number?.toInt()
if(memAddr!=null) {
if(memAddr<0 || memAddr>=65536)
checkResult.add(ExpressionError("address out of range", target.position))
@ -417,15 +417,17 @@ internal class AstChecker(private val program: Program,
if(targetDatatype!=null) {
val constVal = assignment.value.constValue(program)
if(constVal!=null) {
val targetVar =
if(target.identifier!=null)
program.namespace.lookup(target.identifier.nameInSource, assignment) as? VarDecl
else
null
val arrayspec = if(target.identifier!=null) targetVar?.arraysize else null
checkValueTypeAndRange(targetDatatype, targetVar?.struct,
arrayspec ?: ArrayIndex(LiteralValue.optimalInteger(-1, assignment.position), assignment.position),
constVal, program.heap)
checkValueTypeAndRange(targetDatatype, constVal)
// TODO what about arrays etc:
// val targetVar =
// if(target.identifier!=null)
// program.namespace.lookup(target.identifier.nameInSource, assignment) as? VarDecl
// else
// null
// val arrayspec = if(target.identifier!=null) targetVar?.arraysize else null
// checkValueTypeAndRange(targetDatatype, targetVar?.struct,
// arrayspec ?: ArrayIndex(NumericLiteralValue.optimalInteger(-1, assignment.position), assignment.position),
// constVal, program.heap)
} else {
val sourceDatatype: DataType? = assignment.value.inferType(program)
if(sourceDatatype==null) {
@ -490,7 +492,7 @@ internal class AstChecker(private val program: Program,
checkResult.add(SyntaxError("array variable is missing a size specification or an initialization value", decl.position))
return decl
}
if(decl.value is LiteralValue && !(decl.value as LiteralValue).isArray) {
if(decl.value is NumericLiteralValue) {
checkResult.add(SyntaxError("unsized array declaration cannot use a single literal initialization value", decl.position))
return decl
}
@ -516,9 +518,9 @@ internal class AstChecker(private val program: Program,
// initialize numeric var with value zero by default.
val litVal =
when {
decl.datatype in ByteDatatypes -> LiteralValue(decl.datatype, bytevalue = 0, position = decl.position)
decl.datatype in WordDatatypes -> LiteralValue(decl.datatype, wordvalue = 0, position = decl.position)
else -> LiteralValue(decl.datatype, floatvalue = 0.0, position = decl.position)
decl.datatype in ByteDatatypes -> NumericLiteralValue(decl.datatype, 0, decl.position)
decl.datatype in WordDatatypes -> NumericLiteralValue(decl.datatype, 0, decl.position)
else -> NumericLiteralValue(decl.datatype, 0.0, decl.position)
}
litVal.parent = decl
decl.value = litVal
@ -526,29 +528,34 @@ internal class AstChecker(private val program: Program,
decl.datatype == DataType.STRUCT -> {
// TODO structs are not initialized with a literal value yet, should be an array of zeros!
}
decl.type== VarDeclType.VAR -> {
val litVal = LiteralValue(decl.datatype, initHeapId = heapStringSentinel, position = decl.position) // point to the sentinel heap value instead
litVal.parent=decl
decl.value = litVal
decl.type == VarDeclType.VAR -> {
// TODO is an array declaration without an intialization value
TODO("$decl")
// val litVal = ReferenceLiteralValue(decl.datatype, initHeapId = heapIdSentinel, position = decl.position) // point to the sentinel heap value instead
// litVal.parent = decl
// decl.value = litVal
}
else -> err("var/const declaration needs a compile-time constant initializer value for type ${decl.datatype}")
// const fold should have provided it!
}
return super.visit(decl)
}
when {
decl.value is RangeExpr -> {
when(decl.value) {
is RangeExpr -> {
if(decl.arraysize!=null)
checkValueTypeAndRange(decl.datatype, decl.arraysize!!, decl.value as RangeExpr)
}
decl.value is LiteralValue -> {
is ReferenceLiteralValue -> {
val arraySpec = decl.arraysize ?: (
if((decl.value as LiteralValue).isArray)
ArrayIndex.forArray(decl.value as LiteralValue, program.heap)
if((decl.value as ReferenceLiteralValue).isArray)
ArrayIndex.forArray(decl.value as ReferenceLiteralValue, program.heap)
else
ArrayIndex(LiteralValue.optimalInteger(-2, decl.position), decl.position)
ArrayIndex(NumericLiteralValue.optimalInteger(-2, decl.position), decl.position)
)
checkValueTypeAndRange(decl.datatype, decl.struct, arraySpec, decl.value as LiteralValue, program.heap)
checkValueTypeAndRange(decl.datatype, decl.struct, arraySpec, decl.value as ReferenceLiteralValue, program.heap)
}
is NumericLiteralValue -> {
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue)
}
else -> {
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!.javaClass.simpleName}")
@ -573,11 +580,11 @@ internal class AstChecker(private val program: Program,
}
}
if(decl.value !is LiteralValue) {
err("value of memory var decl is not a literal (it is a ${decl.value!!.javaClass.simpleName}).", decl.value?.position)
if(decl.value !is NumericLiteralValue) {
err("value of memory var decl is not a numeric literal (it is a ${decl.value!!.javaClass.simpleName}).", decl.value?.position)
} else {
val value = decl.value as LiteralValue
if (value.asIntegerValue == null || value.asIntegerValue< 0 || value.asIntegerValue > 65535) {
val value = decl.value as NumericLiteralValue
if (value.type !in IntegerDatatypes || value.number.toInt() < 0 || value.number.toInt() > 65535) {
err("memory address must be valid integer 0..\$ffff")
}
}
@ -672,18 +679,18 @@ internal class AstChecker(private val program: Program,
checkResult.add(NameError("included file not found: $filename", directive.position))
}
override fun visit(literalValue: LiteralValue): LiteralValue {
if(!compilerOptions.floats && literalValue.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
checkResult.add(SyntaxError("floating point used, but that is not enabled via options", literalValue.position))
override fun visit(refLiteral: ReferenceLiteralValue): ReferenceLiteralValue {
if(!compilerOptions.floats && refLiteral.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
checkResult.add(SyntaxError("floating point used, but that is not enabled via options", refLiteral.position))
}
val arrayspec =
if(literalValue.isArray)
ArrayIndex.forArray(literalValue, program.heap)
if(refLiteral.isArray)
ArrayIndex.forArray(refLiteral, program.heap)
else
ArrayIndex(LiteralValue.optimalInteger(-3, literalValue.position), literalValue.position)
checkValueTypeAndRange(literalValue.type, null, arrayspec, literalValue, program.heap)
ArrayIndex(NumericLiteralValue.optimalInteger(-3, refLiteral.position), refLiteral.position)
checkValueTypeAndRange(refLiteral.type, null, arrayspec, refLiteral, program.heap)
val lv = super.visit(literalValue)
val lv = super.visit(refLiteral)
when(lv.type) {
in StringDatatypes -> {
if(lv.heapId==null)
@ -715,7 +722,7 @@ internal class AstChecker(private val program: Program,
when(expr.operator){
"/", "%" -> {
val constvalRight = expr.right.constValue(program)
val divisor = constvalRight?.asNumericValue?.toDouble()
val divisor = constvalRight?.number?.toDouble()
if(divisor==0.0)
checkResult.add(ExpressionError("division by zero", expr.right.position))
if(expr.operator=="%") {
@ -733,7 +740,7 @@ internal class AstChecker(private val program: Program,
checkResult.add(ExpressionError("logical operator can only be used on boolean operands", expr.right.position))
val constLeft = expr.left.constValue(program)
val constRight = expr.right.constValue(program)
if(constLeft!=null && constLeft.asIntegerValue !in 0..1 || constRight!=null && constRight.asIntegerValue !in 0..1)
if(constLeft!=null && constLeft.number.toInt() !in 0..1 || constRight!=null && constRight.number.toInt() !in 0..1)
checkResult.add(ExpressionError("const literal argument of logical operator must be boolean (0 or 1)", expr.position))
}
"&", "|", "^" -> {
@ -763,34 +770,37 @@ internal class AstChecker(private val program: Program,
super.visit(range)
val from = range.from.constValue(program)
val to = range.to.constValue(program)
val stepLv = range.step.constValue(program) ?: LiteralValue(DataType.UBYTE, 1, position = range.position)
if (stepLv.asIntegerValue == null || stepLv.asIntegerValue == 0) {
val stepLv = range.step.constValue(program) ?: NumericLiteralValue(DataType.UBYTE, 1, range.position)
if (stepLv.type !in IntegerDatatypes || stepLv.number.toInt() == 0) {
err("range step must be an integer != 0")
return range
}
val step = stepLv.asIntegerValue
val step = stepLv.number.toInt()
if(from!=null && to != null) {
when {
from.asIntegerValue!=null && to.asIntegerValue!=null -> {
if(from.asIntegerValue == to.asIntegerValue)
from.type in IntegerDatatypes && to.type in IntegerDatatypes -> {
val fromValue = from.number.toInt()
val toValue = to.number.toInt()
if(fromValue== toValue)
printWarning("range is just a single value, don't use a loop here", range.position)
else if(from.asIntegerValue < to.asIntegerValue && step<=0)
else if(fromValue < toValue && step<=0)
err("ascending range requires step > 0")
else if(from.asIntegerValue > to.asIntegerValue && step>=0)
err("descending range requires step < 0")
}
from.isString && to.isString -> {
val fromString = from.strvalue!!
val toString = to.strvalue!!
if(fromString.length!=1 || toString.length!=1)
err("range from and to must be a single character")
if(fromString[0] == toString[0])
printWarning("range contains just a single character", range.position)
else if(fromString[0] < toString[0] && step<=0)
err("ascending range requires step > 0")
else if(fromString[0] > toString[0] && step>=0)
else if(fromValue > toValue && step>=0)
err("descending range requires step < 0")
}
// TODO range over single-character strings (... or have they been converted to byte literals already?)
// from.isString && to.isString -> {
// val fromString = from.strvalue!!
// val toString = to.strvalue!!
// if(fromString.length!=1 || toString.length!=1)
// err("range from and to must be a single character")
// if(fromString[0] == toString[0])
// printWarning("range contains just a single character", range.position)
// else if(fromString[0] < toString[0] && step<=0)
// err("ascending range requires step > 0")
// else if(fromString[0] > toString[0] && step>=0)
// err("descending range requires step < 0")
// }
else -> err("range expression must be over integers or over characters")
}
}
@ -865,7 +875,7 @@ internal class AstChecker(private val program: Program,
if(target.isAsmSubroutine) {
if (target.asmParameterRegisters[arg.first.index].registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.XY, RegisterOrPair.X)) {
if (arg.first.value !is LiteralValue && arg.first.value !is IdentifierReference)
if (arg.first.value !is NumericLiteralValue && arg.first.value !is IdentifierReference)
printWarning("calling a subroutine that expects X as a parameter is problematic, more so when providing complex arguments. If you see a compiler error/crash about this later, try to simplify this call", position)
}
@ -924,14 +934,14 @@ internal class AstChecker(private val program: Program,
val arraysize = target.arraysize?.size()
if(arraysize!=null) {
// check out of bounds
val index = (arrayIndexedExpression.arrayspec.index as? LiteralValue)?.asIntegerValue
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
if(index!=null && (index<0 || index>=arraysize))
checkResult.add(ExpressionError("array index out of bounds", arrayIndexedExpression.arrayspec.position))
} else if(target.datatype in StringDatatypes) {
// check string lengths
val heapId = (target.value as LiteralValue).heapId!!
val heapId = (target.value as ReferenceLiteralValue).heapId!!
val stringLen = program.heap.get(heapId).str!!.length
val index = (arrayIndexedExpression.arrayspec.index as? LiteralValue)?.asIntegerValue
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
if(index!=null && (index<0 || index>=stringLen))
checkResult.add(ExpressionError("index out of bounds", arrayIndexedExpression.arrayspec.position))
}
@ -999,86 +1009,47 @@ internal class AstChecker(private val program: Program,
checkResult.add(SyntaxError("can't assign a range to a scalar type", range.position))
return false
}
in StringDatatypes -> {
// range check bytes (chars)
if(!from.isString || !to.isString) {
checkResult.add(ExpressionError("range for string must have single characters from and to values", range.position))
return false
}
val rangeSize=range.size()
if(rangeSize!=null && (rangeSize<0 || rangeSize>255)) {
checkResult.add(ExpressionError("size of range for string must be 0..255, instead of $rangeSize", range.position))
return false
}
return true
}
in ArrayDatatypes -> {
// range and length check bytes
val expectedSize = arrayspec.size()
val rangeSize=range.size()
if(rangeSize!=null && rangeSize != expectedSize) {
checkResult.add(ExpressionError("range size doesn't match array size, expected $expectedSize found $rangeSize", range.position))
return false
}
return true
in PassByReferenceDatatypes -> {
TODO("reference type range check $targetDt")
}
// in StringDatatypes -> {
// // range check bytes (chars)
// if(!from.isString || !to.isString) {
// checkResult.add(ExpressionError("range for string must have single characters from and to values", range.position))
// return false
// }
// val rangeSize=range.size()
// if(rangeSize!=null && (rangeSize<0 || rangeSize>255)) {
// checkResult.add(ExpressionError("size of range for string must be 0..255, instead of $rangeSize", range.position))
// return false
// }
// return true
// }
// in ArrayDatatypes -> {
// // range and length check bytes
// val expectedSize = arrayspec.size()
// val rangeSize=range.size()
// if(rangeSize!=null && rangeSize != expectedSize) {
// checkResult.add(ExpressionError("range size doesn't match array size, expected $expectedSize found $rangeSize", range.position))
// return false
// }
// return true
// }
else -> throw FatalAstException("invalid targetDt")
}
}
private fun checkValueTypeAndRange(targetDt: DataType, struct: StructDecl?,
arrayspec: ArrayIndex, value: LiteralValue, heap: HeapValues) : Boolean {
arrayspec: ArrayIndex, value: ReferenceLiteralValue, heap: HeapValues) : Boolean {
fun err(msg: String) : Boolean {
checkResult.add(ExpressionError(msg, value.position))
return false
}
when (targetDt) {
DataType.FLOAT -> {
val number = when(value.type) {
in ByteDatatypes -> value.bytevalue!!.toDouble()
in WordDatatypes -> value.wordvalue!!.toDouble()
DataType.FLOAT -> value.floatvalue!!
else -> return err("numeric value expected")
}
if (number > 1.7014118345e+38 || number < -1.7014118345e+38)
return err("value '$number' out of range for MFLPT format")
}
DataType.UBYTE -> {
val number = value.asIntegerValue ?: return if (value.floatvalue!=null)
err("unsigned byte value expected instead of float; possible loss of precision")
else
err("unsigned byte value expected")
if (number < 0 || number > 255)
return err("value '$number' out of range for unsigned byte")
}
DataType.BYTE -> {
val number = value.asIntegerValue ?: return if (value.floatvalue!=null)
err("byte value expected instead of float; possible loss of precision")
else
err("byte value expected")
if (number < -128 || number > 127)
return err("value '$number' out of range for byte")
}
DataType.UWORD -> {
val number = value.asIntegerValue ?: return if (value.floatvalue!=null)
err("unsigned word value expected instead of float; possible loss of precision")
else
err("unsigned word value or address expected")
if (number < 0 || number > 65535)
return err("value '$number' out of range for unsigned word")
}
DataType.WORD -> {
val number = value.asIntegerValue ?: return if (value.floatvalue!=null)
err("word value expected instead of float; possible loss of precision")
else
err("word value expected")
if (number < -32768 || number > 32767)
return err("value '$number' out of range for word")
}
DataType.STR, DataType.STR_S -> {
in StringDatatypes -> {
if(!value.isString)
return err("string value expected")
if (value.strvalue!!.length > 255)
if (value.str!!.length > 255)
return err("string length must be 0-255")
}
DataType.ARRAY_UB, DataType.ARRAY_B -> {
@ -1087,14 +1058,14 @@ internal class AstChecker(private val program: Program,
if(!checkArrayValues(value, targetDt))
return false
val arraySpecSize = arrayspec.size()
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize
val arraySize = value.array?.size ?: heap.get(value.heapId!!).arraysize
if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize<1 || arraySpecSize>256)
return err("byte array length must be 1-256")
val constX = arrayspec.index.constValue(program)
if(constX?.asIntegerValue==null)
if(constX?.type !in IntegerDatatypes)
return err("array size specifier must be constant integer value")
val expectedSize = constX.asIntegerValue
val expectedSize = constX!!.number.toInt()
if (arraySize != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
return true
@ -1109,14 +1080,14 @@ internal class AstChecker(private val program: Program,
if(!checkArrayValues(value, targetDt))
return false
val arraySpecSize = arrayspec.size()
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize
val arraySize = value.array?.size ?: heap.get(value.heapId!!).arraysize
if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize<1 || arraySpecSize>128)
return err("word array length must be 1-128")
val constX = arrayspec.index.constValue(program)
if(constX?.asIntegerValue==null)
if(constX?.type !in IntegerDatatypes)
return err("array size specifier must be constant integer value")
val expectedSize = constX.asIntegerValue
val expectedSize = constX!!.number.toInt()
if (arraySize != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
return true
@ -1130,23 +1101,23 @@ internal class AstChecker(private val program: Program,
if(value.type==targetDt) {
if(!checkArrayValues(value, targetDt))
return false
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).doubleArray!!.size
val arraySize = value.array?.size ?: heap.get(value.heapId!!).doubleArray!!.size
val arraySpecSize = arrayspec.size()
if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize < 1 || arraySpecSize>51)
return err("float array length must be 1-51")
val constX = arrayspec.index.constValue(program)
if(constX?.asIntegerValue==null)
if(constX?.type !in IntegerDatatypes)
return err("array size specifier must be constant integer value")
val expectedSize = constX.asIntegerValue
val expectedSize = constX!!.number.toInt()
if (arraySize != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
} else
return err("invalid float array size, must be 1-51")
// check if the floating point values are all within range
val doubles = if(value.arrayvalue!=null)
value.arrayvalue.map {it.constValue(program)?.asNumericValue!!.toDouble()}.toDoubleArray()
val doubles = if(value.array!=null)
value.array.map {it.constValue(program)?.number!!.toDouble()}.toDoubleArray()
else
heap.get(value.heapId!!).doubleArray!!
if(doubles.any { it < FLOAT_MAX_NEGATIVE || it> FLOAT_MAX_POSITIVE })
@ -1157,9 +1128,9 @@ internal class AstChecker(private val program: Program,
}
DataType.STRUCT -> {
if(value.type in ArrayDatatypes) {
if(value.arrayvalue!!.size != struct!!.numberOfElements)
if(value.array!!.size != struct!!.numberOfElements)
return err("number of values is not the same as the number of members in the struct")
for(elt in value.arrayvalue.zip(struct.statements)) {
for(elt in value.array.zip(struct.statements)) {
val vardecl = elt.second as VarDecl
val valuetype = elt.first.inferType(program)!!
if (!(valuetype isAssignableTo vardecl.datatype)) {
@ -1171,27 +1142,72 @@ internal class AstChecker(private val program: Program,
}
return false
}
else -> return false
}
return true
}
private fun checkArrayValues(value: LiteralValue, type: DataType): Boolean {
private fun checkValueTypeAndRange(targetDt: DataType, value: NumericLiteralValue) : Boolean {
fun err(msg: String) : Boolean {
checkResult.add(ExpressionError(msg, value.position))
return false
}
when (targetDt) {
DataType.FLOAT -> {
val number=value.number.toDouble()
if (number > 1.7014118345e+38 || number < -1.7014118345e+38)
return err("value '$number' out of range for MFLPT format")
}
DataType.UBYTE -> {
if(value.type==DataType.FLOAT)
err("unsigned byte value expected instead of float; possible loss of precision")
val number=value.number.toInt()
if (number < 0 || number > 255)
return err("value '$number' out of range for unsigned byte")
}
DataType.BYTE -> {
if(value.type==DataType.FLOAT)
err("byte value expected instead of float; possible loss of precision")
val number=value.number.toInt()
if (number < -128 || number > 127)
return err("value '$number' out of range for byte")
}
DataType.UWORD -> {
if(value.type==DataType.FLOAT)
err("unsigned word value expected instead of float; possible loss of precision")
val number=value.number.toInt()
if (number < 0 || number > 65535)
return err("value '$number' out of range for unsigned word")
}
DataType.WORD -> {
if(value.type==DataType.FLOAT)
err("word value expected instead of float; possible loss of precision")
val number=value.number.toInt()
if (number < -32768 || number > 32767)
return err("value '$number' out of range for word")
}
else -> return false
}
return true
}
private fun checkArrayValues(value: ReferenceLiteralValue, type: DataType): Boolean {
if(value.isArray && value.heapId==null) {
// TODO weird, array literal that hasn't been moved to the heap yet?
val array = value.arrayvalue!!.map { it.constValue(program)!! }
val array = value.array!!.map { it.constValue(program)!! }
val correct: Boolean
when(type) {
DataType.ARRAY_UB -> {
correct=array.all { it.bytevalue!=null && it.bytevalue in 0..255 }
correct=array.all { it.type==DataType.UBYTE && it.number.toInt() in 0..255 }
}
DataType.ARRAY_B -> {
correct=array.all { it.bytevalue!=null && it.bytevalue in -128..127 }
correct=array.all { it.type==DataType.BYTE && it.number.toInt() in -128..127 }
}
DataType.ARRAY_UW -> {
correct=array.all { it.wordvalue!=null && it.wordvalue in 0..65535 }
correct=array.all { it.type==DataType.UWORD && it.number.toInt() in 0..65535 }
}
DataType.ARRAY_W -> {
correct=array.all { it.wordvalue!=null && it.wordvalue in -32768..32767}
correct=array.all { it.type==DataType.WORD && it.number.toInt() in -32768..32767}
}
DataType.ARRAY_F -> correct = true
else -> throw AstException("invalid array type $type")
@ -1202,19 +1218,22 @@ internal class AstChecker(private val program: Program,
}
val array = program.heap.get(value.heapId!!)
if(array.type !in ArrayDatatypes || array.array==null)
throw FatalAstException("should have an array in the heapvar $array")
val correct: Boolean
when(type) {
DataType.ARRAY_UB -> {
correct=array.array!=null && array.array.all { it.integer!=null && it.integer in 0..255 }
correct= array.array.all { it.integer!=null && it.integer in 0..255 }
}
DataType.ARRAY_B -> {
correct=array.array!=null && array.array.all { it.integer!=null && it.integer in -128..127 }
correct=array.array.all { it.integer!=null && it.integer in -128..127 }
}
DataType.ARRAY_UW -> {
correct=array.array!=null && array.array.all { (it.integer!=null && it.integer in 0..65535) || it.addressOf!=null}
correct=array.array.all { (it.integer!=null && it.integer in 0..65535) || it.addressOf!=null}
}
DataType.ARRAY_W -> {
correct=array.array!=null && array.array.all { it.integer!=null && it.integer in -32768..32767 }
correct=array.array.all { it.integer!=null && it.integer in -32768..32767 }
}
DataType.ARRAY_F -> correct = array.doubleArray!=null
else -> throw AstException("invalid array type $type")

View File

@ -12,7 +12,7 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
private val checkResult: MutableList<AstException> = mutableListOf()
private var blocks = mutableMapOf<String, Block>()
internal val anonymousVariablesFromHeap = mutableMapOf<String, Pair<LiteralValue, VarDecl>>()
internal val anonymousVariablesFromHeap = mutableMapOf<String, Pair<ReferenceLiteralValue, VarDecl>>()
internal fun result(): List<AstException> {
return checkResult
@ -116,7 +116,7 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
.filter { it.name !in namesInSub }
.forEach {
val vardecl = VarDecl(VarDeclType.VAR, it.type, ZeropageWish.DONTCARE, null, it.name, null, null,
isArray = false, hiddenButDoNotRemove = true, position = subroutine.position)
isArray = false, autogeneratedDontRemove = true, position = subroutine.position)
vardecl.linkParents(subroutine)
subroutine.statements.add(0, vardecl)
}
@ -155,7 +155,7 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
if(existing==null) {
// create the local scoped for loop variable itself
val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, forLoop.zeropage, null, varName, null, null,
isArray = false, hiddenButDoNotRemove = true, position = forLoop.loopVar.position)
isArray = false, autogeneratedDontRemove = true, position = forLoop.loopVar.position)
vardecl.linkParents(forLoop.body)
forLoop.body.statements.add(0, vardecl)
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
@ -168,7 +168,7 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
if(existing==null) {
// create loop iteration counter variable (without value, to avoid an assignment)
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.PREFER_ZEROPAGE, null, ForLoop.iteratorLoopcounterVarname, null, null,
isArray = false, hiddenButDoNotRemove = true, position = forLoop.loopVar.position)
isArray = false, autogeneratedDontRemove = true, position = forLoop.loopVar.position)
vardecl.linkParents(forLoop.body)
forLoop.body.statements.add(0, vardecl)
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
@ -191,38 +191,39 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
if(subroutine.returntypes.size!=1)
return returnStmt // mismatch in number of return values, error will be printed later.
val newValue: IExpression
val lval = returnStmt.value as? LiteralValue
val lval = returnStmt.value as? NumericLiteralValue
if(lval!=null) {
val adjusted = lval.cast(subroutine.returntypes.single())
if(adjusted!=null && adjusted !== lval)
newValue = adjusted
else
newValue = lval
} else
} else {
newValue = returnStmt.value!!
}
returnStmt.value = newValue
}
return super.visit(returnStmt)
}
override fun visit(literalValue: LiteralValue): LiteralValue {
if(literalValue.heapId!=null && literalValue.parent !is VarDecl) {
override fun visit(refLiteral: ReferenceLiteralValue): ReferenceLiteralValue {
if(refLiteral.heapId!=null && refLiteral.parent !is VarDecl) {
// a literal value that's not declared as a variable, which refers to something on the heap.
// we need to introduce an auto-generated variable for this to be able to refer to the value!
// (note: ususally, this has been taken care of already when the var was created)
val declaredType = if(literalValue.isArray) ArrayElementTypes.getValue(literalValue.type) else literalValue.type
val declaredType = if(refLiteral.isArray) ArrayElementTypes.getValue(refLiteral.type) else refLiteral.type
val variable = VarDecl(VarDeclType.VAR,
declaredType,
ZeropageWish.NOT_IN_ZEROPAGE,
null,
"$autoHeapValuePrefix${literalValue.heapId}",
"$autoHeapValuePrefix${refLiteral.heapId}",
null,
literalValue,
isArray = literalValue.isArray, hiddenButDoNotRemove = true, position = literalValue.position)
anonymousVariablesFromHeap[variable.name] = Pair(literalValue, variable)
refLiteral,
isArray = refLiteral.isArray, autogeneratedDontRemove = true, position = refLiteral.position)
anonymousVariablesFromHeap[variable.name] = Pair(refLiteral, variable)
}
return super.visit(literalValue)
return super.visit(refLiteral)
}
override fun visit(addressOf: AddressOf): IExpression {

View File

@ -100,14 +100,18 @@ interface IAstModifyingVisitor {
return label
}
fun visit(literalValue: LiteralValue): LiteralValue {
if(literalValue.arrayvalue!=null) {
for(av in literalValue.arrayvalue.withIndex()) {
fun visit(literalValue: NumericLiteralValue): NumericLiteralValue {
return literalValue
}
fun visit(refLiteral: ReferenceLiteralValue): ReferenceLiteralValue {
if(refLiteral.array!=null) {
for(av in refLiteral.array.withIndex()) {
val newvalue = av.value.accept(this)
literalValue.arrayvalue[av.index] = newvalue
refLiteral.array[av.index] = newvalue
}
}
return literalValue
return refLiteral
}
fun visit(assignment: Assignment): IStatement {

View File

@ -75,8 +75,11 @@ interface IAstVisitor {
fun visit(label: Label) {
}
fun visit(literalValue: LiteralValue) {
literalValue.arrayvalue?.let { it.forEach { v->v.accept(this) }}
fun visit(numLiteral: NumericLiteralValue) {
}
fun visit(refLiteral: ReferenceLiteralValue) {
refLiteral.array?.let { it.forEach { v->v.accept(this) }}
}
fun visit(assignment: Assignment) {

View File

@ -18,7 +18,7 @@ fun flattenStructAssignment(structAssignment: Assignment, program: Program): Lis
if(!sourceVar.isArray && sourceVar.struct==null)
throw FatalAstException("can only assign arrays or structs to structs")
if(sourceVar.isArray) {
val sourceArray = (sourceVar.value as LiteralValue).arrayvalue!!
val sourceArray = (sourceVar.value as ReferenceLiteralValue).array!!
return struct.statements.zip(sourceArray).map { member ->
val decl = member.first as VarDecl
val mangled = mangledStructMemberName(identifierName, decl.name)
@ -347,7 +347,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
// sort the choices in low-to-high value order (nulls last)
whenStatement.choices
.sortWith(compareBy<WhenChoice, Int?>(nullsLast(), {it.values?.single()?.constValue(program)?.asIntegerValue}))
.sortWith(compareBy<WhenChoice, Int?>(nullsLast(), {it.values?.single()?.constValue(program)?.number?.toInt()}))
return super.visit(whenStatement)
}
@ -355,7 +355,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
// make sure the memory address is an uword
val dt = memread.addressExpression.inferType(program)
if(dt!=DataType.UWORD) {
val literaladdr = memread.addressExpression as? LiteralValue
val literaladdr = memread.addressExpression as? NumericLiteralValue
if(literaladdr!=null) {
memread.addressExpression = literaladdr.cast(DataType.UWORD)!!
} else {
@ -369,7 +369,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
override fun visit(memwrite: DirectMemoryWrite) {
val dt = memwrite.addressExpression.inferType(program)
if(dt!=DataType.UWORD) {
val literaladdr = memwrite.addressExpression as? LiteralValue
val literaladdr = memwrite.addressExpression as? NumericLiteralValue
if(literaladdr!=null) {
memwrite.addressExpression = literaladdr.cast(DataType.UWORD)!!
} else {

View File

@ -3,12 +3,8 @@ package prog8.ast.processing
import prog8.ast.*
import prog8.ast.base.*
import prog8.ast.base.autoHeapValuePrefix
import prog8.ast.expressions.AddressOf
import prog8.ast.expressions.FunctionCall
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.LiteralValue
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.compiler.CompilerException
internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope): IAstModifyingVisitor {
@ -46,7 +42,7 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
addVarDecl(scope, decl.asDefaultValueDecl(null))
val declvalue = decl.value!!
val value =
if(declvalue is LiteralValue) {
if(declvalue is NumericLiteralValue) {
val converted = declvalue.cast(decl.datatype)
converted ?: declvalue
}
@ -88,7 +84,7 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
if(argparam.second is AddressOf)
continue
val idref = argparam.second as? IdentifierReference
val strvalue = argparam.second as? LiteralValue
val strvalue = argparam.second as? ReferenceLiteralValue
if(idref!=null) {
val variable = idref.targetVarDecl(namespace)
if(variable!=null && (variable.datatype in StringDatatypes || variable.datatype in ArrayDatatypes)) {
@ -109,7 +105,7 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
arglist[argparam.first.index] = pointerExpr
// add a vardecl so that the autovar can be resolved in later lookups
val variable = VarDecl(VarDeclType.VAR, strvalue.type, ZeropageWish.NOT_IN_ZEROPAGE, null, autoVarName, null, strvalue,
isArray = false, hiddenButDoNotRemove = false, position = strvalue.position)
isArray = false, autogeneratedDontRemove = false, position = strvalue.position)
addVarDecl(strvalue.definingScope(), variable)
}
}

View File

@ -87,7 +87,7 @@ data class Label(val name: String, override val position: Position) : IStatement
open class Return(var value: IExpression?, override val position: Position) : IStatement {
override lateinit var parent: Node
override val expensiveToInline = value!=null && value !is LiteralValue
override val expensiveToInline = value!=null && value !is NumericLiteralValue
override fun linkParents(parent: Node) {
this.parent = parent
@ -151,7 +151,7 @@ class VarDecl(val type: VarDeclType,
private val structName: String?,
var value: IExpression?,
val isArray: Boolean,
val hiddenButDoNotRemove: Boolean,
val autogeneratedDontRemove: Boolean,
override val position: Position) : IStatement {
override lateinit var parent: Node
var struct: StructDecl? = null // set later (because at parse time, we only know the name)
@ -160,7 +160,7 @@ class VarDecl(val type: VarDeclType,
private set
override val expensiveToInline
get() = value!=null && value !is LiteralValue
get() = value!=null && value !is NumericLiteralValue
val datatypeErrors = mutableListOf<SyntaxError>() // don't crash at init time, report them in the AstChecker
val datatype =
@ -197,11 +197,11 @@ class VarDecl(val type: VarDeclType,
fun asDefaultValueDecl(parent: Node?): VarDecl {
val constValue = when(declaredDatatype) {
DataType.UBYTE -> LiteralValue(DataType.UBYTE, 0, position = position)
DataType.BYTE -> LiteralValue(DataType.BYTE, 0, position = position)
DataType.UWORD -> LiteralValue(DataType.UWORD, wordvalue = 0, position = position)
DataType.WORD -> LiteralValue(DataType.WORD, wordvalue = 0, position = position)
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue = 0.0, position = position)
DataType.UBYTE -> NumericLiteralValue(DataType.UBYTE, 0, position)
DataType.BYTE -> NumericLiteralValue(DataType.BYTE, 0, position)
DataType.UWORD -> NumericLiteralValue(DataType.UWORD, 0, position)
DataType.WORD -> NumericLiteralValue(DataType.WORD, 0, position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, 0.0, position)
else -> throw FatalAstException("can only set a default value for a numeric type")
}
val decl = VarDecl(type, declaredDatatype, zeropage, arraysize, name, structName, constValue, isArray, true, position)
@ -213,7 +213,7 @@ class VarDecl(val type: VarDeclType,
fun flattenStructMembers(): MutableList<IStatement> {
val result = struct!!.statements.withIndex().map {
val member = it.value as VarDecl
val initvalue = if(value!=null) (value as LiteralValue).arrayvalue!![it.index] else null
val initvalue = if(value!=null) (value as ReferenceLiteralValue).array!![it.index] else null
VarDecl(
VarDeclType.VAR,
member.datatype,
@ -241,9 +241,9 @@ class ArrayIndex(var index: IExpression, override val position: Position) : Node
}
companion object {
fun forArray(v: LiteralValue, heap: HeapValues): ArrayIndex {
val arraySize = v.arrayvalue?.size ?: heap.get(v.heapId!!).arraysize
return ArrayIndex(LiteralValue.optimalNumeric(arraySize, v.position), v.position)
fun forArray(v: ReferenceLiteralValue, heap: HeapValues): ArrayIndex {
val arraySize = v.array?.size ?: heap.get(v.heapId!!).arraysize
return ArrayIndex(NumericLiteralValue.optimalNumeric(arraySize, v.position), v.position)
}
}
@ -258,13 +258,13 @@ class ArrayIndex(var index: IExpression, override val position: Position) : Node
return("ArrayIndex($index, pos=$position)")
}
fun size() = (index as? LiteralValue)?.asIntegerValue
fun size() = (index as? NumericLiteralValue)?.number?.toInt()
}
open class Assignment(var target: AssignTarget, val aug_op : String?, var value: IExpression, override val position: Position) : IStatement {
override lateinit var parent: Node
override val expensiveToInline
get() = value !is LiteralValue
get() = value !is NumericLiteralValue
override fun linkParents(parent: Node) {
this.parent = parent
@ -343,8 +343,10 @@ data class AssignTarget(val register: Register?,
if(arrayindexed!=null)
return (if(withTypePrefix) "2arrayidx::" else "") + arrayindexed.identifier.nameInSource.last()
val address = memoryAddress?.addressExpression
if(address is LiteralValue)
return (if(withTypePrefix) "1address::" else "") +address.asIntegerValue.toString()
if(address is NumericLiteralValue)
return (if(withTypePrefix) "1address::" else "") +address.number.toString()
else if(address is ReferenceLiteralValue)
throw FatalAstException("assigntarget is a reference literal")
return if(withTypePrefix) "???::???" else "???"
}
@ -448,7 +450,7 @@ class FunctionCallStatement(override var target: IdentifierReference,
override val position: Position) : IStatement, IFunctionCall {
override lateinit var parent: Node
override val expensiveToInline
get() = arglist.any { it !is LiteralValue }
get() = arglist.any { it !is NumericLiteralValue }
override fun linkParents(parent: Node) {
this.parent = parent
@ -726,7 +728,7 @@ class WhenStatement(val condition: IExpression,
if(choice.values==null)
result.add(null to choice)
else {
val values = choice.values.map { it.constValue(program)?.asNumericValue?.toInt() }
val values = choice.values.map { it.constValue(program)?.number?.toInt() }
if(values.contains(null))
result.add(null to choice)
else

View File

@ -178,7 +178,7 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
private fun outputStatements(statements: List<IStatement>) {
for(stmt in statements) {
if(stmt is VarDecl && stmt.hiddenButDoNotRemove)
if(stmt is VarDecl && stmt.autogeneratedDontRemove)
continue // skip autogenerated decls (to avoid generating a newline)
outputi("")
stmt.accept(this)
@ -252,18 +252,21 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
output("${label.name}:")
}
override fun visit(literalValue: LiteralValue) {
override fun visit(numLiteral: NumericLiteralValue) {
output(numLiteral.number.toString())
}
override fun visit(refLiteral: ReferenceLiteralValue) {
when {
literalValue.isNumeric -> output(literalValue.asNumericValue.toString())
literalValue.isString -> output("\"${escape(literalValue.strvalue!!)}\"")
literalValue.isArray -> {
if(literalValue.arrayvalue!=null) {
refLiteral.isString -> output("\"${escape(refLiteral.str!!)}\"")
refLiteral.isArray -> {
if(refLiteral.array!=null) {
var counter = 0
output("[")
scopelevel++
for (v in literalValue.arrayvalue) {
for (v in refLiteral.array) {
v.accept(this)
if (v !== literalValue.arrayvalue.last())
if (v !== refLiteral.array.last())
output(", ")
counter++
if(counter > 16) {

View File

@ -596,19 +596,20 @@ internal class Compiler(private val program: Program) {
else -> {
val lv = expr.constValue(program) ?: throw CompilerException("constant expression required, not $expr")
when(lv.type) {
in ByteDatatypes -> prog.instr(Opcode.PUSH_BYTE, RuntimeValue(lv.type, lv.bytevalue!!))
in WordDatatypes -> prog.instr(Opcode.PUSH_WORD, RuntimeValue(lv.type, lv.wordvalue!!))
DataType.FLOAT -> prog.instr(Opcode.PUSH_FLOAT, RuntimeValue(lv.type, lv.floatvalue!!))
in StringDatatypes -> {
if(lv.heapId==null)
throw CompilerException("string should have been moved into heap ${lv.position}")
TODO("push address of string with PUSH_ADDR_HEAPVAR")
}
in ArrayDatatypes -> {
if(lv.heapId==null)
throw CompilerException("array should have been moved into heap ${lv.position}")
TODO("push address of array with PUSH_ADDR_HEAPVAR")
}
in ByteDatatypes -> prog.instr(Opcode.PUSH_BYTE, RuntimeValue(lv.type, lv.number.toShort()))
in WordDatatypes -> prog.instr(Opcode.PUSH_WORD, RuntimeValue(lv.type, lv.number.toInt()))
DataType.FLOAT -> prog.instr(Opcode.PUSH_FLOAT, RuntimeValue(lv.type, lv.number.toDouble()))
// TODO what about these ref types:
// in StringDatatypes -> {
// if(lv.heapId==null)
// throw CompilerException("string should have been moved into heap ${lv.position}")
// TODO("push address of string with PUSH_ADDR_HEAPVAR")
// }
// in ArrayDatatypes -> {
// if(lv.heapId==null)
// throw CompilerException("array should have been moved into heap ${lv.position}")
// TODO("push address of array with PUSH_ADDR_HEAPVAR")
// }
else -> throw CompilerException("weird datatype")
}
}
@ -675,11 +676,11 @@ internal class Compiler(private val program: Program) {
throw CompilerException("const ref should have been const-folded away")
VarDeclType.MEMORY -> {
when (target.datatype) {
DataType.UBYTE -> prog.instr(Opcode.PUSH_MEM_UB, RuntimeValue(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!))
DataType.BYTE-> prog.instr(Opcode.PUSH_MEM_B, RuntimeValue(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!))
DataType.UWORD -> prog.instr(Opcode.PUSH_MEM_UW, RuntimeValue(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!))
DataType.WORD -> prog.instr(Opcode.PUSH_MEM_W, RuntimeValue(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!))
DataType.FLOAT -> prog.instr(Opcode.PUSH_MEM_FLOAT, RuntimeValue(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!))
DataType.UBYTE -> prog.instr(Opcode.PUSH_MEM_UB, RuntimeValue(DataType.UWORD, (target.value as NumericLiteralValue).number))
DataType.BYTE-> prog.instr(Opcode.PUSH_MEM_B, RuntimeValue(DataType.UWORD, (target.value as NumericLiteralValue).number))
DataType.UWORD -> prog.instr(Opcode.PUSH_MEM_UW, RuntimeValue(DataType.UWORD, (target.value as NumericLiteralValue).number))
DataType.WORD -> prog.instr(Opcode.PUSH_MEM_W, RuntimeValue(DataType.UWORD, (target.value as NumericLiteralValue).number))
DataType.FLOAT -> prog.instr(Opcode.PUSH_MEM_FLOAT, RuntimeValue(DataType.UWORD, (target.value as NumericLiteralValue).number))
else -> throw CompilerException("invalid datatype for memory variable expression: $target")
}
}
@ -1013,7 +1014,7 @@ internal class Compiler(private val program: Program) {
when (paramDt) {
DataType.UBYTE -> {
valueA = arg.first
valueX = LiteralValue.optimalInteger(0, callPosition)
valueX = NumericLiteralValue.optimalInteger(0, callPosition)
val assignA = Assignment(AssignTarget(Register.A, null, null, null, callPosition), null, valueA, callPosition)
val assignX = Assignment(AssignTarget(Register.X, null, null, null, callPosition), null, valueX, callPosition)
assignA.linkParents(arguments[0].parent)
@ -1036,7 +1037,7 @@ internal class Compiler(private val program: Program) {
when (paramDt) {
DataType.UBYTE -> {
valueA = arg.first
valueY = LiteralValue.optimalInteger(0, callPosition)
valueY = NumericLiteralValue.optimalInteger(0, callPosition)
val assignA = Assignment(AssignTarget(Register.A, null, null, null, callPosition), null, valueA, callPosition)
val assignY = Assignment(AssignTarget(Register.Y, null, null, null, callPosition), null, valueY, callPosition)
assignA.linkParents(arguments[0].parent)
@ -1063,7 +1064,7 @@ internal class Compiler(private val program: Program) {
when (paramDt) {
DataType.UBYTE -> {
valueX = arg.first
valueY = LiteralValue.optimalInteger(0, callPosition)
valueY = NumericLiteralValue.optimalInteger(0, callPosition)
val assignX = Assignment(AssignTarget(Register.X, null, null, null, callPosition), null, valueX, callPosition)
val assignY = Assignment(AssignTarget(Register.Y, null, null, null, callPosition), null, valueY, callPosition)
assignX.linkParents(arguments[0].parent)
@ -1253,10 +1254,10 @@ internal class Compiler(private val program: Program) {
prog.instr(opcode)
}
private fun translateBitshiftedOperator(operator: String, leftDt: DataType, amount: LiteralValue?) {
if(amount?.asIntegerValue == null)
private fun translateBitshiftedOperator(operator: String, leftDt: DataType, amount: NumericLiteralValue?) {
if(amount?.number?.toInt() == null)
throw FatalAstException("bitshift operators should only have constant integer value as right operand")
var shifts=amount.asIntegerValue
var shifts=amount.number.toInt()
if(shifts<0)
throw FatalAstException("bitshift value should be >= 0")
@ -1382,7 +1383,7 @@ internal class Compiler(private val program: Program) {
}
}
stmt.target.memoryAddress != null -> {
val address = stmt.target.memoryAddress?.addressExpression?.constValue(program)?.asIntegerValue
val address = stmt.target.memoryAddress?.addressExpression?.constValue(program)?.number?.toInt()
if(address!=null) {
when(stmt.operator) {
"++" -> prog.instr(Opcode.INC_MEMORY, RuntimeValue(DataType.UWORD, address))
@ -1466,42 +1467,33 @@ internal class Compiler(private val program: Program) {
}
private fun pushHeapVarAddress(value: IExpression, removeLastOpcode: Boolean) {
when (value) {
is LiteralValue -> throw CompilerException("can only push address of string or array (value on the heap)")
is IdentifierReference -> {
val vardecl = value.targetVarDecl(program.namespace)!!
if(removeLastOpcode) prog.removeLastInstruction()
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname)
}
else -> throw CompilerException("can only take address of a literal string value or a string/array variable")
if (value is IdentifierReference) {
val vardecl = value.targetVarDecl(program.namespace)!!
if(removeLastOpcode) prog.removeLastInstruction()
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname)
}
else throw CompilerException("can only take address of a literal string value or a string/array variable")
}
private fun pushFloatAddress(value: IExpression) {
when (value) {
is LiteralValue -> throw CompilerException("can only push address of float that is a variable on the heap")
is IdentifierReference -> {
val vardecl = value.targetVarDecl(program.namespace)!!
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname)
}
else -> throw CompilerException("can only take address of a the float as constant literal or variable")
if (value is IdentifierReference) {
val vardecl = value.targetVarDecl(program.namespace)!!
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname)
}
else throw CompilerException("can only take address of a the float as constant literal or variable")
}
private fun pushStructAddress(value: IExpression) {
when (value) {
is LiteralValue -> throw CompilerException("can only push address of struct that is a variable on the heap")
is IdentifierReference -> {
// notice that the mangled name of the first struct member is the start address of this struct var
val vardecl = value.targetVarDecl(program.namespace)!!
val firstStructMember = (vardecl.struct!!.statements.first() as VarDecl).name
val firstVarName = listOf(vardecl.name, firstStructMember)
// find the flattened var that belongs to this first struct member
val firstVar = value.definingScope().lookup(firstVarName, value) as VarDecl
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = firstVar.scopedname) // TODO
}
else -> throw CompilerException("can only take address of a the float as constant literal or variable")
if (value is IdentifierReference) {
// notice that the mangled name of the first struct member is the start address of this struct var
val vardecl = value.targetVarDecl(program.namespace)!!
val firstStructMember = (vardecl.struct!!.statements.first() as VarDecl).name
val firstVarName = listOf(vardecl.name, firstStructMember)
// find the flattened var that belongs to this first struct member
val firstVar = value.definingScope().lookup(firstVarName, value) as VarDecl
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = firstVar.scopedname) // TODO
}
else throw CompilerException("can only take address of a the float as constant literal or variable")
}
private fun popValueIntoTarget(assignTarget: AssignTarget, datatype: DataType) {
@ -1516,7 +1508,7 @@ internal class Compiler(private val program: Program) {
}
VarDeclType.MEMORY -> {
val opcode = opcodePopmem(datatype)
val address = target.value?.constValue(program)!!.asIntegerValue!!
val address = target.value?.constValue(program)!!.number.toInt()
prog.instr(opcode, RuntimeValue(DataType.UWORD, address))
}
VarDeclType.CONST -> throw CompilerException("cannot assign to const")
@ -1526,7 +1518,7 @@ internal class Compiler(private val program: Program) {
assignTarget.register != null -> prog.instr(Opcode.POP_VAR_BYTE, callLabel = assignTarget.register.name)
assignTarget.arrayindexed != null -> translate(assignTarget.arrayindexed, true) // write value to it
assignTarget.memoryAddress != null -> {
val address = assignTarget.memoryAddress?.addressExpression?.constValue(program)?.asIntegerValue
val address = assignTarget.memoryAddress?.addressExpression?.constValue(program)?.number?.toInt()
if(address!=null) {
// const integer address given
prog.instr(Opcode.POP_MEM_BYTE, arg= RuntimeValue(DataType.UWORD, address))
@ -1613,18 +1605,19 @@ internal class Compiler(private val program: Program) {
loop.iterable is IdentifierReference -> {
val idRef = loop.iterable as IdentifierReference
val vardecl = idRef.targetVarDecl(program.namespace)!!
val iterableValue = vardecl.value as LiteralValue
if(iterableValue.type !in IterableDatatypes)
throw CompilerException("loop over something that isn't iterable ${loop.iterable}")
translateForOverIterableVar(loop, loopvalueDt, iterableValue)
// TODO check loop over iterable or not
// val iterableValue = vardecl.value as LiteralValue
// if(iterableValue.type !in IterableDatatypes)
// throw CompilerException("loop over something that isn't iterable ${loop.iterable}")
translateForOverIterableVar(loop, loopvalueDt, vardecl.value as ReferenceLiteralValue)
}
loop.iterable is LiteralValue -> throw CompilerException("literal value in loop must have been moved to heap already $loop")
// TODO what's this: loop.iterable is LiteralValue -> throw CompilerException("literal value in loop must have been moved to heap already $loop")
else -> throw CompilerException("loopvar is something strange ${loop.iterable}")
}
}
}
private fun translateForOverIterableVar(loop: ForLoop, loopvarDt: DataType, iterableValue: LiteralValue) {
private fun translateForOverIterableVar(loop: ForLoop, loopvarDt: DataType, iterableValue: ReferenceLiteralValue) {
if(loopvarDt== DataType.UBYTE && iterableValue.type !in setOf(DataType.STR, DataType.STR_S, DataType.ARRAY_UB))
throw CompilerException("loop variable type doesn't match iterableValue type")
else if(loopvarDt== DataType.UWORD && iterableValue.type != DataType.ARRAY_UW)
@ -1636,16 +1629,16 @@ internal class Compiler(private val program: Program) {
when(iterableValue.type) {
!in IterableDatatypes -> throw CompilerException("non-iterableValue type")
DataType.STR, DataType.STR_S -> {
numElements = iterableValue.strvalue!!.length
numElements = iterableValue.str!!.length
if(numElements>255) throw CompilerException("string length > 255")
}
DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W -> {
numElements = iterableValue.arrayvalue?.size ?: program.heap.get(iterableValue.heapId!!).arraysize
numElements = iterableValue.array?.size ?: program.heap.get(iterableValue.heapId!!).arraysize
if(numElements>255) throw CompilerException("string length > 255")
}
DataType.ARRAY_F -> {
numElements = iterableValue.arrayvalue?.size ?: program.heap.get(iterableValue.heapId!!).arraysize
numElements = iterableValue.array?.size ?: program.heap.get(iterableValue.heapId!!).arraysize
if(numElements>255) throw CompilerException("string length > 255")
}
else -> throw CompilerException("weird datatype")
@ -1839,7 +1832,7 @@ internal class Compiler(private val program: Program) {
val loopLabel = makeLabel(body, "loop")
val continueLabel = makeLabel(body, "continue")
val breakLabel = makeLabel(body, "break")
val literalStepValue = (range.step as? LiteralValue)?.asNumericValue?.toInt()
val literalStepValue = (range.step as? NumericLiteralValue)?.number?.toInt()
continueStmtLabelStack.push(continueLabel)
breakStmtLabelStack.push(breakLabel)
@ -2053,7 +2046,7 @@ internal class Compiler(private val program: Program) {
private fun translate(memread: DirectMemoryRead) {
// for now, only a single memory location (ubyte) is read at a time.
val address = memread.addressExpression.constValue(program)?.asIntegerValue
val address = memread.addressExpression.constValue(program)?.number?.toInt()
if(address!=null) {
prog.instr(Opcode.PUSH_MEM_UB, arg = RuntimeValue(DataType.UWORD, address))
} else {
@ -2065,7 +2058,7 @@ internal class Compiler(private val program: Program) {
private fun translate(memwrite: DirectMemoryWrite) {
// for now, only a single memory location (ubyte) is written at a time.
// TODO inline this function (it's only used once)
val address = memwrite.addressExpression.constValue(program)?.asIntegerValue
val address = memwrite.addressExpression.constValue(program)?.number?.toInt()
if(address!=null) {
prog.instr(Opcode.POP_MEM_BYTE, arg = RuntimeValue(DataType.UWORD, address))
} else {

View File

@ -3,7 +3,8 @@ package prog8.compiler.intermediate
import prog8.ast.antlr.escape
import prog8.ast.base.*
import prog8.ast.base.printWarning
import prog8.ast.expressions.LiteralValue
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.ReferenceLiteralValue
import prog8.ast.statements.StructDecl
import prog8.ast.statements.VarDecl
import prog8.ast.statements.ZeropageWish
@ -407,16 +408,16 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
val valueparams = VariableParameters(decl.zeropage, decl.struct)
val value = when(decl.datatype) {
in NumericDatatypes -> {
RuntimeValue(decl.datatype, (decl.value as LiteralValue).asNumericValue!!)
RuntimeValue(decl.datatype, (decl.value as NumericLiteralValue).number)
}
in StringDatatypes -> {
val litval = (decl.value as LiteralValue)
val litval = (decl.value as ReferenceLiteralValue)
if(litval.heapId==null)
throw CompilerException("string should already be in the heap")
RuntimeValue(decl.datatype, heapId = litval.heapId)
}
in ArrayDatatypes -> {
val litval = (decl.value as LiteralValue)
val litval = (decl.value as ReferenceLiteralValue)
if(litval.heapId==null)
throw CompilerException("array should already be in the heap")
RuntimeValue(decl.datatype, heapId = litval.heapId)
@ -435,17 +436,17 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
}
VarDeclType.MEMORY -> {
// note that constants are all folded away, but assembly code may still refer to them
val lv = decl.value as LiteralValue
val lv = decl.value as NumericLiteralValue
if(lv.type!= DataType.UWORD && lv.type!= DataType.UBYTE)
throw CompilerException("expected integer memory address $lv")
currentBlock.memoryPointers[scopedname] = Pair(lv.asIntegerValue!!, decl.datatype)
currentBlock.memoryPointers[scopedname] = Pair(lv.number.toInt(), decl.datatype)
}
VarDeclType.CONST -> {
// note that constants are all folded away, but assembly code may still refer to them (if their integers)
// floating point constants are not generated at all!!
val lv = decl.value as LiteralValue
val lv = decl.value as NumericLiteralValue
if(lv.type in IntegerDatatypes)
currentBlock.memoryPointers[scopedname] = Pair(lv.asIntegerValue!!, decl.datatype)
currentBlock.memoryPointers[scopedname] = Pair(lv.number.toInt(), decl.datatype)
}
}
}

View File

@ -4,16 +4,16 @@ import prog8.ast.*
import prog8.ast.base.*
import prog8.ast.expressions.DirectMemoryRead
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.LiteralValue
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.ReferenceLiteralValue
import prog8.ast.statements.VarDecl
import prog8.compiler.CompilerException
import kotlin.math.*
class BuiltinFunctionParam(val name: String, val possibleDatatypes: Set<DataType>)
typealias ConstExpressionCaller = (args: List<IExpression>, position: Position, program: Program) -> LiteralValue
typealias ConstExpressionCaller = (args: List<IExpression>, position: Position, program: Program) -> NumericLiteralValue
class FunctionSignature(val pure: Boolean, // does it have side effects?
@ -119,9 +119,9 @@ val BuiltinFunctions = mapOf(
fun builtinFunctionReturnType(function: String, args: List<IExpression>, program: Program): DataType? {
fun datatypeFromIterableArg(arglist: IExpression): DataType {
if(arglist is LiteralValue) {
if(arglist is ReferenceLiteralValue) {
if(arglist.type== DataType.ARRAY_UB || arglist.type== DataType.ARRAY_UW || arglist.type== DataType.ARRAY_F) {
val dt = arglist.arrayvalue!!.map {it.inferType(program)}
val dt = arglist.array!!.map {it.inferType(program)}
if(dt.any { it!= DataType.UBYTE && it!= DataType.UWORD && it!= DataType.FLOAT}) {
throw FatalAstException("fuction $function only accepts arraysize of numeric values")
}
@ -188,147 +188,151 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, program
class NotConstArgumentException: AstException("not a const argument to a built-in function")
private fun oneDoubleArg(args: List<IExpression>, position: Position, program: Program, function: (arg: Double)->Number): LiteralValue {
private fun oneDoubleArg(args: List<IExpression>, position: Position, program: Program, function: (arg: Double)->Number): NumericLiteralValue {
if(args.size!=1)
throw SyntaxError("built-in function requires one floating point argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val float = getFloatArg(constval, args[0].position)
val float = constval.number.toDouble()
return numericLiteral(function(float), args[0].position)
}
private fun getFloatArg(v: LiteralValue, position: Position): Double {
val nv = v.asNumericValue ?: throw SyntaxError("numerical argument required", position)
return nv.toDouble()
}
private fun oneDoubleArgOutputWord(args: List<IExpression>, position: Position, program: Program, function: (arg: Double)->Number): LiteralValue {
private fun oneDoubleArgOutputWord(args: List<IExpression>, position: Position, program: Program, function: (arg: Double)->Number): NumericLiteralValue {
if(args.size!=1)
throw SyntaxError("built-in function requires one floating point argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val float = getFloatArg(constval, args[0].position)
return LiteralValue(DataType.WORD, wordvalue = function(float).toInt(), position = args[0].position)
val float = constval.number.toDouble()
return NumericLiteralValue(DataType.WORD, function(float).toInt(), args[0].position)
}
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position, program: Program, function: (arg: Int)->Number): LiteralValue {
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position, program: Program, function: (arg: Int)->Number): NumericLiteralValue {
if(args.size!=1)
throw SyntaxError("built-in function requires one integer argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
if(constval.type!= DataType.UBYTE && constval.type!= DataType.UWORD)
if(constval.type != DataType.UBYTE && constval.type!= DataType.UWORD)
throw SyntaxError("built-in function requires one integer argument", position)
val integer = constval.asNumericValue?.toInt()!!
val integer = constval.number.toInt()
return numericLiteral(function(integer).toInt(), args[0].position)
}
private fun collectionArgOutputNumber(args: List<IExpression>, position: Position,
program: Program,
function: (arg: Collection<Double>)->Number): LiteralValue {
function: (arg: Collection<Double>)->Number): NumericLiteralValue {
if(args.size!=1)
throw SyntaxError("builtin function requires one non-scalar argument", position)
val iterable = args[0].constValue(program) ?: throw NotConstArgumentException()
val result = if(iterable.arrayvalue != null) {
val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
if(null in constants)
throw NotConstArgumentException()
function(constants.map { it!!.toDouble() }).toDouble()
} else {
when(iterable.type) {
DataType.UBYTE, DataType.UWORD, DataType.FLOAT -> throw SyntaxError("function expects an iterable type", position)
else -> {
val heapId = iterable.heapId ?: throw FatalAstException("iterable value should be on the heap")
val array = program.heap.get(heapId).array ?: throw SyntaxError("function expects an iterable type", position)
function(array.map {
if(it.integer!=null)
it.integer.toDouble()
else
throw FatalAstException("cannot perform function over array that contains other values besides constant integers")
})
}
}
}
return numericLiteral(result, args[0].position)
TODO("collection functions over iterables (array, string) $iterable")
// val result = if(iterable.array != null) {
// val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
// if (null in constants)
// throw NotConstArgumentException()
// function(constants.map { it!!.toDouble() }).toDouble()
// } else {
// when(iterable.type) {
// DataType.UBYTE, DataType.UWORD, DataType.FLOAT -> throw SyntaxError("function expects an iterable type", position)
// else -> {
// val heapId = iterable.heapId ?: throw FatalAstException("iterable value should be on the heap")
// val array = program.heap.get(heapId).array ?: throw SyntaxError("function expects an iterable type", position)
// function(array.map {
// if(it.integer!=null)
// it.integer.toDouble()
// else
// throw FatalAstException("cannot perform function over array that contains other values besides constant integers")
// })
// }
// }
// }
// return numericLiteral(result, args[0].position)
}
private fun collectionArgOutputBoolean(args: List<IExpression>, position: Position,
program: Program,
function: (arg: Collection<Double>)->Boolean): LiteralValue {
function: (arg: Collection<Double>)->Boolean): NumericLiteralValue {
if(args.size!=1)
throw SyntaxError("builtin function requires one non-scalar argument", position)
val iterable = args[0].constValue(program) ?: throw NotConstArgumentException()
val result = if(iterable.arrayvalue != null) {
val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
if(null in constants)
throw NotConstArgumentException()
function(constants.map { it!!.toDouble() })
} else {
val array = program.heap.get(iterable.heapId!!).array ?: throw SyntaxError("function requires array argument", position)
function(array.map {
if(it.integer!=null)
it.integer.toDouble()
else
throw FatalAstException("cannot perform function over array that contains other values besides constant integers")
})
}
return LiteralValue.fromBoolean(result, position)
TODO("collection functions over iterables (array, string) $iterable")
// val result = if(iterable.arrayvalue != null) {
// val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
// if(null in constants)
// throw NotConstArgumentException()
// function(constants.map { it!!.toDouble() })
// } else {
// val array = program.heap.get(iterable.heapId!!).array ?: throw SyntaxError("function requires array argument", position)
// function(array.map {
// if(it.integer!=null)
// it.integer.toDouble()
// else
// throw FatalAstException("cannot perform function over array that contains other values besides constant integers")
// })
// }
// return LiteralValue.fromBoolean(result, position)
}
private fun builtinAbs(args: List<IExpression>, position: Position, program: Program): LiteralValue {
private fun builtinAbs(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
// 1 arg, type = float or int, result type= isSameAs as argument type
if(args.size!=1)
throw SyntaxError("abs requires one numeric argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
return when (val number = constval.asNumericValue) {
is Int, is Byte, is Short -> numericLiteral(abs(number.toInt()), args[0].position)
is Double -> numericLiteral(abs(number.toDouble()), args[0].position)
return when (constval.type) {
in IntegerDatatypes -> numericLiteral(abs(constval.number.toInt()), args[0].position)
DataType.FLOAT -> numericLiteral(abs(constval.number.toDouble()), args[0].position)
else -> throw SyntaxError("abs requires one numeric argument", position)
}
}
private fun builtinAvg(args: List<IExpression>, position: Position, program: Program): LiteralValue {
private fun builtinAvg(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
if(args.size!=1)
throw SyntaxError("avg requires array argument", position)
val iterable = args[0].constValue(program) ?: throw NotConstArgumentException()
val result = if(iterable.arrayvalue!=null) {
val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
if (null in constants)
throw NotConstArgumentException()
(constants.map { it!!.toDouble() }).average()
}
else {
val heapId = iterable.heapId!!
val integerarray = program.heap.get(heapId).array
if(integerarray!=null) {
if (integerarray.all { it.integer != null }) {
integerarray.map { it.integer!! }.average()
} else {
throw ExpressionError("cannot avg() over array that does not only contain constant numerical values", position)
}
} else {
val doublearray = program.heap.get(heapId).doubleArray
doublearray?.average() ?: throw SyntaxError("avg requires array argument", position)
}
}
return numericLiteral(result, args[0].position)
TODO("collection functions over iterables (array, string) $iterable")
// val result = if(iterable.arrayvalue!=null) {
// val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
// if (null in constants)
// throw NotConstArgumentException()
// (constants.map { it!!.toDouble() }).average()
// }
// else {
// val heapId = iterable.heapId!!
// val integerarray = program.heap.get(heapId).array
// if(integerarray!=null) {
// if (integerarray.all { it.integer != null }) {
// integerarray.map { it.integer!! }.average()
// } else {
// throw ExpressionError("cannot avg() over array that does not only contain constant numerical values", position)
// }
// } else {
// val doublearray = program.heap.get(heapId).doubleArray
// doublearray?.average() ?: throw SyntaxError("avg requires array argument", position)
// }
// }
// return numericLiteral(result, args[0].position)
}
private fun builtinStrlen(args: List<IExpression>, position: Position, program: Program): LiteralValue {
private fun builtinStrlen(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
if (args.size != 1)
throw SyntaxError("strlen requires one argument", position)
val argument = args[0].constValue(program) ?: throw NotConstArgumentException()
if(argument.type !in StringDatatypes)
throw SyntaxError("strlen must have string argument", position)
val string = argument.strvalue!!
val zeroIdx = string.indexOf('\u0000')
return if(zeroIdx>=0)
LiteralValue.optimalInteger(zeroIdx, position=position)
else
LiteralValue.optimalInteger(string.length, position=position)
TODO("collection functions over iterables (array, string) $argument")
// val string = argument.strvalue!!
// val zeroIdx = string.indexOf('\u0000')
// return if(zeroIdx>=0)
// LiteralValue.optimalInteger(zeroIdx, position=position)
// else
// LiteralValue.optimalInteger(string.length, position=position)
}
private fun builtinLen(args: List<IExpression>, position: Position, program: Program): LiteralValue {
private fun builtinLen(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
// note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE.
if(args.size!=1)
throw SyntaxError("len requires one argument", position)
@ -337,7 +341,7 @@ private fun builtinLen(args: List<IExpression>, position: Position, program: Pro
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program.namespace)
val arraySize = directMemVar?.arraysize?.size()
if(arraySize != null)
return LiteralValue.optimalInteger(arraySize, position)
return NumericLiteralValue.optimalInteger(arraySize, position)
if(args[0] !is IdentifierReference)
throw SyntaxError("len argument should be an identifier, but is ${args[0]}", position)
val target = (args[0] as IdentifierReference).targetStatement(program.namespace)
@ -345,105 +349,108 @@ private fun builtinLen(args: List<IExpression>, position: Position, program: Pro
argument = argValue?.constValue(program)
?: throw NotConstArgumentException()
}
return when(argument.type) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
val arraySize = argument.arrayvalue?.size ?: program.heap.get(argument.heapId!!).arraysize
if(arraySize>256)
throw CompilerException("array length exceeds byte limit ${argument.position}")
LiteralValue.optimalInteger(arraySize, args[0].position)
}
DataType.ARRAY_F -> {
val arraySize = argument.arrayvalue?.size ?: program.heap.get(argument.heapId!!).arraysize
if(arraySize>256)
throw CompilerException("array length exceeds byte limit ${argument.position}")
LiteralValue.optimalInteger(arraySize, args[0].position)
}
in StringDatatypes -> {
val str = argument.strvalue!!
if(str.length>255)
throw CompilerException("string length exceeds byte limit ${argument.position}")
LiteralValue.optimalInteger(str.length, args[0].position)
}
in NumericDatatypes -> throw SyntaxError("len of weird argument ${args[0]}", position)
else -> throw CompilerException("weird datatype")
}
TODO("collection functions over iterables (array, string) $argument")
// return when(argument.type) {
// DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
// val arraySize = argument.arrayvalue?.size ?: program.heap.get(argument.heapId!!).arraysize
// if(arraySize>256)
// throw CompilerException("array length exceeds byte limit ${argument.position}")
// LiteralValue.optimalInteger(arraySize, args[0].position)
// }
// DataType.ARRAY_F -> {
// val arraySize = argument.arrayvalue?.size ?: program.heap.get(argument.heapId!!).arraysize
// if(arraySize>256)
// throw CompilerException("array length exceeds byte limit ${argument.position}")
// LiteralValue.optimalInteger(arraySize, args[0].position)
// }
// in StringDatatypes -> {
// val str = argument.strvalue!!
// if(str.length>255)
// throw CompilerException("string length exceeds byte limit ${argument.position}")
// LiteralValue.optimalInteger(str.length, args[0].position)
// }
// in NumericDatatypes -> throw SyntaxError("len of weird argument ${args[0]}", position)
// else -> throw CompilerException("weird datatype")
// }
}
private fun builtinMkword(args: List<IExpression>, position: Position, program: Program): LiteralValue {
private fun builtinMkword(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
if (args.size != 2)
throw SyntaxError("mkword requires lsb and msb arguments", position)
val constLsb = args[0].constValue(program) ?: throw NotConstArgumentException()
val constMsb = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = (constMsb.asIntegerValue!! shl 8) or constLsb.asIntegerValue!!
return LiteralValue(DataType.UWORD, wordvalue = result, position = position)
val result = (constMsb.number.toInt() shl 8) or constLsb.number.toInt()
return NumericLiteralValue(DataType.UWORD, result, position)
}
private fun builtinSin8(args: List<IExpression>, position: Position, program: Program): LiteralValue {
private fun builtinSin8(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
if (args.size != 1)
throw SyntaxError("sin8 requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
return LiteralValue(DataType.BYTE, bytevalue = (127.0 * sin(rad)).toShort(), position = position)
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
return NumericLiteralValue(DataType.BYTE, (127.0 * sin(rad)).toShort(), position)
}
private fun builtinSin8u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
private fun builtinSin8u(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
if (args.size != 1)
throw SyntaxError("sin8u requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
return LiteralValue(DataType.UBYTE, bytevalue = (128.0 + 127.5 * sin(rad)).toShort(), position = position)
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toShort(), position)
}
private fun builtinCos8(args: List<IExpression>, position: Position, program: Program): LiteralValue {
private fun builtinCos8(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
if (args.size != 1)
throw SyntaxError("cos8 requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
return LiteralValue(DataType.BYTE, bytevalue = (127.0 * cos(rad)).toShort(), position = position)
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
return NumericLiteralValue(DataType.BYTE, (127.0 * cos(rad)).toShort(), position)
}
private fun builtinCos8u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
private fun builtinCos8u(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
if (args.size != 1)
throw SyntaxError("cos8u requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
return LiteralValue(DataType.UBYTE, bytevalue = (128.0 + 127.5 * cos(rad)).toShort(), position = position)
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toShort(), position)
}
private fun builtinSin16(args: List<IExpression>, position: Position, program: Program): LiteralValue {
private fun builtinSin16(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
if (args.size != 1)
throw SyntaxError("sin16 requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
return LiteralValue(DataType.WORD, wordvalue = (32767.0 * sin(rad)).toInt(), position = position)
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
return NumericLiteralValue(DataType.WORD, (32767.0 * sin(rad)).toInt(), position)
}
private fun builtinSin16u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
private fun builtinSin16u(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
if (args.size != 1)
throw SyntaxError("sin16u requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
return LiteralValue(DataType.UWORD, wordvalue = (32768.0 + 32767.5 * sin(rad)).toInt(), position = position)
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * sin(rad)).toInt(), position)
}
private fun builtinCos16(args: List<IExpression>, position: Position, program: Program): LiteralValue {
private fun builtinCos16(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
if (args.size != 1)
throw SyntaxError("cos16 requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
return LiteralValue(DataType.WORD, wordvalue = (32767.0 * cos(rad)).toInt(), position = position)
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
return NumericLiteralValue(DataType.WORD, (32767.0 * cos(rad)).toInt(), position)
}
private fun builtinCos16u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
private fun builtinCos16u(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
if (args.size != 1)
throw SyntaxError("cos16u requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
return LiteralValue(DataType.UWORD, wordvalue = (32768.0 + 32767.5 * cos(rad)).toInt(), position = position)
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * cos(rad)).toInt(), position)
}
private fun numericLiteral(value: Number, position: Position): LiteralValue {
private fun numericLiteral(value: Number, position: Position): NumericLiteralValue {
val floatNum=value.toDouble()
val tweakedValue: Number =
if(floatNum== floor(floatNum) && (floatNum>=-32768 && floatNum<=65535))
@ -452,11 +459,11 @@ private fun numericLiteral(value: Number, position: Position): LiteralValue {
floatNum
return when(tweakedValue) {
is Int -> LiteralValue.optimalNumeric(value.toInt(), position)
is Short -> LiteralValue.optimalNumeric(value.toInt(), position)
is Byte -> LiteralValue(DataType.UBYTE, 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)
is Int -> NumericLiteralValue.optimalNumeric(value.toInt(), position)
is Short -> NumericLiteralValue.optimalNumeric(value.toInt(), position)
is Byte -> NumericLiteralValue(DataType.UBYTE, value.toShort(), position)
is Double -> NumericLiteralValue(DataType.FLOAT, value.toDouble(), position)
is Float -> NumericLiteralValue(DataType.FLOAT, value.toDouble(), position)
else -> throw FatalAstException("invalid number type ${value::class}")
}
}

View File

@ -18,6 +18,7 @@ class CallGraph(private val program: Program): IAstVisitor {
val modulesImportedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
val subroutinesCalling = mutableMapOf<INameScope, List<Subroutine>>().withDefault { mutableListOf() }
val subroutinesCalledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
// TODO add dataflow graph: what statements use what variables
val usedSymbols = mutableSetOf<IStatement>()
init {
@ -119,7 +120,7 @@ class CallGraph(private val program: Program): IAstVisitor {
}
override fun visit(decl: VarDecl) {
if(decl.hiddenButDoNotRemove || (decl.definingModule().isLibraryModule && decl.type!=VarDeclType.VAR)) {
if(decl.autogeneratedDontRemove || (decl.definingModule().isLibraryModule && decl.type!=VarDeclType.VAR)) {
// make sure autogenerated vardecls are in the used symbols
addNodeAndParentScopes(decl)
}

View File

@ -1,17 +1,14 @@
package prog8.optimizer
import prog8.ast.*
import prog8.ast.base.DataType
import prog8.ast.base.ExpressionError
import prog8.ast.base.FatalAstException
import prog8.ast.base.Position
import prog8.ast.expressions.LiteralValue
import prog8.ast.base.*
import prog8.ast.expressions.NumericLiteralValue
import kotlin.math.pow
class ConstExprEvaluator {
fun evaluate(left: LiteralValue, operator: String, right: LiteralValue): IExpression {
fun evaluate(left: NumericLiteralValue, operator: String, right: NumericLiteralValue): IExpression {
return when(operator) {
"+" -> plus(left, right)
"-" -> minus(left, right)
@ -25,200 +22,202 @@ class ConstExprEvaluator {
"and" -> logicaland(left, right)
"or" -> logicalor(left, right)
"xor" -> logicalxor(left, right)
"<" -> LiteralValue.fromBoolean(left < right, left.position)
">" -> LiteralValue.fromBoolean(left > right, left.position)
"<=" -> LiteralValue.fromBoolean(left <= right, left.position)
">=" -> LiteralValue.fromBoolean(left >= right, left.position)
"==" -> LiteralValue.fromBoolean(left == right, left.position)
"!=" -> LiteralValue.fromBoolean(left != right, left.position)
"<" -> NumericLiteralValue.fromBoolean(left < right, left.position)
">" -> NumericLiteralValue.fromBoolean(left > right, left.position)
"<=" -> NumericLiteralValue.fromBoolean(left <= right, left.position)
">=" -> NumericLiteralValue.fromBoolean(left >= right, left.position)
"==" -> NumericLiteralValue.fromBoolean(left == right, left.position)
"!=" -> NumericLiteralValue.fromBoolean(left != right, left.position)
"<<" -> shiftedleft(left, right)
">>" -> shiftedright(left, right)
else -> throw FatalAstException("const evaluation for invalid operator $operator")
}
}
private fun shiftedright(left: LiteralValue, amount: LiteralValue): IExpression {
if(left.asIntegerValue==null || amount.asIntegerValue==null)
private fun shiftedright(left: NumericLiteralValue, amount: NumericLiteralValue): IExpression {
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
throw ExpressionError("cannot compute $left >> $amount", left.position)
val result =
if(left.type== DataType.UBYTE || left.type== DataType.UWORD)
left.asIntegerValue.ushr(amount.asIntegerValue)
left.number.toInt().ushr(amount.number.toInt())
else
left.asIntegerValue.shr(amount.asIntegerValue)
return LiteralValue.fromNumber(result, left.type, left.position)
left.number.toInt().shr(amount.number.toInt())
return NumericLiteralValue(left.type, result, left.position)
}
private fun shiftedleft(left: LiteralValue, amount: LiteralValue): IExpression {
if(left.asIntegerValue==null || amount.asIntegerValue==null)
private fun shiftedleft(left: NumericLiteralValue, amount: NumericLiteralValue): IExpression {
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
throw ExpressionError("cannot compute $left << $amount", left.position)
val result = left.asIntegerValue.shl(amount.asIntegerValue)
return LiteralValue.fromNumber(result, left.type, left.position)
val result = left.number.toInt().shl(amount.number.toInt())
return NumericLiteralValue(left.type, result, left.position)
}
private fun logicalxor(left: LiteralValue, right: LiteralValue): LiteralValue {
private fun logicalxor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot compute $left locical-bitxor $right"
return when {
left.asIntegerValue!=null -> when {
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)
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean((left.number.toInt() != 0) xor (right.number.toInt() != 0), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean((left.number.toInt() != 0) xor (right.number.toDouble() != 0.0), left.position)
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
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)
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean((left.number.toDouble() != 0.0) xor (right.number.toInt() != 0), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean((left.number.toDouble() != 0.0) xor (right.number.toDouble() != 0.0), left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
}
private fun logicalor(left: LiteralValue, right: LiteralValue): LiteralValue {
private fun logicalor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot compute $left locical-or $right"
return when {
left.asIntegerValue!=null -> when {
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)
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 || right.number.toInt() != 0, left.position)
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 || right.number.toDouble() != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
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)
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 || right.number.toInt() != 0, left.position)
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 || right.number.toDouble() != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
}
private fun logicaland(left: LiteralValue, right: LiteralValue): LiteralValue {
private fun logicaland(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot compute $left locical-and $right"
return when {
left.asIntegerValue!=null -> when {
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)
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 && right.number.toInt() != 0, left.position)
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 && right.number.toDouble() != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
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)
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 && right.number.toInt() != 0, left.position)
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 && right.number.toDouble() != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
}
private fun bitwisexor(left: LiteralValue, right: LiteralValue): LiteralValue {
private fun bitwisexor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
if(left.type== DataType.UBYTE) {
if(right.asIntegerValue!=null) {
return LiteralValue(DataType.UBYTE, bytevalue = (left.bytevalue!!.toInt() xor (right.asIntegerValue and 255)).toShort(), position = left.position)
if(right.type in IntegerDatatypes) {
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toShort(), left.position)
}
} else if(left.type== DataType.UWORD) {
if(right.asIntegerValue!=null) {
return LiteralValue(DataType.UWORD, wordvalue = left.wordvalue!! xor right.asIntegerValue, position = left.position)
if(right.type in IntegerDatatypes) {
return NumericLiteralValue(DataType.UWORD, left.number.toInt() xor right.number.toInt(), left.position)
}
}
throw ExpressionError("cannot calculate $left ^ $right", left.position)
}
private fun bitwiseor(left: LiteralValue, right: LiteralValue): LiteralValue {
private fun bitwiseor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
if(left.type== DataType.UBYTE) {
if(right.asIntegerValue!=null) {
return LiteralValue(DataType.UBYTE, bytevalue = (left.bytevalue!!.toInt() or (right.asIntegerValue and 255)).toShort(), position = left.position)
if(right.type in IntegerDatatypes) {
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toShort(), left.position)
}
} else if(left.type== DataType.UWORD) {
if(right.asIntegerValue!=null) {
return LiteralValue(DataType.UWORD, wordvalue = left.wordvalue!! or right.asIntegerValue, position = left.position)
if(right.type in IntegerDatatypes) {
return NumericLiteralValue(DataType.UWORD, left.number.toInt() or right.number.toInt(), left.position)
}
}
throw ExpressionError("cannot calculate $left | $right", left.position)
}
private fun bitwiseand(left: LiteralValue, right: LiteralValue): LiteralValue {
private fun bitwiseand(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
if(left.type== DataType.UBYTE) {
if(right.asIntegerValue!=null) {
return LiteralValue(DataType.UBYTE, bytevalue = (left.bytevalue!!.toInt() or (right.asIntegerValue and 255)).toShort(), position = left.position)
if(right.type in IntegerDatatypes) {
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toShort(), left.position)
}
} else if(left.type== DataType.UWORD) {
if(right.asIntegerValue!=null) {
return LiteralValue(DataType.UWORD, wordvalue = left.wordvalue!! or right.asIntegerValue, position = left.position)
if(right.type in IntegerDatatypes) {
return NumericLiteralValue(DataType.UWORD, left.number.toInt() or right.number.toInt(), left.position)
}
}
throw ExpressionError("cannot calculate $left & $right", left.position)
}
private fun power(left: LiteralValue, right: LiteralValue): LiteralValue {
private fun power(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot calculate $left ** $right"
return when {
left.asIntegerValue!=null -> when {
right.asIntegerValue!=null -> LiteralValue.optimalNumeric(left.asIntegerValue.toDouble().pow(right.asIntegerValue), left.position)
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue.toDouble().pow(right.floatvalue), position = left.position)
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt().toDouble().pow(right.number.toInt()), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt().toDouble().pow(right.number.toDouble()), left.position)
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
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)
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble().pow(right.number.toInt()), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble().pow(right.number.toDouble()), left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
}
private fun plus(left: LiteralValue, right: LiteralValue): LiteralValue {
private fun plus(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot add $left and $right"
return when {
left.asIntegerValue!=null -> when {
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)
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() + right.number.toInt(), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() + right.number.toDouble(), left.position)
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
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.isString -> when {
right.isString -> {
val newStr = left.strvalue!! + right.strvalue!!
if(newStr.length > 255) throw ExpressionError("string too long", left.position)
LiteralValue(DataType.STR, strvalue = newStr, position = left.position)
}
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() + right.number.toInt(), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() + right.number.toDouble(), left.position)
else -> throw ExpressionError(error, left.position)
}
// TODO: string concatenation
// left.isString -> when {
// right.isString -> {
// val newStr = left.strvalue!! + right.strvalue!!
// if(newStr.length > 255) throw ExpressionError("string too long", left.position)
// NumericLiteralValue(DataType.STR, strvalue = newStr, left.position)
// }
// else -> throw ExpressionError(error, left.position)
// }
else -> throw ExpressionError(error, left.position)
}
}
private fun minus(left: LiteralValue, right: LiteralValue): LiteralValue {
private fun minus(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot subtract $left and $right"
return when {
left.asIntegerValue!=null -> when {
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)
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() - right.number.toInt(), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() - right.number.toDouble(), left.position)
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
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)
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() - right.number.toInt(), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() - right.number.toDouble(), left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
}
private fun multiply(left: LiteralValue, right: LiteralValue): LiteralValue {
private fun multiply(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot multiply ${left.type} and ${right.type}"
return when {
left.asIntegerValue!=null -> when {
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.isString -> {
if(right.strvalue!!.length * left.asIntegerValue > 255) throw ExpressionError("string too long", left.position)
LiteralValue(DataType.STR, strvalue = right.strvalue.repeat(left.asIntegerValue), position = left.position)
}
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() * right.number.toInt(), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() * right.number.toDouble(), left.position)
// TODO: string multiplication
// right.isString -> {
// if(right.strvalue!!.length * left.number.toInt() > 255) throw ExpressionError("string too long", left.position)
// NumericLiteralValue(DataType.STR, strvalue = right.strvalue.repeat(left.number.toInt()), left.position)
// }
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
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)
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() * right.number.toInt(), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() * right.number.toDouble(), left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
@ -228,29 +227,29 @@ class ConstExprEvaluator {
private fun divideByZeroError(pos: Position): Unit =
throw ExpressionError("division by zero", pos)
private fun divide(left: LiteralValue, right: LiteralValue): LiteralValue {
private fun divide(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot divide $left by $right"
return when {
left.asIntegerValue!=null -> when {
right.asIntegerValue!=null -> {
if(right.asIntegerValue==0) divideByZeroError(right.position)
val result: Int = left.asIntegerValue / right.asIntegerValue
LiteralValue.optimalNumeric(result, left.position)
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
val result: Int = left.number.toInt() / right.number.toInt()
NumericLiteralValue.optimalNumeric(result, left.position)
}
right.floatvalue!=null -> {
if(right.floatvalue==0.0) divideByZeroError(right.position)
LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue / right.floatvalue, position = left.position)
right.type == DataType.FLOAT -> {
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number.toInt() / right.number.toDouble(), left.position)
}
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
right.asIntegerValue!=null -> {
if(right.asIntegerValue==0) divideByZeroError(right.position)
LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue / right.asIntegerValue, position = left.position)
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() / right.number.toInt(), left.position)
}
right.floatvalue!=null -> {
if(right.floatvalue==0.0) divideByZeroError(right.position)
LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue / right.floatvalue, position = left.position)
right.type == DataType.FLOAT -> {
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() / right.number.toDouble(), left.position)
}
else -> throw ExpressionError(error, left.position)
}
@ -258,28 +257,28 @@ class ConstExprEvaluator {
}
}
private fun remainder(left: LiteralValue, right: LiteralValue): LiteralValue {
private fun remainder(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
val error = "cannot compute remainder of $left by $right"
return when {
left.asIntegerValue!=null -> when {
right.asIntegerValue!=null -> {
if(right.asIntegerValue==0) divideByZeroError(right.position)
LiteralValue.optimalNumeric(left.asIntegerValue.toDouble() % right.asIntegerValue.toDouble(), left.position)
left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
NumericLiteralValue.optimalNumeric(left.number.toInt().toDouble() % right.number.toInt().toDouble(), left.position)
}
right.floatvalue!=null -> {
if(right.floatvalue==0.0) divideByZeroError(right.position)
LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue % right.floatvalue, position = left.position)
right.type == DataType.FLOAT -> {
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number.toInt() % right.number.toDouble(), left.position)
}
else -> throw ExpressionError(error, left.position)
}
left.floatvalue!=null -> when {
right.asIntegerValue!=null -> {
if(right.asIntegerValue==0) divideByZeroError(right.position)
LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue % right.asIntegerValue, position = left.position)
left.type == DataType.FLOAT -> when {
right.type in IntegerDatatypes -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() % right.number.toInt(), left.position)
}
right.floatvalue!=null -> {
if(right.floatvalue==0.0) divideByZeroError(right.position)
LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue % right.floatvalue, position = left.position)
right.type == DataType.FLOAT -> {
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() % right.number.toDouble(), left.position)
}
else -> throw ExpressionError(error, left.position)
}

View File

@ -28,28 +28,29 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
override fun visit(decl: VarDecl): IStatement {
// the initializer value can't refer to the variable itself (recursive definition)
// TODO: use call tree for this?
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) {
errors.add(ExpressionError("recursive var declaration", decl.position))
return decl
}
if(decl.type==VarDeclType.CONST || decl.type==VarDeclType.VAR) {
val litval = decl.value as? LiteralValue
if(litval!=null && litval.isArray && litval.heapId!=null)
fixupArrayTypeOnHeap(decl, litval)
val refLv = decl.value as? ReferenceLiteralValue
if(refLv!=null && refLv.isArray && refLv.heapId!=null)
fixupArrayTypeOnHeap(decl, refLv)
if(decl.isArray){
// for arrays that have no size specifier (or a non-constant one) attempt to deduce the size
if(decl.arraysize==null) {
val arrayval = (decl.value as? LiteralValue)?.arrayvalue
val arrayval = (decl.value as? ReferenceLiteralValue)?.array
if(arrayval!=null) {
decl.arraysize = ArrayIndex(LiteralValue.optimalInteger(arrayval.size, decl.position), decl.position)
decl.arraysize = ArrayIndex(NumericLiteralValue.optimalInteger(arrayval.size, decl.position), decl.position)
optimizationsDone++
}
}
else if(decl.arraysize?.size()==null) {
val size = decl.arraysize!!.index.accept(this)
if(size is LiteralValue) {
if(size is NumericLiteralValue) {
decl.arraysize = ArrayIndex(size, decl.position)
optimizationsDone++
}
@ -59,14 +60,22 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
when(decl.datatype) {
DataType.FLOAT -> {
// vardecl: for scalar float vars, promote constant integer initialization values to floats
if (litval != null && litval.type in IntegerDatatypes) {
val newValue = LiteralValue(DataType.FLOAT, floatvalue = litval.asNumericValue!!.toDouble(), position = litval.position)
val litval = decl.value as? NumericLiteralValue
if (litval!=null && litval.type in IntegerDatatypes) {
val newValue = NumericLiteralValue(DataType.FLOAT, litval.number.toDouble(), litval.position)
decl.value = newValue
optimizationsDone++
return decl
}
}
in StringDatatypes -> {
// nothing to do for strings
}
DataType.STRUCT -> {
// struct defintions don't have anything else in them
}
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
val numericLv = decl.value as? NumericLiteralValue
val rangeExpr = decl.value as? RangeExpr
if(rangeExpr!=null) {
// convert the initializer range expression to an actual array (will be put on heap later)
@ -77,12 +86,12 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
if(constRange!=null) {
val eltType = rangeExpr.inferType(program)!!
if(eltType in ByteDatatypes) {
decl.value = LiteralValue(decl.datatype,
arrayvalue = constRange.map { LiteralValue(eltType, bytevalue = it.toShort(), position = decl.value!!.position) }
decl.value = ReferenceLiteralValue(decl.datatype,
array = constRange.map { NumericLiteralValue(eltType, it.toShort(), decl.value!!.position) }
.toTypedArray(), position = decl.value!!.position)
} else {
decl.value = LiteralValue(decl.datatype,
arrayvalue = constRange.map { LiteralValue(eltType, wordvalue = it, position = decl.value!!.position) }
decl.value = ReferenceLiteralValue(decl.datatype,
array = constRange.map { NumericLiteralValue(eltType, it, decl.value!!.position) }
.toTypedArray(), position = decl.value!!.position)
}
decl.value!!.linkParents(decl)
@ -90,57 +99,49 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
return decl
}
}
if(litval?.type== DataType.FLOAT)
errors.add(ExpressionError("arraysize requires only integers here", litval.position))
if(numericLv!=null && numericLv.type== DataType.FLOAT)
errors.add(ExpressionError("arraysize requires only integers here", numericLv.position))
val size = decl.arraysize?.size() ?: return decl
if ((litval==null || !litval.isArray) && rangeExpr==null) {
if (rangeExpr==null && numericLv!=null) {
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
val fillvalue = if (litval == null) 0 else litval.asIntegerValue ?: 0
val fillvalue = numericLv.number.toInt()
when(decl.datatype){
DataType.ARRAY_UB -> {
if(fillvalue !in 0..255)
errors.add(ExpressionError("ubyte value overflow", litval?.position
?: decl.position))
errors.add(ExpressionError("ubyte value overflow", numericLv.position))
}
DataType.ARRAY_B -> {
if(fillvalue !in -128..127)
errors.add(ExpressionError("byte value overflow", litval?.position
?: decl.position))
errors.add(ExpressionError("byte value overflow", numericLv.position))
}
DataType.ARRAY_UW -> {
if(fillvalue !in 0..65535)
errors.add(ExpressionError("uword value overflow", litval?.position
?: decl.position))
errors.add(ExpressionError("uword value overflow", numericLv.position))
}
DataType.ARRAY_W -> {
if(fillvalue !in -32768..32767)
errors.add(ExpressionError("word value overflow", litval?.position
?: decl.position))
errors.add(ExpressionError("word value overflow", numericLv.position))
}
else -> {}
}
val heapId = program.heap.addIntegerArray(decl.datatype, Array(size) { IntegerOrAddressOf(fillvalue, null) })
decl.value = LiteralValue(decl.datatype, initHeapId = heapId, position = litval?.position
?: decl.position)
decl.value = ReferenceLiteralValue(decl.datatype, initHeapId = heapId, position = numericLv.position)
optimizationsDone++
return decl
}
}
DataType.ARRAY_F -> {
val litval = decl.value as? NumericLiteralValue
val size = decl.arraysize?.size() ?: return decl
if (litval==null || !litval.isArray) {
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
val fillvalue = if (litval == null) 0.0 else litval.asNumericValue?.toDouble() ?: 0.0
if(fillvalue< FLOAT_MAX_NEGATIVE || fillvalue> FLOAT_MAX_POSITIVE)
errors.add(ExpressionError("float value overflow", litval?.position
?: decl.position))
else {
val heapId = program.heap.addDoublesArray(DoubleArray(size) { fillvalue })
decl.value = LiteralValue(DataType.ARRAY_F, initHeapId = heapId, position = litval?.position
?: decl.position)
optimizationsDone++
return decl
}
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
val fillvalue = if (litval == null) 0.0 else litval.number.toDouble()
if(fillvalue< FLOAT_MAX_NEGATIVE || fillvalue> FLOAT_MAX_POSITIVE)
errors.add(ExpressionError("float value overflow", litval?.position ?: decl.position))
else {
val heapId = program.heap.addDoublesArray(DoubleArray(size) { fillvalue })
decl.value = ReferenceLiteralValue(DataType.ARRAY_F, initHeapId = heapId, position = litval?.position ?: decl.position)
optimizationsDone++
return decl
}
}
else -> {
@ -152,7 +153,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
return super.visit(decl)
}
private fun fixupArrayTypeOnHeap(decl: VarDecl, litval: LiteralValue) {
private fun fixupArrayTypeOnHeap(decl: VarDecl, litval: ReferenceLiteralValue) {
// fix the type of the array value that's on the heap, to match the vardecl.
// notice that checking the bounds of the actual values is not done here, but in the AstChecker later.
@ -164,7 +165,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
if(array.array!=null) {
program.heap.update(heapId, HeapValues.HeapValue(decl.datatype, null, array.array, null))
decl.value = LiteralValue(decl.datatype, initHeapId = heapId, position = litval.position)
decl.value = ReferenceLiteralValue(decl.datatype, initHeapId = heapId, position = litval.position)
}
}
DataType.ARRAY_F -> {
@ -172,7 +173,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
// convert a non-float array to floats
val doubleArray = array.array.map { it.integer!!.toDouble() }.toDoubleArray()
program.heap.update(heapId, HeapValues.HeapValue(DataType.ARRAY_F, null, null, doubleArray))
decl.value = LiteralValue(decl.datatype, initHeapId = heapId, position = litval.position)
decl.value = ReferenceLiteralValue(decl.datatype, initHeapId = heapId, position = litval.position)
}
}
DataType.STRUCT -> {
@ -188,12 +189,15 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
override fun visit(identifier: IdentifierReference): IExpression {
return try {
val cval = identifier.constValue(program) ?: return identifier
return if(cval.isNumeric) {
val copy = LiteralValue(cval.type, cval.bytevalue, cval.wordvalue, cval.floatvalue, null, cval.arrayvalue, position = identifier.position)
copy.parent = identifier.parent
copy
} else
identifier
return when {
cval.type in NumericDatatypes -> {
val copy = NumericLiteralValue(cval.type, cval.number, identifier.position)
copy.parent = identifier.parent
copy
}
cval.type in PassByReferenceDatatypes -> TODO("ref type $identifier")
else -> identifier
}
} catch (ax: AstException) {
addError(ax)
identifier
@ -253,38 +257,31 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
super.visit(expr)
val subexpr = expr.expression
if (subexpr is LiteralValue) {
if (subexpr is NumericLiteralValue) {
// accept prefixed literal values (such as -3, not true)
return when {
expr.operator == "+" -> subexpr
expr.operator == "-" -> when {
subexpr.asIntegerValue!= null -> {
subexpr.type in IntegerDatatypes -> {
optimizationsDone++
LiteralValue.optimalNumeric(-subexpr.asIntegerValue, subexpr.position)
NumericLiteralValue.optimalNumeric(-subexpr.number.toInt(), subexpr.position)
}
subexpr.floatvalue != null -> {
subexpr.type == DataType.FLOAT -> {
optimizationsDone++
LiteralValue(DataType.FLOAT, floatvalue = -subexpr.floatvalue, position = subexpr.position)
NumericLiteralValue(DataType.FLOAT, -subexpr.number.toDouble(), subexpr.position)
}
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
}
expr.operator == "~" -> when {
subexpr.asIntegerValue != null -> {
subexpr.type in IntegerDatatypes -> {
optimizationsDone++
LiteralValue.optimalNumeric(subexpr.asIntegerValue.inv(), subexpr.position)
NumericLiteralValue.optimalNumeric(subexpr.number.toInt().inv(), subexpr.position)
}
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
}
expr.operator == "not" -> when {
subexpr.asIntegerValue != null -> {
optimizationsDone++
LiteralValue.fromBoolean(subexpr.asIntegerValue == 0, subexpr.position)
}
subexpr.floatvalue != null -> {
optimizationsDone++
LiteralValue.fromBoolean(subexpr.floatvalue == 0.0, subexpr.position)
}
else -> throw ExpressionError("can not take logical not of $subexpr", subexpr.position)
expr.operator == "not" -> {
optimizationsDone++
NumericLiteralValue.fromBoolean(subexpr.number.toDouble() == 0.0, subexpr.position)
}
else -> throw ExpressionError(expr.operator, subexpr.position)
}
@ -544,7 +541,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
override fun visit(forLoop: ForLoop): IStatement {
fun adjustRangeDt(rangeFrom: LiteralValue, targetDt: DataType, rangeTo: LiteralValue, stepLiteral: LiteralValue?, range: RangeExpr): RangeExpr {
fun adjustRangeDt(rangeFrom: NumericLiteralValue, targetDt: DataType, rangeTo: NumericLiteralValue, stepLiteral: NumericLiteralValue?, range: RangeExpr): RangeExpr {
val newFrom = rangeFrom.cast(targetDt)
val newTo = rangeTo.cast(targetDt)
if (newFrom != null && newTo != null) {
@ -558,13 +555,13 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
// adjust the datatype of a range expression in for loops to the loop variable.
val resultStmt = super.visit(forLoop) as ForLoop
val iterableRange = resultStmt.iterable as? RangeExpr ?: return resultStmt
val rangeFrom = iterableRange.from as? LiteralValue
val rangeTo = iterableRange.to as? LiteralValue
val rangeFrom = iterableRange.from as? NumericLiteralValue
val rangeTo = iterableRange.to as? NumericLiteralValue
if(rangeFrom==null || rangeTo==null) return resultStmt
val loopvar = resultStmt.loopVar?.targetVarDecl(program.namespace)
if(loopvar!=null) {
val stepLiteral = iterableRange.step as? LiteralValue
val stepLiteral = iterableRange.step as? NumericLiteralValue
when(loopvar.datatype) {
DataType.UBYTE -> {
if(rangeFrom.type!= DataType.UBYTE) {
@ -596,16 +593,16 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
return resultStmt
}
override fun visit(literalValue: LiteralValue): LiteralValue {
val litval = super.visit(literalValue)
override fun visit(refLiteral: ReferenceLiteralValue): ReferenceLiteralValue {
val litval = super.visit(refLiteral)
if(litval.isString) {
// intern the string; move it into the heap
if(litval.strvalue!!.length !in 1..255)
if(litval.str!!.length !in 1..255)
addError(ExpressionError("string literal length must be between 1 and 255", litval.position))
else {
litval.addToHeap(program.heap) // TODO: we don't know the actual string type yet, STR != STR_S etc...
}
} else if(litval.arrayvalue!=null) {
} else if(litval.isArray) {
// first, adjust the array datatype
val litval2 = adjustArrayValDatatype(litval)
litval2.addToHeap(program.heap)
@ -614,8 +611,14 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
return litval
}
private fun adjustArrayValDatatype(litval: LiteralValue): LiteralValue {
val array = litval.arrayvalue!!
private fun adjustArrayValDatatype(litval: ReferenceLiteralValue): ReferenceLiteralValue {
if(litval.array==null) {
if(litval.heapId!=null)
return litval // thing is already on the heap, assume it's the right type
throw FatalAstException("missing array value")
}
val array = litval.array!!
val typesInArray = array.mapNotNull { it.inferType(program) }.toSet()
val arrayDt =
when {
@ -623,12 +626,12 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
DataType.FLOAT in typesInArray -> DataType.ARRAY_F
DataType.WORD in typesInArray -> DataType.ARRAY_W
else -> {
val allElementsAreConstantOrAddressOf = array.fold(true) { c, expr-> c and (expr is LiteralValue || expr is AddressOf)}
val allElementsAreConstantOrAddressOf = array.fold(true) { c, expr-> c and (expr is NumericLiteralValue|| expr is AddressOf)}
if(!allElementsAreConstantOrAddressOf) {
addError(ExpressionError("array literal can only consist of constant primitive numerical values or memory pointers", litval.position))
return litval
} else {
val integerArray = array.map { it.constValue(program)!!.asIntegerValue!! }
val integerArray = array.map { it.constValue(program)!!.number.toInt() }
val maxValue = integerArray.max()!!
val minValue = integerArray.min()!!
if (minValue >= 0) {
@ -649,70 +652,69 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
}
if(arrayDt!=litval.type) {
return LiteralValue(arrayDt, arrayvalue = litval.arrayvalue, position = litval.position)
return ReferenceLiteralValue(arrayDt, array = litval.array, position = litval.position)
}
return litval
}
override fun visit(assignment: Assignment): IStatement {
super.visit(assignment)
val lv = assignment.value as? LiteralValue
val lv = assignment.value as? NumericLiteralValue
if(lv!=null) {
// see if we can promote/convert a literal value to the required datatype
when(assignment.target.inferType(program, assignment)) {
DataType.UWORD -> {
// we can convert to UWORD: any UBYTE, BYTE/WORD that are >=0, FLOAT that's an integer 0..65535,
if(lv.type== DataType.UBYTE)
assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position = lv.position)
else if(lv.type== DataType.BYTE && lv.bytevalue!!>=0)
assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position = lv.position)
else if(lv.type== DataType.WORD && lv.wordvalue!!>=0)
assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position = lv.position)
assignment.value = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position)
else if(lv.type== DataType.BYTE && lv.number.toInt()>=0)
assignment.value = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position)
else if(lv.type== DataType.WORD && lv.number.toInt()>=0)
assignment.value = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position)
else if(lv.type== DataType.FLOAT) {
val d = lv.floatvalue!!
val d = lv.number.toDouble()
if(floor(d)==d && d>=0 && d<=65535)
assignment.value = LiteralValue(DataType.UWORD, wordvalue = floor(d).toInt(), position = lv.position)
assignment.value = NumericLiteralValue(DataType.UWORD, floor(d).toInt(), lv.position)
}
}
DataType.UBYTE -> {
// we can convert to UBYTE: UWORD <=255, BYTE >=0, FLOAT that's an integer 0..255,
if(lv.type== DataType.UWORD && lv.wordvalue!! <= 255)
assignment.value = LiteralValue(DataType.UBYTE, lv.wordvalue.toShort(), position = lv.position)
else if(lv.type== DataType.BYTE && lv.bytevalue!! >=0)
assignment.value = LiteralValue(DataType.UBYTE, lv.bytevalue.toShort(), position = lv.position)
if(lv.type== DataType.UWORD && lv.number.toInt() <= 255)
assignment.value = NumericLiteralValue(DataType.UBYTE, lv.number.toShort(), lv.position)
else if(lv.type== DataType.BYTE && lv.number.toInt() >=0)
assignment.value = NumericLiteralValue(DataType.UBYTE, lv.number.toShort(), lv.position)
else if(lv.type== DataType.FLOAT) {
val d = lv.floatvalue!!
val d = lv.number.toDouble()
if(floor(d)==d && d >=0 && d<=255)
assignment.value = LiteralValue(DataType.UBYTE, floor(d).toShort(), position = lv.position)
assignment.value = NumericLiteralValue(DataType.UBYTE, floor(d).toShort(), lv.position)
}
}
DataType.BYTE -> {
// we can convert to BYTE: UWORD/UBYTE <= 127, FLOAT that's an integer 0..127
if(lv.type== DataType.UWORD && lv.wordvalue!! <= 127)
assignment.value = LiteralValue(DataType.BYTE, lv.wordvalue.toShort(), position = lv.position)
else if(lv.type== DataType.UBYTE && lv.bytevalue!! <= 127)
assignment.value = LiteralValue(DataType.BYTE, lv.bytevalue, position = lv.position)
if(lv.type== DataType.UWORD && lv.number.toInt() <= 127)
assignment.value = NumericLiteralValue(DataType.BYTE, lv.number.toShort(), lv.position)
else if(lv.type== DataType.UBYTE && lv.number.toInt() <= 127)
assignment.value = NumericLiteralValue(DataType.BYTE, lv.number.toShort(), lv.position)
else if(lv.type== DataType.FLOAT) {
val d = lv.floatvalue!!
val d = lv.number.toDouble()
if(floor(d)==d && d>=0 && d<=127)
assignment.value = LiteralValue(DataType.BYTE, floor(d).toShort(), position = lv.position)
assignment.value = NumericLiteralValue(DataType.BYTE, floor(d).toShort(), lv.position)
}
}
DataType.WORD -> {
// we can convert to WORD: any UBYTE/BYTE, UWORD <= 32767, FLOAT that's an integer -32768..32767,
if(lv.type== DataType.UBYTE || lv.type== DataType.BYTE)
assignment.value = LiteralValue(DataType.WORD, wordvalue = lv.bytevalue!!.toInt(), position = lv.position)
else if(lv.type== DataType.UWORD && lv.wordvalue!! <= 32767)
assignment.value = LiteralValue(DataType.WORD, wordvalue = lv.wordvalue, position = lv.position)
assignment.value = NumericLiteralValue(DataType.WORD, lv.number.toInt(), lv.position)
else if(lv.type== DataType.UWORD && lv.number.toInt() <= 32767)
assignment.value = NumericLiteralValue(DataType.WORD, lv.number.toInt(), lv.position)
else if(lv.type== DataType.FLOAT) {
val d = lv.floatvalue!!
val d = lv.number.toDouble()
if(floor(d)==d && d>=-32768 && d<=32767)
assignment.value = LiteralValue(DataType.BYTE, floor(d).toShort(), position = lv.position)
assignment.value = NumericLiteralValue(DataType.BYTE, floor(d).toShort(), lv.position)
}
}
DataType.FLOAT -> {
if(lv.isNumeric)
assignment.value = LiteralValue(DataType.FLOAT, floatvalue = lv.asNumericValue?.toDouble(), position = lv.position)
assignment.value = NumericLiteralValue(DataType.FLOAT, lv.number.toDouble(), lv.position)
}
else -> {}
}

View File

@ -39,7 +39,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
var tc = typecast
// try to statically convert a literal value into one of the desired type
val literal = tc.expression as? LiteralValue
val literal = tc.expression as? NumericLiteralValue
if(literal!=null) {
val newLiteral = literal.cast(tc.type)
if(newLiteral!=null && newLiteral!==literal) {
@ -132,8 +132,8 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
super.visit(expr)
val leftVal = expr.left.constValue(program)
val rightVal = expr.right.constValue(program)
val constTrue = LiteralValue.fromBoolean(true, expr.position)
val constFalse = LiteralValue.fromBoolean(false, expr.position)
val constTrue = NumericLiteralValue.fromBoolean(true, expr.position)
val constFalse = NumericLiteralValue.fromBoolean(false, expr.position)
val leftDt = expr.left.inferType(program)
val rightDt = expr.right.inferType(program)
@ -174,10 +174,10 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
// X + (-value) --> X - value
if (expr.operator == "+" && rightVal != null) {
val rv = rightVal.asNumericValue?.toDouble()
if (rv != null && rv < 0.0) {
val rv = rightVal.number.toDouble()
if (rv < 0.0) {
expr.operator = "-"
expr.right = LiteralValue.fromNumber(-rv, rightVal.type, rightVal.position)
expr.right = NumericLiteralValue(rightVal.type, -rv, rightVal.position)
optimizationsDone++
return expr
}
@ -185,10 +185,10 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
// (-value) + X --> X - value
if (expr.operator == "+" && leftVal != null) {
val lv = leftVal.asNumericValue?.toDouble()
if (lv != null && lv < 0.0) {
val lv = leftVal.number.toDouble()
if (lv < 0.0) {
expr.operator = "-"
expr.right = LiteralValue.fromNumber(-lv, leftVal.type, leftVal.position)
expr.right = NumericLiteralValue(leftVal.type, -lv, leftVal.position)
optimizationsDone++
return expr
}
@ -204,10 +204,10 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
// X - (-value) --> X + value
if (expr.operator == "-" && rightVal != null) {
val rv = rightVal.asNumericValue?.toDouble()
if (rv != null && rv < 0.0) {
val rv = rightVal.number.toDouble()
if (rv < 0.0) {
expr.operator = "+"
expr.right = LiteralValue.fromNumber(-rv, rightVal.type, rightVal.position)
expr.right = NumericLiteralValue(rightVal.type, -rv, rightVal.position)
optimizationsDone++
return expr
}
@ -225,7 +225,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
val x = expr.right
val y = determineY(x, leftBinExpr)
if(y!=null) {
val yPlus1 = BinaryExpression(y, "+", LiteralValue.fromNumber(1, leftDt!!, y.position), y.position)
val yPlus1 = BinaryExpression(y, "+", NumericLiteralValue(leftDt!!, 1, y.position), y.position)
return BinaryExpression(x, "*", yPlus1, x.position)
}
} else {
@ -234,7 +234,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
val x = expr.right
val y = determineY(x, leftBinExpr)
if(y!=null) {
val yMinus1 = BinaryExpression(y, "-", LiteralValue.fromNumber(1, leftDt!!, y.position), y.position)
val yMinus1 = BinaryExpression(y, "-", NumericLiteralValue(leftDt!!, 1, y.position), y.position)
return BinaryExpression(x, "*", yMinus1, x.position)
}
}
@ -246,7 +246,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
val x = expr.left
val y = determineY(x, rightBinExpr)
if(y!=null) {
val yPlus1 = BinaryExpression(y, "+", LiteralValue.optimalInteger(1, y.position), y.position)
val yPlus1 = BinaryExpression(y, "+", NumericLiteralValue.optimalInteger(1, y.position), y.position)
return BinaryExpression(x, "*", yPlus1, x.position)
}
} else {
@ -255,7 +255,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
val x = expr.left
val y = determineY(x, rightBinExpr)
if(y!=null) {
val oneMinusY = BinaryExpression(LiteralValue.optimalInteger(1, y.position), "-", y, y.position)
val oneMinusY = BinaryExpression(NumericLiteralValue.optimalInteger(1, y.position), "-", y, y.position)
return BinaryExpression(x, "*", oneMinusY, x.position)
}
}
@ -350,58 +350,58 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
}
private fun adjustDatatypes(expr: BinaryExpression,
leftConstVal: LiteralValue?, leftDt: DataType,
rightConstVal: LiteralValue?, rightDt: DataType): Boolean {
leftConstVal: NumericLiteralValue?, leftDt: DataType,
rightConstVal: NumericLiteralValue?, rightDt: DataType): Boolean {
fun adjust(value: LiteralValue, targetDt: DataType): Pair<Boolean, LiteralValue>{
fun adjust(value: NumericLiteralValue, targetDt: DataType): Pair<Boolean, NumericLiteralValue>{
if(value.type==targetDt)
return Pair(false, value)
when(value.type) {
DataType.UBYTE -> {
if (targetDt == DataType.BYTE) {
if(value.bytevalue!! < 127)
return Pair(true, LiteralValue(targetDt, value.bytevalue, position = value.position))
if(value.number.toInt() < 127)
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
}
else if (targetDt == DataType.UWORD || targetDt == DataType.WORD)
return Pair(true, LiteralValue(targetDt, wordvalue = value.bytevalue!!.toInt(), position = value.position))
return Pair(true, NumericLiteralValue(targetDt, value.number.toInt(), value.position))
}
DataType.BYTE -> {
if (targetDt == DataType.UBYTE) {
if(value.bytevalue!! >= 0)
return Pair(true, LiteralValue(targetDt, value.bytevalue, position = value.position))
if(value.number.toInt() >= 0)
return Pair(true, NumericLiteralValue(targetDt, value.number.toInt(), value.position))
}
else if (targetDt == DataType.UWORD) {
if(value.bytevalue!! >= 0)
return Pair(true, LiteralValue(targetDt, wordvalue = value.bytevalue.toInt(), position = value.position))
if(value.number.toInt() >= 0)
return Pair(true, NumericLiteralValue(targetDt, value.number.toInt(), value.position))
}
else if (targetDt == DataType.WORD) return Pair(true, LiteralValue(targetDt, wordvalue = value.bytevalue!!.toInt(), position = value.position))
else if (targetDt == DataType.WORD) return Pair(true, NumericLiteralValue(targetDt, value.number.toInt(), value.position))
}
DataType.UWORD -> {
if (targetDt == DataType.UBYTE) {
if(value.wordvalue!! <= 255)
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
if(value.number.toInt() <= 255)
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
}
else if (targetDt == DataType.BYTE) {
if(value.wordvalue!! <= 127)
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
if(value.number.toInt() <= 127)
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
}
else if (targetDt == DataType.WORD) {
if(value.wordvalue!! <= 32767)
return Pair(true, LiteralValue(targetDt, wordvalue = value.wordvalue, position = value.position))
if(value.number.toInt() <= 32767)
return Pair(true, NumericLiteralValue(targetDt, value.number.toInt(), value.position))
}
}
DataType.WORD -> {
if (targetDt == DataType.UBYTE) {
if(value.wordvalue!! in 0..255)
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
if(value.number.toInt() in 0..255)
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
}
else if (targetDt == DataType.BYTE) {
if(value.wordvalue!! in -128..127)
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
if(value.number.toInt() in -128..127)
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
}
else if (targetDt == DataType.UWORD) {
if(value.wordvalue!! >= 0)
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
if(value.number.toInt() >= 0)
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
}
}
else -> {}
@ -434,9 +434,9 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
}
}
private data class ReorderedAssociativeBinaryExpr(val expr: BinaryExpression, val leftVal: LiteralValue?, val rightVal: LiteralValue?)
private data class ReorderedAssociativeBinaryExpr(val expr: BinaryExpression, val leftVal: NumericLiteralValue?, val rightVal: NumericLiteralValue?)
private fun reorderAssociative(expr: BinaryExpression, leftVal: LiteralValue?): ReorderedAssociativeBinaryExpr {
private fun reorderAssociative(expr: BinaryExpression, leftVal: NumericLiteralValue?): ReorderedAssociativeBinaryExpr {
if(expr.operator in associativeOperators && leftVal!=null) {
// swap left and right so that right is always the constant
val tmp = expr.left
@ -448,15 +448,15 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
return ReorderedAssociativeBinaryExpr(expr, leftVal, expr.right.constValue(program))
}
private fun optimizeAdd(pexpr: BinaryExpression, pleftVal: LiteralValue?, prightVal: LiteralValue?): IExpression {
private fun optimizeAdd(pexpr: BinaryExpression, pleftVal: NumericLiteralValue?, prightVal: NumericLiteralValue?): IExpression {
if(pleftVal==null && prightVal==null)
return pexpr
val (expr, _, rightVal) = reorderAssociative(pexpr, pleftVal)
if(rightVal!=null) {
// right value is a constant, see if we can optimize
val rightConst: LiteralValue = rightVal
when(rightConst.asNumericValue?.toDouble()) {
val rightConst: NumericLiteralValue = rightVal
when(rightConst.number.toDouble()) {
0.0 -> {
// left
optimizationsDone++
@ -469,14 +469,14 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
return expr
}
private fun optimizeSub(expr: BinaryExpression, leftVal: LiteralValue?, rightVal: LiteralValue?): IExpression {
private fun optimizeSub(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): IExpression {
if(leftVal==null && rightVal==null)
return expr
if(rightVal!=null) {
// right value is a constant, see if we can optimize
val rightConst: LiteralValue = rightVal
when(rightConst.asNumericValue?.toDouble()) {
val rightConst: NumericLiteralValue = rightVal
when(rightConst.number.toDouble()) {
0.0 -> {
// left
optimizationsDone++
@ -486,7 +486,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
}
if(leftVal!=null) {
// left value is a constant, see if we can optimize
when(leftVal.asNumericValue?.toDouble()) {
when(leftVal.number.toDouble()) {
0.0 -> {
// -right
optimizationsDone++
@ -498,38 +498,38 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
return expr
}
private fun optimizePower(expr: BinaryExpression, leftVal: LiteralValue?, rightVal: LiteralValue?): IExpression {
private fun optimizePower(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): IExpression {
if(leftVal==null && rightVal==null)
return expr
if(rightVal!=null) {
// right value is a constant, see if we can optimize
val rightConst: LiteralValue = rightVal
when(rightConst.asNumericValue?.toDouble()) {
val rightConst: NumericLiteralValue = rightVal
when(rightConst.number.toDouble()) {
-3.0 -> {
// -1/(left*left*left)
optimizationsDone++
return BinaryExpression(LiteralValue(DataType.FLOAT, floatvalue = -1.0, position = expr.position), "/",
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
BinaryExpression(expr.left, "*", BinaryExpression(expr.left, "*", expr.left, expr.position), expr.position),
expr.position)
}
-2.0 -> {
// -1/(left*left)
optimizationsDone++
return BinaryExpression(LiteralValue(DataType.FLOAT, floatvalue = -1.0, position = expr.position), "/",
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
BinaryExpression(expr.left, "*", expr.left, expr.position),
expr.position)
}
-1.0 -> {
// -1/left
optimizationsDone++
return BinaryExpression(LiteralValue(DataType.FLOAT, floatvalue = -1.0, position = expr.position), "/",
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
expr.left, expr.position)
}
0.0 -> {
// 1
optimizationsDone++
return LiteralValue.fromNumber(1, rightConst.type, expr.position)
return NumericLiteralValue(rightConst.type, 1, expr.position)
}
0.5 -> {
// sqrt(left)
@ -555,21 +555,21 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
}
if(leftVal!=null) {
// left value is a constant, see if we can optimize
when(leftVal.asNumericValue?.toDouble()) {
when(leftVal.number.toDouble()) {
-1.0 -> {
// -1
optimizationsDone++
return LiteralValue(DataType.FLOAT, floatvalue = -1.0, position = expr.position)
return NumericLiteralValue(DataType.FLOAT, -1.0, expr.position)
}
0.0 -> {
// 0
optimizationsDone++
return LiteralValue.fromNumber(0, leftVal.type, expr.position)
return NumericLiteralValue(leftVal.type, 0, expr.position)
}
1.0 -> {
//1
optimizationsDone++
return LiteralValue.fromNumber(1, leftVal.type, expr.position)
return NumericLiteralValue(leftVal.type, 1, expr.position)
}
}
@ -578,22 +578,22 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
return expr
}
private fun optimizeRemainder(expr: BinaryExpression, leftVal: LiteralValue?, rightVal: LiteralValue?): IExpression {
private fun optimizeRemainder(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): IExpression {
if(leftVal==null && rightVal==null)
return expr
// simplify assignments A = B <operator> C
val cv = rightVal?.asIntegerValue?.toDouble()
val cv = rightVal?.number?.toInt()?.toDouble()
when(expr.operator) {
"%" -> {
if (cv == 1.0) {
optimizationsDone++
return LiteralValue.fromNumber(0, expr.inferType(program)!!, expr.position)
return NumericLiteralValue(expr.inferType(program)!!, 0, expr.position)
} else if (cv == 2.0) {
optimizationsDone++
expr.operator = "&"
expr.right = LiteralValue.optimalInteger(1, expr.position)
expr.right = NumericLiteralValue.optimalInteger(1, expr.position)
return expr
}
}
@ -602,7 +602,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
}
private fun optimizeDivision(expr: BinaryExpression, leftVal: LiteralValue?, rightVal: LiteralValue?): IExpression {
private fun optimizeDivision(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): IExpression {
if(leftVal==null && rightVal==null)
return expr
@ -610,8 +610,8 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
if(rightVal!=null) {
// right value is a constant, see if we can optimize
val rightConst: LiteralValue = rightVal
val cv = rightConst.asNumericValue?.toDouble()
val rightConst: NumericLiteralValue = rightVal
val cv = rightConst.number.toDouble()
val leftDt = expr.left.inferType(program)
when(cv) {
-1.0 -> {
@ -633,7 +633,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
// divided by a power of two => shift right
optimizationsDone++
val numshifts = log2(cv).toInt()
return BinaryExpression(expr.left, ">>", LiteralValue.optimalInteger(numshifts, expr.position), expr.position)
return BinaryExpression(expr.left, ">>", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
}
}
-2.0, -4.0, -8.0, -16.0, -32.0, -64.0, -128.0, -256.0, -512.0, -1024.0, -2048.0, -4096.0, -8192.0, -16384.0, -32768.0, -65536.0 -> {
@ -641,32 +641,32 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
// divided by a negative power of two => negate, then shift right
optimizationsDone++
val numshifts = log2(-cv).toInt()
return BinaryExpression(PrefixExpression("-", expr.left, expr.position), ">>", LiteralValue.optimalInteger(numshifts, expr.position), expr.position)
return BinaryExpression(PrefixExpression("-", expr.left, expr.position), ">>", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
}
}
}
if (leftDt == DataType.UBYTE) {
if(abs(rightConst.asNumericValue!!.toDouble()) >= 256.0) {
if(abs(rightConst.number.toDouble()) >= 256.0) {
optimizationsDone++
return LiteralValue(DataType.UBYTE, 0, position = expr.position)
return NumericLiteralValue(DataType.UBYTE, 0, expr.position)
}
}
else if (leftDt == DataType.UWORD) {
if(abs(rightConst.asNumericValue!!.toDouble()) >= 65536.0) {
if(abs(rightConst.number.toDouble()) >= 65536.0) {
optimizationsDone++
return LiteralValue(DataType.UBYTE, 0, position = expr.position)
return NumericLiteralValue(DataType.UBYTE, 0, expr.position)
}
}
}
if(leftVal!=null) {
// left value is a constant, see if we can optimize
when(leftVal.asNumericValue?.toDouble()) {
when(leftVal.number.toDouble()) {
0.0 -> {
// 0
optimizationsDone++
return LiteralValue.fromNumber(0, leftVal.type, expr.position)
return NumericLiteralValue(leftVal.type, 0, expr.position)
}
}
}
@ -674,7 +674,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
return expr
}
private fun optimizeMultiplication(pexpr: BinaryExpression, pleftVal: LiteralValue?, prightVal: LiteralValue?): IExpression {
private fun optimizeMultiplication(pexpr: BinaryExpression, pleftVal: NumericLiteralValue?, prightVal: NumericLiteralValue?): IExpression {
if(pleftVal==null && prightVal==null)
return pexpr
@ -682,8 +682,8 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
if(rightVal!=null) {
// right value is a constant, see if we can optimize
val leftValue: IExpression = expr.left
val rightConst: LiteralValue = rightVal
when(val cv = rightConst.asNumericValue?.toDouble()) {
val rightConst: NumericLiteralValue = rightVal
when(val cv = rightConst.number.toDouble()) {
-1.0 -> {
// -left
optimizationsDone++
@ -692,7 +692,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
0.0 -> {
// 0
optimizationsDone++
return LiteralValue.fromNumber(0, rightConst.type, expr.position)
return NumericLiteralValue(rightConst.type, 0, expr.position)
}
1.0 -> {
// left
@ -704,7 +704,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
// times a power of two => shift left
optimizationsDone++
val numshifts = log2(cv).toInt()
return BinaryExpression(expr.left, "<<", LiteralValue.optimalInteger(numshifts, expr.position), expr.position)
return BinaryExpression(expr.left, "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
}
}
-2.0, -4.0, -8.0, -16.0, -32.0, -64.0, -128.0, -256.0, -512.0, -1024.0, -2048.0, -4096.0, -8192.0, -16384.0, -32768.0, -65536.0 -> {
@ -712,7 +712,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
// times a negative power of two => negate, then shift left
optimizationsDone++
val numshifts = log2(-cv).toInt()
return BinaryExpression(PrefixExpression("-", expr.left, expr.position), "<<", LiteralValue.optimalInteger(numshifts, expr.position), expr.position)
return BinaryExpression(PrefixExpression("-", expr.left, expr.position), "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
}
}
}

View File

@ -209,7 +209,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
var previousAssignmentLine: Int? = null
for (i in 0 until statements.size) {
val stmt = statements[i] as? Assignment
if (stmt != null && stmt.value is LiteralValue) {
if (stmt != null && stmt.value is NumericLiteralValue) {
if (previousAssignmentLine == null) {
previousAssignmentLine = i
continue
@ -241,7 +241,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print") ||
functionCallStatement.target.nameInSource==listOf("c64scr", "print_p")) {
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
if(functionCallStatement.arglist.single() is LiteralValue)
if(functionCallStatement.arglist.single() is NumericLiteralValue)
throw AstException("string argument should be on heap already")
val stringVar = functionCallStatement.arglist.single() as? IdentifierReference
if(stringVar!=null) {
@ -250,7 +250,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
if(string.length==1) {
val petscii = Petscii.encodePetscii(string, true)[0]
functionCallStatement.arglist.clear()
functionCallStatement.arglist.add(LiteralValue.optimalInteger(petscii, functionCallStatement.position))
functionCallStatement.arglist.add(NumericLiteralValue.optimalInteger(petscii.toInt(), functionCallStatement.position))
functionCallStatement.target = IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position)
optimizationsDone++
return functionCallStatement
@ -258,9 +258,9 @@ internal class StatementOptimizer(private val program: Program, private val opti
val petscii = Petscii.encodePetscii(string, true)
val scope = AnonymousScope(mutableListOf(), functionCallStatement.position)
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
mutableListOf(LiteralValue.optimalInteger(petscii[0], functionCallStatement.position)), functionCallStatement.position))
mutableListOf(NumericLiteralValue.optimalInteger(petscii[0].toInt(), functionCallStatement.position)), functionCallStatement.position))
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
mutableListOf(LiteralValue.optimalInteger(petscii[1], functionCallStatement.position)), functionCallStatement.position))
mutableListOf(NumericLiteralValue.optimalInteger(petscii[1].toInt(), functionCallStatement.position)), functionCallStatement.position))
optimizationsDone++
return scope
}
@ -495,12 +495,12 @@ internal class StatementOptimizer(private val program: Program, private val opti
val targetDt = assignment.target.inferType(program, assignment)
val bexpr=assignment.value as? BinaryExpression
if(bexpr!=null) {
val cv = bexpr.right.constValue(program)?.asNumericValue?.toDouble()
val cv = bexpr.right.constValue(program)?.number?.toDouble()
if (cv == null) {
if (bexpr.operator == "+" && targetDt != DataType.FLOAT) {
if (bexpr.left isSameAs bexpr.right && assignment.target isSameAs bexpr.left) {
bexpr.operator = "*"
bexpr.right = LiteralValue.optimalInteger(2, assignment.value.position)
bexpr.right = NumericLiteralValue.optimalInteger(2, assignment.value.position)
optimizationsDone++
return assignment
}
@ -569,7 +569,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
}
if (((targetDt == DataType.UWORD || targetDt == DataType.WORD) && cv > 15.0) ||
((targetDt == DataType.UBYTE || targetDt == DataType.BYTE) && cv > 7.0)) {
assignment.value = LiteralValue.optimalInteger(0, assignment.value.position)
assignment.value = NumericLiteralValue.optimalInteger(0, assignment.value.position)
assignment.value.linkParents(assignment)
optimizationsDone++
} else {
@ -591,7 +591,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
}
if (((targetDt == DataType.UWORD || targetDt == DataType.WORD) && cv > 15.0) ||
((targetDt == DataType.UBYTE || targetDt == DataType.BYTE) && cv > 7.0)) {
assignment.value = LiteralValue.optimalInteger(0, assignment.value.position)
assignment.value = NumericLiteralValue.optimalInteger(0, assignment.value.position)
assignment.value.linkParents(assignment)
optimizationsDone++
} else {

View File

@ -1,7 +1,8 @@
package prog8.vm
import prog8.ast.base.*
import prog8.ast.expressions.LiteralValue
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.ReferenceLiteralValue
import prog8.ast.statements.StructDecl
import prog8.ast.statements.ZeropageWish
import prog8.compiler.HeapValues
@ -11,7 +12,7 @@ import kotlin.math.pow
/**
* Rather than a literal value (LiteralValue) that occurs in the parsed source code,
* Rather than a literal value (NumericLiteralValue) that occurs in the parsed source code,
* this runtime value can be used to *execute* the parsed Ast (or another intermediary form)
* It contains a value of a variable during run time of the program and provides arithmetic operations on the value.
*/
@ -24,16 +25,19 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
val asBoolean: Boolean
companion object {
fun from(literalValue: LiteralValue, heap: HeapValues): RuntimeValue {
fun fromLv(literalValue: NumericLiteralValue): RuntimeValue {
return RuntimeValue(literalValue.type, num = literalValue.number)
}
fun fromLv(literalValue: ReferenceLiteralValue, heap: HeapValues): RuntimeValue {
return when(literalValue.type) {
in NumericDatatypes -> RuntimeValue(literalValue.type, num = literalValue.asNumericValue!!)
in StringDatatypes -> from(literalValue.heapId!!, heap)
in ArrayDatatypes -> from(literalValue.heapId!!, heap)
in StringDatatypes -> fromHeapId(literalValue.heapId!!, heap)
in ArrayDatatypes -> fromHeapId(literalValue.heapId!!, heap)
else -> throw IllegalArgumentException("weird source value $literalValue")
}
}
fun from(heapId: Int, heap: HeapValues): RuntimeValue {
fun fromHeapId(heapId: Int, heap: HeapValues): RuntimeValue {
val value = heap.get(heapId)
return when {
value.type in StringDatatypes ->
@ -115,15 +119,16 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
}
}
fun asLiteralValue(): LiteralValue {
fun asNumericLiteralValue(): NumericLiteralValue {
return when(type) {
in ByteDatatypes -> LiteralValue(type, byteval, position = Position("", 0, 0, 0))
in WordDatatypes -> LiteralValue(type, wordvalue = wordval, position = Position("", 0, 0, 0))
DataType.FLOAT -> LiteralValue(type, floatvalue = floatval, position = Position("", 0, 0, 0))
in StringDatatypes -> LiteralValue(type, strvalue = str, position = Position("", 0, 0, 0))
in ArrayDatatypes -> LiteralValue(type,
arrayvalue = array?.map { LiteralValue.optimalNumeric(it, Position("", 0, 0, 0)) }?.toTypedArray(),
position = Position("", 0, 0, 0))
in ByteDatatypes -> NumericLiteralValue(type, byteval!!, Position("", 0, 0, 0))
in WordDatatypes -> NumericLiteralValue(type, wordval!!, Position("", 0, 0, 0))
DataType.FLOAT -> NumericLiteralValue(type, floatval!!, Position("", 0, 0, 0))
in PassByReferenceDatatypes -> TODO("passbyref???")
// in StringDatatypes -> NumericLiteralValue(type, strvalue = str, Position("", 0, 0, 0))
// in ArrayDatatypes -> NumericLiteralValue(type,
// arrayvalue = array?.map { NumericLiteralValue.optimalNumeric(it, Position("", 0, 0, 0)) }?.toTypedArray(),
// Position("", 0, 0, 0))
else -> throw IllegalArgumentException("weird source value $this")
}
}

View File

@ -4,7 +4,7 @@ import prog8.ast.*
import prog8.ast.base.*
import prog8.ast.base.initvarsSubName
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.LiteralValue
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.statements.*
import prog8.compiler.target.c64.MachineDefinition
import prog8.vm.RuntimeValue
@ -32,31 +32,31 @@ class StatusFlags {
var negative: Boolean = false
var irqd: Boolean = false
private fun setFlags(value: LiteralValue?) {
private fun setFlags(value: NumericLiteralValue?) {
if (value != null) {
when (value.type) {
DataType.UBYTE -> {
val v = value.bytevalue!!.toInt()
val v = value.number.toInt()
negative = v > 127
zero = v == 0
}
DataType.BYTE -> {
val v = value.bytevalue!!.toInt()
val v = value.number.toInt()
negative = v < 0
zero = v == 0
}
DataType.UWORD -> {
val v = value.wordvalue!!
val v = value.number.toInt()
negative = v > 32767
zero = v == 0
}
DataType.WORD -> {
val v = value.wordvalue!!
val v = value.number.toInt()
negative = v < 0
zero = v == 0
}
DataType.FLOAT -> {
val flt = value.floatvalue!!
val flt = value.number.toDouble()
negative = flt < 0.0
zero = flt == 0.0
}
@ -387,7 +387,7 @@ class AstVm(val program: Program) {
runtimeVariables.set(identScope, ident.name, value)
}
VarDeclType.MEMORY -> {
val addr=ident.value!!.constValue(program)!!.asIntegerValue!!
val addr=ident.value!!.constValue(program)!!.number.toInt()
val newval = when {
stmt.operator == "++" -> mem.getUByte(addr)+1 and 255
stmt.operator == "--" -> mem.getUByte(addr)-1 and 255
@ -518,7 +518,7 @@ class AstVm(val program: Program) {
break
} else {
val value = choice.values.single().constValue(evalCtx.program) ?: throw VmExecutionException("can only use const values in when choices ${choice.position}")
val rtval = RuntimeValue.from(value, evalCtx.program.heap)
val rtval = RuntimeValue.fromLv(value)
if(condition==rtval) {
executeAnonymousScope(choice.statements)
break
@ -613,7 +613,7 @@ class AstVm(val program: Program) {
}
}
else {
val address = (vardecl.value as LiteralValue).asIntegerValue!!
val address = (vardecl.value as NumericLiteralValue).number.toInt()
val index = evaluate(target.arrayindexed.arrayspec.index, evalCtx).integerValue()
val elementType = target.arrayindexed.inferType(program)!!
when(elementType) {

View File

@ -27,11 +27,15 @@ class EvalContext(val program: Program, val mem: Memory, val statusflags: Status
fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
val constval = expr.constValue(ctx.program)
if(constval!=null)
return RuntimeValue.from(constval, ctx.program.heap)
return RuntimeValue.fromLv(constval)
when(expr) {
is LiteralValue -> {
return RuntimeValue.from(expr, ctx.program.heap)
is NumericLiteralValue -> {
return RuntimeValue.fromLv(expr)
}
is ReferenceLiteralValue -> {
TODO("REF $expr")
return RuntimeValue.fromLv(expr, ctx.program.heap)
}
is PrefixExpression -> {
return when(expr.operator) {

View File

@ -2,7 +2,8 @@ package prog8.vm.astvm
import prog8.ast.*
import prog8.ast.base.*
import prog8.ast.expressions.LiteralValue
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.ReferenceLiteralValue
import prog8.ast.processing.IAstModifyingVisitor
import prog8.ast.statements.StructDecl
import prog8.ast.statements.VarDecl
@ -20,11 +21,11 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v
val globalpos = Position("<<global>>", 0, 0, 0)
val vdA = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.DONTCARE, null, Register.A.name, null,
LiteralValue.optimalInteger(0, globalpos), isArray = false, hiddenButDoNotRemove = true, position = globalpos)
NumericLiteralValue.optimalInteger(0, globalpos), isArray = false, autogeneratedDontRemove = true, position = globalpos)
val vdX = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.DONTCARE, null, Register.X.name, null,
LiteralValue.optimalInteger(255, globalpos), isArray = false, hiddenButDoNotRemove = true, position = globalpos)
NumericLiteralValue.optimalInteger(255, globalpos), isArray = false, autogeneratedDontRemove = true, position = globalpos)
val vdY = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.DONTCARE, null, Register.Y.name, null,
LiteralValue.optimalInteger(0, globalpos), isArray = false, hiddenButDoNotRemove = true, position = globalpos)
NumericLiteralValue.optimalInteger(0, globalpos), isArray = false, autogeneratedDontRemove = true, position = globalpos)
vdA.linkParents(program.namespace)
vdX.linkParents(program.namespace)
vdY.linkParents(program.namespace)
@ -39,15 +40,20 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v
// if the decl is part of a struct, just skip it
if(decl.parent !is StructDecl) {
when (decl.type) {
// we can assume the value in the vardecl already has been converted into a constant LiteralValue here.
VarDeclType.VAR -> {
if(decl.datatype!=DataType.STRUCT) {
val value = RuntimeValue.from(decl.value as LiteralValue, heap)
val numericLv = decl.value as? NumericLiteralValue
val value = if(numericLv!=null) {
RuntimeValue.fromLv(numericLv)
} else {
val referenceLv = decl.value as ReferenceLiteralValue
RuntimeValue.fromLv(referenceLv, heap)
}
runtimeVariables.define(decl.definingScope(), decl.name, value)
}
}
VarDeclType.MEMORY -> {
runtimeVariables.defineMemory(decl.definingScope(), decl.name, (decl.value as LiteralValue).asIntegerValue!!)
runtimeVariables.defineMemory(decl.definingScope(), decl.name, (decl.value as NumericLiteralValue).number.toInt())
}
VarDeclType.CONST -> {
// consts should have been const-folded away

View File

@ -3,24 +3,29 @@ package prog8tests
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import prog8.ast.base.DataType
import prog8.ast.expressions.LiteralValue
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.base.Position
import prog8.ast.expressions.ReferenceLiteralValue
import kotlin.test.*
private fun sameValueAndType(lv1: LiteralValue, lv2: LiteralValue): Boolean {
private fun sameValueAndType(lv1: NumericLiteralValue, lv2: NumericLiteralValue): Boolean {
return lv1.type==lv2.type && lv1==lv2
}
private fun sameValueAndType(rv1: ReferenceLiteralValue, rv2: ReferenceLiteralValue): Boolean {
return rv1.type==rv2.type && rv1==rv2
}
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestParserLiteralValue {
class TestParserNumericLiteralValue {
private val dummyPos = Position("test", 0, 0, 0)
@Test
fun testIdentity() {
val v = LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos)
val v = NumericLiteralValue(DataType.UWORD, 12345, dummyPos)
assertEquals(v, v)
assertFalse(v != v)
assertTrue(v <= v)
@ -28,104 +33,109 @@ class TestParserLiteralValue {
assertFalse(v < v)
assertFalse(v > v)
assertTrue(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos)))
assertTrue(sameValueAndType(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.UWORD, 12345, dummyPos)))
}
@Test
fun testEqualsAndNotEquals() {
assertEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UBYTE, 100, position = dummyPos))
assertEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 100, position = dummyPos))
assertEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
assertEquals(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos), LiteralValue(DataType.UBYTE, 254, position = dummyPos))
assertEquals(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos))
assertEquals(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 12345.0, position = dummyPos))
assertEquals(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos), LiteralValue(DataType.UBYTE, 100, position = dummyPos))
assertEquals(LiteralValue(DataType.FLOAT, floatvalue = 22239.0, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 22239, position = dummyPos))
assertEquals(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos))
assertEquals(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.UBYTE, 100, dummyPos))
assertEquals(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.UWORD, 100, dummyPos))
assertEquals(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos))
assertEquals(NumericLiteralValue(DataType.UWORD, 254, dummyPos), NumericLiteralValue(DataType.UBYTE, 254, dummyPos))
assertEquals(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.UWORD, 12345, dummyPos))
assertEquals(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.FLOAT, 12345.0, dummyPos))
assertEquals(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos), NumericLiteralValue(DataType.UBYTE, 100, dummyPos))
assertEquals(NumericLiteralValue(DataType.FLOAT, 22239.0, dummyPos), NumericLiteralValue(DataType.UWORD, 22239, dummyPos))
assertEquals(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos))
assertTrue(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UBYTE, 100, position = dummyPos)))
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 100, position = dummyPos)))
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos)))
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos), LiteralValue(DataType.UBYTE, 254, position = dummyPos)))
assertTrue(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos)))
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 12345.0, position = dummyPos)))
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos), LiteralValue(DataType.UBYTE, 100, position = dummyPos)))
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 22239.0, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 22239, position = dummyPos)))
assertTrue(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos)))
assertTrue(sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.UBYTE, 100, dummyPos)))
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.UWORD, 100, dummyPos)))
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos)))
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UWORD, 254, dummyPos), NumericLiteralValue(DataType.UBYTE, 254, dummyPos)))
assertTrue(sameValueAndType(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.UWORD, 12345, dummyPos)))
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.FLOAT, 12345.0, dummyPos)))
assertFalse(sameValueAndType(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos), NumericLiteralValue(DataType.UBYTE, 100, dummyPos)))
assertFalse(sameValueAndType(NumericLiteralValue(DataType.FLOAT, 22239.0, dummyPos), NumericLiteralValue(DataType.UWORD, 22239, dummyPos)))
assertTrue(sameValueAndType(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos)))
assertNotEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UBYTE, 101, position = dummyPos))
assertNotEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 101, position = dummyPos))
assertNotEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 101.0, position = dummyPos))
assertNotEquals(LiteralValue(DataType.UWORD, wordvalue = 245, position = dummyPos), LiteralValue(DataType.UBYTE, 246, position = dummyPos))
assertNotEquals(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12346, position = dummyPos))
assertNotEquals(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 12346.0, position = dummyPos))
assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.UBYTE, 9, position = dummyPos))
assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 9, position = dummyPos))
assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 9.0, position = dummyPos))
assertNotEquals(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.UBYTE, 101, dummyPos))
assertNotEquals(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.UWORD, 101, dummyPos))
assertNotEquals(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.FLOAT, 101.0, dummyPos))
assertNotEquals(NumericLiteralValue(DataType.UWORD, 245, dummyPos), NumericLiteralValue(DataType.UBYTE, 246, dummyPos))
assertNotEquals(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.UWORD, 12346, dummyPos))
assertNotEquals(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.FLOAT, 12346.0, dummyPos))
assertNotEquals(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.UBYTE, 9, dummyPos))
assertNotEquals(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.UWORD, 9, dummyPos))
assertNotEquals(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.FLOAT, 9.0, dummyPos))
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UBYTE, 101, position = dummyPos)))
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 101, position = dummyPos)))
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 101.0, position = dummyPos)))
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 245, position = dummyPos), LiteralValue(DataType.UBYTE, 246, position = dummyPos)))
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12346, position = dummyPos)))
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 12346.0, position = dummyPos)))
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.UBYTE, 9, position = dummyPos)))
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 9, position = dummyPos)))
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 9.0, position = dummyPos)))
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.UBYTE, 101, dummyPos)))
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.UWORD, 101, dummyPos)))
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.FLOAT, 101.0, dummyPos)))
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UWORD, 245, dummyPos), NumericLiteralValue(DataType.UBYTE, 246, dummyPos)))
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.UWORD, 12346, dummyPos)))
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.FLOAT, 12346.0, dummyPos)))
assertFalse(sameValueAndType(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.UBYTE, 9, dummyPos)))
assertFalse(sameValueAndType(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.UWORD, 9, dummyPos)))
assertFalse(sameValueAndType(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.FLOAT, 9.0, dummyPos)))
assertTrue(sameValueAndType(LiteralValue(DataType.STR, strvalue = "hello", position = dummyPos), LiteralValue(DataType.STR, strvalue = "hello", position = dummyPos)))
assertFalse(sameValueAndType(LiteralValue(DataType.STR, strvalue = "hello", position = dummyPos), LiteralValue(DataType.STR, strvalue = "bye", position = dummyPos)))
val lvOne = LiteralValue(DataType.UBYTE, 1, position = dummyPos)
val lvTwo = LiteralValue(DataType.UBYTE, 2, position = dummyPos)
val lvThree = LiteralValue(DataType.UBYTE, 3, position = dummyPos)
val lvOneR = LiteralValue(DataType.UBYTE, 1, position = dummyPos)
val lvTwoR = LiteralValue(DataType.UBYTE, 2, position = dummyPos)
val lvThreeR = LiteralValue(DataType.UBYTE, 3, position = dummyPos)
val lvFour= LiteralValue(DataType.UBYTE, 4, position = dummyPos)
val lv1 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOne, lvTwo, lvThree), position = dummyPos)
val lv2 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOneR, lvTwoR, lvThreeR), position = dummyPos)
val lv3 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOneR, lvTwoR, lvFour), position = dummyPos)
}
@Test
fun testEqualsRef() {
assertTrue(sameValueAndType(ReferenceLiteralValue(DataType.STR, str = "hello", position = dummyPos), ReferenceLiteralValue(DataType.STR, str = "hello", position = dummyPos)))
assertFalse(sameValueAndType(ReferenceLiteralValue(DataType.STR, str = "hello", position = dummyPos), ReferenceLiteralValue(DataType.STR, str = "bye", position = dummyPos)))
val lvOne = NumericLiteralValue(DataType.UBYTE, 1, dummyPos)
val lvTwo = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
val lvThree = NumericLiteralValue(DataType.UBYTE, 3, dummyPos)
val lvOneR = NumericLiteralValue(DataType.UBYTE, 1, dummyPos)
val lvTwoR = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
val lvThreeR = NumericLiteralValue(DataType.UBYTE, 3, dummyPos)
val lvFour= NumericLiteralValue(DataType.UBYTE, 4, dummyPos)
val lv1 = ReferenceLiteralValue(DataType.ARRAY_UB, array = arrayOf(lvOne, lvTwo, lvThree), position = dummyPos)
val lv2 = ReferenceLiteralValue(DataType.ARRAY_UB, array = arrayOf(lvOneR, lvTwoR, lvThreeR), position = dummyPos)
val lv3 = ReferenceLiteralValue(DataType.ARRAY_UB, array = arrayOf(lvOneR, lvTwoR, lvFour), position = dummyPos)
assertEquals(lv1, lv2)
assertNotEquals(lv1, lv3)
}
@Test
fun testGreaterThan(){
assertTrue(LiteralValue(DataType.UBYTE, 100, position = dummyPos) > LiteralValue(DataType.UBYTE, 99, position = dummyPos))
assertTrue(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) > LiteralValue(DataType.UWORD, wordvalue = 253, position = dummyPos))
assertTrue(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) > LiteralValue(DataType.FLOAT, floatvalue = 99.9, position = dummyPos))
assertTrue(NumericLiteralValue(DataType.UBYTE, 100, dummyPos) > NumericLiteralValue(DataType.UBYTE, 99, dummyPos))
assertTrue(NumericLiteralValue(DataType.UWORD, 254, dummyPos) > NumericLiteralValue(DataType.UWORD, 253, dummyPos))
assertTrue(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) > NumericLiteralValue(DataType.FLOAT, 99.9, dummyPos))
assertTrue(LiteralValue(DataType.UBYTE, 100, position = dummyPos) >= LiteralValue(DataType.UBYTE, 100, position = dummyPos))
assertTrue(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) >= LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos))
assertTrue(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) >= LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
assertTrue(NumericLiteralValue(DataType.UBYTE, 100, dummyPos) >= NumericLiteralValue(DataType.UBYTE, 100, dummyPos))
assertTrue(NumericLiteralValue(DataType.UWORD, 254, dummyPos) >= NumericLiteralValue(DataType.UWORD, 254, dummyPos))
assertTrue(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) >= NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos))
assertFalse(LiteralValue(DataType.UBYTE, 100, position = dummyPos) > LiteralValue(DataType.UBYTE, 100, position = dummyPos))
assertFalse(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) > LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos))
assertFalse(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) > LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
assertFalse(NumericLiteralValue(DataType.UBYTE, 100, dummyPos) > NumericLiteralValue(DataType.UBYTE, 100, dummyPos))
assertFalse(NumericLiteralValue(DataType.UWORD, 254, dummyPos) > NumericLiteralValue(DataType.UWORD, 254, dummyPos))
assertFalse(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) > NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos))
assertFalse(LiteralValue(DataType.UBYTE, 100, position = dummyPos) >= LiteralValue(DataType.UBYTE, 101, position = dummyPos))
assertFalse(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) >= LiteralValue(DataType.UWORD, wordvalue = 255, position = dummyPos))
assertFalse(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) >= LiteralValue(DataType.FLOAT, floatvalue = 100.1, position = dummyPos))
assertFalse(NumericLiteralValue(DataType.UBYTE, 100, dummyPos) >= NumericLiteralValue(DataType.UBYTE, 101, dummyPos))
assertFalse(NumericLiteralValue(DataType.UWORD, 254, dummyPos) >= NumericLiteralValue(DataType.UWORD, 255, dummyPos))
assertFalse(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) >= NumericLiteralValue(DataType.FLOAT, 100.1, dummyPos))
}
@Test
fun testLessThan() {
assertTrue(LiteralValue(DataType.UBYTE, 100, position = dummyPos) < LiteralValue(DataType.UBYTE, 101, position = dummyPos))
assertTrue(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) < LiteralValue(DataType.UWORD, wordvalue = 255, position = dummyPos))
assertTrue(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) < LiteralValue(DataType.FLOAT, floatvalue = 100.1, position = dummyPos))
assertTrue(NumericLiteralValue(DataType.UBYTE, 100, dummyPos) < NumericLiteralValue(DataType.UBYTE, 101, dummyPos))
assertTrue(NumericLiteralValue(DataType.UWORD, 254, dummyPos) < NumericLiteralValue(DataType.UWORD, 255, dummyPos))
assertTrue(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) < NumericLiteralValue(DataType.FLOAT, 100.1, dummyPos))
assertTrue(LiteralValue(DataType.UBYTE, 100, position = dummyPos) <= LiteralValue(DataType.UBYTE, 100, position = dummyPos))
assertTrue(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) <= LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos))
assertTrue(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) <= LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
assertTrue(NumericLiteralValue(DataType.UBYTE, 100, dummyPos) <= NumericLiteralValue(DataType.UBYTE, 100, dummyPos))
assertTrue(NumericLiteralValue(DataType.UWORD, 254, dummyPos) <= NumericLiteralValue(DataType.UWORD, 254, dummyPos))
assertTrue(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) <= NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos))
assertFalse(LiteralValue(DataType.UBYTE, 100, position = dummyPos) < LiteralValue(DataType.UBYTE, 100, position = dummyPos))
assertFalse(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) < LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos))
assertFalse(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) < LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
assertFalse(NumericLiteralValue(DataType.UBYTE, 100, dummyPos) < NumericLiteralValue(DataType.UBYTE, 100, dummyPos))
assertFalse(NumericLiteralValue(DataType.UWORD, 254, dummyPos) < NumericLiteralValue(DataType.UWORD, 254, dummyPos))
assertFalse(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) < NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos))
assertFalse(LiteralValue(DataType.UBYTE, 100, position = dummyPos) <= LiteralValue(DataType.UBYTE, 99, position = dummyPos))
assertFalse(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) <= LiteralValue(DataType.UWORD, wordvalue = 253, position = dummyPos))
assertFalse(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) <= LiteralValue(DataType.FLOAT, floatvalue = 99.9, position = dummyPos))
assertFalse(NumericLiteralValue(DataType.UBYTE, 100, dummyPos) <= NumericLiteralValue(DataType.UBYTE, 99, dummyPos))
assertFalse(NumericLiteralValue(DataType.UWORD, 254, dummyPos) <= NumericLiteralValue(DataType.UWORD, 253, dummyPos))
assertFalse(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) <= NumericLiteralValue(DataType.FLOAT, 99.9, dummyPos))
}
}

View File

@ -7,7 +7,8 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import prog8.ast.base.DataType
import prog8.ast.base.Position
import prog8.ast.expressions.LiteralValue
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.ReferenceLiteralValue
import prog8.vm.RuntimeValue
import prog8.compiler.*
import prog8.compiler.target.c64.MachineDefinition.Mflpt5
@ -340,8 +341,8 @@ class TestPetscii {
@Test
fun testLiteralValueComparisons() {
val ten = LiteralValue(DataType.UWORD, wordvalue = 10, position = Position("", 0, 0, 0))
val nine = LiteralValue(DataType.UBYTE, bytevalue = 9, position = Position("", 0, 0, 0))
val ten = NumericLiteralValue(DataType.UWORD, 10, Position("", 0, 0, 0))
val nine = NumericLiteralValue(DataType.UBYTE, 9, Position("", 0, 0, 0))
assertEquals(ten, ten)
assertNotEquals(ten, nine)
assertFalse(ten != ten)
@ -357,17 +358,11 @@ class TestPetscii {
assertTrue(ten <= ten)
assertFalse(ten < ten)
val abc = LiteralValue(DataType.STR, strvalue = "abc", position = Position("", 0, 0, 0))
val abd = LiteralValue(DataType.STR, strvalue = "abd", position = Position("", 0, 0, 0))
val abc = ReferenceLiteralValue(DataType.STR, str = "abc", position = Position("", 0, 0, 0))
val abd = ReferenceLiteralValue(DataType.STR, str = "abd", position = Position("", 0, 0, 0))
assertEquals(abc, abc)
assertTrue(abc!=abd)
assertFalse(abc!=abc)
assertTrue(abc < abd)
assertTrue(abc <= abd)
assertFalse(abd <= abc)
assertTrue(abd >= abc)
assertTrue(abd > abc)
assertFalse(abc > abd)
}
@Test