mirror of
https://github.com/irmen/prog8.git
synced 2025-01-26 19:30:59 +00:00
tweak bool type handling
This commit is contained in:
parent
7a26646e1b
commit
88cbb6913d
@ -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) {
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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<IAstModification> {
|
||||
|
||||
// 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<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) {
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -15,12 +15,15 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
||||
private val errors: IErrorReporter
|
||||
) : AstWalker() {
|
||||
|
||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(typecast.type==DataType.BOOL) {
|
||||
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
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<IAstModification> {
|
||||
// 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<IAstModification> {
|
||||
@ -91,7 +93,6 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
||||
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||
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
|
||||
}
|
||||
|
||||
}
|
114
compiler/src/prog8/compiler/astprocessing/BoolRemover.kt
Normal file
114
compiler/src/prog8/compiler/astprocessing/BoolRemover.kt
Normal file
@ -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<IAstModification> {
|
||||
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<IAstModification> {
|
||||
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<IAstModification> {
|
||||
// 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<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.
|
||||
// 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)
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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<IFunctionCall>()
|
||||
right2.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
right3.left shouldBe instanceOf<IFunctionCall>()
|
||||
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") {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
@ -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)
|
||||
|
@ -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<value)...
|
||||
|
||||
- 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,28 +3,36 @@
|
||||
|
||||
|
||||
main {
|
||||
sub ftrue(ubyte arg) -> 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()
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user