mirror of
https://github.com/irmen/prog8.git
synced 2024-11-25 19:31:36 +00:00
compiler stuff
This commit is contained in:
parent
6b87cbb703
commit
41afeccd51
@ -17,7 +17,7 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, D
|
||||
emptyList()
|
||||
else {
|
||||
val register = when (returntype!!) {
|
||||
in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
||||
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
@ -31,7 +31,7 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, D
|
||||
|
||||
internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
|
||||
return when(returntype) {
|
||||
in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
||||
null -> null
|
||||
|
@ -91,7 +91,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
if(errors.noErrors()) {
|
||||
val sortedList = varsDontCare.sortedByDescending { it.scopedName }
|
||||
for (variable in sortedList) {
|
||||
if(variable.dt in IntegerDatatypes) {
|
||||
if(variable.dt in IntegerDatatypesWithBoolean) {
|
||||
if(zeropage.free.isEmpty()) {
|
||||
break
|
||||
} else {
|
||||
|
@ -70,6 +70,8 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
println("BREAKPOINTINSTR=$breakpointCpuInstruction")
|
||||
|
||||
val outputPath = pathFrom(outputDir)
|
||||
if(!outputPath.toFile().isDirectory) {
|
||||
System.err.println("Output path doesn't exist")
|
||||
|
@ -98,8 +98,8 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
importedFiles = imported
|
||||
|
||||
processAst(program, args.errors, compilationOptions)
|
||||
// println("*********** COMPILER AST RIGHT BEFORE OPTIMIZING *************")
|
||||
// printProgram(program)
|
||||
// println("*********** COMPILER AST RIGHT BEFORE OPTIMIZING *************")
|
||||
// printProgram(program)
|
||||
|
||||
if (compilationOptions.optimize) {
|
||||
optimizeAst(
|
||||
|
@ -123,12 +123,6 @@ internal class AstChecker(private val program: Program,
|
||||
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.
|
||||
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) {
|
||||
// you can return a string or array when an uword (pointer) is returned
|
||||
} 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) {
|
||||
val dt = ifElse.condition.inferType(program)
|
||||
if(!dt.isInteger && !dt.istype(DataType.BOOL)) {
|
||||
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)
|
||||
}
|
||||
if(!ifElse.condition.inferType(program).isBool)
|
||||
errors.err("condition should be a boolean", ifElse.condition.position)
|
||||
|
||||
super.visit(ifElse)
|
||||
}
|
||||
|
||||
@ -480,22 +471,16 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(untilLoop: UntilLoop) {
|
||||
val dt = untilLoop.condition.inferType(program)
|
||||
if(!dt.isInteger && !dt.istype(DataType.BOOL)) {
|
||||
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)
|
||||
}
|
||||
if(!untilLoop.condition.inferType(program).isBool)
|
||||
errors.err("condition should be a boolean", untilLoop.condition.position)
|
||||
|
||||
super.visit(untilLoop)
|
||||
}
|
||||
|
||||
override fun visit(whileLoop: WhileLoop) {
|
||||
val dt = whileLoop.condition.inferType(program)
|
||||
if(!dt.isInteger && !dt.istype(DataType.BOOL)) {
|
||||
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)
|
||||
}
|
||||
if(!whileLoop.condition.inferType(program).isBool)
|
||||
errors.err("condition should be a boolean", whileLoop.condition.position)
|
||||
|
||||
super.visit(whileLoop)
|
||||
}
|
||||
|
||||
@ -539,20 +524,10 @@ internal class AstChecker(private val program: Program,
|
||||
if(numvalue!=null && targetDt.isKnown)
|
||||
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)
|
||||
}
|
||||
|
||||
override fun visit(assignTarget: AssignTarget) {
|
||||
if(assignTarget.inferType(program).istype(DataType.LONG))
|
||||
errors.err("integer overflow", assignTarget.position)
|
||||
|
||||
super.visit(assignTarget)
|
||||
|
||||
val memAddr = assignTarget.memoryAddress?.addressExpression?.constValue(program)?.number?.toInt()
|
||||
@ -586,16 +561,13 @@ internal class AstChecker(private val program: Program,
|
||||
if (assignment is Assignment) {
|
||||
val targetDatatype = assignTarget.inferType(program)
|
||||
if (targetDatatype.isKnown) {
|
||||
val constVal = assignment.value.constValue(program)
|
||||
if(constVal==null) {
|
||||
val sourceDatatype = assignment.value.inferType(program)
|
||||
if (sourceDatatype.isUnknown) {
|
||||
if (assignment.value !is FunctionCallExpression)
|
||||
errors.err("invalid assignment value, maybe forgot '&' (address-of)", assignment.value.position)
|
||||
} else {
|
||||
checkAssignmentCompatible(targetDatatype.getOr(DataType.UNDEFINED),
|
||||
sourceDatatype.getOr(DataType.UNDEFINED), assignment.value)
|
||||
}
|
||||
val sourceDatatype = assignment.value.inferType(program)
|
||||
if (sourceDatatype.isUnknown) {
|
||||
if (assignment.value !is BinaryExpression && assignment.value !is PrefixExpression && assignment.value !is ContainmentCheck)
|
||||
errors.err("invalid assignment value, maybe forgot '&' (address-of)", assignment.value.position)
|
||||
} else {
|
||||
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)
|
||||
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)
|
||||
|
||||
@ -628,8 +604,8 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
// CONST can only occur on simple types (byte, word, float)
|
||||
if(decl.type== VarDeclType.CONST) {
|
||||
if (decl.datatype !in NumericDatatypes)
|
||||
err("const can only be used on numeric types (byte, word, float)")
|
||||
if (decl.datatype !in NumericDatatypesWithBoolean)
|
||||
err("const can only be used on numeric types or booleans")
|
||||
}
|
||||
|
||||
// FLOATS enabled?
|
||||
@ -717,11 +693,11 @@ internal class AstChecker(private val program: Program,
|
||||
val eltDt = ArrayToElementTypes.getValue(decl.datatype)
|
||||
if(iDt isnot eltDt) {
|
||||
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 {
|
||||
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 == "~") {
|
||||
if(dt !in IntegerDatatypes)
|
||||
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)
|
||||
}
|
||||
else if(expr.operator == "not") {
|
||||
if(dt!=DataType.BOOL)
|
||||
errors.err("logical not is for booleans", expr.position)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
"&", "|", "^" -> {
|
||||
// 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")
|
||||
"<<", ">>" -> {
|
||||
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
|
||||
} else if((expr.operator == "<<" || expr.operator == ">>") && (leftDt in WordDatatypes && rightDt in ByteDatatypes)) {
|
||||
// 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)) {
|
||||
// exception allowed: comparing uword (pointer) with string
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
@ -1289,14 +1278,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(target is Label && args.isNotEmpty())
|
||||
errors.err("cannot use arguments when calling a label", position)
|
||||
|
||||
if(target is BuiltinFunctionPlaceholder) {
|
||||
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 is Subroutine) {
|
||||
if(target.isAsmSubroutine) {
|
||||
for (arg in args.zip(target.parameters)) {
|
||||
val argIDt = arg.first.inferType(program)
|
||||
@ -1375,7 +1357,10 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
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)
|
||||
val tally = mutableSetOf<Int>()
|
||||
for((choices, choiceNode) in whenStmt.choiceValues(program)) {
|
||||
@ -1406,7 +1391,7 @@ internal class AstChecker(private val program: Program,
|
||||
for((constvalue, pos) in constvalues) {
|
||||
when {
|
||||
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 -> {
|
||||
if(conditionType.isKnown) {
|
||||
if(conditionType.istype(DataType.BOOL)) {
|
||||
@ -1492,7 +1477,8 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
private fun checkLongType(expression: Expression) {
|
||||
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")
|
||||
}
|
||||
DataType.BOOL -> {
|
||||
return true
|
||||
if(value.type!=DataType.BOOL)
|
||||
err("type of value ${value.type} doesn't match target $targetDt")
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
val eltDt = ArrayToElementTypes.getValue(targetDt)
|
||||
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
|
||||
}
|
||||
@ -1723,11 +1710,11 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
val result = when(targetDatatype) {
|
||||
DataType.BOOL -> sourceDatatype in NumericDatatypes
|
||||
DataType.BYTE -> sourceDatatype == DataType.BYTE || sourceDatatype == DataType.BOOL
|
||||
DataType.UBYTE -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.BOOL
|
||||
DataType.WORD -> sourceDatatype in setOf(DataType.BYTE, DataType.UBYTE, DataType.WORD, DataType.BOOL)
|
||||
DataType.UWORD -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.UWORD || sourceDatatype == DataType.BOOL
|
||||
DataType.BOOL -> sourceDatatype==DataType.BOOL
|
||||
DataType.BYTE -> sourceDatatype == DataType.BYTE
|
||||
DataType.UBYTE -> sourceDatatype == DataType.UBYTE
|
||||
DataType.WORD -> sourceDatatype in setOf(DataType.BYTE, DataType.UBYTE, DataType.WORD)
|
||||
DataType.UWORD -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.UWORD
|
||||
DataType.FLOAT -> sourceDatatype in NumericDatatypes
|
||||
DataType.STR -> sourceDatatype == DataType.STR
|
||||
else -> {
|
||||
@ -1747,12 +1734,14 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
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)
|
||||
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 {
|
||||
if(targetDatatype!=DataType.UWORD && sourceDatatype !in PassByReferenceDatatypes) {
|
||||
// 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)
|
||||
}
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
|
||||
return false
|
||||
|
@ -22,10 +22,6 @@ 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)
|
||||
fixer.visit(this)
|
||||
while (errors.noErrors() && fixer.applyModifications() > 0) {
|
||||
|
@ -136,7 +136,7 @@ class AstPreprocessor(val program: Program,
|
||||
}
|
||||
} else {
|
||||
// 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 assign = Assignment(target, decl.value!!, AssignmentOrigin.VARINIT, decl.position)
|
||||
replacements.add(IAstModification.ReplaceNode(decl, assign, scope))
|
||||
|
@ -26,7 +26,7 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
|
||||
}
|
||||
|
||||
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")
|
||||
|
||||
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> {
|
||||
val binExpr = ifElse.condition as? BinaryExpression
|
||||
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!=null) {
|
||||
if(binExpr.operator !in ComparisonOperators) {
|
||||
val constRight = binExpr.right.constValue(program)
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
// 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 &&
|
||||
(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
|
||||
}
|
||||
|
@ -15,14 +15,6 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
||||
private val errors: IErrorReporter
|
||||
) : 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> {
|
||||
// see if we can remove redundant typecasts (outside of expressions)
|
||||
// such as casting byte<->ubyte, word<->uword or even redundant casts (sourcetype = target type).
|
||||
|
@ -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)
|
||||
}
|
@ -124,6 +124,11 @@ if not CONDITION
|
||||
}
|
||||
|
||||
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 false -> discard
|
||||
|
@ -558,8 +558,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
||||
|
||||
private fun transform(srcExpr: BinaryExpression): PtBinaryExpression {
|
||||
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, actualType, srcExpr.position)
|
||||
val expr = PtBinaryExpression(srcExpr.operator, type, srcExpr.position)
|
||||
expr.add(transformExpression(srcExpr.left))
|
||||
expr.add(transformExpression(srcExpr.right))
|
||||
return expr
|
||||
@ -578,27 +577,27 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
||||
|
||||
fun desugar(range: RangeExpression): PtExpression {
|
||||
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 x2 = transformExpression(srcCheck.element)
|
||||
val eltDt = srcCheck.element.inferType(program)
|
||||
if(eltDt.isInteger) {
|
||||
val low = PtBinaryExpression("<=", DataType.UBYTE, srcCheck.position)
|
||||
val low = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
|
||||
low.add(transformExpression(range.from))
|
||||
low.add(x1)
|
||||
expr.add(low)
|
||||
val high = PtBinaryExpression("<=", DataType.UBYTE, srcCheck.position)
|
||||
val high = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
|
||||
high.add(x2)
|
||||
high.add(transformExpression(range.to))
|
||||
expr.add(high)
|
||||
} else {
|
||||
val low = PtBinaryExpression("<=", DataType.UBYTE, srcCheck.position)
|
||||
val low = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
|
||||
val lowFloat = PtTypeCast(DataType.FLOAT, range.from.position)
|
||||
lowFloat.add(transformExpression(range.from))
|
||||
low.add(lowFloat)
|
||||
low.add(x1)
|
||||
expr.add(low)
|
||||
val high = PtBinaryExpression("<=", DataType.UBYTE, srcCheck.position)
|
||||
val high = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
|
||||
high.add(x2)
|
||||
val highFLoat = PtTypeCast(DataType.FLOAT, range.to.position)
|
||||
highFLoat.add(transformExpression(range.to))
|
||||
@ -655,8 +654,12 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
||||
return mem
|
||||
}
|
||||
|
||||
private fun transform(number: NumericLiteral): PtNumber =
|
||||
PtNumber(number.type, number.number, number.position)
|
||||
private fun transform(number: NumericLiteral): PtExpression {
|
||||
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 {
|
||||
val type = srcPrefix.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||
|
@ -76,8 +76,8 @@ internal class LiteralsToAutoVars(private val program: Program,
|
||||
if(decl.names.size>1) {
|
||||
// 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)
|
||||
if(decl.datatype !in NumericDatatypes)
|
||||
errors.err("can only multi declare numeric variables", decl.position)
|
||||
if(decl.datatype !in NumericDatatypesWithBoolean)
|
||||
errors.err("can only multi declare numeric and boolean variables", decl.position)
|
||||
if(errors.noErrors()) {
|
||||
// desugar into individual vardecl per name.
|
||||
return decl.desugarMultiDecl().map {
|
||||
|
@ -6,13 +6,9 @@ import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.expressions.PrefixExpression
|
||||
import prog8.ast.expressions.invertCondition
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.IntegerDatatypes
|
||||
import prog8.code.core.*
|
||||
|
||||
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
|
||||
if (left != null) {
|
||||
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 == "==") {
|
||||
// (x==something)==0 --> x!=something
|
||||
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,
|
||||
// when the code has one outer 'not' instead of two inner ones.
|
||||
if(expr.operator=="or" || expr.operator=="and") {
|
||||
|
||||
// boolean case
|
||||
val newOper = if(expr.operator=="or") "and" else "or"
|
||||
val leftP = expr.left 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)
|
||||
return listOf(IAstModification.ReplaceNode(expr, notExpr, parent))
|
||||
}
|
||||
val leftB = expr.left as? BinaryExpression
|
||||
val rightB = expr.right as? BinaryExpression
|
||||
if(leftB!=null && leftB.operator=="==" && (leftB.right as? NumericLiteral)?.number==0.0
|
||||
&& rightB!=null && rightB.operator=="==" && (rightB.right as? NumericLiteral)?.number==0.0) {
|
||||
// a==0 or b==0 --> (a!=0 and b!=0)==0
|
||||
// a==0 and b==0 --> (a!=0 or b!=0)==0
|
||||
leftB.operator = "!="
|
||||
rightB.operator = "!="
|
||||
val inner = BinaryExpression(leftB, newOper, rightB, expr.position)
|
||||
val notExpr = BinaryExpression(inner, "==", NumericLiteral.optimalInteger(0, expr.position), expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, notExpr, parent))
|
||||
|
||||
// integer case (only if both are the same type)
|
||||
val leftC = expr.left as? BinaryExpression
|
||||
val rightC = expr.right as? BinaryExpression
|
||||
if(leftC!=null && rightC!=null && leftC.operator=="==" && rightC.operator=="==") {
|
||||
if (leftC.right.constValue(program)?.number == 0.0 && rightC.right.constValue(program)?.number == 0.0) {
|
||||
val leftDt = leftC.left.inferType(program).getOr(DataType.UNDEFINED)
|
||||
val rightDt = rightC.left.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(leftDt==rightDt && leftDt in IntegerDatatypes) {
|
||||
if (rightC.left.isSimple) {
|
||||
// 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> {
|
||||
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(parent is BinaryExpression && parent.right.constValue(program)?.number==0.0) {
|
||||
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))
|
||||
if (parent is BinaryExpression) {
|
||||
if (parent.right.constValue(program)?.number == 0.0) {
|
||||
if(parent.right.inferType(program).isBool) {
|
||||
if(parent.operator=="==") {
|
||||
// (NOT X)==false --> X==true --> X
|
||||
return listOf(IAstModification.ReplaceNode(parent, expr.expression, parent.parent))
|
||||
} else if(parent.operator=="!=") {
|
||||
// (NOT X)!=false --> X!=true -> not X
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ internal class StatementReorderer(
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
if (decl.type == VarDeclType.VAR) {
|
||||
if (decl.datatype in NumericDatatypes) {
|
||||
if (decl.datatype in NumericDatatypes || decl.datatype==DataType.BOOL) {
|
||||
if(decl !in declsProcessedWithInitAssignment) {
|
||||
declsProcessedWithInitAssignment.add(decl)
|
||||
if (decl.value == null) {
|
||||
|
@ -24,12 +24,11 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
val valueDt = declValue.inferType(program)
|
||||
if(valueDt isnot decl.datatype) {
|
||||
|
||||
// don't add a typecast on an array initializer value, unless booleans
|
||||
if(valueDt.isInteger && decl.isArray) {
|
||||
if(decl.datatype == DataType.ARRAY_BOOL) {
|
||||
val integer = declValue.constValue(program)?.number
|
||||
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
|
||||
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) {
|
||||
// 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
|
||||
if(expr.operator in BitwiseOperators && leftDt.isInteger && rightDt.isInteger) {
|
||||
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
|
||||
val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt.getOr(DataType.UNDEFINED), rightDt.getOr(DataType.UNDEFINED), expr.left, expr.right)
|
||||
if(toFix!=null) {
|
||||
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")
|
||||
if(commonDt==DataType.BOOL) {
|
||||
// don't automatically cast to bool
|
||||
errors.err("left and right operands aren't the same type", expr.position)
|
||||
} else {
|
||||
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)
|
||||
// special case, don't typecast STR/arrays to UWORD, we support those assignments "directly"
|
||||
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>()
|
||||
addTypecastOrCastedValueModification(modifications, assignment.value, targettype, assignment)
|
||||
return modifications
|
||||
@ -244,7 +221,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
|
||||
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 sub = call.target.targetStatement(program)
|
||||
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)
|
||||
if(sourceDt == requiredType)
|
||||
return
|
||||
if(requiredType==DataType.BOOL)
|
||||
return
|
||||
if(expressionToCast is NumericLiteral && expressionToCast.type!=DataType.FLOAT) { // refuse to automatically truncate floats
|
||||
val castedValue = expressionToCast.cast(requiredType, true)
|
||||
if (castedValue.isValid) {
|
||||
|
@ -105,12 +105,7 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe
|
||||
if(mismatch>=0) {
|
||||
val actual = argtypes[mismatch]
|
||||
val expected = consideredParamTypes[mismatch]
|
||||
return if(actual==DataType.BOOL && expected in ByteDatatypes)
|
||||
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)
|
||||
return Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
|
||||
}
|
||||
if(target.isAsmSubroutine) {
|
||||
if(target.asmReturnvaluesRegisters.size>1) {
|
||||
|
@ -174,7 +174,7 @@ class IRFileReader {
|
||||
if('.' !in name)
|
||||
throw IRParseException("unscoped varname: $name")
|
||||
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 dummyNode = PtVariable(name, dt, zp, null, null, Position.DUMMY)
|
||||
val newVar = StStaticVariable(name, dt, null, null, null, arraysize, zp, dummyNode)
|
||||
@ -210,7 +210,8 @@ class IRFileReader {
|
||||
var initNumeric: Double? = null
|
||||
var initArray: StArray? = null
|
||||
when(dt) {
|
||||
in NumericDatatypes -> initNumeric = parseIRValue(value).toDouble()
|
||||
DataType.BOOL -> TODO("parse boolean $value")
|
||||
in NumericDatatypes -> initNumeric = parseIRValue(value)
|
||||
in ArrayDatatypes -> {
|
||||
initArray = value.split(',').map {
|
||||
if (it.startsWith('@'))
|
||||
@ -496,24 +497,24 @@ class IRFileReader {
|
||||
private fun parseDatatype(type: String, isArray: Boolean): DataType {
|
||||
if(isArray) {
|
||||
return when(type) {
|
||||
// note: there are no BOOLEANS anymore in the IR. Only UBYTE.
|
||||
"byte" -> DataType.ARRAY_B
|
||||
"ubyte", "str" -> DataType.ARRAY_UB
|
||||
"word" -> DataType.ARRAY_W
|
||||
"uword" -> DataType.ARRAY_UW
|
||||
"float" -> DataType.ARRAY_F
|
||||
"bool" -> DataType.ARRAY_B
|
||||
"uword_split" -> DataType.ARRAY_UW_SPLIT
|
||||
"word_split" -> DataType.ARRAY_W_SPLIT
|
||||
else -> throw IRParseException("invalid dt")
|
||||
}
|
||||
} else {
|
||||
return when(type) {
|
||||
// note: there are no BOOLEANS anymore in the IR. Only UBYTE.
|
||||
"byte" -> DataType.BYTE
|
||||
"ubyte" -> DataType.UBYTE
|
||||
"word" -> DataType.WORD
|
||||
"uword" -> DataType.UWORD
|
||||
"float" -> DataType.FLOAT
|
||||
"bool" -> DataType.BOOL
|
||||
// note: 'str' should not occur anymore in IR. Should be 'uword'
|
||||
else -> throw IRParseException("invalid dt")
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
||||
is IRSubroutine -> {
|
||||
xml.writeStartElement("SUB")
|
||||
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.writeCharacters("\n")
|
||||
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")
|
||||
} else {
|
||||
val value: String = when(variable.dt) {
|
||||
DataType.BOOL -> variable.onetimeInitializationNumericValue?.toInt()?.toString() ?: ""
|
||||
DataType.FLOAT -> (variable.onetimeInitializationNumericValue ?: "").toString()
|
||||
in NumericDatatypes -> (variable.onetimeInitializationNumericValue?.toInt()?.toHex() ?: "").toString()
|
||||
in NumericDatatypes -> variable.onetimeInitializationNumericValue?.toInt()?.toHex() ?: ""
|
||||
DataType.STR -> {
|
||||
val encoded = irProgram.encoding.encodeString(variable.onetimeInitializationStringValue!!.first, variable.onetimeInitializationStringValue.second) + listOf(0u)
|
||||
encoded.joinToString(",") { it.toInt().toString() }
|
||||
|
@ -409,8 +409,8 @@ class IRSubroutine(
|
||||
require(!label.startsWith("main.main.")) {"subroutine name invalid main prefix: $label"}
|
||||
|
||||
// params and return value should not be str
|
||||
require(parameters.all{ it.dt in NumericDatatypes }) {"non-numeric parameter"}
|
||||
require(returnType==null || returnType in NumericDatatypes) {"non-numeric returntype $returnType"}
|
||||
require(parameters.all{ it.dt in NumericDatatypes || it.dt==DataType.BOOL }) {"non-numeric/non-bool parameter"}
|
||||
require(returnType==null || returnType in NumericDatatypes || returnType==DataType.BOOL) {"non-numeric/non-bool returntype $returnType"}
|
||||
}
|
||||
|
||||
operator fun plusAssign(chunk: IRCodeChunkBase) {
|
||||
|
@ -80,7 +80,13 @@ class IRSymbolTable {
|
||||
return newArray
|
||||
}
|
||||
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.onetimeInitializationStringValue,
|
||||
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)
|
||||
}
|
||||
|
||||
@ -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 typeString: String = dt.typeString(length)
|
||||
@ -213,9 +227,17 @@ class IRStStaticVariable(name: String,
|
||||
class IRStArrayElement(val number: Double?, val addressOfSymbol: String?) {
|
||||
companion object {
|
||||
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>
|
||||
|
@ -9,15 +9,15 @@ import prog8.code.right
|
||||
fun DataType.typeString(length: Int?): String {
|
||||
val lengthStr = if(length==0) "" else length.toString()
|
||||
return when (this) {
|
||||
DataType.BOOL -> "bool"
|
||||
DataType.BOOL -> "ubyte" // in IR , a boolean is represented by an ubyte.
|
||||
DataType.UBYTE -> "ubyte"
|
||||
DataType.BYTE -> "byte"
|
||||
DataType.UWORD -> "uword"
|
||||
DataType.WORD -> "word"
|
||||
DataType.LONG -> "long"
|
||||
DataType.FLOAT -> "float"
|
||||
DataType.STR -> "ubyte[$lengthStr]" // here string doesn't exist as a seperate datatype anymore
|
||||
DataType.ARRAY_BOOL -> "bool[$lengthStr]"
|
||||
DataType.STR -> "ubyte[$lengthStr]" // here string doesn't exist as a seperate datatype anymore
|
||||
DataType.ARRAY_BOOL -> "ubyte[$lengthStr]" // in IR , a boolean is represented by an ubyte.
|
||||
DataType.ARRAY_UB -> "ubyte[$lengthStr]"
|
||||
DataType.ARRAY_B -> "byte[$lengthStr]"
|
||||
DataType.ARRAY_UW -> "uword[$lengthStr]"
|
||||
|
Loading…
Reference in New Issue
Block a user