tweak bool type handling

This commit is contained in:
Irmen de Jong 2022-07-11 01:44:34 +02:00
parent 92eb3b0bf6
commit 7a26646e1b
13 changed files with 116 additions and 73 deletions

View File

@ -8,6 +8,12 @@ import kotlin.math.round
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
init {
if(type==DataType.BOOL)
throw java.lang.IllegalArgumentException("bool should have become ubyte @$position")
}
override fun printProperties() {
print(type)
}
@ -127,10 +133,12 @@ class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position)
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
init {
if(type==DataType.BOOL)
throw java.lang.IllegalArgumentException("bool should have become ubyte @$position")
if(type!=DataType.FLOAT) {
val rounded = round(number)
if (rounded != number)
throw IllegalArgumentException("refused rounding of float to avoid loss of precision")
throw IllegalArgumentException("refused rounding of float to avoid loss of precision @$position")
}
}

View File

@ -5,6 +5,7 @@ val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
val LogicalOperators = setOf("and", "or", "xor", "not")
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%")
val BitwiseOperators = setOf("&", "|", "^", "~")
val InvalidOperatorsForBoolean = setOf("-", "*", "/", "%", "<<", ">>") // TODO what about +? TODO add BitWiseOperators
fun invertedComparisonOperator(operator: String) =
when (operator) {

View File

@ -1165,14 +1165,14 @@ $repeatLabel lda $counterVar
return testVariableZeroAndJump(left, dt, operator, jumpIfFalseLabel)
when(dt) {
DataType.UBYTE, DataType.UWORD -> {
DataType.BOOL, DataType.UBYTE, DataType.UWORD -> {
if(operator=="<") {
out(" jmp $jumpIfFalseLabel")
return
} else if(operator==">=") {
return
}
if(dt==DataType.UBYTE) {
if(dt==DataType.UBYTE || dt==DataType.BOOL) {
assignExpressionToRegister(left, RegisterOrPair.A, false)
if (left is IFunctionCall && !left.isSimple)
out(" cmp #0")
@ -1252,7 +1252,7 @@ $repeatLabel lda $counterVar
// optimized code if the expression is just an identifier (variable)
val varname = asmVariableName(variable)
when(dt) {
DataType.UBYTE -> when(operator) {
DataType.UBYTE, DataType.BOOL -> when(operator) {
"==" -> out(" lda $varname | bne $jumpIfFalseLabel")
"!=" -> out(" lda $varname | beq $jumpIfFalseLabel")
">" -> out(" lda $varname | beq $jumpIfFalseLabel")

View File

@ -136,7 +136,7 @@ internal class ExpressionsAsmGen(private val program: Program,
private fun translateExpression(typecast: TypecastExpression) {
translateExpressionInternal(typecast.expression)
when(typecast.expression.inferType(program).getOr(DataType.UNDEFINED)) {
DataType.UBYTE -> {
DataType.UBYTE, DataType.BOOL -> {
when(typecast.type) {
DataType.UBYTE, DataType.BYTE -> {}
DataType.UWORD, DataType.WORD -> {

View File

@ -10,9 +10,7 @@ import prog8.ast.determineGosubArguments
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.code.ast.*
import prog8.code.core.DataType
import prog8.code.core.Position
import prog8.code.core.SourceCode
import prog8.code.core.*
import java.io.File
import kotlin.io.path.Path
import kotlin.io.path.isRegularFile
@ -405,7 +403,17 @@ class IntermediateAstMaker(val program: Program) {
private fun transform(srcExpr: BinaryExpression): PtBinaryExpression {
val type = srcExpr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val expr = PtBinaryExpression(srcExpr.operator, type, srcExpr.position)
var actualType = type
if(type==DataType.BOOL) {
if(srcExpr.operator in LogicalOperators + ComparisonOperators) {
// a comparison or logical expression is a boolean result (0 or 1) so we can safely
// reduce that to just a UBYTE type for the vm code that doesn't know about bools.
actualType = DataType.UBYTE
} else {
throw IllegalArgumentException("Ast expression still having BOOL type: $srcExpr @${srcExpr.position}")
}
}
val expr = PtBinaryExpression(srcExpr.operator, actualType, srcExpr.position)
expr.add(transformExpression(srcExpr.left))
expr.add(transformExpression(srcExpr.right))
return expr

View File

@ -481,7 +481,7 @@ internal class AstChecker(private val program: Program,
if(assignment.isAugmentable && targetDt istype DataType.BOOL) {
val operator = (assignment.value as? BinaryExpression)?.operator
if(operator in setOf("-", "*", "/", "%"))
if(operator in InvalidOperatorsForBoolean)
errors.err("can't use boolean operand with this operator $operator", assignment.position)
}
super.visit(assignment)
@ -918,7 +918,7 @@ internal class AstChecker(private val program: Program,
if(expr.operator in setOf("<", "<=", ">", ">=")) {
errors.err("can't use boolean operand with this comparison operator", expr.position)
}
if(expr.operator in setOf("-", "*", "/", "%") && (leftDt==DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {
if(expr.operator in InvalidOperatorsForBoolean && (leftDt==DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {
errors.err("can't use boolean operand with this operator ${expr.operator}", expr.left.position)
}
if(expr.operator=="+" && (leftDt==DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {

View File

@ -10,6 +10,7 @@ import prog8.ast.statements.VarDeclOrigin
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.*
import prog8.compiler.printProgram
internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: CompilationOptions) {

View File

@ -96,21 +96,6 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
return listOf(IAstModification.ReplaceNode(expr, containment, parent))
}
// convert boolean and/or/xor/not operators to bitwise equivalents.
// the rest of the ast and codegen only has to work with bitwise boolean operations from now on.
if(expr.operator in setOf("and", "or", "xor")) {
expr.operator = when(expr.operator) {
"and" -> "&"
"or" -> "|"
"xor" -> "^"
else -> "invalid"
}
return listOf(
IAstModification.ReplaceNodeSafe(expr.left, wrapWithBooleanCast(expr.left), expr),
IAstModification.ReplaceNodeSafe(expr.right, wrapWithBooleanCast(expr.right), expr),
)
}
return noModifications
}
@ -147,27 +132,4 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
return noModifications
}
private fun wrapWithBooleanCast(expr: Expression): Expression {
fun isBoolean(expr: Expression): Boolean {
return if(expr.inferType(program) istype DataType.BOOL)
true
else if(expr is BinaryExpression && expr.operator in ComparisonOperators + LogicalOperators)
true
else if(expr is PrefixExpression && expr.operator == "not")
true
else if(expr is BinaryExpression && expr.operator in BitwiseOperators) {
if(isBoolean(expr.left) && isBoolean(expr.right))
true
else expr.operator=="&" && expr.right.constValue(program)?.number==1.0 // x & 1 is also a boolean result
}
else
false
}
return if(isBoolean(expr))
expr
else
TypecastExpression(expr, DataType.BOOL, true, expr.position)
}
}

View File

@ -15,12 +15,6 @@ internal class BeforeAsmAstChanger(val program: Program,
private val errors: IErrorReporter
) : AstWalker() {
override fun after(numLiteral: NumericLiteral, parent: Node): Iterable<IAstModification> {
if(numLiteral.type==DataType.BOOL)
return listOf(IAstModification.ReplaceNode(numLiteral, NumericLiteral(DataType.UBYTE, numLiteral.number, numLiteral.position), parent))
return noModifications
}
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
throw InternalCompilerException("break should have been replaced by goto $breakStmt")
}
@ -67,14 +61,30 @@ internal class BeforeAsmAstChanger(val program: Program,
}
if(decl.datatype==DataType.BOOL) {
var newvalue = decl.value
if(newvalue is NumericLiteral) {
if(newvalue.number!=0.0)
newvalue = NumericLiteral(DataType.UBYTE, 1.0, newvalue.position)
}
val ubyteDecl = VarDecl(decl.type, decl.origin, DataType.UBYTE, decl.zeropage, decl.arraysize, decl.name,
decl.value, decl.isArray, decl.sharedWithAsm, decl.subroutineParameter, decl.position)
newvalue, decl.isArray, decl.sharedWithAsm, decl.subroutineParameter, decl.position)
return listOf(IAstModification.ReplaceNode(decl, ubyteDecl, parent))
}
if(decl.datatype==DataType.ARRAY_BOOL) {
var newarray = decl.value
if(decl.value is ArrayLiteral) {
val oldArray = (decl.value as ArrayLiteral).value
val convertedArray = oldArray.map {
var number: Expression = it
if (it is NumericLiteral && it.type == DataType.BOOL)
number = NumericLiteral(DataType.UBYTE, if (it.number == 0.0) 0.0 else 1.0, number.position)
number
}.toTypedArray()
newarray = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_UB), convertedArray, decl.position)
}
val ubyteArrayDecl = VarDecl(decl.type, decl.origin, DataType.ARRAY_UB, decl.zeropage, decl.arraysize, decl.name,
decl.value, decl.isArray, decl.sharedWithAsm, decl.subroutineParameter, decl.position)
newarray, true, decl.sharedWithAsm, decl.subroutineParameter, decl.position)
return listOf(IAstModification.ReplaceNode(decl, ubyteArrayDecl, parent))
}
@ -210,6 +220,23 @@ internal class BeforeAsmAstChanger(val program: Program,
return mods
}
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
// convert boolean and/or/xor/not operators to bitwise equivalents.
// so that codegen only has to work with bitwise boolean operations from now on.
if(expr.operator in setOf("and", "or", "xor")) {
expr.operator = when(expr.operator) {
"and" -> "&"
"or" -> "|"
"xor" -> "^"
else -> "invalid"
}
return listOf(
IAstModification.ReplaceNode(expr.left, wrapWithBooleanCastIfNeeded(expr.left), expr),
IAstModification.ReplaceNode(expr.right, wrapWithBooleanCastIfNeeded(expr.right), expr),)
}
return noModifications
}
override fun after(ifElse: IfElse, parent: Node): Iterable<IAstModification> {
val binExpr = ifElse.condition as? BinaryExpression
if(binExpr==null) {
@ -410,4 +437,27 @@ internal class BeforeAsmAstChanger(val program: Program,
)
return modifications
}
private fun wrapWithBooleanCastIfNeeded(expr: Expression): Expression {
fun isBoolean(expr: Expression): Boolean {
return if(expr.inferType(program) istype DataType.BOOL)
true
else if(expr is BinaryExpression && expr.operator in ComparisonOperators + LogicalOperators)
true
else if(expr is PrefixExpression && expr.operator == "not")
true
else if(expr is BinaryExpression && expr.operator in BitwiseOperators) {
if(isBoolean(expr.left) && isBoolean(expr.right))
true
else expr.operator=="&" && expr.right.constValue(program)?.number==1.0 // x & 1 is also a boolean result
}
else
false
}
return if(isBoolean(expr))
expr
else
TypecastExpression(expr, DataType.BOOL, true, expr.position)
}
}

View File

@ -72,8 +72,7 @@ internal class NotExpressionChanger(val program: Program, val errors: IErrorRepo
// all other not(x) --> x==0
// this means that "not" will never occur anywhere again in the ast after this
val dt = expr.expression.inferType(program).getOr(DataType.UBYTE)
val replacement = BinaryExpression(expr.expression, "==", NumericLiteral(dt,0.0, expr.position), expr.position)
val replacement = BinaryExpression(expr.expression, "==", NumericLiteral(DataType.UBYTE,0.0, expr.position), expr.position)
return listOf(IAstModification.ReplaceNodeSafe(expr, replacement, parent))
}
return noModifications

View File

@ -478,6 +478,11 @@ 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) {

View File

@ -3,7 +3,8 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- fix code gen crashes in logical.p8 / testtypecasts
- fix vm code result of test.p8 (ftrue() not called at all!?)
- add more operators to InvalidOperatorsForBoolean
- have a proper option to move the evalstack rather than just assembly symbol redefine
- then make the cx16 virtual registers in syslib.p8 use that definition to be able to shift them around on non-cx16 targets

View File

@ -3,20 +3,28 @@
main {
ubyte key
sub func() -> ubyte {
return key=='a'
}
sub func2() -> bool {
return key==2
}
sub ftrue(ubyte arg) -> ubyte {
arg++
txt.print(" ftrue ")
return 1
}
sub start() {
bool @shared z1=func()
bool @shared z2=func2()
bool ub1 = true
bool ub2 = true
bool ub3 = true
bool ub4 = 0
bool bvalue
txt.print("expected output: 0 ftrue 0 ftrue 1\n")
bvalue = ub1 xor ub2 xor ub3 xor true
txt.print_ub(bvalue)
txt.spc()
bvalue = ub1 xor ub2 xor ub3 xor ftrue(99)
txt.print_ub(bvalue)
txt.spc()
bvalue = ub1 and ub2 and ftrue(99)
txt.print_ub(bvalue)
txt.nl()
}
}