fix: boolean values of terms in logical expressions are now properly evaluated

This commit is contained in:
Irmen de Jong 2022-06-26 23:55:34 +02:00
parent a6d0ea347c
commit bb1cda0916
8 changed files with 98 additions and 26 deletions

View File

@ -702,10 +702,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
when (val dt = fcall.args.single().inferType(program).getOr(DataType.UNDEFINED)) {
in ByteDatatypes -> {
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.A, dt==DataType.BYTE)
asmgen.out(" beq + | lda #1")
asmgen.out("+")
}
in WordDatatypes -> {
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY, dt==DataType.WORD)
asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1")
asmgen.out(" beq + | lda #1")
asmgen.out("+")
}
DataType.FLOAT -> {
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.FAC1, true)

View File

@ -368,12 +368,20 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
}
VmDataType.WORD -> {
val msbReg = codeGen.vmRegisters.nextFree()
val skipLabel = codeGen.createLabelName()
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
code += VmCodeInstruction(Opcode.MSIG, VmDataType.BYTE, reg1 = msbReg, reg2=resultRegister)
code += VmCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg)
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=resultRegister, labelSymbol = skipLabel)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value = 1)
code += VmCodeLabel(skipLabel)
}
else -> {
val skipLabel = codeGen.createLabelName()
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=resultRegister, labelSymbol = skipLabel)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value = 1)
code += VmCodeLabel(skipLabel)
}
}
return code

View File

@ -249,7 +249,7 @@ internal class StatementReorderer(val program: Program,
}
else if(expr.operator in LogicalOperators) {
fun wrapped(expr: Expression): Expression {
return if(expr.inferType(program).isBytes)
return if(expr is IFunctionCall && expr.target.nameInSource==listOf("boolean"))
expr
else
FunctionCallExpression(IdentifierReference(listOf("boolean"), expr.position), mutableListOf(expr), expr.position)
@ -320,6 +320,17 @@ internal class StatementReorderer(val program: Program,
// rewrite in-place assignment expressions a bit so that the assignment target usually is the leftmost operand
val binExpr = assignment.value as? BinaryExpression
if(binExpr!=null) {
// if (binExpr.operator == "and") {
// val leftBinExpr = binExpr.left as? BinaryExpression
// if (leftBinExpr?.operator == "and")
// return mcCarthyAndAssignment(binExpr, assignment, parent)
// }
// if (binExpr.operator == "or") {
// val leftBinExpr = binExpr.left as? BinaryExpression
// if (leftBinExpr?.operator == "or")
// return mcCarthyOrAssignment(binExpr, assignment, parent)
// }
if(binExpr.left isSameAs assignment.target) {
// A = A <operator> 5, unchanged
return noModifications
@ -425,6 +436,56 @@ internal class StatementReorderer(val program: Program,
checkUnusedReturnValues(functionCallStatement, function, errors)
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
}
private fun mcCarthyAndAssignment(andExpr: BinaryExpression, assignment: Assignment, assignmentParent: Node): Iterable<IAstModification> {
val andTerms = findTerms(andExpr, "and")
if(andTerms.any { it.constValue(program)?.asBooleanValue == false }) {
errors.warn("expression is always false", andExpr.position)
return listOf(IAstModification.ReplaceNode(andExpr, NumericLiteral.fromBoolean(false, andExpr.position), assignment))
}
// TODO:
// repeat for all terms:
// assign term to target
// add if: if target==0: goto done
// done:.
return noModifications
}
private fun mcCarthyOrAssignment(orExpr: BinaryExpression, assignment: Assignment, assignmentParent: Node): Iterable<IAstModification> {
val andTerms = findTerms(orExpr, "or")
if(andTerms.any { it.constValue(program)?.asBooleanValue == true }) {
errors.warn("expression is always true", orExpr.position)
return listOf(IAstModification.ReplaceNode(orExpr, NumericLiteral.fromBoolean(true, orExpr.position), assignment))
}
// TODO:
// repeat for all terms:
// assign term to target
// add if: if target!=0: goto done
// done:.
return noModifications
}
private fun findTerms(expr: BinaryExpression, operator: String, terms: List<Expression> = emptyList()): List<Expression> {
if(expr.operator!=operator)
return listOf(expr) + terms
val leftBinExpr = expr.left as? BinaryExpression ?: return listOf(expr.left, expr.right) + terms
return findTerms(leftBinExpr, operator, listOf(expr.right)+terms)
}
private fun mcCarthyAndExpression(e1: Expression, e2: Expression, e3: Expression, origAndExpression: BinaryExpression, parent: Node): Iterable<IAstModification> {
// TODO
println("AND EXPRESSION:")
println(" $e1")
println(" $e2")
println(" $e3")
println(" (orig) $origAndExpression")
val replacement = NumericLiteral.fromBoolean(false, origAndExpression.position)
return listOf(IAstModification.ReplaceNode(origAndExpression, replacement, parent))
}
}

View File

@ -233,14 +233,5 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
}
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
if(functionCallExpr.target.nameInSource==listOf("boolean")) {
if(functionCallExpr.args[0].inferType(program).isBytes) {
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
}
}
return noModifications
}
}

View File

@ -255,17 +255,10 @@ private fun builtinBoolean(args: List<Expression>, position: Position, program:
if (args.size != 1)
throw SyntaxError("boolean requires one argument", position)
val constvalue = args[0].constValue(program) ?: throw NotConstArgumentException()
return if(constvalue.number==0.0)
NumericLiteral(DataType.UBYTE, 0.0, args[0].position)
else if(constvalue.type==DataType.UBYTE)
constvalue
else if(constvalue.type==DataType.FLOAT)
return if(constvalue.type==DataType.FLOAT)
NumericLiteral(DataType.UBYTE, constvalue.number.sign, args[0].position)
else {
val value = constvalue.number.toInt()
val byteValue = ((value ushr 8) or value) and 255
NumericLiteral(DataType.UBYTE, byteValue.toDouble(), args[0].position)
}
else
NumericLiteral.fromBoolean(constvalue.number!=0.0, args[0].position)
}
private fun builtinSgn(args: List<Expression>, position: Position, program: Program): NumericLiteral {

View File

@ -777,7 +777,7 @@ Miscellaneous
boolean(x)
Returns a byte value representing the boolean (truthy) value of x, where x can be any numeric type.
This means it returns 0 (false) when x equals 0, and a value other than 0 otherwise.
This means it returns 0 (false) when x equals 0, and 1 otherwise.
cmp(x,y)
Compare the integer value x to integer value y. Doesn't return a value or boolean result, only sets the processor's status bits!

View File

@ -4,6 +4,7 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- add McCarthy evaluation to shortcircuit and/or expressions. Both conditional expressions and assignments!
StatementReorder.after(assignment).
- add some more optimizations in vmPeepholeOptimizer
- vm Instruction needs to know what the read-registers/memory are, and what the write-register/memory is.
this info is needed for more advanced optimizations and later code generation steps.

View File

@ -48,10 +48,24 @@ main {
sub start() {
ubyte value
uword wvalue
byte svalue = -99
wvalue = 3*abs(svalue) +abs(svalue)+abs(svalue)+abs(svalue)
txt.print_uw(wvalue)
ubyte ub1 = 11
ubyte ub2 = 22
ubyte ub3 = 33
ubyte ub4 = 44
ubyte ub5 = 55
ub4 = 42
txt.print("and with bytes: ")
ub5 = ub1 and ub2 and ub3 and ub4 and ub5 ; TODO FIX !! should be True (!=0)
txt.print_ub(ub5)
txt.nl()
ub4 = 42
txt.print("or with bytes: ")
ub5 = ub1 or ub2 or ub3 or ub4 or ub5 ; TODO FIX!! should be False (0)
txt.print_ub(ub5)
txt.nl()
txt.print("short and with false (word): ")
wvalue = funcw() and funcFalseWord() and funcw() and funcw()
@ -72,7 +86,7 @@ main {
txt.nl()
txt.print("and with false: ")
value = func1(25) and func2(25) and funcFalse() and func3(25) and func4(25)
value = func1(25) and func2(25) and funcFalse() and false and func3(25) and func4(25)
txt.print_ub(value)
txt.nl()
txt.print("and with true: ")
@ -80,7 +94,7 @@ main {
txt.print_ub(value)
txt.nl()
txt.print("or with false: ")
value = func1(25) or func2(25) or funcFalse() or func3(25) or func4(25)
value = func1(25) or func2(25) or funcFalse() or true or func3(25) or func4(25)
txt.print_ub(value)
txt.nl()
txt.print("or with true: ")