introduce BOOL type

This commit is contained in:
Irmen de Jong 2022-07-04 23:42:49 +02:00
parent 288c57c144
commit 41f4e22a17
22 changed files with 301 additions and 126 deletions

View File

@ -6,6 +6,7 @@ enum class DataType {
UWORD, // pass by value
WORD, // pass by value
FLOAT, // pass by value
BOOL, // pass by value
STR, // pass by reference
ARRAY_UB, // pass by reference
ARRAY_B, // pass by reference
@ -19,11 +20,12 @@ enum class DataType {
*/
infix fun isAssignableTo(targetType: DataType) =
when(this) {
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT)
BYTE -> targetType.oneOf(BYTE, WORD, FLOAT)
UWORD -> targetType.oneOf(UWORD, FLOAT)
WORD -> targetType.oneOf(WORD, FLOAT)
FLOAT -> targetType == FLOAT
BOOL -> targetType.oneOf(BOOL, BYTE, UBYTE, WORD, UWORD, FLOAT)
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT, BOOL)
BYTE -> targetType.oneOf(BYTE, WORD, FLOAT, BOOL)
UWORD -> targetType.oneOf(UWORD, FLOAT, BOOL)
WORD -> targetType.oneOf(WORD, FLOAT, BOOL)
FLOAT -> targetType.oneOf(FLOAT, BOOL)
STR -> targetType.oneOf(STR, UWORD)
in ArrayDatatypes -> targetType == this
else -> false
@ -109,10 +111,10 @@ enum class BranchCondition {
}
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE)
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.BOOL)
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.BOOL)
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT, DataType.BOOL)
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F)
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)

View File

@ -2,6 +2,7 @@ package prog8.code.core
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=")
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
val LogicalOperators = setOf("and", "or", "xor", "not")
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%")
val BitwiseOperators = setOf("&", "|", "^", "~")

View File

@ -44,7 +44,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
"abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope)
"any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope)
"sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope)
"boolean" -> funcBoolean(fcall, resultToStack, resultRegister, sscope)
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
"rol" -> funcRol(fcall)
@ -698,32 +697,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
}
}
private fun funcBoolean(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
when (val dt = fcall.args.single().inferType(program).getOr(DataType.UNDEFINED)) {
in ByteDatatypes -> {
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.A, dt==DataType.BYTE)
asmgen.out(" beq + | lda #1")
asmgen.out("+")
}
in WordDatatypes -> {
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY, dt==DataType.WORD)
asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1")
asmgen.out(" beq + | lda #1")
asmgen.out("+")
}
DataType.FLOAT -> {
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN")
}
else -> throw AssemblyError("weird type")
}
if(resultToStack)
asmgen.out(" sta P8ESTACK_LO,x | dex")
else if(resultRegister!=null)
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister, false, scope, program, asmgen), CpuRegister.A)
}
private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
when(func.name) {
"rnd" -> {

View File

@ -532,8 +532,8 @@ internal class ExpressionsAsmGen(private val program: Program,
}
in ComparisonOperators -> {
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
val rightVal = expr.right.constValue(program)?.number?.toInt()
if(rightVal==0)
val rightVal = expr.right.constValue(program)?.number
if(rightVal==0.0)
return translateComparisonWithZero(expr.left, leftDt, expr.operator)
}
}
@ -560,6 +560,42 @@ internal class ExpressionsAsmGen(private val program: Program,
}
private fun translateComparisonWithZero(expr: Expression, dt: DataType, operator: String) {
if(expr.isSimple) {
if(operator=="!=") {
when (dt) {
in ByteDatatypes -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.A, dt == DataType.BYTE)
asmgen.out("""
beq +
lda #1
+ sta P8ESTACK_LO,x
dex""")
return
}
in WordDatatypes -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, dt == DataType.WORD)
asmgen.out("""
sty P8ZP_SCRATCH_B1
ora P8ZP_SCRATCH_B1
beq +
lda #1
+ sta P8ESTACK_LO,x
dex""")
return
}
DataType.FLOAT -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
asmgen.out("""
jsr floats.SIGN
sta P8ESTACK_LO,x
dex""")
return
}
else -> {}
}
}
/* operator == is not worth it to special case, the code is mostly larger */
}
translateExpressionInternal(expr)
when(operator) {
"==" -> {

View File

@ -303,9 +303,19 @@ internal class AssignmentAsmGen(private val program: Program,
private fun attemptAssignOptimizedBinexpr(expr: BinaryExpression, assign: AsmAssignment): Boolean {
if(expr.operator in ComparisonOperators) {
assignConstantByte(assign.target, 0)
if(expr.right.constValue(program)?.number == 0.0) {
if(expr.operator == "==" || expr.operator=="!=") {
when(assign.target.datatype) {
in ByteDatatypes -> if(attemptAssignToByteCompareZero(expr, assign)) return true
else -> {
// do nothing, this is handled by a type cast.
}
}
}
}
val origTarget = assign.target.origAstTarget
if(origTarget!=null) {
assignConstantByte(assign.target, 0)
val assignTrue = AnonymousScope(mutableListOf(
Assignment(origTarget, NumericLiteral.fromBoolean(true, assign.position), AssignmentOrigin.ASMGEN, assign.position)
), assign.position)
@ -454,6 +464,77 @@ internal class AssignmentAsmGen(private val program: Program,
return false
}
private fun attemptAssignToByteCompareZero(expr: BinaryExpression, assign: AsmAssignment): Boolean {
when (expr.operator) {
"==" -> {
when(val dt = assign.source.datatype) {
in ByteDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
asmgen.out("""
beq +
lda #0
beq ++
+ lda #1
+""")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
in WordDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, dt==DataType.WORD)
asmgen.out("""
sty P8ZP_SCRATCH_B1
ora P8ZP_SCRATCH_B1
beq +
lda #0
beq ++
+ lda #1
+""")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
DataType.FLOAT -> {
assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN | and #1 | eor #1")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
else->{
return false
}
}
}
"!=" -> {
when(val dt = assign.source.datatype) {
in ByteDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
asmgen.out(" beq + | lda #1")
asmgen.out("+")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
in WordDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, dt==DataType.WORD)
asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1")
asmgen.out(" beq + | lda #1")
asmgen.out("+")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
DataType.FLOAT -> {
assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
else->{
return false
}
}
}
else -> return false
}
}
private fun fallbackToStackEval(assign: AsmAssignment) {
// TODO DON'T STACK-EVAL... perhaps by using a temp var? so that it becomes augmentable assignment expression?
// or don't try to solve it here in this one case and rather rewrite the whole stack based value evaluation.

View File

@ -32,7 +32,6 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
"callrom" -> throw AssemblyError("callrom() is for cx16 target only")
"msb" -> funcMsb(call, resultRegister)
"lsb" -> funcLsb(call, resultRegister)
"boolean" -> funcBoolean(call, resultRegister)
"memory" -> funcMemory(call, resultRegister)
"peek" -> funcPeek(call, resultRegister)
"peekw" -> funcPeekW(call, resultRegister)
@ -355,38 +354,6 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
return code
}
private fun funcBoolean(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
val vmDt = codeGen.vmType(call.args[0].type)
when (vmDt) {
VmDataType.FLOAT -> {
val fpValueReg = codeGen.vmRegisters.nextFreeFloat()
val fpSignReg = codeGen.vmRegisters.nextFreeFloat()
code += exprGen.translateExpression(call.args.single(), -1, fpValueReg)
code += VmCodeInstruction(Opcode.SGN, VmDataType.FLOAT, fpReg1 = fpSignReg, fpReg2 = fpValueReg)
code += VmCodeInstruction(Opcode.FTOSB, VmDataType.FLOAT, reg1 = resultRegister, fpReg1 = fpSignReg)
}
VmDataType.WORD -> {
val msbReg = codeGen.vmRegisters.nextFree()
val skipLabel = codeGen.createLabelName()
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
code += VmCodeInstruction(Opcode.MSIG, VmDataType.BYTE, reg1 = msbReg, reg2=resultRegister)
code += VmCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg)
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=resultRegister, labelSymbol = skipLabel)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value = 1)
code += VmCodeLabel(skipLabel)
}
else -> {
val skipLabel = codeGen.createLabelName()
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=resultRegister, labelSymbol = skipLabel)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value = 1)
code += VmCodeLabel(skipLabel)
}
}
return code
}
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val vmDt = codeGen.vmType(call.args[0].type)
val code = VmCodeChunk()

View File

@ -813,6 +813,7 @@ class CodeGen(internal val program: PtProgram,
internal fun vmType(type: DataType): VmDataType {
return when(type) {
DataType.BOOL,
DataType.UBYTE,
DataType.BYTE -> VmDataType.BYTE
DataType.UWORD,

View File

@ -108,7 +108,7 @@ class StatementOptimizer(private val program: Program,
// empty true part? switch with the else part
if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) {
val invertedCondition = BinaryExpression(ifElse.condition, "==", NumericLiteral.fromBoolean(false, ifElse.condition.position), ifElse.condition.position)
val invertedCondition = BinaryExpression(ifElse.condition, "==", NumericLiteral(DataType.UBYTE, 0.0, ifElse.condition.position), ifElse.condition.position)
val emptyscope = AnonymousScope(mutableListOf(), ifElse.elsepart.position)
val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position)
return listOf(

View File

@ -876,6 +876,8 @@ internal class AstChecker(private val program: Program,
// only exception allowed: str * constvalue
if(expr.right.constValue(program)==null)
errors.err("can only use string repeat with a constant number value", expr.left.position)
} else if(leftDt==DataType.BOOL && rightDt in ByteDatatypes || leftDt in ByteDatatypes && rightDt==DataType.BOOL) {
// expression with one side BOOL other side (U)BYTE is allowed; bool==byte
} else {
errors.err("left and right operands aren't the same type", expr.left.position)
}
@ -1072,8 +1074,14 @@ internal class AstChecker(private val program: Program,
}
if(ident!=null && ident.nameInSource[0] == "cx16" && ident.nameInSource[1].startsWith("r")) {
var regname = ident.nameInSource[1].uppercase()
if(regname.endsWith('L') || regname.endsWith('H') || regname.endsWith('s'))
regname=regname.substring(0, regname.length-1)
val lastLetter = regname.last().lowercaseChar()
if(lastLetter in setOf('l', 'h', 's')) {
regname = regname.substring(0, regname.length - 1)
val lastLetter2 = regname.last().lowercaseChar()
if(lastLetter2 in setOf('l', 'h', 's')) {
regname = regname.substring(0, regname.length - 1)
}
}
val reg = RegisterOrPair.valueOf(regname)
val same = params.filter { it.value.registerOrPair==reg }
for(s in same) {
@ -1370,6 +1378,9 @@ internal class AstChecker(private val program: Program,
if (number < -32768 || number > 32767)
return err("value '$number' out of range for word")
}
DataType.BOOL -> {
return true
}
else -> return err("value of type ${value.type} not compatible with $targetDt")
}
return true
@ -1429,12 +1440,13 @@ internal class AstChecker(private val program: Program,
}
val result = when(targetDatatype) {
DataType.BYTE -> sourceDatatype== DataType.BYTE
DataType.UBYTE -> sourceDatatype== DataType.UBYTE
DataType.WORD -> sourceDatatype== DataType.BYTE || sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.WORD
DataType.UWORD -> sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.UWORD
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.FLOAT -> sourceDatatype in NumericDatatypes
DataType.STR -> sourceDatatype== DataType.STR
DataType.STR -> sourceDatatype == DataType.STR
else -> {
errors.err("cannot assign new value to variable of type $targetDatatype", position)
false

View File

@ -1,6 +1,5 @@
package prog8.compiler.astprocessing
import prog8.ast.IFunctionCall
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.SyntaxError
@ -107,8 +106,8 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
else -> "invalid"
}
return listOf(
IAstModification.ReplaceNodeSafe(expr.left, wrapWithBooleanConversion(expr.left), expr),
IAstModification.ReplaceNodeSafe(expr.right, wrapWithBooleanConversion(expr.right), expr),
IAstModification.ReplaceNodeSafe(expr.left, wrapWithBooleanCast(expr.left), expr),
IAstModification.ReplaceNodeSafe(expr.right, wrapWithBooleanCast(expr.right), expr),
)
}
@ -149,11 +148,11 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
return noModifications
}
private fun wrapWithBooleanConversion(expr: Expression): Expression {
private fun wrapWithBooleanCast(expr: Expression): Expression {
fun isBoolean(expr: Expression): Boolean {
return if(expr is IFunctionCall && expr.target.nameInSource==listOf("boolean"))
return if(expr.inferType(program) istype DataType.BOOL)
true
else if(expr is BinaryExpression && expr.operator in ComparisonOperators+listOf("and", "or", "xor"))
else if(expr is BinaryExpression && expr.operator in ComparisonOperators + LogicalOperators)
true
else if(expr is PrefixExpression && expr.operator == "not")
true
@ -169,6 +168,6 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
return if(isBoolean(expr))
expr
else
BuiltinFunctionCall(IdentifierReference(listOf("boolean"), expr.position), mutableListOf(expr), expr.position)
TypecastExpression(expr, DataType.BOOL, true, expr.position)
}
}

View File

@ -15,6 +15,12 @@ internal class BeforeAsmAstChanger(val program: Program,
private val errors: IErrorReporter
) : AstWalker() {
override fun after(numLiteral: NumericLiteral, parent: Node): Iterable<IAstModification> {
if(numLiteral.type==DataType.BOOL)
return listOf(IAstModification.ReplaceNode(numLiteral, NumericLiteral(DataType.UBYTE, numLiteral.number, numLiteral.position), parent))
return noModifications
}
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
throw InternalCompilerException("break should have been replaced by goto $breakStmt")
}
@ -60,6 +66,12 @@ internal class BeforeAsmAstChanger(val program: Program,
throw InternalCompilerException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
}
if(decl.datatype==DataType.BOOL) {
val ubyteDecl = VarDecl(decl.type, decl.origin, DataType.UBYTE, decl.zeropage, decl.arraysize, decl.name,
decl.value, decl.isArray, decl.sharedWithAsm, decl.subroutineParameter, decl.position)
return listOf(IAstModification.ReplaceNode(decl, ubyteDecl, parent))
}
return noModifications
}
@ -169,6 +181,10 @@ internal class BeforeAsmAstChanger(val program: Program,
}
}
if(subroutine.returntypes.any { it==DataType.BOOL } || subroutine.parameters.any {it.type==DataType.BOOL}) {
throw FatalAstException("boolean args and return types to ubyte should already have been done by earlier step")
}
return mods
}

View File

@ -16,6 +16,11 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
) : AstWalker() {
override fun after(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))
}
// see if we can remove redundant typecasts (outside of expressions)
// such as casting byte<->ubyte, word<->uword or even redundant casts (sourcetype = target type).
// the special typecast of a reference type (str, array) to an UWORD will be changed into address-of,

View File

@ -8,6 +8,7 @@ import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.DataType
import prog8.code.core.IErrorReporter
import prog8.code.core.Position
@ -63,7 +64,7 @@ if CONDITION==0
*/
val pos = untilLoop.position
val loopLabel = program.makeLabel("untilloop", pos)
val equalsZero = BinaryExpression(untilLoop.condition, "==", NumericLiteral.fromBoolean(false, pos), pos)
val equalsZero = BinaryExpression(untilLoop.condition, "==", NumericLiteral(DataType.UBYTE, 0.0, pos), pos)
val replacement = AnonymousScope(mutableListOf(
loopLabel,
untilLoop.body,
@ -88,7 +89,7 @@ _after:
val pos = whileLoop.position
val loopLabel = program.makeLabel("whileloop", pos)
val afterLabel = program.makeLabel("afterwhile", pos)
val equalsZero = BinaryExpression(whileLoop.condition, "==", NumericLiteral.fromBoolean(false, pos), pos)
val equalsZero = BinaryExpression(whileLoop.condition, "==", NumericLiteral(DataType.UBYTE, 0.0, pos), pos)
val replacement = AnonymousScope(mutableListOf(
loopLabel,
IfElse(equalsZero,

View File

@ -45,6 +45,15 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
val rightDt = expr.right.inferType(program)
if(leftDt.isKnown && rightDt.isKnown && leftDt!=rightDt) {
// convert bool type to byte
if(leftDt istype DataType.BOOL && rightDt.isBytes) {
return listOf(IAstModification.ReplaceNode(expr.left,
TypecastExpression(expr.left, rightDt.getOr(DataType.UNDEFINED),true, expr.left.position), expr))
} else if(leftDt.isBytes && rightDt istype DataType.BOOL) {
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) {
val leftCv = expr.left.constValue(program)

View File

@ -6,9 +6,7 @@ import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.*
import prog8.ast.statements.AnonymousScope
import prog8.ast.statements.Assignment
import prog8.ast.statements.FunctionCallStatement
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.*
@ -53,6 +51,13 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
}
}
// if the expression is a comparison expression, or a logical expression, it produces the
// correct 'boolean' byte result so the cast can be removed.
val binExpr = typecast.expression as? BinaryExpression
if(binExpr!=null && binExpr.operator in ComparisonOperators + LogicalOperators) {
return listOf(IAstModification.ReplaceNode(typecast, binExpr, parent))
}
return noModifications
}
@ -153,7 +158,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
fun replaceWithFalse(): Iterable<IAstModification> {
errors.warn("condition is always false", containment.position)
return listOf(IAstModification.ReplaceNode(containment, NumericLiteral.fromBoolean(false, containment.position), parent))
return listOf(IAstModification.ReplaceNode(containment, NumericLiteral(DataType.UBYTE, 0.0, containment.position), parent))
}
fun checkArray(array: Array<Expression>): Iterable<IAstModification> {
@ -218,17 +223,20 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
}
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
if(functionCallExpr.target.nameInSource==listOf("boolean")) {
// boolean(expr) can be removed if expr is a comparison expression, or nested boolean()
val binexpr = functionCallExpr.args.single() as? BinaryExpression
if(binexpr!=null && binexpr.operator in ComparisonOperators) {
return listOf(IAstModification.ReplaceNode(functionCallExpr, binexpr, parent))
}
val fcall = functionCallExpr.args.single() as? IFunctionCall
if(fcall!=null && fcall.target.nameInSource==listOf("boolean")) {
return listOf(IAstModification.ReplaceNode(functionCallExpr, fcall as Node, parent))
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
if(subroutine.returntypes.any { it==DataType.BOOL } || subroutine.parameters.any {it.type==DataType.BOOL}) {
val newReturnTypes = subroutine.returntypes.map {
if(it==DataType.BOOL) DataType.UBYTE else it
}
val newParams = subroutine.parameters.map {
if(it.type==DataType.BOOL) SubroutineParameter(it.name, DataType.UBYTE, it.position) else it
}.toMutableList()
val newSubroutine = Subroutine(subroutine.name, newParams, newReturnTypes,
subroutine.asmParameterRegisters, subroutine.asmReturnvaluesRegisters, subroutine.asmClobbers,
subroutine.asmAddress, subroutine.isAsmSubroutine, subroutine.inline, subroutine.statements,
subroutine.position)
return listOf(IAstModification.ReplaceNode(subroutine, newSubroutine, parent))
}
return noModifications
}

View File

@ -339,6 +339,7 @@ internal object BuiltinFunctionScopePlaceholder : INameScope {
fun defaultZero(dt: DataType, position: Position) = when(dt) {
DataType.BOOL -> NumericLiteral(DataType.UBYTE, 0.0, position)
DataType.UBYTE -> NumericLiteral(DataType.UBYTE, 0.0, position)
DataType.BYTE -> NumericLiteral(DataType.BYTE, 0.0, position)
DataType.UWORD, DataType.STR -> NumericLiteral(DataType.UWORD, 0.0, position)

View File

@ -213,15 +213,12 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
}
}
}
"&" -> leftDt
"|" -> leftDt
"^" -> leftDt
"&", "|", "^" -> if(leftDt istype DataType.BOOL) InferredTypes.knownFor(DataType.UBYTE) else leftDt
"and", "or", "xor", "not" -> InferredTypes.knownFor(DataType.UBYTE)
"<", ">",
"<=", ">=",
"==", "!=" -> dynamicBooleanType()
"==", "!=", "in" -> dynamicBooleanType()
"<<", ">>" -> leftDt
"in" -> dynamicBooleanType()
else -> throw FatalAstException("resulting datatype check for invalid operator $operator")
}
}
@ -443,7 +440,7 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
companion object {
fun fromBoolean(bool: Boolean, position: Position) =
NumericLiteral(DataType.UBYTE, if (bool) 1.0 else 0.0, position)
NumericLiteral(DataType.UBYTE, if(bool) 1.0 else 0.0, position)
fun optimalNumeric(value: Number, position: Position): NumericLiteral {
val digits = floor(value.toDouble()) - value.toDouble()
@ -540,6 +537,8 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
return CastValue(true, NumericLiteral(targettype, number, position))
if(targettype== DataType.FLOAT)
return CastValue(true, NumericLiteral(targettype, number, position))
if(targettype==DataType.BOOL)
return CastValue(true, fromBoolean(number!=0.0, position))
}
DataType.BYTE -> {
if(targettype== DataType.UBYTE) {
@ -558,6 +557,8 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
return CastValue(true, NumericLiteral(targettype, number, position))
if(targettype== DataType.FLOAT)
return CastValue(true, NumericLiteral(targettype, number, position))
if(targettype==DataType.BOOL)
return CastValue(true, fromBoolean(number!=0.0, position))
}
DataType.UWORD -> {
if(targettype== DataType.BYTE && number <= 127)
@ -568,6 +569,8 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
return CastValue(true, NumericLiteral(targettype, number, position))
if(targettype== DataType.FLOAT)
return CastValue(true, NumericLiteral(targettype, number, position))
if(targettype==DataType.BOOL)
return CastValue(true, fromBoolean(number!=0.0, position))
}
DataType.WORD -> {
if(targettype== DataType.BYTE && number >= -128 && number <=127)
@ -586,6 +589,8 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
}
if(targettype== DataType.FLOAT)
return CastValue(true, NumericLiteral(targettype, number, position))
if(targettype==DataType.BOOL)
return CastValue(true, fromBoolean(number!=0.0, position))
}
DataType.FLOAT -> {
try {
@ -597,11 +602,18 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
return CastValue(true, NumericLiteral(targettype, number, position))
if (targettype == DataType.UWORD && number >= 0 && number <= 65535)
return CastValue(true, NumericLiteral(targettype, number, position))
if(targettype==DataType.BOOL)
return CastValue(true, fromBoolean(number!=0.0, position))
} catch (x: ExpressionError) {
return CastValue(false, null)
}
}
else -> {}
DataType.BOOL -> {
return CastValue(true, NumericLiteral(targettype, number, position))
}
else -> {
throw FatalAstException("type cast of weird type $type")
}
}
return CastValue(false, null)
}

View File

@ -47,6 +47,7 @@ object InferredTypes {
infix fun isNotAssignableTo(targetDt: InferredType): Boolean = !this.isAssignableTo(targetDt)
infix fun isNotAssignableTo(targetDt: DataType): Boolean = !this.isAssignableTo(targetDt)
val isBool get() = datatype == DataType.BOOL
val isBytes get() = datatype in ByteDatatypes
val isWords get() = datatype in WordDatatypes
val isInteger get() = datatype in IntegerDatatypes
@ -67,6 +68,7 @@ object InferredTypes {
DataType.UWORD to InferredType.known(DataType.UWORD),
DataType.WORD to InferredType.known(DataType.WORD),
DataType.FLOAT to InferredType.known(DataType.FLOAT),
DataType.BOOL to InferredType.known(DataType.BOOL),
DataType.STR to InferredType.known(DataType.STR),
DataType.ARRAY_UB to InferredType.known(DataType.ARRAY_UB),
DataType.ARRAY_B to InferredType.known(DataType.ARRAY_B),

View File

@ -106,7 +106,6 @@ private val functionSignatures: List<FSignature> = listOf(
FSignature("lsb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x and 255).toDouble() } },
FSignature("msb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} },
FSignature("mkword" , true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
FSignature("boolean" , true, listOf(FParam("value", NumericDatatypes)), DataType.UBYTE, ::builtinBoolean),
FSignature("peek" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
FSignature("peekw" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
FSignature("poke" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
@ -251,16 +250,6 @@ private fun builtinMkword(args: List<Expression>, position: Position, program: P
return NumericLiteral(DataType.UWORD, result.toDouble(), position)
}
private fun builtinBoolean(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 1)
throw SyntaxError("boolean requires one argument", position)
val constvalue = args[0].constValue(program) ?: throw NotConstArgumentException()
return if(constvalue.type==DataType.FLOAT)
NumericLiteral(DataType.UBYTE, constvalue.number.sign, args[0].position)
else
NumericLiteral.fromBoolean(constvalue.number!=0.0, args[0].position)
}
private fun builtinSgn(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 1)
throw SyntaxError("sgn requires one argument", position)

View File

@ -3,7 +3,12 @@ TODO
For next release
^^^^^^^^^^^^^^^^
...
- fix compiler crash txt.print_w(value(99) as ubyte) where value()->word
- fix imageviewer compilation crash
- petaxian is larger again after introduction of BOOL type (against main branch). WHY??? FIX.
caused by replacing cast-to-bool by !=0 expression perhaps rather than optimized asm for boolean() func?
boolcheck.p8 illustrates problem: stack eval
Need help with
@ -97,11 +102,14 @@ So the idea is to add a true 'bool' type
- idea: let BooleanLiteral subclass from NumericLiteral ?
- add 'bool' type to grammar and parser
- remove builtin function boolean() replace with typecast to BOOL
- add ARRAY_OF_BOOL array type
- logical expressions don't cast operands of BOOL type to BOOL anymore...
- boolvar & 1 -> boolvar
- logical expressions don't cast operands of BOOL type to BOOL anymore (is this done???)
- before codegen, BOOL type is simply discarded and replaced by UBYTE
THE ABOVE HAS BEEN DONE
- rewrite: boolvar & 1 -> boolvar, (boolvar & 1 == 0) -> not boolvar
- add ARRAY_OF_BOOL array type
STRUCTS again?
--------------

View File

@ -3,7 +3,59 @@
main {
sub value(word input) -> word {
return input-999
}
sub boolfunc(bool data) -> bool {
return not data
}
sub start() {
txt.print("hello!\n")
ubyte ubb
if ubb and 10
ubb++
bool b1 = 1
bool b2 = 0
bool b3 = true
bool b4 = false
bool bb5 = -99
bool bb6 = 123
txt.print_ub(b1)
txt.spc()
txt.print_ub(b2)
txt.spc()
txt.print_ub(b3)
txt.spc()
txt.print_ub(b4)
txt.spc()
txt.print_b(bb5)
txt.spc()
txt.print_b(bb6)
txt.nl()
b1 = value(99) as bool
txt.print_ub(b1) ; should be 1
txt.spc()
txt.print_ub(value(99) as ubyte) ; should be 124
txt.spc()
txt.print_ub(value(99) as bool) ; should be 1
txt.nl()
txt.print_ub(boolfunc(true))
txt.spc()
txt.print_ub(boolfunc(false))
txt.nl()
ubb = ubb != 0
ubb++
ubb = bb6 != 0
txt.print_ub(ubb)
txt.nl()
}
}

View File

@ -136,7 +136,7 @@ constdecl: 'const' varinitializer ;
memoryvardecl: ADDRESS_OF varinitializer;
datatype: 'ubyte' | 'byte' | 'uword' | 'word' | 'float' | 'str' ;
datatype: 'ubyte' | 'byte' | 'uword' | 'word' | 'float' | 'str' | 'bool' ;
arrayindex: '[' expression ']' ;