From 88cbb6913dc56e9dc7faf4c23b2748e6ddc95af7 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 11 Jul 2022 12:22:14 +0200 Subject: [PATCH] tweak bool type handling --- codeCore/src/prog8/code/core/Operators.kt | 2 +- .../prog8/compiler/IntermediateAstMaker.kt | 11 +- .../compiler/astprocessing/AstChecker.kt | 24 ++-- .../compiler/astprocessing/AstExtensions.kt | 7 +- .../astprocessing/BeforeAsmAstChanger.kt | 89 -------------- .../astprocessing/BeforeAsmTypecastCleaner.kt | 10 +- .../compiler/astprocessing/BoolRemover.kt | 114 ++++++++++++++++++ .../compiler/astprocessing/VariousCleanups.kt | 4 +- compiler/test/TestOptimization.kt | 4 +- compiler/test/TestTypecasts.kt | 64 +++++++++- compiler/test/ast/TestIntermediateAst.kt | 4 +- compiler/test/ast/TestProg8Parser.kt | 7 +- .../prog8/ast/expressions/AstExpressions.kt | 5 +- docs/source/todo.rst | 5 +- examples/test.p8 | 46 ++++--- 15 files changed, 242 insertions(+), 154 deletions(-) create mode 100644 compiler/src/prog8/compiler/astprocessing/BoolRemover.kt diff --git a/codeCore/src/prog8/code/core/Operators.kt b/codeCore/src/prog8/code/core/Operators.kt index cfac9e9b3..6bf34b0bd 100644 --- a/codeCore/src/prog8/code/core/Operators.kt +++ b/codeCore/src/prog8/code/core/Operators.kt @@ -5,7 +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 +// val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators fun invertedComparisonOperator(operator: String) = when (operator) { diff --git a/compiler/src/prog8/compiler/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/IntermediateAstMaker.kt index 1a5f3dd8b..b9881fd13 100644 --- a/compiler/src/prog8/compiler/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/IntermediateAstMaker.kt @@ -403,16 +403,7 @@ class IntermediateAstMaker(val program: Program) { private fun transform(srcExpr: BinaryExpression): PtBinaryExpression { val type = srcExpr.inferType(program).getOrElse { throw FatalAstException("unknown dt") } - 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 actualType = if(type==DataType.BOOL) DataType.UBYTE else type val expr = PtBinaryExpression(srcExpr.operator, actualType, srcExpr.position) expr.add(transformExpression(srcExpr.left)) expr.add(transformExpression(srcExpr.right)) diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index c213e64a5..e8bafe04c 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -479,11 +479,13 @@ internal class AstChecker(private val program: Program, if(numvalue!=null && targetDt.isKnown) checkValueTypeAndRange(targetDt.getOr(DataType.UNDEFINED), numvalue) - if(assignment.isAugmentable && targetDt istype DataType.BOOL) { - val operator = (assignment.value as? BinaryExpression)?.operator - if(operator in InvalidOperatorsForBoolean) - errors.err("can't use boolean operand with this operator $operator", assignment.position) - } +// for now, don't enforce bool type with only logical operators... +// if(assignment.isAugmentable && targetDt istype DataType.BOOL) { +// val operator = (assignment.value as? BinaryExpression)?.operator +// if(operator in InvalidOperatorsForBoolean) +// errors.err("can't use boolean operand with this operator $operator", assignment.position) +// } + super.visit(assignment) } @@ -918,14 +920,10 @@ 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 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)) { - val rightNum = expr.right.constValue(program)?.number ?: 0.0 - if(rightNum > 1.0) - errors.err("can't use boolean operand with this operator ${expr.operator}", expr.left.position) - } +// for now, don't enforce bool type with only logical operators... +// 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 == "==" || expr.operator == "!=") { val leftNum = expr.left.constValue(program)?.number ?: 0.0 val rightNum = expr.right.constValue(program)?.number ?: 0.0 diff --git a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt index a7619870b..0eb600678 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt @@ -22,14 +22,17 @@ internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: Compila } internal fun Program.processAstBeforeAsmGeneration(compilerOptions: CompilationOptions, errors: IErrorReporter) { + val boolRemover = BoolRemover(this) + boolRemover.visit(this) + boolRemover.applyModifications() val fixer = BeforeAsmAstChanger(this, compilerOptions, errors) fixer.visit(this) - while(errors.noErrors() && fixer.applyModifications()>0) { + while (errors.noErrors() && fixer.applyModifications() > 0) { fixer.visit(this) } val cleaner = BeforeAsmTypecastCleaner(this, errors) cleaner.visit(this) - while(errors.noErrors() && cleaner.applyModifications()>0) { + while (errors.noErrors() && cleaner.applyModifications() > 0) { cleaner.visit(this) } } diff --git a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt index e28ce6b4e..f9e54d4c0 100644 --- a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt +++ b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt @@ -60,34 +60,6 @@ internal class BeforeAsmAstChanger(val program: Program, throw InternalCompilerException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl") } - 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, - 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, - newarray, true, decl.sharedWithAsm, decl.subroutineParameter, decl.position) - return listOf(IAstModification.ReplaceNode(decl, ubyteArrayDecl, parent)) - } - return noModifications } @@ -152,23 +124,6 @@ internal class BeforeAsmAstChanger(val program: Program, } override fun after(subroutine: Subroutine, parent: Node): Iterable { - - // replace BOOL return type and parameters by UBYTE - if(subroutine.returntypes.any { it==DataType.BOOL } || subroutine.parameters.any {it.type==DataType.BOOL}) { - val newReturnTypes = subroutine.returntypes.map { - if(it==DataType.BOOL) DataType.UBYTE else it - } - val newParams = subroutine.parameters.map { - if(it.type==DataType.BOOL) SubroutineParameter(it.name, DataType.UBYTE, it.position) else it - }.toMutableList() - val newSubroutine = Subroutine(subroutine.name, newParams, newReturnTypes, - subroutine.asmParameterRegisters, subroutine.asmReturnvaluesRegisters, subroutine.asmClobbers, - subroutine.asmAddress, subroutine.isAsmSubroutine, subroutine.inline, subroutine.statements, - subroutine.position) - return listOf(IAstModification.ReplaceNode(subroutine, newSubroutine, parent)) - } - - // Most code generation targets only support subroutine inlining on asmsub subroutines // So we reset the flag here to be sure it doesn't cause problems down the line in the codegen. if(!subroutine.isAsmSubroutine && options.compTarget.name!=VMTarget.NAME) @@ -213,30 +168,9 @@ internal class BeforeAsmAstChanger(val program: Program, } } - if(subroutine.returntypes.any { it==DataType.BOOL } || subroutine.parameters.any {it.type==DataType.BOOL}) { - throw FatalAstException("boolean args and return types to ubyte should already have been done by earlier step") - } - return mods } - override fun after(expr: BinaryExpression, parent: Node): Iterable { - // 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 { val binExpr = ifElse.condition as? BinaryExpression if(binExpr==null) { @@ -437,27 +371,4 @@ 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) - } } diff --git a/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt b/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt index 5041d4e60..4ba484248 100644 --- a/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt +++ b/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt @@ -15,12 +15,15 @@ internal class BeforeAsmTypecastCleaner(val program: Program, private val errors: IErrorReporter ) : AstWalker() { - override fun after(typecast: TypecastExpression, parent: Node): Iterable { - if(typecast.type==DataType.BOOL) { + override fun before(typecast: TypecastExpression, parent: Node): Iterable { + if(typecast.type == DataType.BOOL) { val notZero = BinaryExpression(typecast.expression, "!=", NumericLiteral(DataType.UBYTE, 0.0, typecast.position), typecast.position) return listOf(IAstModification.ReplaceNode(typecast, notZero, parent)) } + return noModifications + } + override fun after(typecast: TypecastExpression, parent: Node): Iterable { // see if we can remove redundant typecasts (outside of expressions) // such as casting byte<->ubyte, word<->uword or even redundant casts (sourcetype = target type). // the special typecast of a reference type (str, array) to an UWORD will be changed into address-of, @@ -75,7 +78,6 @@ internal class BeforeAsmTypecastCleaner(val program: Program, return noModifications } - // also convert calls to builtin functions to BuiltinFunctionCall nodes to make things easier for codegen override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable { @@ -91,7 +93,6 @@ internal class BeforeAsmTypecastCleaner(val program: Program, return noModifications } - override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable { if(functionCallExpr.target.nameInSource.size==1 && functionCallExpr.target.nameInSource[0] in program.builtinFunctions.names) { @@ -128,5 +129,4 @@ internal class BeforeAsmTypecastCleaner(val program: Program, } return noModifications } - } \ No newline at end of file diff --git a/compiler/src/prog8/compiler/astprocessing/BoolRemover.kt b/compiler/src/prog8/compiler/astprocessing/BoolRemover.kt new file mode 100644 index 000000000..311c7e109 --- /dev/null +++ b/compiler/src/prog8/compiler/astprocessing/BoolRemover.kt @@ -0,0 +1,114 @@ +package prog8.compiler.astprocessing + +import prog8.ast.Node +import prog8.ast.Program +import prog8.ast.expressions.* +import prog8.ast.statements.Subroutine +import prog8.ast.statements.SubroutineParameter +import prog8.ast.statements.VarDecl +import prog8.ast.walk.AstWalker +import prog8.ast.walk.IAstModification +import prog8.code.core.* + +internal class BoolRemover(val program: Program) : AstWalker() { + + override fun before(typecast: TypecastExpression, parent: Node): Iterable { + if(typecast.type == DataType.BOOL) { + val notZero = BinaryExpression(typecast.expression, "!=", NumericLiteral(DataType.UBYTE, 0.0, typecast.position), typecast.position) + return listOf(IAstModification.ReplaceNode(typecast, notZero, parent)) + } + return noModifications + } + + override fun after(decl: VarDecl, parent: Node): Iterable { + 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, + 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, + newarray, true, decl.sharedWithAsm, decl.subroutineParameter, decl.position) + return listOf(IAstModification.ReplaceNode(decl, ubyteArrayDecl, parent)) + } + + return noModifications + } + + override fun after(subroutine: Subroutine, parent: Node): Iterable { + // replace BOOL return type and parameters by UBYTE + if(subroutine.returntypes.any { it==DataType.BOOL } || subroutine.parameters.any {it.type==DataType.BOOL}) { + val newReturnTypes = subroutine.returntypes.map { + if(it==DataType.BOOL) DataType.UBYTE else it + } + val newParams = subroutine.parameters.map { + if(it.type==DataType.BOOL) SubroutineParameter(it.name, DataType.UBYTE, it.position) else it + }.toMutableList() + val newSubroutine = Subroutine(subroutine.name, newParams, newReturnTypes, + subroutine.asmParameterRegisters, subroutine.asmReturnvaluesRegisters, subroutine.asmClobbers, + subroutine.asmAddress, subroutine.isAsmSubroutine, subroutine.inline, subroutine.statements, + subroutine.position) + return listOf(IAstModification.ReplaceNode(subroutine, newSubroutine, parent)) + } + + return noModifications + } + + override fun after(expr: BinaryExpression, parent: Node): Iterable { + // convert boolean and/or/xor/not operators to bitwise equivalents. + // so that codegen only has to work with bitwise boolean operations from now on. + // note: this has to be done here and not in BeforeAsmTypecastCleaner! (code size will increase if done there...) + if(expr.operator in setOf("and", "or", "xor")) { + expr.operator = when(expr.operator) { + "and" -> "&" + "or" -> "|" + "xor" -> "^" + else -> "invalid" + } + return listOf( + IAstModification.ReplaceNodeSafe(expr.left, wrapWithBooleanCastIfNeeded(expr.left), expr), + IAstModification.ReplaceNodeSafe(expr.right, wrapWithBooleanCastIfNeeded(expr.right), expr),) + } + return noModifications + } + + 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) + } +} diff --git a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt index ae628543f..23732c5b3 100644 --- a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt +++ b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt @@ -84,7 +84,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter, // but only if the containment check is the top-level expression. if(parent is BinaryExpression) return noModifications - if(expr.operator == "|") { + if(expr.operator == "|" || expr.operator=="or") { val leftBinExpr1 = expr.left as? BinaryExpression val rightBinExpr1 = expr.right as? BinaryExpression @@ -100,7 +100,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter, } return false } - if(expr.operator!="|") + if(expr.operator!="|" && expr.operator!="or") return false val leftBinExpr = expr.left as? BinaryExpression val rightBinExpr = expr.right as? BinaryExpression diff --git a/compiler/test/TestOptimization.kt b/compiler/test/TestOptimization.kt index 0ff41c9e6..633d96691 100644 --- a/compiler/test/TestOptimization.kt +++ b/compiler/test/TestOptimization.kt @@ -263,10 +263,10 @@ class TestOptimization: FunSpec({ } } """ - val result = compileText(C64Target(), false, src, writeAssembly = false)!! + val result = compileText(C64Target(), false, src, writeAssembly = true)!! printProgram(result.program) val stmts = result.program.entrypoint.statements - stmts.size shouldBe 7 + stmts.size shouldBe 8 val value1 = (stmts[4] as Assignment).value as BinaryExpression val value2 = (stmts[5] as Assignment).value as BinaryExpression diff --git a/compiler/test/TestTypecasts.kt b/compiler/test/TestTypecasts.kt index 9d861844e..dc5417d7f 100644 --- a/compiler/test/TestTypecasts.kt +++ b/compiler/test/TestTypecasts.kt @@ -14,11 +14,67 @@ import prog8.ast.statements.VarDecl import prog8.code.core.DataType import prog8.code.core.Position import prog8.code.target.C64Target +import prog8.code.target.VMTarget import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.compileText class TestTypecasts: FunSpec({ + test("bool expressions with functioncalls") { + val text=""" +main { + sub ftrue(ubyte arg) -> ubyte { + arg++ + return 64 + } + + sub start() { + bool ub1 = true + bool ub2 = true + bool ub3 = true + bool ub4 = 0 + bool @shared bvalue + + bvalue = ub1 xor ub2 xor ub3 xor true + bvalue = ub1 xor ub2 xor ub3 xor ftrue(99) + bvalue = ub1 and ub2 and ftrue(99) + } +}""" + val result = compileText(C64Target(), true, text, writeAssembly = true)!! + val stmts = result.program.entrypoint.statements + /* + ubyte ub1 + ub1 = 1 + ubyte ub2 + ub2 = 1 + ubyte ub3 + ub3 = 1 + ubyte @shared bvalue + bvalue = (((ub1^ub2)^ub3)^(1!=0)) + bvalue = (((ub1^ub2)^ub3)^(ftrue(99)!=0)) + bvalue = ((ub1&ub2)&(ftrue(99)!=0)) + return + */ + stmts.size shouldBe 11 + val assignValue1 = (stmts[7] as Assignment).value as BinaryExpression + val assignValue2 = (stmts[8] as Assignment).value as BinaryExpression + val assignValue3 = (stmts[9] as Assignment).value as BinaryExpression + assignValue1.operator shouldBe "^" + assignValue2.operator shouldBe "^" + assignValue3.operator shouldBe "&" + val right1 = assignValue1.right as BinaryExpression + val right2 = assignValue2.right as BinaryExpression + val right3 = assignValue3.right as BinaryExpression + right1.operator shouldBe "!=" + right2.operator shouldBe "!=" + right3.operator shouldBe "!=" + right1.left shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY) + right1.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) + right2.left shouldBe instanceOf() + right2.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) + right3.left shouldBe instanceOf() + right3.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) + } test("logical with byte instead of bool") { val text=""" @@ -504,7 +560,7 @@ main { (fcall2.args[0] as TypecastExpression).type shouldBe DataType.BOOL (fcall2.args[1] as TypecastExpression).type shouldBe DataType.BOOL val ifCond = (stmts[8] as IfElse).condition as BinaryExpression - ifCond.operator shouldBe "&" + ifCond.operator shouldBe "and" // no asm writing so logical expressions haven't been replaced with bitwise equivalents yet (ifCond.left as IdentifierReference).nameInSource shouldBe listOf("boolvalue1") (ifCond.right as IdentifierReference).nameInSource shouldBe listOf("boolvalue2") } @@ -523,11 +579,13 @@ main { ww = (camg & ${'$'}0004) ww++ ww = (${'$'}0004 & camg) + + ubyte @shared wordNr2 = (interlaced >= ${'$'}33) + (interlaced >= ${'$'}66) + (interlaced >= ${'$'}99) + (interlaced >= ${'$'}CC) } }""" - val result = compileText(C64Target(), false, text, writeAssembly = false)!! + val result = compileText(VMTarget(), false, text, writeAssembly = true)!! val stmts = result.program.entrypoint.statements - stmts.size shouldBe 11 + stmts.size shouldBe 14 } test("word to byte casts") { diff --git a/compiler/test/ast/TestIntermediateAst.kt b/compiler/test/ast/TestIntermediateAst.kt index 37467e556..848a09c87 100644 --- a/compiler/test/ast/TestIntermediateAst.kt +++ b/compiler/test/ast/TestIntermediateAst.kt @@ -51,7 +51,9 @@ class TestIntermediateAst: FunSpec({ arraydecl.name shouldBe "array" arraydecl.type shouldBe DataType.ARRAY_UB - val containment = (entry.children[2] as PtAssignment).value as PtContainmentCheck + val containmentCast = (entry.children[2] as PtAssignment).value as PtTypeCast + containmentCast.type shouldBe DataType.UBYTE + val containment = containmentCast.value as PtContainmentCheck (containment.element as PtNumber).number shouldBe 11.0 val fcall = (entry.children[3] as PtAssignment).value as PtFunctionCall fcall.void shouldBe false diff --git a/compiler/test/ast/TestProg8Parser.kt b/compiler/test/ast/TestProg8Parser.kt index ec2bcbfb8..9a5adeae7 100644 --- a/compiler/test/ast/TestProg8Parser.kt +++ b/compiler/test/ast/TestProg8Parser.kt @@ -786,7 +786,7 @@ class TestProg8Parser: FunSpec( { val left = zz2.left as TypecastExpression val right = zz2.right as PrefixExpression left.inferType(program).getOrElse { fail("dt") } shouldBe DataType.UWORD - right.inferType(program).getOrElse { fail("dt") } shouldBe DataType.UWORD + right.inferType(program).getOrElse { fail("dt") } shouldBe DataType.BOOL zz2.inferType(program).getOrElse { fail("dt") } shouldBe DataType.BOOL } @@ -885,6 +885,7 @@ class TestProg8Parser: FunSpec( { str string = "hello" ubyte[] array = [1,2,3,4] + bool bb ubyte cc if cc in [' ', '@', 0] { } @@ -892,8 +893,8 @@ class TestProg8Parser: FunSpec( { if cc in "email" { } - cc = 99 in array - cc = '@' in string + bb = 99 in array + bb = '@' in string } } """ diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index bee76ffcf..971f692e4 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -124,7 +124,8 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid val inferred = expression.inferType(program) return when(operator) { "+" -> inferred - "~", "not" -> { + "not" -> InferredTypes.knownFor(DataType.BOOL) + "~" -> { when(inferred.getOr(DataType.UNDEFINED)) { in ByteDatatypes -> InferredTypes.knownFor(DataType.UBYTE) in WordDatatypes -> InferredTypes.knownFor(DataType.UWORD) @@ -1108,7 +1109,7 @@ class ContainmentCheck(var element: Expression, return iterable.referencesIdentifier(nameInSource) } - override fun inferType(program: Program) = InferredTypes.knownFor(DataType.UBYTE) + override fun inferType(program: Program) = InferredTypes.knownFor(DataType.BOOL) override fun replaceChildNode(node: Node, replacement: Node) { if(replacement !is Expression) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 1a32b891a..21916b66c 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,8 +3,9 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- fix vm code result of test.p8 (ftrue() not called at all!?) -- add more operators to InvalidOperatorsForBoolean +- petaxian has become larger (18630) why so much larger. problematic lines: + gun.p8 95,110 if ( x <= GUN_MAX_LEFT and leftmost ) ... + enemy.p8 456 + bombs.p8 33 (while i ubyte { - arg++ - txt.print(" ftrue ") - return 1 - } + ubyte key + + sub pushing_fire() -> ubyte { + return key == 'z' + } + + sub pushing_left() -> ubyte { + return key == 'k' or key == 157 + } + + sub pushing_right() -> ubyte { + return key == 'l' or key == 29 + } sub start() { - bool ub1 = true - bool ub2 = true - bool ub3 = true - bool ub4 = 0 - bool bvalue + void pushing_fire() + void pushing_left() + void pushing_right() - 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) + ubyte rnr = $99 + ubyte wordNr = ((rnr >= $33) as ubyte) + + ((rnr >= $66) as ubyte) + + ((rnr >= $99) as ubyte) + + ((rnr >= $CC) as ubyte) + + ubyte wordNr2 = (rnr >= $33) as ubyte + (rnr >= $66) as ubyte + (rnr >= $99) as ubyte + (rnr >= $CC) as ubyte + + txt.print_uw(wordNr) + txt.nl() + txt.print_uw(wordNr2) txt.nl() } }