diff --git a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt index 890176900..f95b4b6a5 100644 --- a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt +++ b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt @@ -294,10 +294,11 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program: } override fun visit(numLiteral: NumericLiteral) { - if(numLiteral.type==DataType.FLOAT) - output(numLiteral.number.toString()) - else - output(numLiteral.number.toInt().toString()) + when (numLiteral.type) { + DataType.BOOL -> output(if(numLiteral.number==0.0) "false" else "true") + DataType.FLOAT -> output(numLiteral.number.toString()) + else -> output(numLiteral.number.toInt().toString()) + } } override fun visit(char: CharLiteral) { diff --git a/compilerAst/src/prog8/ast/AstToplevel.kt b/compilerAst/src/prog8/ast/AstToplevel.kt index 1392703ff..8121110cc 100644 --- a/compilerAst/src/prog8/ast/AstToplevel.kt +++ b/compilerAst/src/prog8/ast/AstToplevel.kt @@ -360,7 +360,7 @@ internal object BuiltinFunctionScopePlaceholder : INameScope { fun defaultZero(dt: DataType, position: Position) = when(dt) { - DataType.BOOL -> NumericLiteral(DataType.UBYTE, 0.0, position) + DataType.BOOL -> NumericLiteral(DataType.BOOL, 0.0, position) DataType.UBYTE -> NumericLiteral(DataType.UBYTE, 0.0, position) DataType.BYTE -> NumericLiteral(DataType.BYTE, 0.0, position) DataType.UWORD, DataType.STR -> NumericLiteral(DataType.UWORD, 0.0, position) diff --git a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt index a539f0532..497586265 100644 --- a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt +++ b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt @@ -454,7 +454,7 @@ private fun ExpressionContext.toAst(insideParentheses: Boolean=false) : Expressi litval.charliteral()!=null -> litval.charliteral().toAst() litval.arrayliteral()!=null -> { val array = litval.arrayliteral().toAst() - // the actual type of the arraysize can not yet be determined here (missing namespace & heap) + // the actual type of the arraysize can not yet be determined here // the ConstantFold takes care of that and converts the type if needed. ArrayLiteral(InferredTypes.InferredType.unknown(), array, position = litval.toPosition()) } diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index 80993cb9a..6ed900cd4 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -128,18 +128,14 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid "+" -> inferred "not" -> InferredTypes.knownFor(DataType.BOOL) "~" -> { - when(inferred.getOr(DataType.UNDEFINED)) { - in ByteDatatypes -> InferredTypes.knownFor(DataType.UBYTE) - in WordDatatypes -> InferredTypes.knownFor(DataType.UWORD) - else -> inferred - } + if(inferred.isBytes) InferredTypes.knownFor(DataType.UBYTE) + else if(inferred.isWords) InferredTypes.knownFor(DataType.UWORD) + else InferredTypes.InferredType.unknown() } "-" -> { - when(inferred.getOr(DataType.UNDEFINED)) { - in ByteDatatypes -> InferredTypes.knownFor(DataType.BYTE) - in WordDatatypes -> InferredTypes.knownFor(DataType.WORD) - else -> inferred - } + if(inferred.isBytes) InferredTypes.knownFor(DataType.BYTE) + else if(inferred.isWords) InferredTypes.knownFor(DataType.WORD) + else inferred } else -> throw FatalAstException("weird prefix expression operator") } @@ -225,7 +221,12 @@ class BinaryExpression( } } } - "&", "|", "^" -> if(leftDt istype DataType.BOOL) InferredTypes.knownFor(DataType.UBYTE) else leftDt + "&", "|", "^" -> when(leftDt.getOr(DataType.UNDEFINED)) { + DataType.BYTE -> InferredTypes.knownFor(DataType.UBYTE) + DataType.WORD -> InferredTypes.knownFor(DataType.UWORD) + DataType.BOOL -> InferredTypes.knownFor(DataType.UBYTE) + else -> leftDt + } "and", "or", "xor", "not", "in", "not in", "<", ">", "<=", ">=", @@ -246,6 +247,12 @@ class BinaryExpression( // a combination with a float will be float (but give a warning about this!) return when (leftDt) { + DataType.BOOL -> { + if(rightDt==DataType.BOOL) + return Pair(DataType.BOOL, null) + else + return Pair(DataType.BOOL, right) + } DataType.UBYTE -> { when (rightDt) { DataType.UBYTE -> Pair(DataType.UBYTE, null) @@ -348,6 +355,10 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp expression.linkParents(this) } + init { + if(type==DataType.BOOL) require(!implicit) {"no implicit cast to boolean allowed"} + } + override val isSimple = expression.isSimple override fun replaceChildNode(node: Node, replacement: Node) { @@ -364,9 +375,9 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp override fun inferType(program: Program) = InferredTypes.knownFor(type) override fun constValue(program: Program): NumericLiteral? { val cv = expression.constValue(program) ?: return null - val cast = cv.cast(type) + val cast = cv.cast(type, implicit) return if(cast.isValid) { - val newval = cast.value!! + val newval = cast.valueOrZero() newval.linkParents(parent) return newval } @@ -482,7 +493,7 @@ class NumericLiteral(val type: DataType, // only numerical types allowed companion object { fun fromBoolean(bool: Boolean, position: Position) = - NumericLiteral(DataType.UBYTE, if(bool) 1.0 else 0.0, position) + NumericLiteral(DataType.BOOL, if(bool) 1.0 else 0.0, position) fun optimalNumeric(value: Number, position: Position): NumericLiteral { val digits = floor(value.toDouble()) - value.toDouble() @@ -523,11 +534,6 @@ class NumericLiteral(val type: DataType, // only numerical types allowed } } - init { - if(type==DataType.BOOL) - throw FatalAstException("should not create NumericLiteral with BOOL type @$position") - } - val asBooleanValue: Boolean = number != 0.0 override fun linkParents(parent: Node) { @@ -566,17 +572,29 @@ class NumericLiteral(val type: DataType, // only numerical types allowed operator fun compareTo(other: NumericLiteral): Int = number.compareTo(other.number) - data class ValueAfterCast(val isValid: Boolean, val whyFailed: String?, val value: NumericLiteral?) + class ValueAfterCast(val isValid: Boolean, val whyFailed: String?, private val value: NumericLiteral?) { + fun valueOrZero() = if(isValid) value!! else NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) + fun linkParent(parent: Node) { + value?.linkParents(parent) + } + } - fun cast(targettype: DataType): ValueAfterCast { - val result = internalCast(targettype) - result.value?.linkParents(this.parent) + fun cast(targettype: DataType, implicit: Boolean): ValueAfterCast { + val result = internalCast(targettype, implicit) + result.linkParent(this.parent) return result } - private fun internalCast(targettype: DataType): ValueAfterCast { + private fun internalCast(targettype: DataType, implicit: Boolean): ValueAfterCast { if(type==targettype) return ValueAfterCast(true, null, this) + if (implicit) { + if (targettype == DataType.BOOL) + return ValueAfterCast(false, "no implicit cast to boolean allowed", this) + if (targettype in IntegerDatatypes && type==DataType.BOOL) + return ValueAfterCast(false, "no implicit cast from boolean to integer allowed", this) + } + when(type) { DataType.UBYTE -> { if(targettype==DataType.BYTE) @@ -660,14 +678,15 @@ class NumericLiteral(val type: DataType, // only numerical types allowed return ValueAfterCast(true, null, NumericLiteral(targettype, number, position)) if(targettype==DataType.LONG && number >=0 && number <= 2147483647) return ValueAfterCast(true, null, NumericLiteral(targettype, number, position)) - if(targettype==DataType.BOOL) - return ValueAfterCast(true, null, fromBoolean(number!=0.0, position)) } catch (x: ExpressionError) { return ValueAfterCast(false, x.message,null) } } DataType.BOOL -> { - return ValueAfterCast(true, null, NumericLiteral(targettype, number, position)) + if(implicit) + return ValueAfterCast(false, "no implicit cast from boolean to integer allowed", null) + else if(targettype in IntegerDatatypes) + return ValueAfterCast(true, null, NumericLiteral(targettype, number, position)) } DataType.LONG -> { try { @@ -679,8 +698,6 @@ class NumericLiteral(val type: DataType, // only numerical types allowed return ValueAfterCast(true, null, NumericLiteral(targettype, number, position)) if (targettype == DataType.UWORD && number >= 0 && number <= 65535) return ValueAfterCast(true, null, NumericLiteral(targettype, number, position)) - if(targettype==DataType.BOOL) - return ValueAfterCast(true, null, fromBoolean(number!=0.0, position)) if(targettype==DataType.FLOAT) return ValueAfterCast(true, null, NumericLiteral(targettype, number, position)) } catch (x: ExpressionError) { @@ -691,7 +708,7 @@ class NumericLiteral(val type: DataType, // only numerical types allowed throw FatalAstException("type cast of weird type $type") } } - return ValueAfterCast(false, "no cast available between these types", null) + return ValueAfterCast(false, "no cast available from $type to $targettype", null) } } @@ -839,6 +856,12 @@ class ArrayLiteral(val type: InferredTypes.InferredType, // inferred because DataType.WORD in dts -> InferredTypes.InferredType.known(DataType.ARRAY_W) DataType.UWORD in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW) DataType.BYTE in dts -> InferredTypes.InferredType.known(DataType.ARRAY_B) + DataType.BOOL in dts -> { + if(dts.all { it==DataType.BOOL}) + InferredTypes.InferredType.known(DataType.ARRAY_BOOL) + else + InferredTypes.InferredType.unknown() + } DataType.UBYTE in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UB) DataType.ARRAY_UW in dts || DataType.ARRAY_W in dts || @@ -862,24 +885,33 @@ class ArrayLiteral(val type: InferredTypes.InferredType, // inferred because // otherwise: return null (cast cannot be done) if(value.all { it is NumericLiteral }) { - val castArray = value.map { - val cast = (it as NumericLiteral).cast(elementType) - if(cast.isValid) - cast.value!! as Expression - else - return null // abort - }.toTypedArray() - return ArrayLiteral(InferredTypes.InferredType.known(targettype), castArray, position = position) + val castArray = if(elementType==DataType.BOOL) { + value.map { + if((it as NumericLiteral).type==DataType.BOOL) + it + else + return null // abort + } + } else { + value.map { + val cast = (it as NumericLiteral).cast(elementType, true) + if(cast.isValid) + cast.valueOrZero() + else + return null // abort + } + } + return ArrayLiteral(InferredTypes.InferredType.known(targettype), castArray.toTypedArray(), position = position) } else if(elementType in WordDatatypes && value.all { it is NumericLiteral || it is AddressOf || it is IdentifierReference}) { val castArray = value.map { when(it) { - is AddressOf -> it - is IdentifierReference -> it + is AddressOf -> it as Expression + is IdentifierReference -> it as Expression is NumericLiteral -> { - val numcast = it.cast(elementType) + val numcast = it.cast(elementType, true) if(numcast.isValid) - numcast.value!! + numcast.valueOrZero() as Expression else return null // abort } diff --git a/virtualmachine/src/prog8/vm/VmVariableAllocator.kt b/virtualmachine/src/prog8/vm/VmVariableAllocator.kt index 369606877..e465c9e0e 100644 --- a/virtualmachine/src/prog8/vm/VmVariableAllocator.kt +++ b/virtualmachine/src/prog8/vm/VmVariableAllocator.kt @@ -18,7 +18,7 @@ internal class VmVariableAllocator(st: IRSymbolTable, val encoding: IStringEncod val memsize = when (variable.dt) { DataType.STR -> variable.onetimeInitializationStringValue!!.first.length + 1 // include the zero byte - in NumericDatatypes -> memsizer.memorySize(variable.dt) + in NumericDatatypes, DataType.BOOL -> memsizer.memorySize(variable.dt) in ArrayDatatypes -> memsizer.memorySize(variable.dt, variable.length!!) else -> throw InternalCompilerException("weird dt") }