mirror of
https://github.com/irmen/prog8.git
synced 2025-02-25 20:29:04 +00:00
Merge branch 'master' into c128target
# Conflicts: # examples/test.p8
This commit is contained in:
commit
05a1ddad05
@ -786,8 +786,8 @@ class AsmGen(private val program: Program,
|
|||||||
is When -> translate(stmt)
|
is When -> translate(stmt)
|
||||||
is AnonymousScope -> translate(stmt)
|
is AnonymousScope -> translate(stmt)
|
||||||
is BuiltinFunctionPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore")
|
is BuiltinFunctionPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore")
|
||||||
is UntilLoop -> throw AssemblyError("do..until should have been desugared to jumps")
|
is UntilLoop -> throw AssemblyError("do..until should have been converted to jumps")
|
||||||
is WhileLoop -> throw AssemblyError("while should have been desugared to jumps")
|
is WhileLoop -> throw AssemblyError("while should have been converted to jumps")
|
||||||
is Block -> throw AssemblyError("block should have been handled elsewhere")
|
is Block -> throw AssemblyError("block should have been handled elsewhere")
|
||||||
is Break -> throw AssemblyError("break should have been replaced by goto")
|
is Break -> throw AssemblyError("break should have been replaced by goto")
|
||||||
else -> throw AssemblyError("missing asm translation for $stmt")
|
else -> throw AssemblyError("missing asm translation for $stmt")
|
||||||
|
@ -37,9 +37,11 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
is NumericLiteralValue -> translateExpression(expression)
|
is NumericLiteralValue -> translateExpression(expression)
|
||||||
is IdentifierReference -> translateExpression(expression)
|
is IdentifierReference -> translateExpression(expression)
|
||||||
is FunctionCallExpr -> translateFunctionCallResultOntoStack(expression)
|
is FunctionCallExpr -> translateFunctionCallResultOntoStack(expression)
|
||||||
|
is ContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
|
||||||
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
|
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
|
||||||
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
|
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
|
||||||
is CharLiteral -> throw AssemblyError("charliteral should have been replaced by ubyte using certain encoding")
|
is CharLiteral -> throw AssemblyError("charliteral should have been replaced by ubyte using certain encoding")
|
||||||
|
else -> TODO("missing expression asmgen for $expression")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ import prog8.ast.expressions.RangeExpr
|
|||||||
import prog8.ast.statements.ForLoop
|
import prog8.ast.statements.ForLoop
|
||||||
import prog8.ast.toHex
|
import prog8.ast.toHex
|
||||||
import prog8.codegen.target.AssemblyError
|
import prog8.codegen.target.AssemblyError
|
||||||
import prog8.compilerinterface.toConstantIntegerRange
|
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
@ -271,6 +271,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
else -> throw AssemblyError("invalid prefix operator")
|
else -> throw AssemblyError("invalid prefix operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is ContainmentCheck -> {
|
||||||
|
containmentCheckIntoA(value)
|
||||||
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// Everything else just evaluate via the stack.
|
// Everything else just evaluate via the stack.
|
||||||
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
|
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
|
||||||
@ -294,6 +298,124 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun containmentCheckIntoA(containment: ContainmentCheck) {
|
||||||
|
val elementDt = containment.element.inferType(program)
|
||||||
|
val range = containment.iterable as? RangeExpr
|
||||||
|
if(range!=null) {
|
||||||
|
val constRange = range.toConstantIntegerRange()
|
||||||
|
if(constRange!=null)
|
||||||
|
return containmentCheckIntoA(containment.element, elementDt.getOr(DataType.UNDEFINED), constRange.toList())
|
||||||
|
TODO("non-const range containment check ${containment.position}")
|
||||||
|
}
|
||||||
|
val variable = (containment.iterable as? IdentifierReference)?.targetVarDecl(program)
|
||||||
|
if(variable!=null) {
|
||||||
|
if(elementDt istype DataType.FLOAT)
|
||||||
|
throw AssemblyError("containment check of floats not supported")
|
||||||
|
if(variable.autogeneratedDontRemove) {
|
||||||
|
when(variable.datatype) {
|
||||||
|
DataType.STR -> {
|
||||||
|
require(elementDt.isBytes)
|
||||||
|
val stringVal = variable.value as StringLiteralValue
|
||||||
|
val encoded = program.encoding.encodeString(stringVal.value, stringVal.altEncoding)
|
||||||
|
return containmentCheckIntoA(containment.element, elementDt.getOr(DataType.UNDEFINED), encoded.map { it.toInt() })
|
||||||
|
}
|
||||||
|
DataType.ARRAY_F -> {
|
||||||
|
// require(elementDt istype DataType.FLOAT)
|
||||||
|
throw AssemblyError("containment check of floats not supported")
|
||||||
|
}
|
||||||
|
in ArrayDatatypes -> {
|
||||||
|
require(elementDt.isInteger)
|
||||||
|
val arrayVal = variable.value as ArrayLiteralValue
|
||||||
|
val values = arrayVal.value.map { it.constValue(program)!!.number.toInt() }
|
||||||
|
return containmentCheckIntoA(containment.element, elementDt.getOr(DataType.UNDEFINED), values)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val varname = asmgen.asmVariableName(containment.iterable as IdentifierReference)
|
||||||
|
when(variable.datatype) {
|
||||||
|
DataType.STR -> {
|
||||||
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
|
||||||
|
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
|
||||||
|
val stringVal = variable.value as StringLiteralValue
|
||||||
|
asmgen.out(" ldy #${stringVal.value.length}")
|
||||||
|
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
DataType.ARRAY_F -> throw AssemblyError("containment check of floats not supported")
|
||||||
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
|
val arrayVal = variable.value as ArrayLiteralValue
|
||||||
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
|
||||||
|
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
|
||||||
|
asmgen.out(" ldy #${arrayVal.value.size}")
|
||||||
|
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
|
val arrayVal = variable.value as ArrayLiteralValue
|
||||||
|
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine)
|
||||||
|
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname)
|
||||||
|
asmgen.out(" ldy #${arrayVal.value.size}")
|
||||||
|
asmgen.out(" jsr prog8_lib.containment_wordarray")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val stringVal = containment.iterable as? StringLiteralValue
|
||||||
|
if(stringVal!=null) {
|
||||||
|
require(elementDt.isBytes)
|
||||||
|
val encoded = program.encoding.encodeString(stringVal.value, stringVal.altEncoding)
|
||||||
|
return containmentCheckIntoA(containment.element, elementDt.getOr(DataType.UNDEFINED), encoded.map { it.toInt() })
|
||||||
|
}
|
||||||
|
val arrayVal = containment.iterable as? ArrayLiteralValue
|
||||||
|
if(arrayVal!=null) {
|
||||||
|
require(elementDt.isInteger)
|
||||||
|
val values = arrayVal.value.map { it.constValue(program)!!.number.toInt() }
|
||||||
|
return containmentCheckIntoA(containment.element, elementDt.getOr(DataType.UNDEFINED), values)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw AssemblyError("invalid containment iterable type")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun containmentCheckIntoA(element: Expression, dt: DataType, values: List<Number>) {
|
||||||
|
if(values.size<2)
|
||||||
|
throw AssemblyError("containment check against 0 or 1 values should have been optimized away")
|
||||||
|
|
||||||
|
// TODO don't generate a huge cmp-list when we go over a certain number of values
|
||||||
|
val containsLabel = asmgen.makeLabel("contains")
|
||||||
|
when(dt) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
asmgen.assignExpressionToRegister(element, RegisterOrPair.A, dt==DataType.BYTE)
|
||||||
|
for (value in values) {
|
||||||
|
asmgen.out(" cmp #$value | beq +")
|
||||||
|
}
|
||||||
|
asmgen.out("""
|
||||||
|
lda #0
|
||||||
|
beq ++
|
||||||
|
+ lda #1
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
asmgen.assignExpressionToRegister(element, RegisterOrPair.AY, dt==DataType.WORD)
|
||||||
|
for (value in values) {
|
||||||
|
asmgen.out("""
|
||||||
|
cmp #<$value
|
||||||
|
bne +
|
||||||
|
cpy #>$value
|
||||||
|
beq $containsLabel
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
asmgen.out("""
|
||||||
|
lda #0
|
||||||
|
beq +
|
||||||
|
$containsLabel lda #1
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun assignStatusFlagByte(target: AsmAssignTarget, statusflag: Statusflag) {
|
private fun assignStatusFlagByte(target: AsmAssignTarget, statusflag: Statusflag) {
|
||||||
when(statusflag) {
|
when(statusflag) {
|
||||||
Statusflag.Pc -> {
|
Statusflag.Pc -> {
|
||||||
|
@ -22,7 +22,7 @@ class BinExprSplitter(private val program: Program, private val options: Compila
|
|||||||
|
|
||||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
|
|
||||||
if(assignment.value.inferType(program).istype(DataType.FLOAT) && !options.optimizeFloatExpressions)
|
if(assignment.value.inferType(program) istype DataType.FLOAT && !options.optimizeFloatExpressions)
|
||||||
return noModifications
|
return noModifications
|
||||||
|
|
||||||
val binExpr = assignment.value as? BinaryExpression
|
val binExpr = assignment.value as? BinaryExpression
|
||||||
|
@ -23,6 +23,13 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||||||
noModifications
|
noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> {
|
||||||
|
val result = containment.constValue(program)
|
||||||
|
if(result!=null)
|
||||||
|
return listOf(IAstModification.ReplaceNode(containment, result, parent))
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
||||||
// Try to turn a unary prefix expression into a single constant value.
|
// Try to turn a unary prefix expression into a single constant value.
|
||||||
// Compile-time constant sub expressions will be evaluated on the spot.
|
// Compile-time constant sub expressions will be evaluated on the spot.
|
||||||
|
@ -9,8 +9,6 @@ import prog8.ast.walk.AstWalker
|
|||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.compilerinterface.ICompilationTarget
|
import prog8.compilerinterface.ICompilationTarget
|
||||||
import prog8.compilerinterface.IErrorReporter
|
import prog8.compilerinterface.IErrorReporter
|
||||||
import prog8.compilerinterface.size
|
|
||||||
import prog8.compilerinterface.toConstantIntegerRange
|
|
||||||
|
|
||||||
// Fix up the literal value's type to match that of the vardecl
|
// Fix up the literal value's type to match that of the vardecl
|
||||||
// (also check range literal operands types before they get expanded into arrays for instance)
|
// (also check range literal operands types before they get expanded into arrays for instance)
|
||||||
|
@ -344,6 +344,25 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> {
|
||||||
|
val range = containment.iterable as? RangeExpr
|
||||||
|
if(range!=null && range.step.constValue(program)?.number==1.0) {
|
||||||
|
val from = range.from.constValue(program)
|
||||||
|
val to = range.to.constValue(program)
|
||||||
|
val value = containment.element
|
||||||
|
if(from!=null && to!=null && value.isSimple) {
|
||||||
|
if(to.number-from.number>6.0) {
|
||||||
|
// replace containment test with X>=from and X<=to
|
||||||
|
val left = BinaryExpression(value, ">=", from, containment.position)
|
||||||
|
val right = BinaryExpression(value.copy(), "<=", to, containment.position)
|
||||||
|
val comparison = BinaryExpression(left, "and", right, containment.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(containment, comparison, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
private fun determineY(x: Expression, subBinExpr: BinaryExpression): Expression? {
|
private fun determineY(x: Expression, subBinExpr: BinaryExpression): Expression? {
|
||||||
return when {
|
return when {
|
||||||
subBinExpr.left isSameAs x -> subBinExpr.right
|
subBinExpr.left isSameAs x -> subBinExpr.right
|
||||||
|
@ -72,10 +72,10 @@ fun Program.splitBinaryExpressions(options: CompilationOptions, compTarget: ICom
|
|||||||
fun getTempVarName(dt: InferredTypes.InferredType): List<String> {
|
fun getTempVarName(dt: InferredTypes.InferredType): List<String> {
|
||||||
return when {
|
return when {
|
||||||
// TODO assume (hope) cx16.r9 isn't used for anything else during the use of this temporary variable...
|
// TODO assume (hope) cx16.r9 isn't used for anything else during the use of this temporary variable...
|
||||||
dt.istype(DataType.UBYTE) -> listOf("cx16", "r9L")
|
dt istype DataType.UBYTE -> listOf("cx16", "r9L")
|
||||||
dt.istype(DataType.BYTE) -> listOf("cx16", "r9sL")
|
dt istype DataType.BYTE -> listOf("cx16", "r9sL")
|
||||||
dt.istype(DataType.UWORD) -> listOf("cx16", "r9")
|
dt istype DataType.UWORD -> listOf("cx16", "r9")
|
||||||
dt.istype(DataType.WORD) -> listOf("cx16", "r9s")
|
dt istype DataType.WORD -> listOf("cx16", "r9s")
|
||||||
dt.isPassByReference -> listOf("cx16", "r9")
|
dt.isPassByReference -> listOf("cx16", "r9")
|
||||||
else -> throw FatalAstException("invalid dt $dt")
|
else -> throw FatalAstException("invalid dt $dt")
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import prog8.ast.walk.AstWalker
|
|||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.compilerinterface.ICompilationTarget
|
import prog8.compilerinterface.ICompilationTarget
|
||||||
import prog8.compilerinterface.IErrorReporter
|
import prog8.compilerinterface.IErrorReporter
|
||||||
import prog8.compilerinterface.size
|
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
|
||||||
@ -154,11 +153,11 @@ class StatementOptimizer(private val program: Program,
|
|||||||
if(constvalue!=null) {
|
if(constvalue!=null) {
|
||||||
return if(constvalue.asBooleanValue){
|
return if(constvalue.asBooleanValue){
|
||||||
// always true -> keep only if-part
|
// always true -> keep only if-part
|
||||||
errors.warn("condition is always true", ifElse.position)
|
errors.warn("condition is always true", ifElse.condition.position)
|
||||||
listOf(IAstModification.ReplaceNode(ifElse, ifElse.truepart, parent))
|
listOf(IAstModification.ReplaceNode(ifElse, ifElse.truepart, parent))
|
||||||
} else {
|
} else {
|
||||||
// always false -> keep only else-part
|
// always false -> keep only else-part
|
||||||
errors.warn("condition is always false", ifElse.position)
|
errors.warn("condition is always false", ifElse.condition.position)
|
||||||
listOf(IAstModification.ReplaceNode(ifElse, ifElse.elsepart, parent))
|
listOf(IAstModification.ReplaceNode(ifElse, ifElse.elsepart, parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1083,3 +1083,45 @@ strlen .proc
|
|||||||
bne -
|
bne -
|
||||||
+ rts
|
+ rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
containment_bytearray .proc
|
||||||
|
; -- check if a value exists in a byte array.
|
||||||
|
; parameters: P8ZP_SCRATCH_W1: address of the byte array, A = byte to check, Y = length of array (>=1).
|
||||||
|
; returns boolean 0/1 in A.
|
||||||
|
dey
|
||||||
|
- cmp (P8ZP_SCRATCH_W1),y
|
||||||
|
beq +
|
||||||
|
dey
|
||||||
|
cpy #255
|
||||||
|
bne -
|
||||||
|
lda #0
|
||||||
|
rts
|
||||||
|
+ lda #1
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
containment_wordarray .proc
|
||||||
|
; -- check if a value exists in a word array.
|
||||||
|
; parameters: P8ZP_SCRATCH_W1: value to check, P8ZP_SCRATCH_W2: address of the word array, Y = length of array (>=1).
|
||||||
|
; returns boolean 0/1 in A.
|
||||||
|
dey
|
||||||
|
tya
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
- lda P8ZP_SCRATCH_W1
|
||||||
|
cmp (P8ZP_SCRATCH_W2),y
|
||||||
|
bne +
|
||||||
|
lda P8ZP_SCRATCH_W1+1
|
||||||
|
iny
|
||||||
|
cmp (P8ZP_SCRATCH_W2),y
|
||||||
|
beq _found
|
||||||
|
+ dey
|
||||||
|
dey
|
||||||
|
cpy #254
|
||||||
|
bne -
|
||||||
|
lda #0
|
||||||
|
rts
|
||||||
|
_found lda #1
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
@ -36,11 +36,11 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
|
override fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
|
||||||
throw FatalAstException("while should have been desugared to jumps")
|
throw FatalAstException("while should have been converted to jumps")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
|
override fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
|
||||||
throw FatalAstException("do..until should have been desugared to jumps")
|
throw FatalAstException("do..until should have been converted to jumps")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(block: Block, parent: Node): Iterable<IAstModification> {
|
override fun before(block: Block, parent: Node): Iterable<IAstModification> {
|
||||||
@ -67,7 +67,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o
|
|||||||
&& !assignment.target.isIOAddress(options.compTarget.machine)) {
|
&& !assignment.target.isIOAddress(options.compTarget.machine)) {
|
||||||
val binExpr = assignment.value as? BinaryExpression
|
val binExpr = assignment.value as? BinaryExpression
|
||||||
|
|
||||||
if(binExpr!=null && binExpr.inferType(program).istype(DataType.FLOAT) && !options.optimizeFloatExpressions)
|
if(binExpr!=null && binExpr.inferType(program) istype DataType.FLOAT && !options.optimizeFloatExpressions)
|
||||||
return noModifications
|
return noModifications
|
||||||
|
|
||||||
if (binExpr != null && binExpr.operator !in ComparisonOperators) {
|
if (binExpr != null && binExpr.operator !in ComparisonOperators) {
|
||||||
@ -249,8 +249,8 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o
|
|||||||
var rightAssignment: Assignment? = null
|
var rightAssignment: Assignment? = null
|
||||||
var rightOperandReplacement: Expression? = null
|
var rightOperandReplacement: Expression? = null
|
||||||
|
|
||||||
val separateLeftExpr = !expr.left.isSimple && expr.left !is IFunctionCall
|
val separateLeftExpr = !expr.left.isSimple && expr.left !is IFunctionCall && expr.left !is ContainmentCheck
|
||||||
val separateRightExpr = !expr.right.isSimple && expr.right !is IFunctionCall
|
val separateRightExpr = !expr.right.isSimple && expr.right !is IFunctionCall && expr.right !is ContainmentCheck
|
||||||
val leftDt = expr.left.inferType(program)
|
val leftDt = expr.left.inferType(program)
|
||||||
val rightDt = expr.right.inferType(program)
|
val rightDt = expr.right.inferType(program)
|
||||||
|
|
||||||
@ -270,10 +270,10 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o
|
|||||||
}
|
}
|
||||||
if(separateRightExpr) {
|
if(separateRightExpr) {
|
||||||
val name = when {
|
val name = when {
|
||||||
rightDt.istype(DataType.UBYTE) -> listOf("prog8_lib","retval_interm_ub")
|
rightDt istype DataType.UBYTE -> listOf("prog8_lib","retval_interm_ub")
|
||||||
rightDt.istype(DataType.UWORD) -> listOf("prog8_lib","retval_interm_uw")
|
rightDt istype DataType.UWORD -> listOf("prog8_lib","retval_interm_uw")
|
||||||
rightDt.istype(DataType.BYTE) -> listOf("prog8_lib","retval_interm_b2")
|
rightDt istype DataType.BYTE -> listOf("prog8_lib","retval_interm_b2")
|
||||||
rightDt.istype(DataType.WORD) -> listOf("prog8_lib","retval_interm_w2")
|
rightDt istype DataType.WORD -> listOf("prog8_lib","retval_interm_w2")
|
||||||
else -> throw AssemblyError("invalid dt")
|
else -> throw AssemblyError("invalid dt")
|
||||||
}
|
}
|
||||||
rightOperandReplacement = IdentifierReference(name, expr.position)
|
rightOperandReplacement = IdentifierReference(name, expr.position)
|
||||||
|
@ -257,7 +257,7 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
|
|||||||
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||||
// perform initial syntax checks and processings
|
// perform initial syntax checks and processings
|
||||||
println("Processing for target ${compilerOptions.compTarget.name}...")
|
println("Processing for target ${compilerOptions.compTarget.name}...")
|
||||||
program.preprocessAst(program)
|
program.preprocessAst(program, errors)
|
||||||
program.checkIdentifiers(errors, program, compilerOptions)
|
program.checkIdentifiers(errors, program, compilerOptions)
|
||||||
errors.report()
|
errors.report()
|
||||||
// TODO: turning char literals into UBYTEs via an encoding should really happen in code gen - but for that we'd need DataType.CHAR
|
// TODO: turning char literals into UBYTEs via an encoding should really happen in code gen - but for that we'd need DataType.CHAR
|
||||||
|
@ -874,6 +874,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
|
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
|
||||||
errors.err("bitwise operator can only be used on integer operands", expr.right.position)
|
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(leftDt !in NumericDatatypes && leftDt != DataType.STR)
|
if(leftDt !in NumericDatatypes && leftDt != DataType.STR)
|
||||||
@ -1206,6 +1207,17 @@ internal class AstChecker(private val program: Program,
|
|||||||
super.visit(whenChoice)
|
super.visit(whenChoice)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visit(containment: ContainmentCheck) {
|
||||||
|
if(!containment.iterable.inferType(program).isIterable)
|
||||||
|
errors.err("value set for containment check must be an iterable type", containment.iterable.position)
|
||||||
|
if(containment.parent is BinaryExpression)
|
||||||
|
errors.err("containment check is currently not supported in complex expressions", containment.position)
|
||||||
|
|
||||||
|
// TODO check that iterable contains the same types as the element that is searched
|
||||||
|
|
||||||
|
super.visit(containment)
|
||||||
|
}
|
||||||
|
|
||||||
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? {
|
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? {
|
||||||
when (val targetStatement = target.targetStatement(program)) {
|
when (val targetStatement = target.targetStatement(program)) {
|
||||||
is Label, is Subroutine, is BuiltinFunctionPlaceholder -> return targetStatement
|
is Label, is Subroutine, is BuiltinFunctionPlaceholder -> return targetStatement
|
||||||
|
@ -72,8 +72,8 @@ internal fun Program.verifyFunctionArgTypes() {
|
|||||||
fixer.visit(this)
|
fixer.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.preprocessAst(program: Program) {
|
internal fun Program.preprocessAst(program: Program, errors: IErrorReporter) {
|
||||||
val transforms = AstPreprocessor(program)
|
val transforms = AstPreprocessor(program, errors)
|
||||||
transforms.visit(this)
|
transforms.visit(this)
|
||||||
var mods = transforms.applyModifications()
|
var mods = transforms.applyModifications()
|
||||||
while(mods>0)
|
while(mods>0)
|
||||||
@ -91,7 +91,8 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, program: Program,
|
|||||||
transforms.applyModifications()
|
transforms.applyModifications()
|
||||||
val lit2decl = LiteralsToAutoVars(this)
|
val lit2decl = LiteralsToAutoVars(this)
|
||||||
lit2decl.visit(this)
|
lit2decl.visit(this)
|
||||||
lit2decl.applyModifications()
|
if(errors.noErrors())
|
||||||
|
lit2decl.applyModifications()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,21 +2,15 @@ package prog8.compiler.astprocessing
|
|||||||
|
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.NumericDatatypes
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.SyntaxError
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.base.VarDeclType
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.expressions.IdentifierReference
|
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
|
||||||
import prog8.ast.expressions.RangeExpr
|
|
||||||
import prog8.ast.statements.AnonymousScope
|
|
||||||
import prog8.ast.statements.AssignTarget
|
|
||||||
import prog8.ast.statements.Assignment
|
|
||||||
import prog8.ast.statements.VarDecl
|
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
|
import prog8.compilerinterface.IErrorReporter
|
||||||
|
|
||||||
|
|
||||||
class AstPreprocessor(val program: Program) : AstWalker() {
|
class AstPreprocessor(val program: Program, val errors: IErrorReporter) : AstWalker() {
|
||||||
|
|
||||||
override fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> {
|
override fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> {
|
||||||
// has to be done before the constant folding, otherwise certain checks there will fail on invalid range sizes
|
// has to be done before the constant folding, otherwise certain checks there will fail on invalid range sizes
|
||||||
@ -82,4 +76,13 @@ class AstPreprocessor(val program: Program) : AstWalker() {
|
|||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
// this has to be done here becuse otherwise the string / range literal values will have been replaced by variables
|
||||||
|
if(expr.operator=="in") {
|
||||||
|
val containment = ContainmentCheck(expr.left, expr.right, expr.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, containment, parent))
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,7 @@ import prog8.ast.Node
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.ParentSentinel
|
import prog8.ast.base.ParentSentinel
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.expressions.DirectMemoryRead
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.expressions.FunctionCallExpr
|
|
||||||
import prog8.ast.expressions.IdentifierReference
|
|
||||||
import prog8.ast.expressions.PrefixExpression
|
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
@ -25,6 +22,8 @@ internal class CodeDesugarer(val program: Program, private val errors: IErrorRep
|
|||||||
//
|
//
|
||||||
// List of modifications:
|
// List of modifications:
|
||||||
// - replace 'break' statements by a goto + generated after label.
|
// - replace 'break' statements by a goto + generated after label.
|
||||||
|
// - replace while and do-until loops by just jumps.
|
||||||
|
// - replace peek() and poke() by direct memory accesses.
|
||||||
|
|
||||||
|
|
||||||
private var generatedLabelSequenceNumber: Int = 0
|
private var generatedLabelSequenceNumber: Int = 0
|
||||||
@ -135,4 +134,11 @@ _after:
|
|||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(expr.operator=="in") {
|
||||||
|
println("IN-TEST: $expr\n in: $parent")
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package prog8.compiler.astprocessing
|
package prog8.compiler.astprocessing
|
||||||
|
|
||||||
import prog8.ast.IStatementContainer
|
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.expressions.ArrayLiteralValue
|
import prog8.ast.expressions.ArrayLiteralValue
|
||||||
|
import prog8.ast.expressions.ContainmentCheck
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.StringLiteralValue
|
import prog8.ast.expressions.StringLiteralValue
|
||||||
import prog8.ast.statements.VarDecl
|
import prog8.ast.statements.VarDecl
|
||||||
@ -16,7 +16,7 @@ import prog8.ast.walk.IAstModification
|
|||||||
internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
|
internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
|
||||||
|
|
||||||
override fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> {
|
override fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> {
|
||||||
if(string.parent !is VarDecl && string.parent !is WhenChoice) {
|
if(string.parent !is VarDecl && string.parent !is WhenChoice && string.parent !is ContainmentCheck) {
|
||||||
// replace the literal string by an identifier reference to the interned string
|
// replace the literal string by an identifier reference to the interned string
|
||||||
val scopedName = program.internString(string)
|
val scopedName = program.internString(string)
|
||||||
val identifier = IdentifierReference(scopedName, string.position)
|
val identifier = IdentifierReference(scopedName, string.position)
|
||||||
@ -28,26 +28,27 @@ internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
|
|||||||
override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {
|
override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {
|
||||||
val vardecl = array.parent as? VarDecl
|
val vardecl = array.parent as? VarDecl
|
||||||
if(vardecl!=null) {
|
if(vardecl!=null) {
|
||||||
// adjust the datatype of the array (to an educated guess)
|
// adjust the datatype of the array (to an educated guess from the vardecl type)
|
||||||
val arrayDt = array.type
|
val arrayDt = array.type
|
||||||
if(arrayDt isnot vardecl.datatype) {
|
if(arrayDt isnot vardecl.datatype) {
|
||||||
val cast = array.cast(vardecl.datatype)
|
val cast = array.cast(vardecl.datatype)
|
||||||
if (cast != null && cast !== array)
|
if(cast!=null && cast !== array)
|
||||||
return listOf(IAstModification.ReplaceNode(vardecl.value!!, cast, vardecl))
|
return listOf(IAstModification.ReplaceNode(vardecl.value!!, cast, vardecl))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if(array.parent is ContainmentCheck)
|
||||||
|
return noModifications
|
||||||
|
|
||||||
val arrayDt = array.guessDatatype(program)
|
val arrayDt = array.guessDatatype(program)
|
||||||
if(arrayDt.isKnown) {
|
if(arrayDt.isKnown) {
|
||||||
// this array literal is part of an expression, turn it into an identifier reference
|
// turn the array literal it into an identifier reference
|
||||||
val litval2 = array.cast(arrayDt.getOr(DataType.UNDEFINED))
|
val litval2 = array.cast(arrayDt.getOr(DataType.UNDEFINED))
|
||||||
if(litval2!=null) {
|
if(litval2!=null) {
|
||||||
if(array.parent !is IStatementContainer)
|
|
||||||
return noModifications
|
|
||||||
val vardecl2 = VarDecl.createAuto(litval2)
|
val vardecl2 = VarDecl.createAuto(litval2)
|
||||||
val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position)
|
val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.ReplaceNode(array, identifier, parent),
|
IAstModification.ReplaceNode(array, identifier, parent),
|
||||||
IAstModification.InsertFirst(vardecl2, array.parent as IStatementContainer)
|
IAstModification.InsertFirst(vardecl2, array.definingScope)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ package prog8.compiler.astprocessing
|
|||||||
import prog8.ast.IStatementContainer
|
import prog8.ast.IStatementContainer
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.ArrayDatatypes
|
||||||
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
@ -91,6 +93,19 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter)
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(expr.operator == "or") {
|
||||||
|
val leftBinExpr = expr.left as? BinaryExpression
|
||||||
|
val rightBinExpr = expr.right as? BinaryExpression
|
||||||
|
if(leftBinExpr!=null && leftBinExpr.operator=="==" && rightBinExpr!=null && rightBinExpr.operator=="==") {
|
||||||
|
if(leftBinExpr.right is NumericLiteralValue && rightBinExpr.right is NumericLiteralValue) {
|
||||||
|
errors.warn("consider using 'in' or 'when' to test for multiple values", expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
if(expr.operator in ComparisonOperators) {
|
if(expr.operator in ComparisonOperators) {
|
||||||
val leftConstVal = expr.left.constValue(program)
|
val leftConstVal = expr.left.constValue(program)
|
||||||
@ -111,4 +126,76 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter)
|
|||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> {
|
||||||
|
// replace trivial containment checks with just false or a single comparison
|
||||||
|
fun replaceWithEquals(value: NumericLiteralValue): Iterable<IAstModification> {
|
||||||
|
errors.warn("containment could be written as just a single comparison", containment.position)
|
||||||
|
val equals = BinaryExpression(containment.element, "==", value, containment.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(containment, equals, parent))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun replaceWithFalse(): Iterable<IAstModification> {
|
||||||
|
errors.warn("condition is always false", containment.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(containment, NumericLiteralValue.fromBoolean(false, containment.position), parent))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkArray(array: Array<Expression>): Iterable<IAstModification> {
|
||||||
|
if(array.isEmpty())
|
||||||
|
return replaceWithFalse()
|
||||||
|
if(array.size==1) {
|
||||||
|
val constVal = array[0].constValue(program)
|
||||||
|
if(constVal!=null)
|
||||||
|
return replaceWithEquals(constVal)
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkString(stringVal: StringLiteralValue): Iterable<IAstModification> {
|
||||||
|
if(stringVal.value.isEmpty())
|
||||||
|
return replaceWithFalse()
|
||||||
|
if(stringVal.value.length==1) {
|
||||||
|
val string = program.encoding.encodeString(stringVal.value, stringVal.altEncoding)
|
||||||
|
return replaceWithEquals(NumericLiteralValue(DataType.UBYTE, string[0].toDouble(), stringVal.position))
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
when(containment.iterable) {
|
||||||
|
is ArrayLiteralValue -> {
|
||||||
|
val array = (containment.iterable as ArrayLiteralValue).value
|
||||||
|
return checkArray(array)
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val variable = (containment.iterable as IdentifierReference).targetVarDecl(program)!!
|
||||||
|
when(variable.datatype) {
|
||||||
|
DataType.STR -> {
|
||||||
|
val stringVal = (variable.value as StringLiteralValue)
|
||||||
|
return checkString(stringVal)
|
||||||
|
}
|
||||||
|
in ArrayDatatypes -> {
|
||||||
|
val array = (variable.value as ArrayLiteralValue).value
|
||||||
|
return checkArray(array)
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RangeExpr -> {
|
||||||
|
val constValues = (containment.iterable as RangeExpr).toConstantIntegerRange()
|
||||||
|
if(constValues!=null) {
|
||||||
|
if (constValues.isEmpty())
|
||||||
|
return replaceWithFalse()
|
||||||
|
if (constValues.count()==1)
|
||||||
|
return replaceWithEquals(NumericLiteralValue.optimalNumeric(constValues.first, containment.position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is StringLiteralValue -> {
|
||||||
|
val stringVal = containment.iterable as StringLiteralValue
|
||||||
|
return checkString(stringVal)
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,9 +11,6 @@ import prog8.ast.statements.FunctionCallStatement
|
|||||||
import prog8.ast.statements.Label
|
import prog8.ast.statements.Label
|
||||||
import prog8.codegen.target.Cx16Target
|
import prog8.codegen.target.Cx16Target
|
||||||
import prog8tests.helpers.*
|
import prog8tests.helpers.*
|
||||||
import prog8tests.helpers.assertFailure
|
|
||||||
import prog8tests.helpers.assertSuccess
|
|
||||||
import prog8tests.helpers.compileFile
|
|
||||||
import kotlin.io.path.name
|
import kotlin.io.path.name
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,8 +12,6 @@ import prog8.ast.statements.ForLoop
|
|||||||
import prog8.ast.statements.VarDecl
|
import prog8.ast.statements.VarDecl
|
||||||
import prog8.codegen.target.C64Target
|
import prog8.codegen.target.C64Target
|
||||||
import prog8.codegen.target.Cx16Target
|
import prog8.codegen.target.Cx16Target
|
||||||
import prog8.compilerinterface.size
|
|
||||||
import prog8.compilerinterface.toConstantIntegerRange
|
|
||||||
import prog8tests.helpers.*
|
import prog8tests.helpers.*
|
||||||
import prog8tests.helpers.ErrorReporterForTests
|
import prog8tests.helpers.ErrorReporterForTests
|
||||||
import prog8tests.helpers.assertFailure
|
import prog8tests.helpers.assertFailure
|
||||||
@ -301,4 +299,133 @@ class TestCompilerOnRanges: FunSpec({
|
|||||||
forloop.iterable shouldBe instanceOf<RangeExpr>()
|
forloop.iterable shouldBe instanceOf<RangeExpr>()
|
||||||
(forloop.iterable as RangeExpr).step shouldBe NumericLiteralValue(DataType.UBYTE, -2.0, Position.DUMMY)
|
(forloop.iterable as RangeExpr).step shouldBe NumericLiteralValue(DataType.UBYTE, -2.0, Position.DUMMY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
test("for statement on all possible iterable expressions") {
|
||||||
|
compileText(C64Target, false, """
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
ubyte xx
|
||||||
|
uword ww
|
||||||
|
str name = "irmen"
|
||||||
|
ubyte[] values = [1,2,3,4,5,6,7]
|
||||||
|
uword[] wvalues = [1000,2000,3000]
|
||||||
|
|
||||||
|
for xx in name {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
|
||||||
|
for xx in values {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
|
||||||
|
for xx in 10 to 20 step 3 {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
|
||||||
|
for xx in "abcdef" {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
|
||||||
|
for xx in [2,4,6,8] {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
|
||||||
|
for ww in [9999,8888,7777] {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
|
||||||
|
for ww in wvalues {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}""", writeAssembly = true).assertSuccess()
|
||||||
|
}
|
||||||
|
|
||||||
|
test("if containment check on all possible iterable expressions") {
|
||||||
|
compileText(C64Target, false, """
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
ubyte xx
|
||||||
|
uword ww
|
||||||
|
str name = "irmen"
|
||||||
|
ubyte[] values = [1,2,3,4,5,6,7]
|
||||||
|
uword[] wvalues = [1000,2000,3000]
|
||||||
|
|
||||||
|
if 'm' in name {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
|
||||||
|
if 5 in values {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
|
||||||
|
if 16 in 10 to 20 step 3 {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
|
||||||
|
if 'b' in "abcdef" {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
|
||||||
|
if 8 in [2,4,6,8] {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
|
||||||
|
if xx in name {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
|
||||||
|
if xx in values {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
|
||||||
|
if xx in 10 to 20 step 3 {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
|
||||||
|
if xx in "abcdef" {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
|
||||||
|
if xx in [2,4,6,8] {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
|
||||||
|
if ww in [9999,8888,7777] {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
|
||||||
|
if ww in wvalues {
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}""", writeAssembly = true).assertSuccess()
|
||||||
|
}
|
||||||
|
|
||||||
|
test("containment check in expressions") {
|
||||||
|
compileText(C64Target, false, """
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
ubyte xx
|
||||||
|
uword ww
|
||||||
|
str name = "irmen"
|
||||||
|
ubyte[] values = [1,2,3,4,5,6,7]
|
||||||
|
uword[] wvalues = [1000,2000,3000]
|
||||||
|
|
||||||
|
xx = 'm' in name
|
||||||
|
xx = 5 in values
|
||||||
|
xx = 16 in 10 to 20 step 3
|
||||||
|
xx = 'b' in "abcdef"
|
||||||
|
xx = 8 in [2,4,6,8]
|
||||||
|
xx = xx in name
|
||||||
|
xx = xx in values
|
||||||
|
xx = xx in 10 to 20 step 3
|
||||||
|
xx = xx in "abcdef"
|
||||||
|
xx = xx in [2,4,6,8]
|
||||||
|
xx = ww in [9000,8000,7000]
|
||||||
|
xx = ww in wvalues
|
||||||
|
}
|
||||||
|
}""", writeAssembly = true).assertSuccess()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
@ -278,7 +278,7 @@ class TestOptimization: FunSpec({
|
|||||||
|
|
||||||
wwAssign.target.identifier?.nameInSource shouldBe listOf("ww")
|
wwAssign.target.identifier?.nameInSource shouldBe listOf("ww")
|
||||||
expr.type shouldBe DataType.UWORD
|
expr.type shouldBe DataType.UWORD
|
||||||
expr.expression.inferType(result.program).istype(DataType.UBYTE) shouldBe true
|
expr.expression.inferType(result.program) istype DataType.UBYTE shouldBe true
|
||||||
}
|
}
|
||||||
|
|
||||||
test("intermediate assignment steps have correct types for codegen phase (BeforeAsmGenerationAstChanger)") {
|
test("intermediate assignment steps have correct types for codegen phase (BeforeAsmGenerationAstChanger)") {
|
||||||
|
@ -48,6 +48,12 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
|
|||||||
outputln("}\n")
|
outputln("}\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visit(containment: ContainmentCheck) {
|
||||||
|
containment.element.accept(this)
|
||||||
|
output(" in ")
|
||||||
|
containment.iterable.accept(this)
|
||||||
|
}
|
||||||
|
|
||||||
override fun visit(expr: PrefixExpression) {
|
override fun visit(expr: PrefixExpression) {
|
||||||
if(expr.operator.any { it.isLetter() })
|
if(expr.operator.any { it.isLetter() })
|
||||||
output(" ${expr.operator} ")
|
output(" ${expr.operator} ")
|
||||||
|
@ -4,6 +4,7 @@ import prog8.ast.base.DataType
|
|||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.base.VarDeclType
|
import prog8.ast.base.VarDeclType
|
||||||
|
import prog8.ast.expressions.ContainmentCheck
|
||||||
import prog8.ast.expressions.StringLiteralValue
|
import prog8.ast.expressions.StringLiteralValue
|
||||||
import prog8.ast.statements.Block
|
import prog8.ast.statements.Block
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
@ -82,7 +83,7 @@ class Program(val name: String,
|
|||||||
// Move a string literal into the internal, deduplicated, string pool
|
// Move a string literal into the internal, deduplicated, string pool
|
||||||
// replace it with a variable declaration that points to the entry in the pool.
|
// replace it with a variable declaration that points to the entry in the pool.
|
||||||
|
|
||||||
if(string.parent is VarDecl) {
|
if(string.parent is VarDecl || string.parent is ContainmentCheck) {
|
||||||
// deduplication can only be performed safely for known-const strings (=string literals OUTSIDE OF A VARDECL)!
|
// deduplication can only be performed safely for known-const strings (=string literals OUTSIDE OF A VARDECL)!
|
||||||
throw FatalAstException("cannot intern a string literal that's part of a vardecl")
|
throw FatalAstException("cannot intern a string literal that's part of a vardecl")
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import prog8.ast.statements.*
|
|||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.math.abs
|
||||||
import kotlin.math.round
|
import kotlin.math.round
|
||||||
|
|
||||||
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
||||||
@ -219,6 +220,7 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
|
|||||||
"<=", ">=",
|
"<=", ">=",
|
||||||
"==", "!=" -> InferredTypes.knownFor(DataType.UBYTE)
|
"==", "!=" -> InferredTypes.knownFor(DataType.UBYTE)
|
||||||
"<<", ">>" -> leftDt
|
"<<", ">>" -> leftDt
|
||||||
|
"in" -> InferredTypes.knownFor(DataType.UBYTE)
|
||||||
else -> throw FatalAstException("resulting datatype check for invalid operator $operator")
|
else -> throw FatalAstException("resulting datatype check for invalid operator $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -801,6 +803,42 @@ class RangeExpr(var from: Expression,
|
|||||||
return "RangeExpr(from $from, to $to, step $step, pos=$position)"
|
return "RangeExpr(from $from, to $to, step $step, pos=$position)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun toConstantIntegerRange(): IntProgression? {
|
||||||
|
|
||||||
|
fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
|
||||||
|
return when {
|
||||||
|
fromVal <= toVal -> when {
|
||||||
|
stepVal <= 0 -> IntRange.EMPTY
|
||||||
|
stepVal == 1 -> fromVal..toVal
|
||||||
|
else -> fromVal..toVal step stepVal
|
||||||
|
}
|
||||||
|
else -> when {
|
||||||
|
stepVal >= 0 -> IntRange.EMPTY
|
||||||
|
stepVal == -1 -> fromVal downTo toVal
|
||||||
|
else -> fromVal downTo toVal step abs(stepVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val fromLv = from as? NumericLiteralValue
|
||||||
|
val toLv = to as? NumericLiteralValue
|
||||||
|
val stepLv = step as? NumericLiteralValue
|
||||||
|
if(fromLv==null || toLv==null || stepLv==null)
|
||||||
|
return null
|
||||||
|
val fromVal = fromLv.number.toInt()
|
||||||
|
val toVal = toLv.number.toInt()
|
||||||
|
val stepVal = stepLv.number.toInt()
|
||||||
|
return makeRange(fromVal, toVal, stepVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun size(): Int? {
|
||||||
|
val fromLv = (from as? NumericLiteralValue)
|
||||||
|
val toLv = (to as? NumericLiteralValue)
|
||||||
|
if(fromLv==null || toLv==null)
|
||||||
|
return null
|
||||||
|
return toConstantIntegerRange()?.count()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -951,6 +989,90 @@ class FunctionCallExpr(override var target: IdentifierReference,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ContainmentCheck(var element: Expression,
|
||||||
|
var iterable: Expression,
|
||||||
|
override val position: Position): Expression() {
|
||||||
|
|
||||||
|
override lateinit var parent: Node
|
||||||
|
|
||||||
|
override fun linkParents(parent: Node) {
|
||||||
|
this.parent = parent
|
||||||
|
element.parent = this
|
||||||
|
iterable.linkParents(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val isSimple: Boolean = false
|
||||||
|
override fun copy() = ContainmentCheck(element.copy(), iterable.copy(), position)
|
||||||
|
override fun constValue(program: Program): NumericLiteralValue? {
|
||||||
|
val elementConst = element.constValue(program)
|
||||||
|
if(elementConst!=null) {
|
||||||
|
when(iterable){
|
||||||
|
is ArrayLiteralValue -> {
|
||||||
|
val exists = (iterable as ArrayLiteralValue).value.any { it.constValue(program)==elementConst }
|
||||||
|
return NumericLiteralValue.fromBoolean(exists, position)
|
||||||
|
}
|
||||||
|
is RangeExpr -> {
|
||||||
|
val intRange = (iterable as RangeExpr).toConstantIntegerRange()
|
||||||
|
if(intRange!=null && elementConst.type in IntegerDatatypes) {
|
||||||
|
val exists = elementConst.number.toInt() in intRange
|
||||||
|
return NumericLiteralValue.fromBoolean(exists, position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is StringLiteralValue -> {
|
||||||
|
if(elementConst.type in ByteDatatypes) {
|
||||||
|
val stringval = iterable as StringLiteralValue
|
||||||
|
val exists = program.encoding.encodeString(stringval.value, stringval.altEncoding).contains(elementConst.number.toInt().toUByte() )
|
||||||
|
return NumericLiteralValue.fromBoolean(exists, position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when(iterable){
|
||||||
|
is ArrayLiteralValue -> {
|
||||||
|
val array= iterable as ArrayLiteralValue
|
||||||
|
if(array.value.isEmpty())
|
||||||
|
return NumericLiteralValue.fromBoolean(false, position)
|
||||||
|
}
|
||||||
|
is RangeExpr -> {
|
||||||
|
val size = (iterable as RangeExpr).size()
|
||||||
|
if(size!=null && size==0)
|
||||||
|
return NumericLiteralValue.fromBoolean(false, position)
|
||||||
|
}
|
||||||
|
is StringLiteralValue -> {
|
||||||
|
if((iterable as StringLiteralValue).value.isEmpty())
|
||||||
|
return NumericLiteralValue.fromBoolean(false, position)
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
override fun referencesIdentifier(nameInSource: List<String>): Boolean {
|
||||||
|
if(element is IdentifierReference)
|
||||||
|
return element.referencesIdentifier(nameInSource)
|
||||||
|
return iterable?.referencesIdentifier(nameInSource) ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun inferType(program: Program) = InferredTypes.knownFor(DataType.UBYTE)
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
if(replacement !is Expression)
|
||||||
|
throw FatalAstException("invalid replace")
|
||||||
|
if(node===element)
|
||||||
|
element=replacement
|
||||||
|
else if(node===iterable)
|
||||||
|
iterable=replacement
|
||||||
|
else
|
||||||
|
throw FatalAstException("invalid replace")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun invertCondition(cond: Expression): BinaryExpression? {
|
fun invertCondition(cond: Expression): BinaryExpression? {
|
||||||
if(cond is BinaryExpression) {
|
if(cond is BinaryExpression) {
|
||||||
val invertedOperator = invertedComparisonOperator(cond.operator)
|
val invertedOperator = invertedComparisonOperator(cond.operator)
|
||||||
|
@ -87,6 +87,7 @@ abstract class AstWalker {
|
|||||||
open fun before(block: Block, parent: Node): Iterable<IAstModification> = noModifications
|
open fun before(block: Block, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun before(branch: Branch, parent: Node): Iterable<IAstModification> = noModifications
|
open fun before(branch: Branch, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> = noModifications
|
open fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun before(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> = noModifications
|
open fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun before(directive: Directive, parent: Node): Iterable<IAstModification> = noModifications
|
open fun before(directive: Directive, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = noModifications
|
open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
@ -128,6 +129,7 @@ abstract class AstWalker {
|
|||||||
open fun after(block: Block, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(block: Block, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(branch: Branch, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(branch: Branch, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(breakStmt: Break, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(breakStmt: Break, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(builtinFunctionPlaceholder: BuiltinFunctionPlaceholder, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(builtinFunctionPlaceholder: BuiltinFunctionPlaceholder, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(directive: Directive, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(directive: Directive, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
@ -228,6 +230,13 @@ abstract class AstWalker {
|
|||||||
track(after(directive, parent), directive, parent)
|
track(after(directive, parent), directive, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun visit(containment: ContainmentCheck, parent: Node) {
|
||||||
|
track(before(containment, parent), containment, parent)
|
||||||
|
containment.element.accept(this, containment)
|
||||||
|
containment.iterable?.accept(this, containment)
|
||||||
|
track(after(containment, parent), containment, parent)
|
||||||
|
}
|
||||||
|
|
||||||
fun visit(block: Block, parent: Node) {
|
fun visit(block: Block, parent: Node) {
|
||||||
track(before(block, parent), block, parent)
|
track(before(block, parent), block, parent)
|
||||||
block.statements.forEach { it.accept(this, block) }
|
block.statements.forEach { it.accept(this, block) }
|
||||||
|
@ -26,6 +26,11 @@ interface IAstVisitor {
|
|||||||
fun visit(directive: Directive) {
|
fun visit(directive: Directive) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun visit(containment: ContainmentCheck) {
|
||||||
|
containment.element.accept(this)
|
||||||
|
containment.iterable?.accept(this)
|
||||||
|
}
|
||||||
|
|
||||||
fun visit(block: Block) {
|
fun visit(block: Block) {
|
||||||
block.statements.forEach { it.accept(this) }
|
block.statements.forEach { it.accept(this) }
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,7 @@ import prog8.ast.base.FatalAstException
|
|||||||
import prog8.ast.base.VarDeclType
|
import prog8.ast.base.VarDeclType
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.expressions.RangeExpr
|
|
||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
import kotlin.math.abs
|
|
||||||
|
|
||||||
fun AssignTarget.isIOAddress(machine: IMachineDefinition): Boolean {
|
fun AssignTarget.isIOAddress(machine: IMachineDefinition): Boolean {
|
||||||
val memAddr = memoryAddress
|
val memAddr = memoryAddress
|
||||||
@ -49,39 +47,3 @@ fun AssignTarget.isIOAddress(machine: IMachineDefinition): Boolean {
|
|||||||
else -> return false
|
else -> return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RangeExpr.toConstantIntegerRange(): IntProgression? {
|
|
||||||
|
|
||||||
fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
|
|
||||||
return when {
|
|
||||||
fromVal <= toVal -> when {
|
|
||||||
stepVal <= 0 -> IntRange.EMPTY
|
|
||||||
stepVal == 1 -> fromVal..toVal
|
|
||||||
else -> fromVal..toVal step stepVal
|
|
||||||
}
|
|
||||||
else -> when {
|
|
||||||
stepVal >= 0 -> IntRange.EMPTY
|
|
||||||
stepVal == -1 -> fromVal downTo toVal
|
|
||||||
else -> fromVal downTo toVal step abs(stepVal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val fromLv = from as? NumericLiteralValue
|
|
||||||
val toLv = to as? NumericLiteralValue
|
|
||||||
val stepLv = step as? NumericLiteralValue
|
|
||||||
if(fromLv==null || toLv==null || stepLv==null)
|
|
||||||
return null
|
|
||||||
val fromVal = fromLv.number.toInt()
|
|
||||||
val toVal = toLv.number.toInt()
|
|
||||||
val stepVal = stepLv.number.toInt()
|
|
||||||
return makeRange(fromVal, toVal, stepVal)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun RangeExpr.size(): Int? {
|
|
||||||
val fromLv = (from as? NumericLiteralValue)
|
|
||||||
val toLv = (to as? NumericLiteralValue)
|
|
||||||
if(fromLv==null || toLv==null)
|
|
||||||
return null
|
|
||||||
return toConstantIntegerRange()?.count()
|
|
||||||
}
|
|
||||||
|
@ -283,6 +283,8 @@ It's possible to assign a new array to another array, this will overwrite all el
|
|||||||
array with those in the value array. The number and types of elements have to match.
|
array with those in the value array. The number and types of elements have to match.
|
||||||
For large arrays this is a slow operation because every element is copied over. It should probably be avoided.
|
For large arrays this is a slow operation because every element is copied over. It should probably be avoided.
|
||||||
|
|
||||||
|
Using the ``in`` operator you can easily check if a value is present in an array,
|
||||||
|
example: ``if choice in [1,2,3,4] {....}``
|
||||||
|
|
||||||
**Arrays at a specific memory location:**
|
**Arrays at a specific memory location:**
|
||||||
Using the memory-mapped syntax it is possible to define an array to be located at a specific memory location.
|
Using the memory-mapped syntax it is possible to define an array to be located at a specific memory location.
|
||||||
@ -332,6 +334,11 @@ as newlines, quote characters themselves, and so on. The ones used most often ar
|
|||||||
``\\``, ``\"``, ``\n``, ``\r``. For a detailed description of all of them and what they mean,
|
``\\``, ``\"``, ``\n``, ``\r``. For a detailed description of all of them and what they mean,
|
||||||
read the syntax reference on strings.
|
read the syntax reference on strings.
|
||||||
|
|
||||||
|
Using the ``in`` operator you can easily check if a characater is present in a string,
|
||||||
|
example: ``if '@' in email_address {....}`` (however this gives no clue about the location
|
||||||
|
in the string where the character is present, if you need that, use the ``string.find()``
|
||||||
|
library function instead)
|
||||||
|
|
||||||
.. hint::
|
.. hint::
|
||||||
Strings/arrays and uwords (=memory address) can often be interchanged.
|
Strings/arrays and uwords (=memory address) can often be interchanged.
|
||||||
An array of strings is actually an array of uwords where every element is the memory
|
An array of strings is actually an array of uwords where every element is the memory
|
||||||
@ -542,6 +549,9 @@ The when-*value* can be any expression but the choice values have to evaluate to
|
|||||||
compile-time constant integers (bytes or words). They also have to be the same
|
compile-time constant integers (bytes or words). They also have to be the same
|
||||||
datatype as the when-value, otherwise no efficient comparison can be done.
|
datatype as the when-value, otherwise no efficient comparison can be done.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Instead of chaining several value equality checks together using ``or`` (ex.: ``if x==1 or xx==5 or xx==9``),
|
||||||
|
consider using a ``when`` statement or ``in`` containment check instead. These are more efficient.
|
||||||
|
|
||||||
Assignments
|
Assignments
|
||||||
-----------
|
-----------
|
||||||
@ -587,6 +597,9 @@ Expressions can contain procedure and function calls.
|
|||||||
There are various built-in functions such as sin(), cos(), min(), max() that can be used in expressions (see :ref:`builtinfunctions`).
|
There are various built-in functions such as sin(), cos(), min(), max() that can be used in expressions (see :ref:`builtinfunctions`).
|
||||||
You can also reference idendifiers defined elsewhere in your code.
|
You can also reference idendifiers defined elsewhere in your code.
|
||||||
|
|
||||||
|
Read the :ref:`syntaxreference` chapter for all details on the available operators and kinds of expressions you can write.
|
||||||
|
|
||||||
|
|
||||||
.. attention::
|
.. attention::
|
||||||
**Floating points used in expressions:**
|
**Floating points used in expressions:**
|
||||||
|
|
||||||
|
@ -497,6 +497,24 @@ range creation: ``to``
|
|||||||
; i loops 0, 1, 2, ... 127
|
; i loops 0, 1, 2, ... 127
|
||||||
}
|
}
|
||||||
|
|
||||||
|
containment check: ``in``
|
||||||
|
Tests if a value is present in a list of values, which can be a string or an array.
|
||||||
|
The result is a simple boolean ``true`` or ``false``.
|
||||||
|
Consider using this instead of chaining multiple value tests with ``or``, because the
|
||||||
|
containment check is more efficient.
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
ubyte cc
|
||||||
|
if cc in [' ', '@', 0] {
|
||||||
|
txt.print("cc is one of the values")
|
||||||
|
}
|
||||||
|
|
||||||
|
str email_address = "?????????"
|
||||||
|
if '@' in email_address {
|
||||||
|
txt.print("email address seems ok")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
address of: ``&``
|
address of: ``&``
|
||||||
This is a prefix operator that can be applied to a string or array variable or literal value.
|
This is a prefix operator that can be applied to a string or array variable or literal value.
|
||||||
It results in the memory address (UWORD) of that string or array in memory: ``uword a = &stringvar``
|
It results in the memory address (UWORD) of that string or array in memory: ``uword a = &stringvar``
|
||||||
@ -799,4 +817,3 @@ case you have to use { } to enclose them::
|
|||||||
}
|
}
|
||||||
else -> txt.print("don't know")
|
else -> txt.print("don't know")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,12 +13,16 @@ Blocked by an official Commander-x16 v39 release
|
|||||||
|
|
||||||
Future
|
Future
|
||||||
^^^^^^
|
^^^^^^
|
||||||
- rethink the whole "isAugmentable" business. Because the way this is determined, should always also be exactly mirrorred in the AugmentableAssignmentAsmGen or you'll get a crash at code gen time.
|
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as ``v_``
|
||||||
- simplifyConditionalExpression() should not split expression if it still results in stack-based evaluation
|
then we can get rid of the instruction lists in the machinedefinitions as well?
|
||||||
- fix the asm-labels problem (github issue #62)
|
- fix the asm-labels problem (github issue #62)
|
||||||
|
- make (an option) to let 64tass produce a listing file as well as output.
|
||||||
|
- simplifyConditionalExpression() should not split expression if it still results in stack-based evaluation
|
||||||
|
- simplifyConditionalExpression() sometimes introduces needless assignment to r9 tempvar
|
||||||
- get rid of all TODO's in the code
|
- get rid of all TODO's in the code
|
||||||
- improve testability further, add more tests
|
- improve testability further, add more tests
|
||||||
- use more of Result<> and Either<> to handle errors/ nulls better
|
- use more of Result<> and Either<> to handle errors/ nulls better
|
||||||
|
- rethink the whole "isAugmentable" business. Because the way this is determined, should always also be exactly mirrorred in the AugmentableAssignmentAsmGen or you'll get a crash at code gen time.
|
||||||
- can we get rid of pieces of asmgen.AssignmentAsmGen by just reusing the AugmentableAssignment ? generated code should not suffer
|
- can we get rid of pieces of asmgen.AssignmentAsmGen by just reusing the AugmentableAssignment ? generated code should not suffer
|
||||||
- add a switch to not create the globals-initialization logic, but instead create a smaller program (that can only run once though)
|
- add a switch to not create the globals-initialization logic, but instead create a smaller program (that can only run once though)
|
||||||
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
|
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
|
||||||
@ -27,7 +31,7 @@ Future
|
|||||||
- add a flood fill routine to gfx2?
|
- add a flood fill routine to gfx2?
|
||||||
- add a diskio.f_seek() routine for the Cx16 that uses its seek dos api?
|
- add a diskio.f_seek() routine for the Cx16 that uses its seek dos api?
|
||||||
- make it possible for diskio to read and write from more than one file at the same time (= use multiple io channels)?
|
- make it possible for diskio to read and write from more than one file at the same time (= use multiple io channels)?
|
||||||
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as ``v_``
|
- fix problems in c128 target
|
||||||
- [problematic due to 64tass:] add a compiler option to not remove unused subroutines. this allows for building library programs. But this won't work with 64tass's .proc ...
|
- [problematic due to 64tass:] add a compiler option to not remove unused subroutines. this allows for building library programs. But this won't work with 64tass's .proc ...
|
||||||
Perhaps replace all uses of .proc/.pend by .block/.bend will fix that?
|
Perhaps replace all uses of .proc/.pend by .block/.bend will fix that?
|
||||||
(but we lose the optimizing aspect of the assembler where it strips out unused code.
|
(but we lose the optimizing aspect of the assembler where it strips out unused code.
|
||||||
@ -36,13 +40,10 @@ Future
|
|||||||
|
|
||||||
More code optimization ideas
|
More code optimization ideas
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
- automatically convert if statements that test for multiple values (if X==1 or X==2..) to if X in [1,2,..] statements, instead of just a warning
|
||||||
|
-
|
||||||
- byte typed expressions should be evaluated in the accumulator where possible, without (temp)var
|
- byte typed expressions should be evaluated in the accumulator where possible, without (temp)var
|
||||||
for instance value = otherbyte >> 1 --> lda otherbite ; lsr a; sta value
|
for instance value = otherbyte >> 1 --> lda otherbite ; lsr a; sta value
|
||||||
- rewrite multiple choice if into when:
|
|
||||||
if X==1 or X==2 or X==3 { truepart } else { falsepart }
|
|
||||||
-> when X { 1,2,3->truepart else->falsepart }
|
|
||||||
same with assignment if the lhs is simple var or memaddr
|
|
||||||
|
|
||||||
- rewrite expression tree evaluation such that it doesn't use an eval stack but flatten the tree into linear code that uses a fixed number of predetermined value 'variables'
|
- rewrite expression tree evaluation such that it doesn't use an eval stack but flatten the tree into linear code that uses a fixed number of predetermined value 'variables'
|
||||||
- this removes the need for the BinExprSplitter? (which is problematic and very limited now)
|
- this removes the need for the BinExprSplitter? (which is problematic and very limited now)
|
||||||
- introduce byte-index operator to avoid index multiplications in loops over arrays? see github issue #4
|
- introduce byte-index operator to avoid index multiplications in loops over arrays? see github issue #4
|
||||||
|
@ -167,6 +167,7 @@ expression :
|
|||||||
| left = expression EOL? bop = '|' EOL? right = expression
|
| left = expression EOL? bop = '|' EOL? right = expression
|
||||||
| left = expression EOL? bop = ('==' | '!=') EOL? right = expression
|
| left = expression EOL? bop = ('==' | '!=') EOL? right = expression
|
||||||
| rangefrom = expression rto = ('to'|'downto') rangeto = expression ('step' rangestep = expression)? // can't create separate rule due to mutual left-recursion
|
| rangefrom = expression rto = ('to'|'downto') rangeto = expression ('step' rangestep = expression)? // can't create separate rule due to mutual left-recursion
|
||||||
|
| left = expression EOL? bop = 'in' EOL? right = expression
|
||||||
| prefix = 'not' expression
|
| prefix = 'not' expression
|
||||||
| left = expression EOL? bop = 'and' EOL? right = expression
|
| left = expression EOL? bop = 'and' EOL? right = expression
|
||||||
| left = expression EOL? bop = 'or' EOL? right = expression
|
| left = expression EOL? bop = 'or' EOL? right = expression
|
||||||
|
Loading…
x
Reference in New Issue
Block a user