compiler stuff

This commit is contained in:
Irmen de Jong 2024-02-05 01:09:33 +01:00
parent 6b87cbb703
commit 41afeccd51
22 changed files with 220 additions and 357 deletions

View File

@ -17,7 +17,7 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, D
emptyList() emptyList()
else { else {
val register = when (returntype!!) { val register = when (returntype!!) {
in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null) in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null) in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null) DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
else -> RegisterOrStatusflag(RegisterOrPair.AY, null) else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
@ -31,7 +31,7 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, D
internal fun PtSub.returnRegister(): RegisterOrStatusflag? { internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
return when(returntype) { return when(returntype) {
in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null) in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null) in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null) DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
null -> null null -> null

View File

@ -91,7 +91,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
if(errors.noErrors()) { if(errors.noErrors()) {
val sortedList = varsDontCare.sortedByDescending { it.scopedName } val sortedList = varsDontCare.sortedByDescending { it.scopedName }
for (variable in sortedList) { for (variable in sortedList) {
if(variable.dt in IntegerDatatypes) { if(variable.dt in IntegerDatatypesWithBoolean) {
if(zeropage.free.isEmpty()) { if(zeropage.free.isEmpty()) {
break break
} else { } else {

View File

@ -70,6 +70,8 @@ private fun compileMain(args: Array<String>): Boolean {
return false return false
} }
println("BREAKPOINTINSTR=$breakpointCpuInstruction")
val outputPath = pathFrom(outputDir) val outputPath = pathFrom(outputDir)
if(!outputPath.toFile().isDirectory) { if(!outputPath.toFile().isDirectory) {
System.err.println("Output path doesn't exist") System.err.println("Output path doesn't exist")

View File

@ -98,8 +98,8 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
importedFiles = imported importedFiles = imported
processAst(program, args.errors, compilationOptions) processAst(program, args.errors, compilationOptions)
// println("*********** COMPILER AST RIGHT BEFORE OPTIMIZING *************") // println("*********** COMPILER AST RIGHT BEFORE OPTIMIZING *************")
// printProgram(program) // printProgram(program)
if (compilationOptions.optimize) { if (compilationOptions.optimize) {
optimizeAst( optimizeAst(

View File

@ -123,12 +123,6 @@ internal class AstChecker(private val program: Program,
if(valueDt istype DataType.BOOL && expectedReturnValues[0] == DataType.UBYTE) { if(valueDt istype DataType.BOOL && expectedReturnValues[0] == DataType.UBYTE) {
// if the return value is a bool and the return type is ubyte, allow this. But give a warning. // if the return value is a bool and the return type is ubyte, allow this. But give a warning.
errors.info("return type of the subroutine should probably be bool instead of ubyte", returnStmt.position) errors.info("return type of the subroutine should probably be bool instead of ubyte", returnStmt.position)
} else if(valueDt istype DataType.UBYTE && expectedReturnValues[0] == DataType.BOOL) {
// if the return value is ubyte and the return type is bool, allow this only if value is 0 or 1
val returnValue = returnStmt.value as? NumericLiteral
if (returnValue == null || returnValue.type != DataType.UBYTE || (returnValue.number!=0.0 && returnValue.number!=1.0)) {
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position)
}
} else if(valueDt.isIterable && expectedReturnValues[0]==DataType.UWORD) { } else if(valueDt.isIterable && expectedReturnValues[0]==DataType.UWORD) {
// you can return a string or array when an uword (pointer) is returned // you can return a string or array when an uword (pointer) is returned
} else if(valueDt istype DataType.UWORD && expectedReturnValues[0]==DataType.STR) { } else if(valueDt istype DataType.UWORD && expectedReturnValues[0]==DataType.STR) {
@ -156,12 +150,9 @@ internal class AstChecker(private val program: Program,
} }
override fun visit(ifElse: IfElse) { override fun visit(ifElse: IfElse) {
val dt = ifElse.condition.inferType(program) if(!ifElse.condition.inferType(program).isBool)
if(!dt.isInteger && !dt.istype(DataType.BOOL)) { errors.err("condition should be a boolean", ifElse.condition.position)
val identifier = ifElse.condition as? IdentifierReference
if(identifier==null || identifier.targetStatement(program)!=null)
errors.err("condition value should be an integer type or bool", ifElse.condition.position)
}
super.visit(ifElse) super.visit(ifElse)
} }
@ -480,22 +471,16 @@ internal class AstChecker(private val program: Program,
} }
override fun visit(untilLoop: UntilLoop) { override fun visit(untilLoop: UntilLoop) {
val dt = untilLoop.condition.inferType(program) if(!untilLoop.condition.inferType(program).isBool)
if(!dt.isInteger && !dt.istype(DataType.BOOL)) { errors.err("condition should be a boolean", untilLoop.condition.position)
val identifier = untilLoop.condition as? IdentifierReference
if(identifier==null || identifier.targetStatement(program)!=null)
errors.err("condition value should be an integer type or bool", untilLoop.condition.position)
}
super.visit(untilLoop) super.visit(untilLoop)
} }
override fun visit(whileLoop: WhileLoop) { override fun visit(whileLoop: WhileLoop) {
val dt = whileLoop.condition.inferType(program) if(!whileLoop.condition.inferType(program).isBool)
if(!dt.isInteger && !dt.istype(DataType.BOOL)) { errors.err("condition should be a boolean", whileLoop.condition.position)
val identifier = whileLoop.condition as? IdentifierReference
if(identifier==null || identifier.targetStatement(program)!=null)
errors.err("condition value should be an integer type or bool", whileLoop.condition.position)
}
super.visit(whileLoop) super.visit(whileLoop)
} }
@ -539,20 +524,10 @@ internal class AstChecker(private val program: Program,
if(numvalue!=null && targetDt.isKnown) if(numvalue!=null && targetDt.isKnown)
checkValueTypeAndRange(targetDt.getOr(DataType.UNDEFINED), numvalue) checkValueTypeAndRange(targetDt.getOr(DataType.UNDEFINED), numvalue)
// 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) super.visit(assignment)
} }
override fun visit(assignTarget: AssignTarget) { override fun visit(assignTarget: AssignTarget) {
if(assignTarget.inferType(program).istype(DataType.LONG))
errors.err("integer overflow", assignTarget.position)
super.visit(assignTarget) super.visit(assignTarget)
val memAddr = assignTarget.memoryAddress?.addressExpression?.constValue(program)?.number?.toInt() val memAddr = assignTarget.memoryAddress?.addressExpression?.constValue(program)?.number?.toInt()
@ -586,16 +561,13 @@ internal class AstChecker(private val program: Program,
if (assignment is Assignment) { if (assignment is Assignment) {
val targetDatatype = assignTarget.inferType(program) val targetDatatype = assignTarget.inferType(program)
if (targetDatatype.isKnown) { if (targetDatatype.isKnown) {
val constVal = assignment.value.constValue(program) val sourceDatatype = assignment.value.inferType(program)
if(constVal==null) { if (sourceDatatype.isUnknown) {
val sourceDatatype = assignment.value.inferType(program) if (assignment.value !is BinaryExpression && assignment.value !is PrefixExpression && assignment.value !is ContainmentCheck)
if (sourceDatatype.isUnknown) { errors.err("invalid assignment value, maybe forgot '&' (address-of)", assignment.value.position)
if (assignment.value !is FunctionCallExpression) } else {
errors.err("invalid assignment value, maybe forgot '&' (address-of)", assignment.value.position) checkAssignmentCompatible(targetDatatype.getOr(DataType.UNDEFINED),
} else { sourceDatatype.getOr(DataType.UNDEFINED), assignment.value)
checkAssignmentCompatible(targetDatatype.getOr(DataType.UNDEFINED),
sourceDatatype.getOr(DataType.UNDEFINED), assignment.value)
}
} }
} }
} }
@ -619,6 +591,10 @@ internal class AstChecker(private val program: Program,
if(decl.datatype==DataType.LONG) if(decl.datatype==DataType.LONG)
errors.err("integer overflow", decl.position) errors.err("integer overflow", decl.position)
if(decl.type==VarDeclType.MEMORY) {
if (decl.datatype == DataType.BOOL || decl.datatype == DataType.ARRAY_BOOL)
errors.err("variables mapped in memory should be numeric", decl.position)
}
fun err(msg: String) = errors.err(msg, decl.position) fun err(msg: String) = errors.err(msg, decl.position)
@ -628,8 +604,8 @@ internal class AstChecker(private val program: Program,
// CONST can only occur on simple types (byte, word, float) // CONST can only occur on simple types (byte, word, float)
if(decl.type== VarDeclType.CONST) { if(decl.type== VarDeclType.CONST) {
if (decl.datatype !in NumericDatatypes) if (decl.datatype !in NumericDatatypesWithBoolean)
err("const can only be used on numeric types (byte, word, float)") err("const can only be used on numeric types or booleans")
} }
// FLOATS enabled? // FLOATS enabled?
@ -717,11 +693,11 @@ internal class AstChecker(private val program: Program,
val eltDt = ArrayToElementTypes.getValue(decl.datatype) val eltDt = ArrayToElementTypes.getValue(decl.datatype)
if(iDt isnot eltDt) { if(iDt isnot eltDt) {
if(!(iDt.isBool && eltDt==DataType.UBYTE || iDt.istype(DataType.UBYTE) && eltDt==DataType.BOOL)) if(!(iDt.isBool && eltDt==DataType.UBYTE || iDt.istype(DataType.UBYTE) && eltDt==DataType.BOOL))
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})") err("initialisation value has incompatible type ($iDt) for the variable (${decl.datatype})")
} }
} else { } else {
if(!(iDt.isBool && decl.datatype==DataType.UBYTE || iDt.istype(DataType.UBYTE) && decl.datatype==DataType.BOOL)) if(!(iDt.isBool && decl.datatype==DataType.UBYTE || iDt.istype(DataType.UBYTE) && decl.datatype==DataType.BOOL))
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})") err("initialisation value has incompatible type ($iDt) for the variable (${decl.datatype})")
} }
} }
} }
@ -971,9 +947,13 @@ internal class AstChecker(private val program: Program,
else if(expr.operator == "~") { else if(expr.operator == "~") {
if(dt !in IntegerDatatypes) if(dt !in IntegerDatatypes)
errors.err("can only use bitwise invert on integer types", expr.position) errors.err("can only use bitwise invert on integer types", expr.position)
if(dt==DataType.BOOL) else if(dt==DataType.BOOL)
errors.err("bitwise invert is for integer types, use 'not' on booleans", expr.position) errors.err("bitwise invert is for integer types, use 'not' on booleans", expr.position)
} }
else if(expr.operator == "not") {
if(dt!=DataType.BOOL)
errors.err("logical not is for booleans", expr.position)
}
super.visit(expr) super.visit(expr)
} }
@ -1014,11 +994,6 @@ internal class AstChecker(private val program: Program,
errors.err("remainder can only be used on unsigned integer operands", expr.right.position) errors.err("remainder can only be used on unsigned integer operands", expr.right.position)
} }
} }
"&", "|", "^" -> {
// only integer numeric operands accepted
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
errors.err("bitwise operator can only be used on integer operands", expr.right.position)
}
"in" -> throw FatalAstException("in expression should have been replaced by containmentcheck") "in" -> throw FatalAstException("in expression should have been replaced by containmentcheck")
"<<", ">>" -> { "<<", ">>" -> {
if(rightDt in WordDatatypes) { if(rightDt in WordDatatypes) {
@ -1043,10 +1018,12 @@ internal class AstChecker(private val program: Program,
// expression with one side BOOL other side (U)BYTE is allowed; bool==byte // expression with one side BOOL other side (U)BYTE is allowed; bool==byte
} else if((expr.operator == "<<" || expr.operator == ">>") && (leftDt in WordDatatypes && rightDt in ByteDatatypes)) { } else if((expr.operator == "<<" || expr.operator == ">>") && (leftDt in WordDatatypes && rightDt in ByteDatatypes)) {
// exception allowed: shifting a word by a byte // exception allowed: shifting a word by a byte
} else if((expr.operator in BitwiseOperators) && (leftDt in IntegerDatatypes && rightDt in IntegerDatatypes)) {
// exception allowed: bitwise operations with any integers
} else if((leftDt==DataType.UWORD && rightDt==DataType.STR) || (leftDt==DataType.STR && rightDt==DataType.UWORD)) { } else if((leftDt==DataType.UWORD && rightDt==DataType.STR) || (leftDt==DataType.STR && rightDt==DataType.UWORD)) {
// exception allowed: comparing uword (pointer) with string // exception allowed: comparing uword (pointer) with string
} else { } else {
errors.err("left and right operands aren't the same type", expr.left.position) errors.err("left and right operands aren't the same type", expr.position)
} }
} }
@ -1089,6 +1066,18 @@ internal class AstChecker(private val program: Program,
errors.err("can't use boolean operand with this operator ${expr.operator}", expr.right.position) errors.err("can't use boolean operand with this operator ${expr.operator}", expr.right.position)
} }
} }
if(expr.operator in LogicalOperators) {
if (leftDt != DataType.BOOL || rightDt != DataType.BOOL)
errors.err("logical operator requires boolean operands", expr.right.position)
}
else {
if (leftDt == DataType.BOOL || rightDt == DataType.BOOL) {
if(expr.operator!="==" && expr.operator!="!=")
errors.err("operator requires numeric operands", expr.right.position)
}
}
} }
override fun visit(typecast: TypecastExpression) { override fun visit(typecast: TypecastExpression) {
@ -1289,14 +1278,7 @@ internal class AstChecker(private val program: Program,
if(target is Label && args.isNotEmpty()) if(target is Label && args.isNotEmpty())
errors.err("cannot use arguments when calling a label", position) errors.err("cannot use arguments when calling a label", position)
if(target is BuiltinFunctionPlaceholder) { if(target is Subroutine) {
if(target.name=="all" || target.name=="any") {
if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program)?.datatype == DataType.STR
|| args[0].inferType(program).getOr(DataType.STR) == DataType.STR) {
errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
}
}
} else if(target is Subroutine) {
if(target.isAsmSubroutine) { if(target.isAsmSubroutine) {
for (arg in args.zip(target.parameters)) { for (arg in args.zip(target.parameters)) {
val argIDt = arg.first.inferType(program) val argIDt = arg.first.inferType(program)
@ -1375,7 +1357,10 @@ internal class AstChecker(private val program: Program,
} }
override fun visit(whenStmt: When) { override fun visit(whenStmt: When) {
if(!whenStmt.condition.inferType(program).isInteger) val conditionDt = whenStmt.condition.inferType(program)
if(conditionDt.isBool)
errors.err("condition is boolean, use if statement instead", whenStmt.position)
else if(!conditionDt.isInteger)
errors.err("when condition must be an integer value", whenStmt.position) errors.err("when condition must be an integer value", whenStmt.position)
val tally = mutableSetOf<Int>() val tally = mutableSetOf<Int>()
for((choices, choiceNode) in whenStmt.choiceValues(program)) { for((choices, choiceNode) in whenStmt.choiceValues(program)) {
@ -1406,7 +1391,7 @@ internal class AstChecker(private val program: Program,
for((constvalue, pos) in constvalues) { for((constvalue, pos) in constvalues) {
when { when {
constvalue == null -> errors.err("choice value must be a constant", pos) constvalue == null -> errors.err("choice value must be a constant", pos)
constvalue.type !in IntegerDatatypes -> errors.err("choice value must be a byte or word", pos) constvalue.type !in IntegerDatatypesWithBoolean -> errors.err("choice value must be a byte or word", pos)
conditionType isnot constvalue.type -> { conditionType isnot constvalue.type -> {
if(conditionType.isKnown) { if(conditionType.isKnown) {
if(conditionType.istype(DataType.BOOL)) { if(conditionType.istype(DataType.BOOL)) {
@ -1492,7 +1477,8 @@ internal class AstChecker(private val program: Program,
private fun checkLongType(expression: Expression) { private fun checkLongType(expression: Expression) {
if(expression.inferType(program).istype(DataType.LONG)) { if(expression.inferType(program).istype(DataType.LONG)) {
errors.err("integer overflow", expression.position) if(errors.noErrorForLine(expression.position))
errors.err("integer overflow", expression.position)
} }
} }
@ -1654,13 +1640,14 @@ internal class AstChecker(private val program: Program,
return err("value '$number' out of range for word") return err("value '$number' out of range for word")
} }
DataType.BOOL -> { DataType.BOOL -> {
return true if(value.type!=DataType.BOOL)
err("type of value ${value.type} doesn't match target $targetDt")
} }
in ArrayDatatypes -> { in ArrayDatatypes -> {
val eltDt = ArrayToElementTypes.getValue(targetDt) val eltDt = ArrayToElementTypes.getValue(targetDt)
return checkValueTypeAndRange(eltDt, value) return checkValueTypeAndRange(eltDt, value)
} }
else -> return err("value of type ${value.type} not compatible with $targetDt") else -> return err("type of value ${value.type} doesn't match target $targetDt")
} }
return true return true
} }
@ -1723,11 +1710,11 @@ internal class AstChecker(private val program: Program,
} }
val result = when(targetDatatype) { val result = when(targetDatatype) {
DataType.BOOL -> sourceDatatype in NumericDatatypes DataType.BOOL -> sourceDatatype==DataType.BOOL
DataType.BYTE -> sourceDatatype == DataType.BYTE || sourceDatatype == DataType.BOOL DataType.BYTE -> sourceDatatype == DataType.BYTE
DataType.UBYTE -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.BOOL DataType.UBYTE -> sourceDatatype == DataType.UBYTE
DataType.WORD -> sourceDatatype in setOf(DataType.BYTE, DataType.UBYTE, DataType.WORD, DataType.BOOL) DataType.WORD -> sourceDatatype in setOf(DataType.BYTE, DataType.UBYTE, DataType.WORD)
DataType.UWORD -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.UWORD || sourceDatatype == DataType.BOOL DataType.UWORD -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.UWORD
DataType.FLOAT -> sourceDatatype in NumericDatatypes DataType.FLOAT -> sourceDatatype in NumericDatatypes
DataType.STR -> sourceDatatype == DataType.STR DataType.STR -> sourceDatatype == DataType.STR
else -> { else -> {
@ -1747,12 +1734,14 @@ internal class AstChecker(private val program: Program,
} }
else if(sourceDatatype== DataType.FLOAT && targetDatatype in IntegerDatatypes) else if(sourceDatatype== DataType.FLOAT && targetDatatype in IntegerDatatypes)
errors.err("cannot assign float to ${targetDatatype.name.lowercase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position) errors.err("cannot assign float to ${targetDatatype.name.lowercase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
else if((sourceValue as? BinaryExpression)?.operator in BitwiseOperators && targetDatatype.equalsSize(sourceDatatype)) {
// this is allowed: bitwise operation between different types as long as they're the same size.
}
else if(targetDatatype==DataType.UWORD && sourceDatatype in PassByReferenceDatatypes) {
// this is allowed: a pass-by-reference datatype into a uword (pointer value).
}
else { else {
if(targetDatatype!=DataType.UWORD && sourceDatatype !in PassByReferenceDatatypes) { errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
// allow bitwise operations on different types as long as the size is the same
if (!((sourceValue as? BinaryExpression)?.operator in BitwiseOperators && targetDatatype.equalsSize(sourceDatatype)))
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
}
} }
return false return false

View File

@ -22,10 +22,6 @@ internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: Compila
} }
internal fun Program.processAstBeforeAsmGeneration(compilerOptions: CompilationOptions, errors: IErrorReporter) { internal fun Program.processAstBeforeAsmGeneration(compilerOptions: CompilationOptions, errors: IErrorReporter) {
val boolRemover = BoolRemover(this)
boolRemover.visit(this)
boolRemover.applyModifications()
val fixer = BeforeAsmAstChanger(this, compilerOptions) val fixer = BeforeAsmAstChanger(this, compilerOptions)
fixer.visit(this) fixer.visit(this)
while (errors.noErrors() && fixer.applyModifications() > 0) { while (errors.noErrors() && fixer.applyModifications() > 0) {

View File

@ -136,7 +136,7 @@ class AstPreprocessor(val program: Program,
} }
} else { } else {
// handle declaration of a single variable // handle declaration of a single variable
if(decl.value!=null && decl.datatype in NumericDatatypes) { if(decl.value!=null && (decl.datatype in NumericDatatypes || decl.datatype==DataType.BOOL)) {
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position) val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
val assign = Assignment(target, decl.value!!, AssignmentOrigin.VARINIT, decl.position) val assign = Assignment(target, decl.value!!, AssignmentOrigin.VARINIT, decl.position)
replacements.add(IAstModification.ReplaceNode(decl, assign, scope)) replacements.add(IAstModification.ReplaceNode(decl, assign, scope))

View File

@ -26,7 +26,7 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
} }
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> { override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if (decl.type == VarDeclType.VAR && decl.value != null && decl.datatype in NumericDatatypes) if (decl.type == VarDeclType.VAR && decl.value != null && (decl.datatype in NumericDatatypes || decl.datatype==DataType.BOOL))
throw InternalCompilerException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl") throw InternalCompilerException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
return noModifications return noModifications
@ -104,17 +104,7 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
override fun after(ifElse: IfElse, parent: Node): Iterable<IAstModification> { override fun after(ifElse: IfElse, parent: Node): Iterable<IAstModification> {
val binExpr = ifElse.condition as? BinaryExpression val binExpr = ifElse.condition as? BinaryExpression
if(binExpr==null) { if(binExpr!=null) {
// if x -> if x!=0
val booleanExpr = BinaryExpression(
ifElse.condition,
"!=",
NumericLiteral.optimalInteger(0, ifElse.condition.position),
ifElse.condition.position
)
return listOf(IAstModification.ReplaceNode(ifElse.condition, booleanExpr, ifElse))
}
if(binExpr.operator !in ComparisonOperators) { if(binExpr.operator !in ComparisonOperators) {
val constRight = binExpr.right.constValue(program) val constRight = binExpr.right.constValue(program)
if(constRight!=null) { if(constRight!=null) {
@ -131,20 +121,13 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
return listOf(IAstModification.ReplaceNode(ifElse.condition, booleanExpr, ifElse)) return listOf(IAstModification.ReplaceNode(ifElse.condition, booleanExpr, ifElse))
} }
} }
// if x*5 -> if x*5 != 0
val booleanExpr = BinaryExpression(
ifElse.condition,
"!=",
NumericLiteral.optimalInteger(0, ifElse.condition.position),
ifElse.condition.position
)
return listOf(IAstModification.ReplaceNode(ifElse.condition, booleanExpr, ifElse))
} }
if((binExpr.left as? NumericLiteral)?.number==0.0 && if((binExpr.left as? NumericLiteral)?.number==0.0 &&
(binExpr.right as? NumericLiteral)?.number!=0.0) (binExpr.right as? NumericLiteral)?.number!=0.0)
throw InternalCompilerException("0==X should have been swapped to if X==0") throw InternalCompilerException("0==X should be just X")
}
return noModifications return noModifications
} }

View File

@ -15,14 +15,6 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
private val errors: IErrorReporter private val errors: IErrorReporter
) : AstWalker() { ) : 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(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> { override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
// see if we can remove redundant typecasts (outside of expressions) // see if we can remove redundant typecasts (outside of expressions)
// such as casting byte<->ubyte, word<->uword or even redundant casts (sourcetype = target type). // such as casting byte<->ubyte, word<->uword or even redundant casts (sourcetype = target type).

View File

@ -1,133 +0,0 @@
package prog8.compiler.astprocessing
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.FatalAstException
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 valueDt = typecast.expression.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val notZero = BinaryExpression(typecast.expression, "!=", NumericLiteral(valueDt, 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, null, decl.name, emptyList(),
newvalue, decl.sharedWithAsm, false, 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, emptyList(),
newarray, decl.sharedWithAsm, decl.splitArray, 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, false, subroutine.statements,
subroutine.position)
return listOf(IAstModification.ReplaceNode(subroutine, newSubroutine, parent))
}
return noModifications
}
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
if(expr.operator in setOf("and", "or", "xor")) {
// see if any of the arguments to a logical boolean expression need type casting to bool
val mods = mutableListOf<IAstModification>()
val newLeft = wrapWithBooleanCastIfNeeded(expr.left, program)
val newRight = wrapWithBooleanCastIfNeeded(expr.right, program)
if(newLeft!=null)
mods += IAstModification.ReplaceNodeSafe(expr.left, newLeft, expr)
if(newRight!=null)
mods += IAstModification.ReplaceNodeSafe(expr.right, newRight, expr)
return mods
}
return noModifications
}
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
if(expr.operator=="not") {
val binExpr = expr.expression as? BinaryExpression
if(binExpr!=null) {
val invertedOperator = invertedComparisonOperator(binExpr.operator)
if(invertedOperator!=null) {
val inverted = BinaryExpression(binExpr.left, invertedOperator, binExpr.right, binExpr.position)
return listOf(IAstModification.ReplaceNode(expr, inverted, parent))
}
}
val exprDt = expr.expression.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val nonBoolDt = if(exprDt==DataType.BOOL) DataType.UBYTE else exprDt
val equalZero = BinaryExpression(expr.expression, "==", NumericLiteral(nonBoolDt, 0.0, expr.expression.position), expr.expression.position)
return listOf(IAstModification.ReplaceNode(expr, equalZero, parent))
}
return noModifications
}
}
internal fun wrapWithBooleanCastIfNeeded(expr: Expression, program: Program): Expression? {
fun isBoolean(expr: Expression): Boolean {
return if(expr.inferType(program) istype DataType.BOOL)
true
else if(expr is NumericLiteral && expr.type in IntegerDatatypes && (expr.number==0.0 || expr.number==1.0))
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))
null
else
TypecastExpression(expr, DataType.BOOL, true, expr.position)
}

View File

@ -124,6 +124,11 @@ if not CONDITION
} }
override fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> { override fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
if(!whileLoop.condition.inferType(program).isBool)
errors.err("condition should be a boolean", whileLoop.condition.position)
/* /*
while true -> repeat while true -> repeat
while false -> discard while false -> discard

View File

@ -558,8 +558,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
private fun transform(srcExpr: BinaryExpression): PtBinaryExpression { private fun transform(srcExpr: BinaryExpression): PtBinaryExpression {
val type = srcExpr.inferType(program).getOrElse { throw FatalAstException("unknown dt") } val type = srcExpr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val actualType = if(type==DataType.BOOL) DataType.UBYTE else type val expr = PtBinaryExpression(srcExpr.operator, type, srcExpr.position)
val expr = PtBinaryExpression(srcExpr.operator, actualType, srcExpr.position)
expr.add(transformExpression(srcExpr.left)) expr.add(transformExpression(srcExpr.left))
expr.add(transformExpression(srcExpr.right)) expr.add(transformExpression(srcExpr.right))
return expr return expr
@ -578,27 +577,27 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
fun desugar(range: RangeExpression): PtExpression { fun desugar(range: RangeExpression): PtExpression {
require(range.from.inferType(program)==range.to.inferType(program)) require(range.from.inferType(program)==range.to.inferType(program))
val expr = PtBinaryExpression("and", DataType.UBYTE, srcCheck.position) val expr = PtBinaryExpression("and", DataType.BOOL, srcCheck.position)
val x1 = transformExpression(srcCheck.element) val x1 = transformExpression(srcCheck.element)
val x2 = transformExpression(srcCheck.element) val x2 = transformExpression(srcCheck.element)
val eltDt = srcCheck.element.inferType(program) val eltDt = srcCheck.element.inferType(program)
if(eltDt.isInteger) { if(eltDt.isInteger) {
val low = PtBinaryExpression("<=", DataType.UBYTE, srcCheck.position) val low = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
low.add(transformExpression(range.from)) low.add(transformExpression(range.from))
low.add(x1) low.add(x1)
expr.add(low) expr.add(low)
val high = PtBinaryExpression("<=", DataType.UBYTE, srcCheck.position) val high = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
high.add(x2) high.add(x2)
high.add(transformExpression(range.to)) high.add(transformExpression(range.to))
expr.add(high) expr.add(high)
} else { } else {
val low = PtBinaryExpression("<=", DataType.UBYTE, srcCheck.position) val low = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
val lowFloat = PtTypeCast(DataType.FLOAT, range.from.position) val lowFloat = PtTypeCast(DataType.FLOAT, range.from.position)
lowFloat.add(transformExpression(range.from)) lowFloat.add(transformExpression(range.from))
low.add(lowFloat) low.add(lowFloat)
low.add(x1) low.add(x1)
expr.add(low) expr.add(low)
val high = PtBinaryExpression("<=", DataType.UBYTE, srcCheck.position) val high = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
high.add(x2) high.add(x2)
val highFLoat = PtTypeCast(DataType.FLOAT, range.to.position) val highFLoat = PtTypeCast(DataType.FLOAT, range.to.position)
highFLoat.add(transformExpression(range.to)) highFLoat.add(transformExpression(range.to))
@ -655,8 +654,12 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
return mem return mem
} }
private fun transform(number: NumericLiteral): PtNumber = private fun transform(number: NumericLiteral): PtExpression {
PtNumber(number.type, number.number, number.position) return if(number.type==DataType.BOOL)
PtBool(number.asBooleanValue, number.position)
else
PtNumber(number.type, number.number, number.position)
}
private fun transform(srcPrefix: PrefixExpression): PtPrefix { private fun transform(srcPrefix: PrefixExpression): PtPrefix {
val type = srcPrefix.inferType(program).getOrElse { throw FatalAstException("unknown dt") } val type = srcPrefix.inferType(program).getOrElse { throw FatalAstException("unknown dt") }

View File

@ -76,8 +76,8 @@ internal class LiteralsToAutoVars(private val program: Program,
if(decl.names.size>1) { if(decl.names.size>1) {
// note: the desugaring of a multi-variable vardecl has to be done here // note: the desugaring of a multi-variable vardecl has to be done here
// and not in CodeDesugarer, that one is too late (identifiers can't be found otherwise) // and not in CodeDesugarer, that one is too late (identifiers can't be found otherwise)
if(decl.datatype !in NumericDatatypes) if(decl.datatype !in NumericDatatypesWithBoolean)
errors.err("can only multi declare numeric variables", decl.position) errors.err("can only multi declare numeric and boolean variables", decl.position)
if(errors.noErrors()) { if(errors.noErrors()) {
// desugar into individual vardecl per name. // desugar into individual vardecl per name.
return decl.desugarMultiDecl().map { return decl.desugarMultiDecl().map {

View File

@ -6,13 +6,9 @@ import prog8.ast.base.FatalAstException
import prog8.ast.expressions.BinaryExpression import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.NumericLiteral import prog8.ast.expressions.NumericLiteral
import prog8.ast.expressions.PrefixExpression import prog8.ast.expressions.PrefixExpression
import prog8.ast.expressions.invertCondition
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.DataType import prog8.code.core.*
import prog8.code.core.ICompilationTarget
import prog8.code.core.IErrorReporter
import prog8.code.core.IntegerDatatypes
internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val errors: IErrorReporter, val compTarget: ICompilationTarget) : AstWalker() { internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val errors: IErrorReporter, val compTarget: ICompilationTarget) : AstWalker() {
@ -21,7 +17,7 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
val left = expr.left as? BinaryExpression val left = expr.left as? BinaryExpression
if (left != null) { if (left != null) {
val rightValue = expr.right.constValue(program) val rightValue = expr.right.constValue(program)
if (rightValue?.number == 0.0 && rightValue.type in IntegerDatatypes) { if (rightValue?.number == 0.0 && rightValue.type in IntegerDatatypesWithBoolean) {
if (left.operator == "==" && expr.operator == "==") { if (left.operator == "==" && expr.operator == "==") {
// (x==something)==0 --> x!=something // (x==something)==0 --> x!=something
left.operator = "!=" left.operator = "!="
@ -43,15 +39,11 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
} }
} }
if(expr.operator=="^" && expr.left.inferType(program) istype DataType.BOOL && expr.right.constValue(program)?.number == 1.0) {
// boolean ^ 1 --> not boolean
return listOf(IAstModification.ReplaceNode(expr, invertCondition(expr.left, program), parent))
}
// applying De Morgan's laws proved beneficial for the code generator, // applying De Morgan's laws proved beneficial for the code generator,
// when the code has one outer 'not' instead of two inner ones. // when the code has one outer 'not' instead of two inner ones.
if(expr.operator=="or" || expr.operator=="and") { if(expr.operator=="or" || expr.operator=="and") {
// boolean case
val newOper = if(expr.operator=="or") "and" else "or" val newOper = if(expr.operator=="or") "and" else "or"
val leftP = expr.left as? PrefixExpression val leftP = expr.left as? PrefixExpression
val rightP = expr.right as? PrefixExpression val rightP = expr.right as? PrefixExpression
@ -62,17 +54,25 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
val notExpr = PrefixExpression("not", inner, expr.position) val notExpr = PrefixExpression("not", inner, expr.position)
return listOf(IAstModification.ReplaceNode(expr, notExpr, parent)) return listOf(IAstModification.ReplaceNode(expr, notExpr, parent))
} }
val leftB = expr.left as? BinaryExpression
val rightB = expr.right as? BinaryExpression // integer case (only if both are the same type)
if(leftB!=null && leftB.operator=="==" && (leftB.right as? NumericLiteral)?.number==0.0 val leftC = expr.left as? BinaryExpression
&& rightB!=null && rightB.operator=="==" && (rightB.right as? NumericLiteral)?.number==0.0) { val rightC = expr.right as? BinaryExpression
// a==0 or b==0 --> (a!=0 and b!=0)==0 if(leftC!=null && rightC!=null && leftC.operator=="==" && rightC.operator=="==") {
// a==0 and b==0 --> (a!=0 or b!=0)==0 if (leftC.right.constValue(program)?.number == 0.0 && rightC.right.constValue(program)?.number == 0.0) {
leftB.operator = "!=" val leftDt = leftC.left.inferType(program).getOr(DataType.UNDEFINED)
rightB.operator = "!=" val rightDt = rightC.left.inferType(program).getOr(DataType.UNDEFINED)
val inner = BinaryExpression(leftB, newOper, rightB, expr.position) if(leftDt==rightDt && leftDt in IntegerDatatypes) {
val notExpr = BinaryExpression(inner, "==", NumericLiteral.optimalInteger(0, expr.position), expr.position) if (rightC.left.isSimple) {
return listOf(IAstModification.ReplaceNode(expr, notExpr, parent)) // x==0 or y==0 -> (x & y)==0
// x==0 and y==0 -> (x | y)==0
val newOperator = if(expr.operator=="or") "&" else "|"
val inner = BinaryExpression(leftC.left, newOperator, rightC.left, expr.position)
val compare = BinaryExpression(inner, "==", NumericLiteral(leftDt, 0.0, expr.position), expr.position)
return listOf(IAstModification.ReplaceNode(expr, compare, parent))
}
}
}
} }
} }
@ -82,17 +82,50 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> { override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
if(expr.operator == "not") { if(expr.operator == "not") {
// first check if we're already part of a "boolean" expresion (i.e. comparing against 0) // first check if we're already part of a "boolean" expression (i.e. comparing against 0 or 1)
// if so, simplify THAT whole expression rather than making it more complicated // if so, simplify THAT whole expression rather than making it more complicated
if(parent is BinaryExpression && parent.right.constValue(program)?.number==0.0) { if (parent is BinaryExpression) {
if(parent.operator=="==") { if (parent.right.constValue(program)?.number == 0.0) {
// (NOT X)==0 --> X!=0 if(parent.right.inferType(program).isBool) {
val replacement = BinaryExpression(expr.expression, "!=", NumericLiteral.optimalInteger(0, expr.position), expr.position) if(parent.operator=="==") {
return listOf(IAstModification.ReplaceNode(parent, replacement, parent.parent)) // (NOT X)==false --> X==true --> X
} else if(parent.operator=="!=") { return listOf(IAstModification.ReplaceNode(parent, expr.expression, parent.parent))
// (NOT X)!=0 --> X==0 } else if(parent.operator=="!=") {
val replacement = BinaryExpression(expr.expression, "==", NumericLiteral.optimalInteger(0, expr.position), expr.position) // (NOT X)!=false --> X!=true -> not X
return listOf(IAstModification.ReplaceNode(parent, replacement, parent.parent)) return listOf(IAstModification.ReplaceNode(parent, expr, parent.parent))
}
} else {
if(parent.operator=="==") {
// (NOT X)==0 --> X!=0
val replacement = BinaryExpression(expr.expression, "!=", NumericLiteral.optimalInteger(0, expr.position), expr.position)
return listOf(IAstModification.ReplaceNode(parent, replacement, parent.parent))
} else if(parent.operator=="!=") {
// (NOT X)!=0 --> X==0
val replacement = BinaryExpression(expr.expression, "==", NumericLiteral.optimalInteger(0, expr.position), expr.position)
return listOf(IAstModification.ReplaceNode(parent, replacement, parent.parent))
}
}
}
else if (parent.right.constValue(program)?.number == 1.0) {
if(parent.right.inferType(program).isBool) {
if(parent.operator=="==") {
// (NOT X)==true --> X==false --> not X
return listOf(IAstModification.ReplaceNode(parent, expr, parent.parent))
} else if(parent.operator=="!=") {
// (NOT X)!=true --> X!=false -> X
return listOf(IAstModification.ReplaceNode(parent, expr.expression, parent.parent))
}
} else {
if(parent.operator=="==") {
// (NOT X)==1 --> X==0
val replacement = BinaryExpression(expr.expression, "==", NumericLiteral.optimalInteger(0, expr.position), expr.position)
return listOf(IAstModification.ReplaceNode(parent, replacement, parent.parent))
} else if(parent.operator=="!=") {
// (NOT X)!=1 --> X!=0
val replacement = BinaryExpression(expr.expression, "!=", NumericLiteral.optimalInteger(0, expr.position), expr.position)
return listOf(IAstModification.ReplaceNode(parent, replacement, parent.parent))
}
}
} }
} }
@ -120,13 +153,8 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
return listOf(IAstModification.ReplaceNode(expr, subBinExpr, parent)) return listOf(IAstModification.ReplaceNode(expr, subBinExpr, parent))
} }
} }
// not simpleX -> simpleX==0
if(expr.expression.isSimple) {
val replacement = BinaryExpression(expr.expression.copy(),"==", NumericLiteral(DataType.UBYTE, 0.0, expr.position), expr.position)
return listOf(IAstModification.ReplaceNodeSafe(expr, replacement, parent))
}
} }
return noModifications return noModifications
} }
} }

View File

@ -44,7 +44,7 @@ internal class StatementReorderer(
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> { override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if (decl.type == VarDeclType.VAR) { if (decl.type == VarDeclType.VAR) {
if (decl.datatype in NumericDatatypes) { if (decl.datatype in NumericDatatypes || decl.datatype==DataType.BOOL) {
if(decl !in declsProcessedWithInitAssignment) { if(decl !in declsProcessedWithInitAssignment) {
declsProcessedWithInitAssignment.add(decl) declsProcessedWithInitAssignment.add(decl)
if (decl.value == null) { if (decl.value == null) {

View File

@ -24,12 +24,11 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
val valueDt = declValue.inferType(program) val valueDt = declValue.inferType(program)
if(valueDt isnot decl.datatype) { if(valueDt isnot decl.datatype) {
// don't add a typecast on an array initializer value, unless booleans
if(valueDt.isInteger && decl.isArray) { if(valueDt.isInteger && decl.isArray) {
if(decl.datatype == DataType.ARRAY_BOOL) { if(decl.datatype == DataType.ARRAY_BOOL) {
val integer = declValue.constValue(program)?.number val integer = declValue.constValue(program)?.number
if(integer!=null) { if(integer!=null) {
val num = NumericLiteral(DataType.UBYTE, if(integer==0.0) 0.0 else 1.0, declValue.position) val num = NumericLiteral(DataType.BOOL, if(integer==0.0) 0.0 else 1.0, declValue.position)
num.parent = decl num.parent = decl
decl.value = num decl.value = num
} }
@ -87,30 +86,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
} }
} }
if(expr.operator in LogicalOperators && leftDt.isInteger && rightDt.isInteger) {
// see if any of the operands needs conversion to bool
val modifications = mutableListOf<IAstModification>()
val newLeft = wrapWithBooleanCastIfNeeded(expr.left, program)
val newRight = wrapWithBooleanCastIfNeeded(expr.right, program)
if(newLeft!=null)
modifications += IAstModification.ReplaceNode(expr.left, newLeft, expr)
if(newRight!=null)
modifications += IAstModification.ReplaceNode(expr.right, newRight, expr)
if(modifications.isNotEmpty())
return modifications
}
if(leftDt!=rightDt) { if(leftDt!=rightDt) {
// convert bool type to byte if needed
if(leftDt istype DataType.BOOL && rightDt.isBytes && !rightDt.istype(DataType.BOOL)) {
if(rightCv==null || (rightCv.number!=1.0 && rightCv.number!=0.0))
return listOf(IAstModification.ReplaceNode(expr.left,
TypecastExpression(expr.left, rightDt.getOr(DataType.UNDEFINED),true, expr.left.position), expr))
} else if(leftDt.isBytes && !leftDt.istype(DataType.BOOL) && rightDt istype DataType.BOOL) {
if(leftCv==null || (leftCv.number!=1.0 && leftCv.number!=0.0))
return listOf(IAstModification.ReplaceNode(expr.right,
TypecastExpression(expr.right, leftDt.getOr(DataType.UNDEFINED),true, expr.right.position), expr))
}
// convert a negative operand for bitwise operator to the 2's complement positive number instead // convert a negative operand for bitwise operator to the 2's complement positive number instead
if(expr.operator in BitwiseOperators && leftDt.isInteger && rightDt.isInteger) { if(expr.operator in BitwiseOperators && leftDt.isInteger && rightDt.isInteger) {
if(leftCv!=null && leftCv.number<0) { if(leftCv!=null && leftCv.number<0) {
@ -169,13 +145,18 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
// determine common datatype and add typecast as required to make left and right equal types // determine common datatype and add typecast as required to make left and right equal types
val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt.getOr(DataType.UNDEFINED), rightDt.getOr(DataType.UNDEFINED), expr.left, expr.right) val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt.getOr(DataType.UNDEFINED), rightDt.getOr(DataType.UNDEFINED), expr.left, expr.right)
if(toFix!=null) { if(toFix!=null) {
val modifications = mutableListOf<IAstModification>() if(commonDt==DataType.BOOL) {
when { // don't automatically cast to bool
toFix===expr.left -> addTypecastOrCastedValueModification(modifications, expr.left, commonDt, expr) errors.err("left and right operands aren't the same type", expr.position)
toFix===expr.right -> addTypecastOrCastedValueModification(modifications, expr.right, commonDt, expr) } else {
else -> throw FatalAstException("confused binary expression side") val modifications = mutableListOf<IAstModification>()
when {
toFix===expr.left -> addTypecastOrCastedValueModification(modifications, expr.left, commonDt, expr)
toFix===expr.right -> addTypecastOrCastedValueModification(modifications, expr.right, commonDt, expr)
else -> throw FatalAstException("confused binary expression side")
}
return modifications
} }
return modifications
} }
} }
} }
@ -196,10 +177,6 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
if(valuetype in IterableDatatypes && targettype==DataType.UWORD) if(valuetype in IterableDatatypes && targettype==DataType.UWORD)
// special case, don't typecast STR/arrays to UWORD, we support those assignments "directly" // special case, don't typecast STR/arrays to UWORD, we support those assignments "directly"
return noModifications return noModifications
if((assignment.value as? BinaryExpression)?.operator in ComparisonOperators && targettype in IntegerDatatypes) {
// special case, treat a boolean comparison result as the same type as the target value to avoid needless casts later
return noModifications
}
val modifications = mutableListOf<IAstModification>() val modifications = mutableListOf<IAstModification>()
addTypecastOrCastedValueModification(modifications, assignment.value, targettype, assignment) addTypecastOrCastedValueModification(modifications, assignment.value, targettype, assignment)
return modifications return modifications
@ -244,7 +221,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
} }
private fun afterFunctionCallArgs(call: IFunctionCall): Iterable<IAstModification> { private fun afterFunctionCallArgs(call: IFunctionCall): Iterable<IAstModification> {
// see if a typecast is needed to convert the arguments into the required parameter's type // see if a typecast is needed to convert the arguments into the required parameter type
val modifications = mutableListOf<IAstModification>() val modifications = mutableListOf<IAstModification>()
val sub = call.target.targetStatement(program) val sub = call.target.targetStatement(program)
val params = when(sub) { val params = when(sub) {
@ -398,6 +375,8 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
val sourceDt = expressionToCast.inferType(program).getOr(DataType.UNDEFINED) val sourceDt = expressionToCast.inferType(program).getOr(DataType.UNDEFINED)
if(sourceDt == requiredType) if(sourceDt == requiredType)
return return
if(requiredType==DataType.BOOL)
return
if(expressionToCast is NumericLiteral && expressionToCast.type!=DataType.FLOAT) { // refuse to automatically truncate floats if(expressionToCast is NumericLiteral && expressionToCast.type!=DataType.FLOAT) { // refuse to automatically truncate floats
val castedValue = expressionToCast.cast(requiredType, true) val castedValue = expressionToCast.cast(requiredType, true)
if (castedValue.isValid) { if (castedValue.isValid) {

View File

@ -105,12 +105,7 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe
if(mismatch>=0) { if(mismatch>=0) {
val actual = argtypes[mismatch] val actual = argtypes[mismatch]
val expected = consideredParamTypes[mismatch] val expected = consideredParamTypes[mismatch]
return if(actual==DataType.BOOL && expected in ByteDatatypes) return Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
null // a bool is just 1 or 0.
else if(expected==DataType.BOOL && actual==DataType.UBYTE && call.args[mismatch].constValue(program)?.number in setOf(0.0, 1.0))
null // specifying a 1 or 0 as a BOOL is okay
else
Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
} }
if(target.isAsmSubroutine) { if(target.isAsmSubroutine) {
if(target.asmReturnvaluesRegisters.size>1) { if(target.asmReturnvaluesRegisters.size>1) {

View File

@ -174,7 +174,7 @@ class IRFileReader {
if('.' !in name) if('.' !in name)
throw IRParseException("unscoped varname: $name") throw IRParseException("unscoped varname: $name")
val arraysize = if(arrayspec.isNotBlank()) arrayspec.substring(1, arrayspec.length-1).toInt() else null val arraysize = if(arrayspec.isNotBlank()) arrayspec.substring(1, arrayspec.length-1).toInt() else null
val dt: DataType = parseDatatype(type, arraysize!=null) val dt = parseDatatype(type, arraysize!=null)
val zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish) val zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish)
val dummyNode = PtVariable(name, dt, zp, null, null, Position.DUMMY) val dummyNode = PtVariable(name, dt, zp, null, null, Position.DUMMY)
val newVar = StStaticVariable(name, dt, null, null, null, arraysize, zp, dummyNode) val newVar = StStaticVariable(name, dt, null, null, null, arraysize, zp, dummyNode)
@ -210,7 +210,8 @@ class IRFileReader {
var initNumeric: Double? = null var initNumeric: Double? = null
var initArray: StArray? = null var initArray: StArray? = null
when(dt) { when(dt) {
in NumericDatatypes -> initNumeric = parseIRValue(value).toDouble() DataType.BOOL -> TODO("parse boolean $value")
in NumericDatatypes -> initNumeric = parseIRValue(value)
in ArrayDatatypes -> { in ArrayDatatypes -> {
initArray = value.split(',').map { initArray = value.split(',').map {
if (it.startsWith('@')) if (it.startsWith('@'))
@ -496,24 +497,24 @@ class IRFileReader {
private fun parseDatatype(type: String, isArray: Boolean): DataType { private fun parseDatatype(type: String, isArray: Boolean): DataType {
if(isArray) { if(isArray) {
return when(type) { return when(type) {
// note: there are no BOOLEANS anymore in the IR. Only UBYTE.
"byte" -> DataType.ARRAY_B "byte" -> DataType.ARRAY_B
"ubyte", "str" -> DataType.ARRAY_UB "ubyte", "str" -> DataType.ARRAY_UB
"word" -> DataType.ARRAY_W "word" -> DataType.ARRAY_W
"uword" -> DataType.ARRAY_UW "uword" -> DataType.ARRAY_UW
"float" -> DataType.ARRAY_F "float" -> DataType.ARRAY_F
"bool" -> DataType.ARRAY_B
"uword_split" -> DataType.ARRAY_UW_SPLIT "uword_split" -> DataType.ARRAY_UW_SPLIT
"word_split" -> DataType.ARRAY_W_SPLIT "word_split" -> DataType.ARRAY_W_SPLIT
else -> throw IRParseException("invalid dt") else -> throw IRParseException("invalid dt")
} }
} else { } else {
return when(type) { return when(type) {
// note: there are no BOOLEANS anymore in the IR. Only UBYTE.
"byte" -> DataType.BYTE "byte" -> DataType.BYTE
"ubyte" -> DataType.UBYTE "ubyte" -> DataType.UBYTE
"word" -> DataType.WORD "word" -> DataType.WORD
"uword" -> DataType.UWORD "uword" -> DataType.UWORD
"float" -> DataType.FLOAT "float" -> DataType.FLOAT
"bool" -> DataType.BOOL
// note: 'str' should not occur anymore in IR. Should be 'uword' // note: 'str' should not occur anymore in IR. Should be 'uword'
else -> throw IRParseException("invalid dt") else -> throw IRParseException("invalid dt")
} }

View File

@ -100,7 +100,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
is IRSubroutine -> { is IRSubroutine -> {
xml.writeStartElement("SUB") xml.writeStartElement("SUB")
xml.writeAttribute("NAME", child.label) xml.writeAttribute("NAME", child.label)
xml.writeAttribute("RETURNTYPE", child.returnType?.toString()?.lowercase() ?: "") xml.writeAttribute("RETURNTYPE", child.returnType?.typeString(null)?.lowercase() ?: "")
xml.writeAttribute("POS", child.position.toString()) xml.writeAttribute("POS", child.position.toString())
xml.writeCharacters("\n") xml.writeCharacters("\n")
xml.writeStartElement("PARAMS") xml.writeStartElement("PARAMS")
@ -247,8 +247,9 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_msb=$msbValue zp=${variable.zpwish}\n") xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_msb=$msbValue zp=${variable.zpwish}\n")
} else { } else {
val value: String = when(variable.dt) { val value: String = when(variable.dt) {
DataType.BOOL -> variable.onetimeInitializationNumericValue?.toInt()?.toString() ?: ""
DataType.FLOAT -> (variable.onetimeInitializationNumericValue ?: "").toString() DataType.FLOAT -> (variable.onetimeInitializationNumericValue ?: "").toString()
in NumericDatatypes -> (variable.onetimeInitializationNumericValue?.toInt()?.toHex() ?: "").toString() in NumericDatatypes -> variable.onetimeInitializationNumericValue?.toInt()?.toHex() ?: ""
DataType.STR -> { DataType.STR -> {
val encoded = irProgram.encoding.encodeString(variable.onetimeInitializationStringValue!!.first, variable.onetimeInitializationStringValue.second) + listOf(0u) val encoded = irProgram.encoding.encodeString(variable.onetimeInitializationStringValue!!.first, variable.onetimeInitializationStringValue.second) + listOf(0u)
encoded.joinToString(",") { it.toInt().toString() } encoded.joinToString(",") { it.toInt().toString() }

View File

@ -409,8 +409,8 @@ class IRSubroutine(
require(!label.startsWith("main.main.")) {"subroutine name invalid main prefix: $label"} require(!label.startsWith("main.main.")) {"subroutine name invalid main prefix: $label"}
// params and return value should not be str // params and return value should not be str
require(parameters.all{ it.dt in NumericDatatypes }) {"non-numeric parameter"} require(parameters.all{ it.dt in NumericDatatypes || it.dt==DataType.BOOL }) {"non-numeric/non-bool parameter"}
require(returnType==null || returnType in NumericDatatypes) {"non-numeric returntype $returnType"} require(returnType==null || returnType in NumericDatatypes || returnType==DataType.BOOL) {"non-numeric/non-bool returntype $returnType"}
} }
operator fun plusAssign(chunk: IRCodeChunkBase) { operator fun plusAssign(chunk: IRCodeChunkBase) {

View File

@ -80,7 +80,13 @@ class IRSymbolTable {
return newArray return newArray
} }
scopedName = variable.scopedName scopedName = variable.scopedName
varToadd = IRStStaticVariable(scopedName, variable.dt, val dt = when(variable.dt) {
DataType.BOOL -> DataType.UBYTE
DataType.ARRAY_BOOL -> DataType.ARRAY_UB
else -> variable.dt
}
varToadd = IRStStaticVariable(scopedName,
dt,
variable.onetimeInitializationNumericValue, variable.onetimeInitializationNumericValue,
variable.onetimeInitializationStringValue, variable.onetimeInitializationStringValue,
fixupAddressOfInArray(variable.onetimeInitializationArrayValue), fixupAddressOfInArray(variable.onetimeInitializationArrayValue),
@ -166,6 +172,10 @@ class IRStMemVar(name: String,
} }
} }
init {
require(dt!=DataType.BOOL && dt!=DataType.ARRAY_BOOL)
}
val typeString: String = dt.typeString(length) val typeString: String = dt.typeString(length)
} }
@ -205,6 +215,10 @@ class IRStStaticVariable(name: String,
} }
} }
init {
require(dt!=DataType.BOOL && dt!=DataType.ARRAY_BOOL)
}
val uninitialized = onetimeInitializationArrayValue==null && onetimeInitializationStringValue==null && onetimeInitializationNumericValue==null val uninitialized = onetimeInitializationArrayValue==null && onetimeInitializationStringValue==null && onetimeInitializationNumericValue==null
val typeString: String = dt.typeString(length) val typeString: String = dt.typeString(length)
@ -213,9 +227,17 @@ class IRStStaticVariable(name: String,
class IRStArrayElement(val number: Double?, val addressOfSymbol: String?) { class IRStArrayElement(val number: Double?, val addressOfSymbol: String?) {
companion object { companion object {
fun from(elt: StArrayElement): IRStArrayElement { fun from(elt: StArrayElement): IRStArrayElement {
return IRStArrayElement(elt.number, elt.addressOfSymbol) if(elt.boolean!=null)
return IRStArrayElement(if(elt.boolean==true) 1.0 else 0.0, elt.addressOfSymbol)
else
return IRStArrayElement(elt.number, elt.addressOfSymbol)
} }
} }
init {
// TODO TEMPORARY
require(number!=null || addressOfSymbol!=null)
}
} }
typealias IRStArray = List<IRStArrayElement> typealias IRStArray = List<IRStArrayElement>

View File

@ -9,15 +9,15 @@ import prog8.code.right
fun DataType.typeString(length: Int?): String { fun DataType.typeString(length: Int?): String {
val lengthStr = if(length==0) "" else length.toString() val lengthStr = if(length==0) "" else length.toString()
return when (this) { return when (this) {
DataType.BOOL -> "bool" DataType.BOOL -> "ubyte" // in IR , a boolean is represented by an ubyte.
DataType.UBYTE -> "ubyte" DataType.UBYTE -> "ubyte"
DataType.BYTE -> "byte" DataType.BYTE -> "byte"
DataType.UWORD -> "uword" DataType.UWORD -> "uword"
DataType.WORD -> "word" DataType.WORD -> "word"
DataType.LONG -> "long" DataType.LONG -> "long"
DataType.FLOAT -> "float" DataType.FLOAT -> "float"
DataType.STR -> "ubyte[$lengthStr]" // here string doesn't exist as a seperate datatype anymore DataType.STR -> "ubyte[$lengthStr]" // here string doesn't exist as a seperate datatype anymore
DataType.ARRAY_BOOL -> "bool[$lengthStr]" DataType.ARRAY_BOOL -> "ubyte[$lengthStr]" // in IR , a boolean is represented by an ubyte.
DataType.ARRAY_UB -> "ubyte[$lengthStr]" DataType.ARRAY_UB -> "ubyte[$lengthStr]"
DataType.ARRAY_B -> "byte[$lengthStr]" DataType.ARRAY_B -> "byte[$lengthStr]"
DataType.ARRAY_UW -> "uword[$lengthStr]" DataType.ARRAY_UW -> "uword[$lengthStr]"