mirror of
https://github.com/irmen/prog8.git
synced 2024-12-23 09:32:43 +00:00
tweak bool type handling
This commit is contained in:
parent
92eb3b0bf6
commit
7a26646e1b
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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")
|
||||
|
@ -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 -> {
|
||||
|
@ -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
|
||||
|
@ -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)) {
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user