mirror of
https://github.com/irmen/prog8.git
synced 2025-06-18 08:23:37 +00:00
Compare commits
44 Commits
Author | SHA1 | Date | |
---|---|---|---|
b37231d0f5 | |||
3c55719bf1 | |||
af8279a9b9 | |||
c38508c262 | |||
b0e8738ab8 | |||
cae480768e | |||
a70276c190 | |||
0c461ffe2e | |||
237511f2d6 | |||
cdcb652033 | |||
71e678b382 | |||
3050156325 | |||
4bfdbad2e4 | |||
06137ecdc4 | |||
d89f5b0df8 | |||
b6e2b36692 | |||
a6d789cfbc | |||
c07907e7bd | |||
7d8496c874 | |||
164ac56db1 | |||
fdddb8ca64 | |||
a9d4b8b0fa | |||
ec7b9f54c2 | |||
307558a7e7 | |||
febf423eab | |||
a999c23014 | |||
69f1ade595 | |||
b166576e54 | |||
ee2ba5f398 | |||
cb9825484d | |||
76cda82e23 | |||
37b61d9e6b | |||
52f0222a6d | |||
75ccac2f2c | |||
5c771a91f7 | |||
a242ad10e6 | |||
b5086b6a8f | |||
3e47dad12a | |||
235610f40c | |||
6b59559c65 | |||
23e954f716 | |||
983c899cad | |||
c2f9385965 | |||
ceb2c9e4f8 |
@ -18,7 +18,7 @@ which aims to provide many conveniences over raw assembly code (even when using
|
|||||||
- modularity, symbol scoping, subroutines
|
- modularity, symbol scoping, subroutines
|
||||||
- various data types other than just bytes (16-bit words, floats, strings)
|
- various data types other than just bytes (16-bit words, floats, strings)
|
||||||
- automatic variable allocations, automatic string and array variables and string sharing
|
- automatic variable allocations, automatic string and array variables and string sharing
|
||||||
- subroutines with a input- and output parameter signature
|
- subroutines with an input- and output parameter signature
|
||||||
- constant folding in expressions
|
- constant folding in expressions
|
||||||
- conditional branches
|
- conditional branches
|
||||||
- 'when' statement to provide a concise jump table alternative to if/elseif chains
|
- 'when' statement to provide a concise jump table alternative to if/elseif chains
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 8.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.0 KiB |
@ -781,3 +781,18 @@ set_array_float .proc
|
|||||||
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
||||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
swap_floats .proc
|
||||||
|
; -- swap floats pointed to by SCRATCH_ZPWORD1, SCRATCH_ZPWORD2
|
||||||
|
ldy #4
|
||||||
|
- lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
pha
|
||||||
|
lda (c64.SCRATCH_ZPWORD2),y
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
pla
|
||||||
|
sta (c64.SCRATCH_ZPWORD2),y
|
||||||
|
dey
|
||||||
|
bpl -
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
@ -2078,3 +2078,127 @@ ror2_array_uw .proc
|
|||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
+ rts
|
+ rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
strcpy .proc
|
||||||
|
; copy a string (0-terminated) from A/Y to (ZPWORD1)
|
||||||
|
; it is assumed the target string is large enough.
|
||||||
|
sta c64.SCRATCH_ZPWORD2
|
||||||
|
sty c64.SCRATCH_ZPWORD2+1
|
||||||
|
ldy #$ff
|
||||||
|
- iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD2),y
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
bne -
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
func_leftstr .proc
|
||||||
|
; leftstr(source, target, length) with params on stack
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
tay ; length
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD2
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD2+1
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
lda #0
|
||||||
|
sta (c64.SCRATCH_ZPWORD2),y
|
||||||
|
- dey
|
||||||
|
cpy #$ff
|
||||||
|
bne +
|
||||||
|
rts
|
||||||
|
+ lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
sta (c64.SCRATCH_ZPWORD2),y
|
||||||
|
jmp -
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_rightstr .proc
|
||||||
|
; rightstr(source, target, length) with params on stack
|
||||||
|
; make place for the 4 parameters for substr()
|
||||||
|
dex
|
||||||
|
dex
|
||||||
|
dex
|
||||||
|
dex
|
||||||
|
; X-> .
|
||||||
|
; x+1 -> length of segment
|
||||||
|
; x+2 -> start index
|
||||||
|
; X+3 -> target LO+HI
|
||||||
|
; X+4 -> source LO+HI
|
||||||
|
; original parameters:
|
||||||
|
; x+5 -> original length LO
|
||||||
|
; x+6 -> original targetLO + HI
|
||||||
|
; x+7 -> original sourceLO + HI
|
||||||
|
; replicate paramters:
|
||||||
|
lda c64.ESTACK_LO+5,x
|
||||||
|
sta c64.ESTACK_LO+1,x
|
||||||
|
lda c64.ESTACK_LO+6,x
|
||||||
|
sta c64.ESTACK_LO+3,x
|
||||||
|
lda c64.ESTACK_HI+6,x
|
||||||
|
sta c64.ESTACK_HI+3,x
|
||||||
|
lda c64.ESTACK_LO+7,x
|
||||||
|
sta c64.ESTACK_LO+4,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI+7,x
|
||||||
|
sta c64.ESTACK_HI+4,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
; determine string length
|
||||||
|
ldy #0
|
||||||
|
- lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
beq +
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
+ tya
|
||||||
|
sec
|
||||||
|
sbc c64.ESTACK_LO+1,x ; start index = strlen - segment length
|
||||||
|
sta c64.ESTACK_LO+2,x
|
||||||
|
jsr func_substr
|
||||||
|
; unwind original params
|
||||||
|
inx
|
||||||
|
inx
|
||||||
|
inx
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_substr .proc
|
||||||
|
; substr(source, target, start, length) with params on stack
|
||||||
|
inx
|
||||||
|
ldy c64.ESTACK_LO,x ; length
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x ; start
|
||||||
|
sta c64.SCRATCH_ZPB1
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD2
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD2+1
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
; adjust src location
|
||||||
|
clc
|
||||||
|
lda c64.SCRATCH_ZPWORD1
|
||||||
|
adc c64.SCRATCH_ZPB1
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
bcc +
|
||||||
|
inc c64.SCRATCH_ZPWORD1+1
|
||||||
|
+ lda #0
|
||||||
|
sta (c64.SCRATCH_ZPWORD2),y
|
||||||
|
jmp _startloop
|
||||||
|
- lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
sta (c64.SCRATCH_ZPWORD2),y
|
||||||
|
_startloop dey
|
||||||
|
cpy #$ff
|
||||||
|
bne -
|
||||||
|
rts
|
||||||
|
|
||||||
|
.pend
|
||||||
|
@ -1 +1 @@
|
|||||||
2.1
|
3.0
|
||||||
|
@ -102,6 +102,12 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(decl: VarDecl) {
|
override fun visit(decl: VarDecl) {
|
||||||
|
|
||||||
|
// if the vardecl is a parameter of a subroutine, don't output it again
|
||||||
|
val paramNames = (decl.definingScope() as? Subroutine)?.parameters?.map { it.name }
|
||||||
|
if(paramNames!=null && decl.name in paramNames)
|
||||||
|
return
|
||||||
|
|
||||||
when(decl.type) {
|
when(decl.type) {
|
||||||
VarDeclType.VAR -> {}
|
VarDeclType.VAR -> {}
|
||||||
VarDeclType.CONST -> output("const ")
|
VarDeclType.CONST -> output("const ")
|
||||||
@ -304,10 +310,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
|
|
||||||
override fun visit(forLoop: ForLoop) {
|
override fun visit(forLoop: ForLoop) {
|
||||||
output("for ")
|
output("for ")
|
||||||
if(forLoop.loopRegister!=null)
|
forLoop.loopVar.accept(this)
|
||||||
output(forLoop.loopRegister.toString())
|
|
||||||
else
|
|
||||||
forLoop.loopVar!!.accept(this)
|
|
||||||
output(" in ")
|
output(" in ")
|
||||||
forLoop.iterable.accept(this)
|
forLoop.iterable.accept(this)
|
||||||
output(" ")
|
output(" ")
|
||||||
@ -321,16 +324,18 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
whileLoop.body.accept(this)
|
whileLoop.body.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(foreverLoop: ForeverLoop) {
|
|
||||||
output("forever ")
|
|
||||||
foreverLoop.body.accept(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(repeatLoop: RepeatLoop) {
|
override fun visit(repeatLoop: RepeatLoop) {
|
||||||
output("repeat ")
|
output("repeat ")
|
||||||
|
repeatLoop.iterations?.accept(this)
|
||||||
|
output(" ")
|
||||||
repeatLoop.body.accept(this)
|
repeatLoop.body.accept(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(untilLoop: UntilLoop) {
|
||||||
|
output("do ")
|
||||||
|
untilLoop.body.accept(this)
|
||||||
output(" until ")
|
output(" until ")
|
||||||
repeatLoop.untilCondition.accept(this)
|
untilLoop.untilCondition.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(returnStmt: Return) {
|
override fun visit(returnStmt: Return) {
|
||||||
@ -346,12 +351,8 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(assignTarget: AssignTarget) {
|
override fun visit(assignTarget: AssignTarget) {
|
||||||
if(assignTarget.register!=null)
|
|
||||||
output(assignTarget.register.toString())
|
|
||||||
else {
|
|
||||||
assignTarget.memoryAddress?.accept(this)
|
assignTarget.memoryAddress?.accept(this)
|
||||||
assignTarget.identifier?.accept(this)
|
assignTarget.identifier?.accept(this)
|
||||||
}
|
|
||||||
assignTarget.arrayindexed?.accept(this)
|
assignTarget.arrayindexed?.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,10 +393,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
outputlni("}}")
|
outputlni("}}")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(registerExpr: RegisterExpr) {
|
|
||||||
output(registerExpr.register.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
|
override fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
|
||||||
output(builtinFunctionStatementPlaceholder.name)
|
output(builtinFunctionStatementPlaceholder.name)
|
||||||
}
|
}
|
||||||
@ -430,10 +427,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
outputln("")
|
outputln("")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(structLv: StructLiteralValue) {
|
|
||||||
outputListMembers(structLv.values.asSequence(), '{', '}')
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(nopStatement: NopStatement) {
|
override fun visit(nopStatement: NopStatement) {
|
||||||
output("; NOP @ ${nopStatement.position} $nopStatement")
|
output("; NOP @ ${nopStatement.position} $nopStatement")
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import prog8.ast.base.*
|
|||||||
import prog8.ast.expressions.Expression
|
import prog8.ast.expressions.Expression
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.processing.AstWalker
|
import prog8.ast.processing.AstWalker
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
@ -58,7 +57,7 @@ interface INameScope {
|
|||||||
when(stmt) {
|
when(stmt) {
|
||||||
// NOTE: if other nodes are introduced that are a scope, or contain subscopes, they must be added here!
|
// NOTE: if other nodes are introduced that are a scope, or contain subscopes, they must be added here!
|
||||||
is ForLoop -> if(stmt.body.name==name) return stmt.body
|
is ForLoop -> if(stmt.body.name==name) return stmt.body
|
||||||
is RepeatLoop -> if(stmt.body.name==name) return stmt.body
|
is UntilLoop -> if(stmt.body.name==name) return stmt.body
|
||||||
is WhileLoop -> if(stmt.body.name==name) return stmt.body
|
is WhileLoop -> if(stmt.body.name==name) return stmt.body
|
||||||
is BranchStatement -> {
|
is BranchStatement -> {
|
||||||
if(stmt.truepart.name==name) return stmt.truepart
|
if(stmt.truepart.name==name) return stmt.truepart
|
||||||
@ -156,6 +155,7 @@ interface INameScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun containsCodeOrVars() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm"}
|
fun containsCodeOrVars() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm"}
|
||||||
|
fun containsNoVars() = statements.all { it !is VarDecl }
|
||||||
fun containsNoCodeNorVars() = !containsCodeOrVars()
|
fun containsNoCodeNorVars() = !containsCodeOrVars()
|
||||||
|
|
||||||
fun remove(stmt: Statement) {
|
fun remove(stmt: Statement) {
|
||||||
@ -175,8 +175,8 @@ interface INameScope {
|
|||||||
find(it.truepart)
|
find(it.truepart)
|
||||||
find(it.elsepart)
|
find(it.elsepart)
|
||||||
}
|
}
|
||||||
|
is UntilLoop -> find(it.body)
|
||||||
is RepeatLoop -> find(it.body)
|
is RepeatLoop -> find(it.body)
|
||||||
is ForeverLoop -> find(it.body)
|
|
||||||
is WhileLoop -> find(it.body)
|
is WhileLoop -> find(it.body)
|
||||||
is WhenStatement -> it.choices.forEach { choice->find(choice.statements) }
|
is WhenStatement -> it.choices.forEach { choice->find(choice.statements) }
|
||||||
else -> { /* do nothing */ }
|
else -> { /* do nothing */ }
|
||||||
@ -187,6 +187,14 @@ interface INameScope {
|
|||||||
find(this)
|
find(this)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun nextSibling(stmt: Statement): Statement? {
|
||||||
|
val nextIdx = statements.indexOfFirst { it===stmt } + 1
|
||||||
|
return if(nextIdx < statements.size)
|
||||||
|
statements[nextIdx]
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IAssignable {
|
interface IAssignable {
|
||||||
@ -230,7 +238,7 @@ class Program(val name: String, val modules: MutableList<Module>): Node {
|
|||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(node is Module && replacement is Module)
|
require(node is Module && replacement is Module)
|
||||||
val idx = modules.indexOf(node)
|
val idx = modules.indexOfFirst { it===node }
|
||||||
modules[idx] = replacement
|
modules[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
@ -257,14 +265,13 @@ class Module(override val name: String,
|
|||||||
override fun definingScope(): INameScope = program.namespace
|
override fun definingScope(): INameScope = program.namespace
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(node is Statement && replacement is Statement)
|
require(node is Statement && replacement is Statement)
|
||||||
val idx = statements.indexOf(node)
|
val idx = statements.indexOfFirst { it===node }
|
||||||
statements[idx] = replacement
|
statements[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString() = "Module(name=$name, pos=$position, lib=$isLibraryModule)"
|
override fun toString() = "Module(name=$name, pos=$position, lib=$isLibraryModule)"
|
||||||
|
|
||||||
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
@ -205,14 +205,14 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
|
|||||||
val forloop = forloop()?.toAst()
|
val forloop = forloop()?.toAst()
|
||||||
if(forloop!=null) return forloop
|
if(forloop!=null) return forloop
|
||||||
|
|
||||||
val repeatloop = repeatloop()?.toAst()
|
val untilloop = untilloop()?.toAst()
|
||||||
if(repeatloop!=null) return repeatloop
|
if(untilloop!=null) return untilloop
|
||||||
|
|
||||||
val whileloop = whileloop()?.toAst()
|
val whileloop = whileloop()?.toAst()
|
||||||
if(whileloop!=null) return whileloop
|
if(whileloop!=null) return whileloop
|
||||||
|
|
||||||
val foreverloop = foreverloop()?.toAst()
|
val repeatloop = repeatloop()?.toAst()
|
||||||
if(foreverloop!=null) return foreverloop
|
if(repeatloop!=null) return repeatloop
|
||||||
|
|
||||||
val breakstmt = breakstmt()?.toAst()
|
val breakstmt = breakstmt()?.toAst()
|
||||||
if(breakstmt!=null) return breakstmt
|
if(breakstmt!=null) return breakstmt
|
||||||
@ -247,7 +247,7 @@ private class AsmsubDecl(val name: String,
|
|||||||
val returntypes: List<DataType>,
|
val returntypes: List<DataType>,
|
||||||
val asmParameterRegisters: List<RegisterOrStatusflag>,
|
val asmParameterRegisters: List<RegisterOrStatusflag>,
|
||||||
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
||||||
val asmClobbers: Set<Register>)
|
val asmClobbers: Set<CpuRegister>)
|
||||||
|
|
||||||
private fun prog8Parser.Asmsub_declContext.toAst(): AsmsubDecl {
|
private fun prog8Parser.Asmsub_declContext.toAst(): AsmsubDecl {
|
||||||
val name = identifier().text
|
val name = identifier().text
|
||||||
@ -274,24 +274,43 @@ private class AsmSubroutineReturn(val type: DataType,
|
|||||||
val stack: Boolean,
|
val stack: Boolean,
|
||||||
val position: Position)
|
val position: Position)
|
||||||
|
|
||||||
private fun prog8Parser.ClobberContext.toAst(): Set<Register>
|
|
||||||
= this.register().asSequence().map { it.toAst() }.toSet()
|
|
||||||
|
|
||||||
private fun prog8Parser.Asmsub_returnsContext.toAst(): List<AsmSubroutineReturn>
|
private fun prog8Parser.Asmsub_returnsContext.toAst(): List<AsmSubroutineReturn>
|
||||||
= asmsub_return().map { AsmSubroutineReturn(it.datatype().toAst(), it.registerorpair()?.toAst(), it.statusregister()?.toAst(), !it.stack?.text.isNullOrEmpty(), toPosition()) }
|
= asmsub_return().map {
|
||||||
|
val register = it.identifier()?.toAst()
|
||||||
|
var registerorpair: RegisterOrPair? = null
|
||||||
|
var statusregister: Statusflag? = null
|
||||||
|
if(register!=null) {
|
||||||
|
when (val name = register.nameInSource.single()) {
|
||||||
|
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(name)
|
||||||
|
in Statusflag.names -> statusregister = Statusflag.valueOf(name)
|
||||||
|
else -> throw FatalAstException("invalid register or status flag in $it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AsmSubroutineReturn(
|
||||||
|
it.datatype().toAst(),
|
||||||
|
registerorpair,
|
||||||
|
statusregister,
|
||||||
|
!it.stack?.text.isNullOrEmpty(), toPosition())
|
||||||
|
}
|
||||||
|
|
||||||
private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
|
private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
|
||||||
= asmsub_param().map {
|
= asmsub_param().map {
|
||||||
val vardecl = it.vardecl()
|
val vardecl = it.vardecl()
|
||||||
val datatype = vardecl.datatype()?.toAst() ?: DataType.STRUCT
|
val datatype = vardecl.datatype()?.toAst() ?: DataType.STRUCT
|
||||||
AsmSubroutineParameter(vardecl.varname.text, datatype,
|
val register = it.identifier()?.toAst()
|
||||||
it.registerorpair()?.toAst(),
|
var registerorpair: RegisterOrPair? = null
|
||||||
it.statusregister()?.toAst(),
|
var statusregister: Statusflag? = null
|
||||||
|
if(register!=null) {
|
||||||
|
when (val name = register.nameInSource.single()) {
|
||||||
|
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(name)
|
||||||
|
in Statusflag.names -> statusregister = Statusflag.valueOf(name)
|
||||||
|
else -> throw FatalAstException("invalid register or status flag in $it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AsmSubroutineParameter(vardecl.varname.text, datatype, registerorpair, statusregister,
|
||||||
!it.stack?.text.isNullOrEmpty(), toPosition())
|
!it.stack?.text.isNullOrEmpty(), toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.StatusregisterContext.toAst() = Statusflag.valueOf(text)
|
|
||||||
|
|
||||||
private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement {
|
private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement {
|
||||||
val void = this.VOID() != null
|
val void = this.VOID() != null
|
||||||
val location = scoped_identifier().toAst()
|
val location = scoped_identifier().toAst()
|
||||||
@ -350,23 +369,22 @@ private fun prog8Parser.Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.Assign_targetContext.toAst() : AssignTarget {
|
private fun prog8Parser.Assign_targetContext.toAst() : AssignTarget {
|
||||||
val register = register()?.toAst()
|
|
||||||
val identifier = scoped_identifier()
|
val identifier = scoped_identifier()
|
||||||
return when {
|
return when {
|
||||||
register!=null -> AssignTarget(register, null, null, null, toPosition())
|
identifier!=null -> AssignTarget(identifier.toAst(), null, null, toPosition())
|
||||||
identifier!=null -> AssignTarget(null, identifier.toAst(), null, null, toPosition())
|
arrayindexed()!=null -> AssignTarget(null, arrayindexed().toAst(), null, toPosition())
|
||||||
arrayindexed()!=null -> AssignTarget(null, null, arrayindexed().toAst(), null, toPosition())
|
directmemory()!=null -> AssignTarget(null, null, DirectMemoryWrite(directmemory().expression().toAst(), toPosition()), toPosition())
|
||||||
directmemory()!=null -> AssignTarget(null, null, null, DirectMemoryWrite(directmemory().expression().toAst(), toPosition()), toPosition())
|
else -> AssignTarget(scoped_identifier()?.toAst(), null, null, toPosition())
|
||||||
else -> AssignTarget(null, scoped_identifier()?.toAst(), null, null, toPosition())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.RegisterContext.toAst() = Register.valueOf(text.toUpperCase())
|
private fun prog8Parser.ClobberContext.toAst() : Set<CpuRegister> {
|
||||||
|
val names = this.identifier().map { it.toAst().nameInSource.single() }
|
||||||
|
return names.map { CpuRegister.valueOf(it) }.toSet()
|
||||||
|
}
|
||||||
|
|
||||||
private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperCase())
|
private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperCase())
|
||||||
|
|
||||||
private fun prog8Parser.RegisterorpairContext.toAst() = RegisterOrPair.valueOf(text.toUpperCase())
|
|
||||||
|
|
||||||
private fun prog8Parser.ArrayindexContext.toAst() : ArrayIndex =
|
private fun prog8Parser.ArrayindexContext.toAst() : ArrayIndex =
|
||||||
ArrayIndex(expression().toAst(), toPosition())
|
ArrayIndex(expression().toAst(), toPosition())
|
||||||
|
|
||||||
@ -469,18 +487,11 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
|
|||||||
// the ConstantFold takes care of that and converts the type if needed.
|
// the ConstantFold takes care of that and converts the type if needed.
|
||||||
ArrayLiteralValue(InferredTypes.InferredType.unknown(), array, position = litval.toPosition())
|
ArrayLiteralValue(InferredTypes.InferredType.unknown(), array, position = litval.toPosition())
|
||||||
}
|
}
|
||||||
litval.structliteral()!=null -> {
|
|
||||||
val values = litval.structliteral().expression().map { it.toAst() }
|
|
||||||
StructLiteralValue(values, litval.toPosition())
|
|
||||||
}
|
|
||||||
else -> throw FatalAstException("invalid parsed literal")
|
else -> throw FatalAstException("invalid parsed literal")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(register()!=null)
|
|
||||||
return RegisterExpr(register().toAst(), register().toPosition())
|
|
||||||
|
|
||||||
if(scoped_identifier()!=null)
|
if(scoped_identifier()!=null)
|
||||||
return scoped_identifier().toAst()
|
return scoped_identifier().toAst()
|
||||||
|
|
||||||
@ -572,15 +583,14 @@ private fun prog8Parser.Branch_stmtContext.toAst(): BranchStatement {
|
|||||||
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase())
|
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase())
|
||||||
|
|
||||||
private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
||||||
val loopregister = register()?.toAst()
|
val loopvar = identifier().toAst()
|
||||||
val loopvar = identifier()?.toAst()
|
|
||||||
val iterable = expression()!!.toAst()
|
val iterable = expression()!!.toAst()
|
||||||
val scope =
|
val scope =
|
||||||
if(statement()!=null)
|
if(statement()!=null)
|
||||||
AnonymousScope(mutableListOf(statement().toAst()), statement().toPosition())
|
AnonymousScope(mutableListOf(statement().toAst()), statement().toPosition())
|
||||||
else
|
else
|
||||||
AnonymousScope(statement_block().toAst(), statement_block().toPosition())
|
AnonymousScope(statement_block().toAst(), statement_block().toPosition())
|
||||||
return ForLoop(loopregister, loopvar, iterable, scope, toPosition())
|
return ForLoop(loopvar, iterable, scope, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.ContinuestmtContext.toAst() = Continue(toPosition())
|
private fun prog8Parser.ContinuestmtContext.toAst() = Continue(toPosition())
|
||||||
@ -595,19 +605,20 @@ private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {
|
|||||||
return WhileLoop(condition, scope, toPosition())
|
return WhileLoop(condition, scope, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.ForeverloopContext.toAst(): ForeverLoop {
|
private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop {
|
||||||
|
val iterations = expression()?.toAst()
|
||||||
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||||
val scope = AnonymousScope(statements, statement_block()?.toPosition()
|
val scope = AnonymousScope(statements, statement_block()?.toPosition()
|
||||||
?: statement().toPosition())
|
?: statement().toPosition())
|
||||||
return ForeverLoop(scope, toPosition())
|
return RepeatLoop(iterations, scope, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop {
|
private fun prog8Parser.UntilloopContext.toAst(): UntilLoop {
|
||||||
val untilCondition = expression().toAst()
|
val untilCondition = expression().toAst()
|
||||||
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||||
val scope = AnonymousScope(statements, statement_block()?.toPosition()
|
val scope = AnonymousScope(statements, statement_block()?.toPosition()
|
||||||
?: statement().toPosition())
|
?: statement().toPosition())
|
||||||
return RepeatLoop(scope, untilCondition, toPosition())
|
return UntilLoop(scope, untilCondition, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.WhenstmtContext.toAst(): WhenStatement {
|
private fun prog8Parser.WhenstmtContext.toAst(): WhenStatement {
|
||||||
|
@ -64,7 +64,7 @@ enum class DataType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Register {
|
enum class CpuRegister {
|
||||||
A,
|
A,
|
||||||
X,
|
X,
|
||||||
Y
|
Y
|
||||||
@ -76,14 +76,23 @@ enum class RegisterOrPair {
|
|||||||
Y,
|
Y,
|
||||||
AX,
|
AX,
|
||||||
AY,
|
AY,
|
||||||
XY
|
XY;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val names by lazy { values().map { it.toString()} }
|
||||||
|
}
|
||||||
|
|
||||||
} // only used in parameter and return value specs in asm subroutines
|
} // only used in parameter and return value specs in asm subroutines
|
||||||
|
|
||||||
enum class Statusflag {
|
enum class Statusflag {
|
||||||
Pc,
|
Pc,
|
||||||
Pz,
|
Pz,
|
||||||
Pv,
|
Pv,
|
||||||
Pn
|
Pn;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val names by lazy { values().map { it.toString()} }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class BranchCondition {
|
enum class BranchCondition {
|
||||||
|
@ -2,7 +2,7 @@ package prog8.ast.base
|
|||||||
|
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
|
|
||||||
class FatalAstException (override var message: String) : Exception(message)
|
open class FatalAstException (override var message: String) : Exception(message)
|
||||||
|
|
||||||
open class AstException (override var message: String) : Exception(message)
|
open class AstException (override var message: String) : Exception(message)
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ import prog8.ast.processing.*
|
|||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.BeforeAsmGenerationAstChanger
|
import prog8.compiler.BeforeAsmGenerationAstChanger
|
||||||
import prog8.optimizer.AssignmentTransformer
|
import prog8.optimizer.AssignmentTransformer
|
||||||
import prog8.optimizer.FlattenAnonymousScopesAndNopRemover
|
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) {
|
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) {
|
||||||
@ -32,6 +31,11 @@ internal fun Program.addTypecasts(errors: ErrorReporter) {
|
|||||||
caster.applyModifications()
|
caster.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun Program.verifyFunctionArgTypes() {
|
||||||
|
val fixer = VerifyFunctionArgTypes(this)
|
||||||
|
fixer.visit(this)
|
||||||
|
}
|
||||||
|
|
||||||
internal fun Program.transformAssignments(errors: ErrorReporter) {
|
internal fun Program.transformAssignments(errors: ErrorReporter) {
|
||||||
val transform = AssignmentTransformer(this, errors)
|
val transform = AssignmentTransformer(this, errors)
|
||||||
transform.visit(this)
|
transform.visit(this)
|
||||||
@ -56,21 +60,23 @@ internal fun Program.checkRecursion(errors: ErrorReporter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.checkIdentifiers(errors: ErrorReporter) {
|
internal fun Program.checkIdentifiers(errors: ErrorReporter) {
|
||||||
val checker = AstIdentifiersChecker(this, errors)
|
|
||||||
checker.visit(this)
|
val checker2 = AstIdentifiersChecker(this, errors)
|
||||||
|
checker2.visit(this)
|
||||||
|
|
||||||
|
if(errors.isEmpty()) {
|
||||||
|
val transforms = AstVariousTransforms(this)
|
||||||
|
transforms.visit(this)
|
||||||
|
transforms.applyModifications()
|
||||||
|
}
|
||||||
|
|
||||||
if (modules.map { it.name }.toSet().size != modules.size) {
|
if (modules.map { it.name }.toSet().size != modules.size) {
|
||||||
throw FatalAstException("modules should all be unique")
|
throw FatalAstException("modules should all be unique")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.makeForeverLoops() {
|
internal fun Program.variousCleanups() {
|
||||||
val checker = ForeverLoopsMaker()
|
val process = VariousCleanups()
|
||||||
checker.visit(this)
|
process.visit(this)
|
||||||
checker.applyModifications()
|
process.applyModifications()
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Program.removeNopsFlattenAnonScopes() {
|
|
||||||
val flattener = FlattenAnonymousScopesAndNopRemover()
|
|
||||||
flattener.visit(this)
|
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,11 @@ import prog8.ast.*
|
|||||||
import prog8.ast.antlr.escape
|
import prog8.ast.antlr.escape
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.processing.AstWalker
|
import prog8.ast.processing.AstWalker
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
|
import prog8.functions.CannotEvaluateException
|
||||||
import prog8.functions.NotConstArgumentException
|
import prog8.functions.NotConstArgumentException
|
||||||
import prog8.functions.builtinFunctionReturnType
|
import prog8.functions.builtinFunctionReturnType
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -20,31 +20,28 @@ val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "=
|
|||||||
|
|
||||||
sealed class Expression: Node {
|
sealed class Expression: Node {
|
||||||
abstract fun constValue(program: Program): NumericLiteralValue?
|
abstract fun constValue(program: Program): NumericLiteralValue?
|
||||||
abstract fun accept(visitor: IAstModifyingVisitor): Expression
|
|
||||||
abstract fun accept(visitor: IAstVisitor)
|
abstract fun accept(visitor: IAstVisitor)
|
||||||
abstract fun accept(visitor: AstWalker, parent: Node)
|
abstract fun accept(visitor: AstWalker, parent: Node)
|
||||||
abstract fun referencesIdentifiers(vararg name: String): Boolean // todo: remove this and add identifier usage tracking into CallGraph instead
|
abstract fun referencesIdentifiers(vararg name: String): Boolean
|
||||||
abstract fun inferType(program: Program): InferredTypes.InferredType
|
abstract fun inferType(program: Program): InferredTypes.InferredType
|
||||||
|
|
||||||
infix fun isSameAs(other: Expression): Boolean {
|
infix fun isSameAs(other: Expression): Boolean {
|
||||||
if(this===other)
|
if(this===other)
|
||||||
return true
|
return true
|
||||||
when(this) {
|
return when(this) {
|
||||||
is RegisterExpr ->
|
|
||||||
return (other is RegisterExpr && other.register==register)
|
|
||||||
is IdentifierReference ->
|
is IdentifierReference ->
|
||||||
return (other is IdentifierReference && other.nameInSource==nameInSource)
|
(other is IdentifierReference && other.nameInSource==nameInSource)
|
||||||
is PrefixExpression ->
|
is PrefixExpression ->
|
||||||
return (other is PrefixExpression && other.operator==operator && other.expression isSameAs expression)
|
(other is PrefixExpression && other.operator==operator && other.expression isSameAs expression)
|
||||||
is BinaryExpression ->
|
is BinaryExpression ->
|
||||||
return (other is BinaryExpression && other.operator==operator
|
(other is BinaryExpression && other.operator==operator
|
||||||
&& other.left isSameAs left
|
&& other.left isSameAs left
|
||||||
&& other.right isSameAs right)
|
&& other.right isSameAs right)
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
return (other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource
|
(other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource
|
||||||
&& other.arrayspec.index isSameAs arrayspec.index)
|
&& other.arrayspec.index isSameAs arrayspec.index)
|
||||||
}
|
}
|
||||||
else -> return other==this
|
else -> other==this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,7 +62,6 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -123,7 +119,6 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
|
|||||||
// binary expression should actually have been optimized away into a single value, before const value was requested...
|
// binary expression should actually have been optimized away into a single value, before const value was requested...
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -237,7 +232,6 @@ class ArrayIndexedExpression(var identifier: IdentifierReference,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -274,7 +268,6 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -309,7 +302,6 @@ data class AddressOf(var identifier: IdentifierReference, override val position:
|
|||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun referencesIdentifiers(vararg name: String) = false
|
override fun referencesIdentifiers(vararg name: String) = false
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UWORD)
|
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UWORD)
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
@ -328,7 +320,6 @@ class DirectMemoryRead(var addressExpression: Expression, override val position:
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -388,7 +379,6 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
|||||||
override fun referencesIdentifiers(vararg name: String) = false
|
override fun referencesIdentifiers(vararg name: String) = false
|
||||||
override fun constValue(program: Program) = this
|
override fun constValue(program: Program) = this
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -465,32 +455,6 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StructLiteralValue(var values: List<Expression>,
|
|
||||||
override val position: Position): Expression() {
|
|
||||||
override lateinit var parent: Node
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
|
||||||
this.parent=parent
|
|
||||||
values.forEach { it.linkParents(this) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
|
||||||
throw FatalAstException("can't replace here")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = values.any { it.referencesIdentifiers(*name) }
|
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.STRUCT)
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "struct{ ${values.joinToString(", ")} }"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var heapIdSequence = 0 // unique ids for strings and arrays "on the heap"
|
private var heapIdSequence = 0 // unique ids for strings and arrays "on the heap"
|
||||||
|
|
||||||
class StringLiteralValue(val value: String,
|
class StringLiteralValue(val value: String,
|
||||||
@ -510,7 +474,6 @@ class StringLiteralValue(val value: String,
|
|||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = false
|
override fun referencesIdentifiers(vararg name: String) = false
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -539,19 +502,18 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
|||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Expression)
|
require(replacement is Expression)
|
||||||
val idx = value.indexOf(node)
|
val idx = value.indexOfFirst { it===node }
|
||||||
value[idx] = replacement
|
value[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = value.any { it.referencesIdentifiers(*name) }
|
override fun referencesIdentifiers(vararg name: String) = value.any { it.referencesIdentifiers(*name) }
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String = "$value"
|
override fun toString(): String = "$value"
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType = if(type.isUnknown) type else guessDatatype(program)
|
override fun inferType(program: Program): InferredTypes.InferredType = if(type.isKnown) type else guessDatatype(program)
|
||||||
|
|
||||||
operator fun compareTo(other: ArrayLiteralValue): Int = throw ExpressionError("cannot order compare arrays", position)
|
operator fun compareTo(other: ArrayLiteralValue): Int = throw ExpressionError("cannot order compare arrays", position)
|
||||||
override fun hashCode(): Int = Objects.hash(value, type)
|
override fun hashCode(): Int = Objects.hash(value, type)
|
||||||
@ -640,7 +602,6 @@ class RangeExpr(var from: Expression,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -708,30 +669,6 @@ internal fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RegisterExpr(val register: Register, override val position: Position) : Expression(), IAssignable {
|
|
||||||
override lateinit var parent: Node
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
|
||||||
this.parent = parent
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
|
||||||
throw FatalAstException("can't replace here")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String): Boolean = register.name in name
|
|
||||||
override fun toString(): String {
|
|
||||||
return "RegisterExpr(register=$register, pos=$position)"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UBYTE)
|
|
||||||
}
|
|
||||||
|
|
||||||
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression(), IAssignable {
|
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression(), IAssignable {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
@ -744,6 +681,9 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
fun targetVarDecl(namespace: INameScope): VarDecl? = targetStatement(namespace) as? VarDecl
|
fun targetVarDecl(namespace: INameScope): VarDecl? = targetStatement(namespace) as? VarDecl
|
||||||
fun targetSubroutine(namespace: INameScope): Subroutine? = targetStatement(namespace) as? Subroutine
|
fun targetSubroutine(namespace: INameScope): Subroutine? = targetStatement(namespace) as? Subroutine
|
||||||
|
|
||||||
|
override fun equals(other: Any?) = other is IdentifierReference && other.nameInSource==nameInSource
|
||||||
|
override fun hashCode() = nameInSource.hashCode()
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
@ -768,7 +708,6 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
return "IdentifierRef($nameInSource)"
|
return "IdentifierRef($nameInSource)"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -812,7 +751,7 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
if(node===target)
|
if(node===target)
|
||||||
target=replacement as IdentifierReference
|
target=replacement as IdentifierReference
|
||||||
else {
|
else {
|
||||||
val idx = args.indexOf(node)
|
val idx = args.indexOfFirst { it===node }
|
||||||
args[idx] = replacement as Expression
|
args[idx] = replacement as Expression
|
||||||
}
|
}
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
@ -848,13 +787,16 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
// const-evaluating the builtin function call failed.
|
// const-evaluating the builtin function call failed.
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
catch(x: CannotEvaluateException) {
|
||||||
|
// const-evaluating the builtin function call failed.
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "FunctionCall(target=$target, pos=$position)"
|
return "FunctionCall(target=$target, pos=$position)"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
|
@ -110,22 +110,11 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(forLoop: ForLoop) {
|
override fun visit(forLoop: ForLoop) {
|
||||||
if(forLoop.body.containsNoCodeNorVars())
|
|
||||||
errors.warn("for loop body is empty", forLoop.position)
|
|
||||||
|
|
||||||
val iterableDt = forLoop.iterable.inferType(program).typeOrElse(DataType.BYTE)
|
val iterableDt = forLoop.iterable.inferType(program).typeOrElse(DataType.BYTE)
|
||||||
if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) {
|
if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) {
|
||||||
errors.err("can only loop over an iterable type", forLoop.position)
|
errors.err("can only loop over an iterable type", forLoop.position)
|
||||||
} else {
|
} else {
|
||||||
if (forLoop.loopRegister != null) {
|
val loopvar = forLoop.loopVar.targetVarDecl(program.namespace)
|
||||||
// loop register
|
|
||||||
if (iterableDt != DataType.ARRAY_UB && iterableDt != DataType.ARRAY_B && iterableDt != DataType.STR)
|
|
||||||
errors.err("register can only loop over bytes", forLoop.position)
|
|
||||||
if(forLoop.loopRegister!=Register.A)
|
|
||||||
errors.err("it's only possible to use A as a loop register", forLoop.position)
|
|
||||||
} else {
|
|
||||||
// loop variable
|
|
||||||
val loopvar = forLoop.loopVar!!.targetVarDecl(program.namespace)
|
|
||||||
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
|
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
|
||||||
errors.err("for loop requires a variable to loop with", forLoop.position)
|
errors.err("for loop requires a variable to loop with", forLoop.position)
|
||||||
} else {
|
} else {
|
||||||
@ -155,7 +144,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
super.visit(forLoop)
|
super.visit(forLoop)
|
||||||
}
|
}
|
||||||
@ -260,27 +248,27 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val regCounts = mutableMapOf<Register, Int>().withDefault { 0 }
|
val regCounts = mutableMapOf<CpuRegister, Int>().withDefault { 0 }
|
||||||
val statusflagCounts = mutableMapOf<Statusflag, Int>().withDefault { 0 }
|
val statusflagCounts = mutableMapOf<Statusflag, Int>().withDefault { 0 }
|
||||||
fun countRegisters(from: Iterable<RegisterOrStatusflag>) {
|
fun countRegisters(from: Iterable<RegisterOrStatusflag>) {
|
||||||
regCounts.clear()
|
regCounts.clear()
|
||||||
statusflagCounts.clear()
|
statusflagCounts.clear()
|
||||||
for(p in from) {
|
for(p in from) {
|
||||||
when(p.registerOrPair) {
|
when(p.registerOrPair) {
|
||||||
RegisterOrPair.A -> regCounts[Register.A]=regCounts.getValue(Register.A)+1
|
RegisterOrPair.A -> regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1
|
||||||
RegisterOrPair.X -> regCounts[Register.X]=regCounts.getValue(Register.X)+1
|
RegisterOrPair.X -> regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1
|
||||||
RegisterOrPair.Y -> regCounts[Register.Y]=regCounts.getValue(Register.Y)+1
|
RegisterOrPair.Y -> regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1
|
||||||
RegisterOrPair.AX -> {
|
RegisterOrPair.AX -> {
|
||||||
regCounts[Register.A]=regCounts.getValue(Register.A)+1
|
regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1
|
||||||
regCounts[Register.X]=regCounts.getValue(Register.X)+1
|
regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1
|
||||||
}
|
}
|
||||||
RegisterOrPair.AY -> {
|
RegisterOrPair.AY -> {
|
||||||
regCounts[Register.A]=regCounts.getValue(Register.A)+1
|
regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1
|
||||||
regCounts[Register.Y]=regCounts.getValue(Register.Y)+1
|
regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1
|
||||||
}
|
}
|
||||||
RegisterOrPair.XY -> {
|
RegisterOrPair.XY -> {
|
||||||
regCounts[Register.X]=regCounts.getValue(Register.X)+1
|
regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1
|
||||||
regCounts[Register.Y]=regCounts.getValue(Register.Y)+1
|
regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1
|
||||||
}
|
}
|
||||||
null ->
|
null ->
|
||||||
if(p.statusflag!=null)
|
if(p.statusflag!=null)
|
||||||
@ -325,17 +313,13 @@ internal class AstChecker(private val program: Program,
|
|||||||
visitStatements(subroutine.statements)
|
visitStatements(subroutine.statements)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(repeatLoop: RepeatLoop) {
|
override fun visit(untilLoop: UntilLoop) {
|
||||||
if(repeatLoop.untilCondition.referencesIdentifiers("A", "X", "Y")) // TODO use callgraph?
|
if(untilLoop.untilCondition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
|
||||||
errors.warn("using a register in the loop condition is risky (it could get clobbered)", repeatLoop.untilCondition.position)
|
errors.err("condition value should be an integer type", untilLoop.untilCondition.position)
|
||||||
if(repeatLoop.untilCondition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
|
super.visit(untilLoop)
|
||||||
errors.err("condition value should be an integer type", repeatLoop.untilCondition.position)
|
|
||||||
super.visit(repeatLoop)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(whileLoop: WhileLoop) {
|
override fun visit(whileLoop: WhileLoop) {
|
||||||
if(whileLoop.condition.referencesIdentifiers("A", "X", "Y")) // TODO use callgraph?
|
|
||||||
errors.warn("using a register in the loop condition is risky (it could get clobbered)", whileLoop.condition.position)
|
|
||||||
if(whileLoop.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
|
if(whileLoop.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
|
||||||
errors.err("condition value should be an integer type", whileLoop.condition.position)
|
errors.err("condition value should be an integer type", whileLoop.condition.position)
|
||||||
super.visit(whileLoop)
|
super.visit(whileLoop)
|
||||||
@ -361,9 +345,9 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(targetIdent!=null) {
|
if(targetIdent!=null) {
|
||||||
val targetVar = targetIdent.targetVarDecl(program.namespace)
|
val targetVar = targetIdent.targetVarDecl(program.namespace)
|
||||||
if(targetVar?.struct != null) {
|
if(targetVar?.struct != null) {
|
||||||
val sourceStructLv = assignment.value as? StructLiteralValue
|
val sourceStructLv = assignment.value as? ArrayLiteralValue
|
||||||
if (sourceStructLv != null) {
|
if (sourceStructLv != null) {
|
||||||
if (sourceStructLv.values.size != targetVar.struct?.numberOfElements)
|
if (sourceStructLv.value.size != targetVar.struct?.numberOfElements)
|
||||||
errors.err("number of elements doesn't match struct definition", sourceStructLv.position)
|
errors.err("number of elements doesn't match struct definition", sourceStructLv.position)
|
||||||
} else {
|
} else {
|
||||||
val sourceIdent = assignment.value as? IdentifierReference
|
val sourceIdent = assignment.value as? IdentifierReference
|
||||||
@ -397,8 +381,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
val targetIdentifier = assignTarget.identifier
|
val targetIdentifier = assignTarget.identifier
|
||||||
if (targetIdentifier != null) {
|
if (targetIdentifier != null) {
|
||||||
val targetName = targetIdentifier.nameInSource
|
val targetName = targetIdentifier.nameInSource
|
||||||
val targetSymbol = program.namespace.lookup(targetName, assignment)
|
when (val targetSymbol = program.namespace.lookup(targetName, assignment)) {
|
||||||
when (targetSymbol) {
|
|
||||||
null -> {
|
null -> {
|
||||||
errors.err("undefined symbol: ${targetIdentifier.nameInSource.joinToString(".")}", targetIdentifier.position)
|
errors.err("undefined symbol: ${targetIdentifier.nameInSource.joinToString(".")}", targetIdentifier.position)
|
||||||
return
|
return
|
||||||
@ -463,7 +446,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// the initializer value can't refer to the variable itself (recursive definition)
|
// the initializer value can't refer to the variable itself (recursive definition)
|
||||||
// TODO use callgraph for check?
|
|
||||||
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) {
|
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) {
|
||||||
err("recursive var declaration")
|
err("recursive var declaration")
|
||||||
}
|
}
|
||||||
@ -518,21 +500,14 @@ internal class AstChecker(private val program: Program,
|
|||||||
checkValueTypeAndRangeString(decl.datatype, decl.value as StringLiteralValue)
|
checkValueTypeAndRangeString(decl.datatype, decl.value as StringLiteralValue)
|
||||||
}
|
}
|
||||||
is ArrayLiteralValue -> {
|
is ArrayLiteralValue -> {
|
||||||
val arraySpec = decl.arraysize ?: ArrayIndex.forArray(decl.value as ArrayLiteralValue)
|
|
||||||
checkValueTypeAndRangeArray(decl.datatype, decl.struct, arraySpec, decl.value as ArrayLiteralValue)
|
|
||||||
}
|
|
||||||
is NumericLiteralValue -> {
|
|
||||||
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue)
|
|
||||||
}
|
|
||||||
is StructLiteralValue -> {
|
|
||||||
if(decl.datatype==DataType.STRUCT) {
|
if(decl.datatype==DataType.STRUCT) {
|
||||||
val struct = decl.struct!!
|
val struct = decl.struct!!
|
||||||
val structLv = decl.value as StructLiteralValue
|
val structLv = decl.value as ArrayLiteralValue
|
||||||
if(struct.numberOfElements != structLv.values.size) {
|
if(struct.numberOfElements != structLv.value.size) {
|
||||||
errors.err("struct value has incorrect number of elements", structLv.position)
|
errors.err("struct value has incorrect number of elements", structLv.position)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for(value in structLv.values.zip(struct.statements)) {
|
for(value in structLv.value.zip(struct.statements)) {
|
||||||
val memberdecl = value.second as VarDecl
|
val memberdecl = value.second as VarDecl
|
||||||
val constValue = value.first.constValue(program)
|
val constValue = value.first.constValue(program)
|
||||||
if(constValue==null) {
|
if(constValue==null) {
|
||||||
@ -546,9 +521,13 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
errors.err("struct literal is wrong type to initialize this variable", decl.value!!.position)
|
val arraySpec = decl.arraysize ?: ArrayIndex.forArray(decl.value as ArrayLiteralValue)
|
||||||
|
checkValueTypeAndRangeArray(decl.datatype, decl.struct, arraySpec, decl.value as ArrayLiteralValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue)
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!.javaClass.simpleName}")
|
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!.javaClass.simpleName}")
|
||||||
super.visit(decl)
|
super.visit(decl)
|
||||||
@ -585,8 +564,18 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
val declValue = decl.value
|
val declValue = decl.value
|
||||||
if(declValue!=null && decl.type==VarDeclType.VAR && !declValue.inferType(program).istype(decl.datatype))
|
if(declValue!=null && decl.type==VarDeclType.VAR) {
|
||||||
|
if(decl.datatype==DataType.STRUCT) {
|
||||||
|
val valueIdt = declValue.inferType(program)
|
||||||
|
if(valueIdt.isUnknown)
|
||||||
|
throw AstException("invalid value type")
|
||||||
|
val valueDt = valueIdt.typeOrElse(DataType.STRUCT)
|
||||||
|
if(valueDt !in ArrayDatatypes)
|
||||||
|
err("initialisation of struct should be with array value", declValue.position)
|
||||||
|
} else if (!declValue.inferType(program).istype(decl.datatype)) {
|
||||||
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})", declValue.position)
|
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})", declValue.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
super.visit(decl)
|
super.visit(decl)
|
||||||
}
|
}
|
||||||
@ -733,7 +722,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
"**" -> {
|
"**" -> {
|
||||||
if(leftDt in IntegerDatatypes)
|
if(leftDt in IntegerDatatypes)
|
||||||
errors.err("power operator requires floating point", expr.position)
|
errors.err("power operator requires floating point operands", expr.position)
|
||||||
}
|
}
|
||||||
"and", "or", "xor" -> {
|
"and", "or", "xor" -> {
|
||||||
// only integer numeric operands accepted, and if literal constants, only boolean values accepted (0 or 1)
|
// only integer numeric operands accepted, and if literal constants, only boolean values accepted (0 or 1)
|
||||||
@ -845,7 +834,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
if(functionCallStatement.target.nameInSource.last() in setOf("lsl", "lsr", "rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) {
|
if(functionCallStatement.target.nameInSource.last() in setOf("lsl", "lsr", "rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) {
|
||||||
// in-place modification, can't be done on literals
|
// in-place modification, can't be done on literals
|
||||||
if(functionCallStatement.args.any { it !is IdentifierReference && it !is RegisterExpr && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
|
if(functionCallStatement.args.any { it !is IdentifierReference && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
|
||||||
errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position)
|
errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1293,8 +1282,8 @@ internal class AstChecker(private val program: Program,
|
|||||||
DataType.STR -> sourceDatatype== DataType.STR
|
DataType.STR -> sourceDatatype== DataType.STR
|
||||||
DataType.STRUCT -> {
|
DataType.STRUCT -> {
|
||||||
if(sourceDatatype==DataType.STRUCT) {
|
if(sourceDatatype==DataType.STRUCT) {
|
||||||
val structLv = sourceValue as StructLiteralValue
|
val structLv = sourceValue as ArrayLiteralValue
|
||||||
val numValues = structLv.values.size
|
val numValues = structLv.value.size
|
||||||
val targetstruct = target.identifier!!.targetVarDecl(program.namespace)!!.struct!!
|
val targetstruct = target.identifier!!.targetVarDecl(program.namespace)!!.struct!!
|
||||||
return targetstruct.numberOfElements == numValues
|
return targetstruct.numberOfElements == numValues
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package prog8.ast.processing
|
package prog8.ast.processing
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
|
||||||
import prog8.ast.Module
|
import prog8.ast.Module
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
@ -10,98 +8,69 @@ import prog8.ast.statements.*
|
|||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
|
|
||||||
// TODO implement using AstWalker instead of IAstModifyingVisitor
|
internal class AstIdentifiersChecker(private val program: Program, private val errors: ErrorReporter) : IAstVisitor {
|
||||||
internal class AstIdentifiersChecker(private val program: Program,
|
|
||||||
private val errors: ErrorReporter) : IAstModifyingVisitor {
|
|
||||||
private var blocks = mutableMapOf<String, Block>()
|
private var blocks = mutableMapOf<String, Block>()
|
||||||
private val vardeclsToAdd = mutableMapOf<INameScope, MutableList<VarDecl>>()
|
|
||||||
|
|
||||||
private fun nameError(name: String, position: Position, existing: Statement) {
|
private fun nameError(name: String, position: Position, existing: Statement) {
|
||||||
errors.err("name conflict '$name', also defined in ${existing.position.file} line ${existing.position.line}", position)
|
errors.err("name conflict '$name', also defined in ${existing.position.file} line ${existing.position.line}", position)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(module: Module) {
|
override fun visit(module: Module) {
|
||||||
vardeclsToAdd.clear()
|
|
||||||
blocks.clear() // blocks may be redefined within a different module
|
blocks.clear() // blocks may be redefined within a different module
|
||||||
|
|
||||||
super.visit(module)
|
super.visit(module)
|
||||||
// add any new vardecls to the various scopes
|
|
||||||
for((where, decls) in vardeclsToAdd) {
|
|
||||||
where.statements.addAll(0, decls)
|
|
||||||
decls.forEach { it.linkParents(where as Node) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(block: Block): Statement {
|
override fun visit(block: Block) {
|
||||||
val existing = blocks[block.name]
|
val existing = blocks[block.name]
|
||||||
if(existing!=null)
|
if(existing!=null)
|
||||||
nameError(block.name, block.position, existing)
|
nameError(block.name, block.position, existing)
|
||||||
else
|
else
|
||||||
blocks[block.name] = block
|
blocks[block.name] = block
|
||||||
|
|
||||||
return super.visit(block)
|
super.visit(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCall: FunctionCall): Expression {
|
override fun visit(decl: VarDecl) {
|
||||||
if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0]=="lsb") {
|
|
||||||
// lsb(...) is just an alias for type cast to ubyte, so replace with "... as ubyte"
|
|
||||||
val typecast = TypecastExpression(functionCall.args.single(), DataType.UBYTE, false, functionCall.position)
|
|
||||||
typecast.linkParents(functionCall.parent)
|
|
||||||
return super.visit(typecast)
|
|
||||||
}
|
|
||||||
return super.visit(functionCall)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(decl: VarDecl): Statement {
|
|
||||||
// first, check if there are datatype errors on the vardecl
|
|
||||||
decl.datatypeErrors.forEach { errors.err(it.message, it.position) }
|
decl.datatypeErrors.forEach { errors.err(it.message, it.position) }
|
||||||
|
|
||||||
// now check the identifier
|
|
||||||
if(decl.name in BuiltinFunctions)
|
if(decl.name in BuiltinFunctions)
|
||||||
// the builtin functions can't be redefined
|
|
||||||
errors.err("builtin function cannot be redefined", decl.position)
|
errors.err("builtin function cannot be redefined", decl.position)
|
||||||
|
|
||||||
if(decl.name in CompilationTarget.machine.opcodeNames)
|
if(decl.name in CompilationTarget.machine.opcodeNames)
|
||||||
errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position)
|
errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position)
|
||||||
|
|
||||||
// is it a struct variable? then define all its struct members as mangled names,
|
|
||||||
// and include the original decl as well.
|
|
||||||
if(decl.datatype==DataType.STRUCT) {
|
if(decl.datatype==DataType.STRUCT) {
|
||||||
if(decl.structHasBeenFlattened)
|
if (decl.structHasBeenFlattened)
|
||||||
return super.visit(decl) // don't do this multiple times
|
return super.visit(decl) // don't do this multiple times
|
||||||
|
|
||||||
if(decl.struct==null) {
|
if (decl.struct == null) {
|
||||||
errors.err("undefined struct type", decl.position)
|
errors.err("undefined struct type", decl.position)
|
||||||
return super.visit(decl)
|
return super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(decl.struct!!.statements.any { (it as VarDecl).datatype !in NumericDatatypes})
|
if (decl.struct!!.statements.any { (it as VarDecl).datatype !in NumericDatatypes })
|
||||||
return super.visit(decl) // a non-numeric member, not supported. proper error is given by AstChecker later
|
return super.visit(decl) // a non-numeric member, not supported. proper error is given by AstChecker later
|
||||||
|
|
||||||
if(decl.value is NumericLiteralValue) {
|
if (decl.value is NumericLiteralValue) {
|
||||||
errors.err("you cannot initialize a struct using a single value", decl.position)
|
errors.err("you cannot initialize a struct using a single value", decl.position)
|
||||||
return super.visit(decl)
|
return super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(decl.value != null && decl.value !is StructLiteralValue) {
|
if (decl.value != null && decl.value !is ArrayLiteralValue) {
|
||||||
errors.err("initializing requires struct literal value", decl.value?.position ?: decl.position)
|
errors.err("initializing a struct requires array literal value", decl.value?.position ?: decl.position)
|
||||||
return super.visit(decl)
|
return super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
val decls = decl.flattenStructMembers()
|
|
||||||
decls.add(decl)
|
|
||||||
val result = AnonymousScope(decls, decl.position)
|
|
||||||
result.linkParents(decl.parent)
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val existing = program.namespace.lookup(listOf(decl.name), decl)
|
val existing = program.namespace.lookup(listOf(decl.name), decl)
|
||||||
if (existing != null && existing !== decl)
|
if (existing != null && existing !== decl)
|
||||||
nameError(decl.name, decl.position, existing)
|
nameError(decl.name, decl.position, existing)
|
||||||
|
|
||||||
return super.visit(decl)
|
super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(subroutine: Subroutine): Statement {
|
override fun visit(subroutine: Subroutine) {
|
||||||
if(subroutine.name in CompilationTarget.machine.opcodeNames) {
|
if(subroutine.name in CompilationTarget.machine.opcodeNames) {
|
||||||
errors.err("can't use a cpu opcode name as a symbol: '${subroutine.name}'", subroutine.position)
|
errors.err("can't use a cpu opcode name as a symbol: '${subroutine.name}'", subroutine.position)
|
||||||
} else if(subroutine.name in BuiltinFunctions) {
|
} else if(subroutine.name in BuiltinFunctions) {
|
||||||
@ -138,30 +107,15 @@ internal class AstIdentifiersChecker(private val program: Program,
|
|||||||
nameError(name, sub.position, subroutine)
|
nameError(name, sub.position, subroutine)
|
||||||
}
|
}
|
||||||
|
|
||||||
// inject subroutine params as local variables (if they're not there yet) (for non-kernel subroutines and non-asm parameters)
|
|
||||||
// NOTE:
|
|
||||||
// - numeric types BYTE and WORD and FLOAT are passed by value;
|
|
||||||
// - strings, arrays, matrices are passed by reference (their 16-bit address is passed as an uword parameter)
|
|
||||||
if(subroutine.asmAddress==null) {
|
|
||||||
if(subroutine.asmParameterRegisters.isEmpty()) {
|
|
||||||
subroutine.parameters
|
|
||||||
.filter { it.name !in namesInSub }
|
|
||||||
.forEach {
|
|
||||||
val vardecl = ParameterVarDecl(it.name, it.type, subroutine.position)
|
|
||||||
vardecl.linkParents(subroutine)
|
|
||||||
subroutine.statements.add(0, vardecl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(subroutine.isAsmSubroutine && subroutine.statements.any{it !is InlineAssembly}) {
|
if(subroutine.isAsmSubroutine && subroutine.statements.any{it !is InlineAssembly}) {
|
||||||
errors.err("asmsub can only contain inline assembly (%asm)", subroutine.position)
|
errors.err("asmsub can only contain inline assembly (%asm)", subroutine.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.visit(subroutine)
|
|
||||||
|
super.visit(subroutine)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(label: Label): Statement {
|
override fun visit(label: Label) {
|
||||||
if(label.name in CompilationTarget.machine.opcodeNames)
|
if(label.name in CompilationTarget.machine.opcodeNames)
|
||||||
errors.err("can't use a cpu opcode name as a symbol: '${label.name}'", label.position)
|
errors.err("can't use a cpu opcode name as a symbol: '${label.name}'", label.position)
|
||||||
|
|
||||||
@ -179,163 +133,24 @@ internal class AstIdentifiersChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.visit(label)
|
|
||||||
|
super.visit(label)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(forLoop: ForLoop): Statement {
|
override fun visit(string: StringLiteralValue) {
|
||||||
// If the for loop has a decltype, it means to declare the loopvar inside the loop body
|
|
||||||
// rather than reusing an already declared loopvar from an outer scope.
|
|
||||||
// For loops that loop over an interable variable (instead of a range of numbers) get an
|
|
||||||
// additional interation count variable in their scope.
|
|
||||||
if(forLoop.loopRegister!=null) {
|
|
||||||
if(forLoop.loopRegister == Register.X)
|
|
||||||
errors.warn("writing to the X register is dangerous, because it's used as an internal pointer", forLoop.position)
|
|
||||||
} else {
|
|
||||||
val loopVar = forLoop.loopVar
|
|
||||||
if (loopVar != null) {
|
|
||||||
val validName = forLoop.body.name.replace("<", "").replace(">", "").replace("-", "")
|
|
||||||
val loopvarName = "prog8_loopvar_$validName"
|
|
||||||
if (forLoop.iterable !is RangeExpr) {
|
|
||||||
val existing = if (forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(listOf(loopvarName), forLoop.body.statements.first())
|
|
||||||
if (existing == null) {
|
|
||||||
// create loop iteration counter variable (without value, to avoid an assignment)
|
|
||||||
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.PREFER_ZEROPAGE, null, loopvarName, null, null,
|
|
||||||
isArray = false, autogeneratedDontRemove = true, position = loopVar.position)
|
|
||||||
vardecl.linkParents(forLoop.body)
|
|
||||||
forLoop.body.statements.add(0, vardecl)
|
|
||||||
loopVar.parent = forLoop.body // loopvar 'is defined in the body'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.visit(forLoop)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(assignTarget: AssignTarget): AssignTarget {
|
|
||||||
if(assignTarget.register== Register.X)
|
|
||||||
errors.warn("writing to the X register is dangerous, because it's used as an internal pointer", assignTarget.position)
|
|
||||||
return super.visit(assignTarget)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(arrayLiteral: ArrayLiteralValue): Expression {
|
|
||||||
val array = super.visit(arrayLiteral)
|
|
||||||
if(array is ArrayLiteralValue) {
|
|
||||||
val vardecl = array.parent as? VarDecl
|
|
||||||
// adjust the datatype of the array (to an educated guess)
|
|
||||||
if(vardecl!=null) {
|
|
||||||
val arrayDt = array.type
|
|
||||||
if(!arrayDt.istype(vardecl.datatype)) {
|
|
||||||
val cast = array.cast(vardecl.datatype)
|
|
||||||
if (cast != null) {
|
|
||||||
vardecl.value = cast
|
|
||||||
cast.linkParents(vardecl)
|
|
||||||
return cast
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return array
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val arrayDt = array.guessDatatype(program)
|
|
||||||
if(arrayDt.isKnown) {
|
|
||||||
// this array literal is part of an expression, turn it into an identifier reference
|
|
||||||
val litval2 = array.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
|
||||||
return if (litval2 != null) {
|
|
||||||
litval2.parent = array.parent
|
|
||||||
makeIdentifierFromRefLv(litval2)
|
|
||||||
} else array
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return array
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(stringLiteral: StringLiteralValue): Expression {
|
|
||||||
val string = super.visit(stringLiteral)
|
|
||||||
if(string is StringLiteralValue) {
|
|
||||||
val vardecl = string.parent as? VarDecl
|
|
||||||
// intern the string; move it into the heap
|
|
||||||
if (string.value.length !in 1..255)
|
if (string.value.length !in 1..255)
|
||||||
errors.err("string literal length must be between 1 and 255", string.position)
|
errors.err("string literal length must be between 1 and 255", string.position)
|
||||||
return if (vardecl != null)
|
|
||||||
string
|
super.visit(string)
|
||||||
else
|
|
||||||
makeIdentifierFromRefLv(string) // replace the literal string by a identifier reference.
|
|
||||||
}
|
|
||||||
return string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeIdentifierFromRefLv(array: ArrayLiteralValue): IdentifierReference {
|
override fun visit(structDecl: StructDecl) {
|
||||||
// a referencetype literal value that's not declared as a variable
|
|
||||||
// we need to introduce an auto-generated variable for this to be able to refer to the value
|
|
||||||
// note: if the var references the same literal value, it is not yet de-duplicated here.
|
|
||||||
val scope = array.definingScope()
|
|
||||||
val variable = VarDecl.createAuto(array)
|
|
||||||
return replaceWithIdentifier(variable, scope, array.parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun makeIdentifierFromRefLv(string: StringLiteralValue): IdentifierReference {
|
|
||||||
// a referencetype literal value that's not declared as a variable
|
|
||||||
// we need to introduce an auto-generated variable for this to be able to refer to the value
|
|
||||||
// note: if the var references the same literal value, it is not yet de-duplicated here.
|
|
||||||
val scope = string.definingScope()
|
|
||||||
val variable = VarDecl.createAuto(string)
|
|
||||||
return replaceWithIdentifier(variable, scope, string.parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun replaceWithIdentifier(variable: VarDecl, scope: INameScope, parent: Node): IdentifierReference {
|
|
||||||
val variable1 = addVarDecl(scope, variable)
|
|
||||||
// replace the reference literal by a identifier reference
|
|
||||||
val identifier = IdentifierReference(listOf(variable1.name), variable1.position)
|
|
||||||
identifier.parent = parent
|
|
||||||
return identifier
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(structDecl: StructDecl): Statement {
|
|
||||||
for(member in structDecl.statements){
|
for(member in structDecl.statements){
|
||||||
val decl = member as? VarDecl
|
val decl = member as? VarDecl
|
||||||
if(decl!=null && decl.datatype !in NumericDatatypes)
|
if(decl!=null && decl.datatype !in NumericDatatypes)
|
||||||
errors.err("structs can only contain numerical types", decl.position)
|
errors.err("structs can only contain numerical types", decl.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.visit(structDecl)
|
super.visit(structDecl)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(expr: BinaryExpression): Expression {
|
|
||||||
return when {
|
|
||||||
expr.left is StringLiteralValue ->
|
|
||||||
processBinaryExprWithString(expr.left as StringLiteralValue, expr.right, expr)
|
|
||||||
expr.right is StringLiteralValue ->
|
|
||||||
processBinaryExprWithString(expr.right as StringLiteralValue, expr.left, expr)
|
|
||||||
else -> super.visit(expr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun processBinaryExprWithString(string: StringLiteralValue, operand: Expression, expr: BinaryExpression): Expression {
|
|
||||||
val constvalue = operand.constValue(program)
|
|
||||||
if(constvalue!=null) {
|
|
||||||
if (expr.operator == "*") {
|
|
||||||
// repeat a string a number of times
|
|
||||||
return StringLiteralValue(string.value.repeat(constvalue.number.toInt()), string.altEncoding, expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(expr.operator == "+" && operand is StringLiteralValue) {
|
|
||||||
// concatenate two strings
|
|
||||||
return StringLiteralValue("${string.value}${operand.value}", string.altEncoding, expr.position)
|
|
||||||
}
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addVarDecl(scope: INameScope, variable: VarDecl): VarDecl {
|
|
||||||
if(scope !in vardeclsToAdd)
|
|
||||||
vardeclsToAdd[scope] = mutableListOf()
|
|
||||||
val declList = vardeclsToAdd.getValue(scope)
|
|
||||||
val existing = declList.singleOrNull { it.name==variable.name }
|
|
||||||
return if(existing!=null) {
|
|
||||||
existing
|
|
||||||
} else {
|
|
||||||
declList.add(variable)
|
|
||||||
variable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
176
compiler/src/prog8/ast/processing/AstVariousTransforms.kt
Normal file
176
compiler/src/prog8/ast/processing/AstVariousTransforms.kt
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
package prog8.ast.processing
|
||||||
|
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
|
||||||
|
|
||||||
|
internal class AstVariousTransforms(private val program: Program) : AstWalker() {
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(functionCallStatement.target.nameInSource == listOf("swap")) {
|
||||||
|
// if x and y are both just identifiers, do not rewrite (there should be asm generation for that)
|
||||||
|
// otherwise:
|
||||||
|
// rewrite swap(x,y) as follows:
|
||||||
|
// - declare a temp variable of the same datatype
|
||||||
|
// - temp = x, x = y, y= temp
|
||||||
|
val first = functionCallStatement.args[0]
|
||||||
|
val second = functionCallStatement.args[1]
|
||||||
|
if(first !is IdentifierReference && second !is IdentifierReference) {
|
||||||
|
val dt = first.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
val tempname = "prog8_swaptmp_${functionCallStatement.hashCode()}"
|
||||||
|
val tempvardecl = VarDecl(VarDeclType.VAR, dt, ZeropageWish.DONTCARE, null, tempname, null, null, isArray = false, autogeneratedDontRemove = true, position = first.position)
|
||||||
|
val tempvar = IdentifierReference(listOf(tempname), first.position)
|
||||||
|
val assignTemp = Assignment(
|
||||||
|
AssignTarget(tempvar, null, null, first.position),
|
||||||
|
null,
|
||||||
|
first,
|
||||||
|
first.position
|
||||||
|
)
|
||||||
|
val assignFirst = Assignment(
|
||||||
|
AssignTarget.fromExpr(first),
|
||||||
|
null,
|
||||||
|
second,
|
||||||
|
first.position
|
||||||
|
)
|
||||||
|
val assignSecond = Assignment(
|
||||||
|
AssignTarget.fromExpr(second),
|
||||||
|
null,
|
||||||
|
tempvar,
|
||||||
|
first.position
|
||||||
|
)
|
||||||
|
val scope = AnonymousScope(mutableListOf(tempvardecl, assignTemp, assignFirst, assignSecond), first.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(functionCallStatement, scope, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0]=="lsb") {
|
||||||
|
// lsb(...) is just an alias for type cast to ubyte, so replace with "... as ubyte"
|
||||||
|
val typecast = TypecastExpression(functionCall.args.single(), DataType.UBYTE, false, functionCall.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(
|
||||||
|
functionCall, typecast, parent
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
// is it a struct variable? then define all its struct members as mangled names,
|
||||||
|
// and include the original decl as well.
|
||||||
|
if(decl.datatype==DataType.STRUCT && !decl.structHasBeenFlattened) {
|
||||||
|
val decls = decl.flattenStructMembers()
|
||||||
|
decls.add(decl)
|
||||||
|
val result = AnonymousScope(decls, decl.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(
|
||||||
|
decl, result, parent
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
|
// For non-kernel subroutines and non-asm parameters:
|
||||||
|
// inject subroutine params as local variables (if they're not there yet).
|
||||||
|
val symbolsInSub = subroutine.allDefinedSymbols()
|
||||||
|
val namesInSub = symbolsInSub.map{ it.first }.toSet()
|
||||||
|
if(subroutine.asmAddress==null) {
|
||||||
|
if(subroutine.asmParameterRegisters.isEmpty() && subroutine.parameters.isNotEmpty()) {
|
||||||
|
val vars = subroutine.statements.filterIsInstance<VarDecl>().map { it.name }.toSet()
|
||||||
|
if(!vars.containsAll(subroutine.parameters.map{it.name})) {
|
||||||
|
return subroutine.parameters
|
||||||
|
.filter { it.name !in namesInSub }
|
||||||
|
.map {
|
||||||
|
val vardecl = ParameterVarDecl(it.name, it.type, subroutine.position)
|
||||||
|
IAstModification.InsertFirst(vardecl, subroutine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
when {
|
||||||
|
expr.left is StringLiteralValue ->
|
||||||
|
return listOf(IAstModification.ReplaceNode(
|
||||||
|
expr,
|
||||||
|
processBinaryExprWithString(expr.left as StringLiteralValue, expr.right, expr),
|
||||||
|
parent
|
||||||
|
))
|
||||||
|
expr.right is StringLiteralValue ->
|
||||||
|
return listOf(IAstModification.ReplaceNode(
|
||||||
|
expr,
|
||||||
|
processBinaryExprWithString(expr.right as StringLiteralValue, expr.left, expr),
|
||||||
|
parent
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(string.parent !is VarDecl) {
|
||||||
|
// replace the literal string by a identifier reference to a new local vardecl
|
||||||
|
val vardecl = VarDecl.createAuto(string)
|
||||||
|
val identifier = IdentifierReference(listOf(vardecl.name), vardecl.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.ReplaceNode(string, identifier, parent),
|
||||||
|
IAstModification.InsertFirst(vardecl, string.definingScope() as Node)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {
|
||||||
|
val vardecl = array.parent as? VarDecl
|
||||||
|
if(vardecl!=null) {
|
||||||
|
// adjust the datatype of the array (to an educated guess)
|
||||||
|
val arrayDt = array.type
|
||||||
|
if(!arrayDt.istype(vardecl.datatype)) {
|
||||||
|
val cast = array.cast(vardecl.datatype)
|
||||||
|
if (cast != null && cast!=array)
|
||||||
|
return listOf(IAstModification.ReplaceNode(vardecl.value!!, cast, vardecl))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val arrayDt = array.guessDatatype(program)
|
||||||
|
if(arrayDt.isKnown) {
|
||||||
|
// this array literal is part of an expression, turn it into an identifier reference
|
||||||
|
val litval2 = array.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
||||||
|
if(litval2!=null && litval2!=array) {
|
||||||
|
val vardecl2 = VarDecl.createAuto(litval2)
|
||||||
|
val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.ReplaceNode(array, identifier, parent),
|
||||||
|
IAstModification.InsertFirst(vardecl2, array.definingScope() as Node)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processBinaryExprWithString(string: StringLiteralValue, operand: Expression, expr: BinaryExpression): Expression {
|
||||||
|
val constvalue = operand.constValue(program)
|
||||||
|
if(constvalue!=null) {
|
||||||
|
if (expr.operator == "*") {
|
||||||
|
// repeat a string a number of times
|
||||||
|
return StringLiteralValue(string.value.repeat(constvalue.number.toInt()), string.altEncoding, expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(expr.operator == "+" && operand is StringLiteralValue) {
|
||||||
|
// concatenate two strings
|
||||||
|
return StringLiteralValue("${string.value}${operand.value}", string.altEncoding, expr.position)
|
||||||
|
}
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,6 @@
|
|||||||
package prog8.ast.processing
|
package prog8.ast.processing
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
import prog8.ast.*
|
||||||
import prog8.ast.Module
|
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
|
||||||
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.*
|
||||||
@ -15,7 +12,7 @@ interface IAstModification {
|
|||||||
class Remove(val node: Node, val parent: Node) : IAstModification {
|
class Remove(val node: Node, val parent: Node) : IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
if(parent is INameScope) {
|
if(parent is INameScope) {
|
||||||
if (!parent.statements.remove(node))
|
if (!parent.statements.remove(node) && parent !is GlobalNamespace)
|
||||||
throw FatalAstException("attempt to remove non-existing node $node")
|
throw FatalAstException("attempt to remove non-existing node $node")
|
||||||
} else {
|
} else {
|
||||||
throw FatalAstException("parent of a remove modification is not an INameScope")
|
throw FatalAstException("parent of a remove modification is not an INameScope")
|
||||||
@ -55,7 +52,7 @@ interface IAstModification {
|
|||||||
class InsertAfter(val after: Statement, val stmt: Statement, val parent: Node) : IAstModification {
|
class InsertAfter(val after: Statement, val stmt: Statement, val parent: Node) : IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
if(parent is INameScope) {
|
if(parent is INameScope) {
|
||||||
val idx = parent.statements.indexOf(after)+1
|
val idx = parent.statements.indexOfFirst { it===after } + 1
|
||||||
parent.statements.add(idx, stmt)
|
parent.statements.add(idx, stmt)
|
||||||
stmt.linkParents(parent)
|
stmt.linkParents(parent)
|
||||||
} else {
|
} else {
|
||||||
@ -97,7 +94,7 @@ abstract class AstWalker {
|
|||||||
open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(foreverLoop: ForeverLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
@ -113,13 +110,11 @@ abstract class AstWalker {
|
|||||||
open fun before(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(registerExpr: RegisterExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(structLv: StructLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
@ -141,7 +136,7 @@ abstract class AstWalker {
|
|||||||
open fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(foreverLoop: ForeverLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
@ -157,13 +152,11 @@ abstract class AstWalker {
|
|||||||
open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(registerExpr: RegisterExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(structLv: StructLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
@ -328,7 +321,7 @@ abstract class AstWalker {
|
|||||||
|
|
||||||
fun visit(forLoop: ForLoop, parent: Node) {
|
fun visit(forLoop: ForLoop, parent: Node) {
|
||||||
track(before(forLoop, parent), forLoop, parent)
|
track(before(forLoop, parent), forLoop, parent)
|
||||||
forLoop.loopVar?.accept(this, forLoop)
|
forLoop.loopVar.accept(this, forLoop)
|
||||||
forLoop.iterable.accept(this, forLoop)
|
forLoop.iterable.accept(this, forLoop)
|
||||||
forLoop.body.accept(this, forLoop)
|
forLoop.body.accept(this, forLoop)
|
||||||
track(after(forLoop, parent), forLoop, parent)
|
track(after(forLoop, parent), forLoop, parent)
|
||||||
@ -341,19 +334,20 @@ abstract class AstWalker {
|
|||||||
track(after(whileLoop, parent), whileLoop, parent)
|
track(after(whileLoop, parent), whileLoop, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(foreverLoop: ForeverLoop, parent: Node) {
|
|
||||||
track(before(foreverLoop, parent), foreverLoop, parent)
|
|
||||||
foreverLoop.body.accept(this, foreverLoop)
|
|
||||||
track(after(foreverLoop, parent), foreverLoop, parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(repeatLoop: RepeatLoop, parent: Node) {
|
fun visit(repeatLoop: RepeatLoop, parent: Node) {
|
||||||
track(before(repeatLoop, parent), repeatLoop, parent)
|
track(before(repeatLoop, parent), repeatLoop, parent)
|
||||||
repeatLoop.untilCondition.accept(this, repeatLoop)
|
repeatLoop.iterations?.accept(this, repeatLoop)
|
||||||
repeatLoop.body.accept(this, repeatLoop)
|
repeatLoop.body.accept(this, repeatLoop)
|
||||||
track(after(repeatLoop, parent), repeatLoop, parent)
|
track(after(repeatLoop, parent), repeatLoop, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun visit(untilLoop: UntilLoop, parent: Node) {
|
||||||
|
track(before(untilLoop, parent), untilLoop, parent)
|
||||||
|
untilLoop.untilCondition.accept(this, untilLoop)
|
||||||
|
untilLoop.body.accept(this, untilLoop)
|
||||||
|
track(after(untilLoop, parent), untilLoop, parent)
|
||||||
|
}
|
||||||
|
|
||||||
fun visit(returnStmt: Return, parent: Node) {
|
fun visit(returnStmt: Return, parent: Node) {
|
||||||
track(before(returnStmt, parent), returnStmt, parent)
|
track(before(returnStmt, parent), returnStmt, parent)
|
||||||
returnStmt.value?.accept(this, returnStmt)
|
returnStmt.value?.accept(this, returnStmt)
|
||||||
@ -410,11 +404,6 @@ abstract class AstWalker {
|
|||||||
track(after(inlineAssembly, parent), inlineAssembly, parent)
|
track(after(inlineAssembly, parent), inlineAssembly, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(registerExpr: RegisterExpr, parent: Node) {
|
|
||||||
track(before(registerExpr, parent), registerExpr, parent)
|
|
||||||
track(after(registerExpr, parent), registerExpr, parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node) {
|
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node) {
|
||||||
track(before(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)
|
track(before(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)
|
||||||
track(after(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)
|
track(after(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)
|
||||||
@ -444,11 +433,5 @@ abstract class AstWalker {
|
|||||||
structDecl.statements.forEach { it.accept(this, structDecl) }
|
structDecl.statements.forEach { it.accept(this, structDecl) }
|
||||||
track(after(structDecl, parent), structDecl, parent)
|
track(after(structDecl, parent), structDecl, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(structLv: StructLiteralValue, parent: Node) {
|
|
||||||
track(before(structLv, parent), structLv, parent)
|
|
||||||
structLv.values.forEach { it.accept(this, structLv) }
|
|
||||||
track(after(structLv, parent), structLv, parent)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
package prog8.ast.processing
|
|
||||||
|
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
|
||||||
import prog8.ast.statements.ForeverLoop
|
|
||||||
import prog8.ast.statements.RepeatLoop
|
|
||||||
import prog8.ast.statements.WhileLoop
|
|
||||||
|
|
||||||
|
|
||||||
internal class ForeverLoopsMaker: AstWalker() {
|
|
||||||
override fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
|
|
||||||
val numeric = repeatLoop.untilCondition as? NumericLiteralValue
|
|
||||||
if(numeric!=null && numeric.number.toInt() == 0) {
|
|
||||||
val forever = ForeverLoop(repeatLoop.body, repeatLoop.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(repeatLoop, forever, parent))
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
|
|
||||||
val numeric = whileLoop.condition as? NumericLiteralValue
|
|
||||||
if(numeric!=null && numeric.number.toInt() != 0) {
|
|
||||||
val forever = ForeverLoop(whileLoop.body, whileLoop.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(whileLoop, forever, parent))
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,267 +0,0 @@
|
|||||||
package prog8.ast.processing
|
|
||||||
|
|
||||||
import prog8.ast.Module
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.base.FatalAstException
|
|
||||||
import prog8.ast.expressions.*
|
|
||||||
import prog8.ast.statements.*
|
|
||||||
|
|
||||||
// TODO replace all occurrences of this with AstWalker
|
|
||||||
interface IAstModifyingVisitor {
|
|
||||||
fun visit(program: Program) {
|
|
||||||
program.modules.forEach { it.accept(this) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(module: Module) {
|
|
||||||
module.statements = module.statements.map { it.accept(this) }.toMutableList()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(expr: PrefixExpression): Expression {
|
|
||||||
expr.expression = expr.expression.accept(this)
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(expr: BinaryExpression): Expression {
|
|
||||||
expr.left = expr.left.accept(this)
|
|
||||||
expr.right = expr.right.accept(this)
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(directive: Directive): Statement {
|
|
||||||
return directive
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(block: Block): Statement {
|
|
||||||
block.statements = block.statements.map { it.accept(this) }.toMutableList()
|
|
||||||
return block
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(decl: VarDecl): Statement {
|
|
||||||
decl.value = decl.value?.accept(this)
|
|
||||||
decl.arraysize?.accept(this)
|
|
||||||
return decl
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(subroutine: Subroutine): Statement {
|
|
||||||
subroutine.statements = subroutine.statements.map { it.accept(this) }.toMutableList()
|
|
||||||
return subroutine
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(functionCall: FunctionCall): Expression {
|
|
||||||
val newtarget = functionCall.target.accept(this)
|
|
||||||
if(newtarget is IdentifierReference)
|
|
||||||
functionCall.target = newtarget
|
|
||||||
else
|
|
||||||
throw FatalAstException("cannot change class of function call target")
|
|
||||||
functionCall.args = functionCall.args.map { it.accept(this) }.toMutableList()
|
|
||||||
return functionCall
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
|
||||||
val newtarget = functionCallStatement.target.accept(this)
|
|
||||||
if(newtarget is IdentifierReference)
|
|
||||||
functionCallStatement.target = newtarget
|
|
||||||
else
|
|
||||||
throw FatalAstException("cannot change class of function call target")
|
|
||||||
functionCallStatement.args = functionCallStatement.args.map { it.accept(this) }.toMutableList()
|
|
||||||
return functionCallStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(identifier: IdentifierReference): Expression {
|
|
||||||
// note: this is an identifier that is used in an expression.
|
|
||||||
// other identifiers are simply part of the other statements (such as jumps, subroutine defs etc)
|
|
||||||
return identifier
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(jump: Jump): Statement {
|
|
||||||
if(jump.identifier!=null) {
|
|
||||||
val ident = jump.identifier.accept(this)
|
|
||||||
if(ident is IdentifierReference && ident!==jump.identifier) {
|
|
||||||
return Jump(null, ident, null, jump.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return jump
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(ifStatement: IfStatement): Statement {
|
|
||||||
ifStatement.condition = ifStatement.condition.accept(this)
|
|
||||||
ifStatement.truepart = ifStatement.truepart.accept(this) as AnonymousScope
|
|
||||||
ifStatement.elsepart = ifStatement.elsepart.accept(this) as AnonymousScope
|
|
||||||
return ifStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(branchStatement: BranchStatement): Statement {
|
|
||||||
branchStatement.truepart = branchStatement.truepart.accept(this) as AnonymousScope
|
|
||||||
branchStatement.elsepart = branchStatement.elsepart.accept(this) as AnonymousScope
|
|
||||||
return branchStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(range: RangeExpr): Expression {
|
|
||||||
range.from = range.from.accept(this)
|
|
||||||
range.to = range.to.accept(this)
|
|
||||||
range.step = range.step.accept(this)
|
|
||||||
return range
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(label: Label): Statement {
|
|
||||||
return label
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(literalValue: NumericLiteralValue): NumericLiteralValue {
|
|
||||||
return literalValue
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(stringLiteral: StringLiteralValue): Expression {
|
|
||||||
return stringLiteral
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(arrayLiteral: ArrayLiteralValue): Expression {
|
|
||||||
for(av in arrayLiteral.value.withIndex()) {
|
|
||||||
val newvalue = av.value.accept(this)
|
|
||||||
arrayLiteral.value[av.index] = newvalue
|
|
||||||
}
|
|
||||||
return arrayLiteral
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(assignment: Assignment): Statement {
|
|
||||||
assignment.target = assignment.target.accept(this)
|
|
||||||
assignment.value = assignment.value.accept(this)
|
|
||||||
return assignment
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(postIncrDecr: PostIncrDecr): Statement {
|
|
||||||
postIncrDecr.target = postIncrDecr.target.accept(this)
|
|
||||||
return postIncrDecr
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(contStmt: Continue): Statement {
|
|
||||||
return contStmt
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(breakStmt: Break): Statement {
|
|
||||||
return breakStmt
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(forLoop: ForLoop): Statement {
|
|
||||||
when(val newloopvar = forLoop.loopVar?.accept(this)) {
|
|
||||||
is IdentifierReference -> forLoop.loopVar = newloopvar
|
|
||||||
null -> forLoop.loopVar = null
|
|
||||||
else -> throw FatalAstException("can't change class of loopvar")
|
|
||||||
}
|
|
||||||
forLoop.iterable = forLoop.iterable.accept(this)
|
|
||||||
forLoop.body = forLoop.body.accept(this) as AnonymousScope
|
|
||||||
return forLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(whileLoop: WhileLoop): Statement {
|
|
||||||
whileLoop.condition = whileLoop.condition.accept(this)
|
|
||||||
whileLoop.body = whileLoop.body.accept(this) as AnonymousScope
|
|
||||||
return whileLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(foreverLoop: ForeverLoop): Statement {
|
|
||||||
foreverLoop.body = foreverLoop.body.accept(this) as AnonymousScope
|
|
||||||
return foreverLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(repeatLoop: RepeatLoop): Statement {
|
|
||||||
repeatLoop.untilCondition = repeatLoop.untilCondition.accept(this)
|
|
||||||
repeatLoop.body = repeatLoop.body.accept(this) as AnonymousScope
|
|
||||||
return repeatLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(returnStmt: Return): Statement {
|
|
||||||
returnStmt.value = returnStmt.value?.accept(this)
|
|
||||||
return returnStmt
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(arrayIndexedExpression: ArrayIndexedExpression): ArrayIndexedExpression {
|
|
||||||
val ident = arrayIndexedExpression.identifier.accept(this)
|
|
||||||
if(ident is IdentifierReference)
|
|
||||||
arrayIndexedExpression.identifier = ident
|
|
||||||
arrayIndexedExpression.arrayspec.accept(this)
|
|
||||||
return arrayIndexedExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(assignTarget: AssignTarget): AssignTarget {
|
|
||||||
when (val ident = assignTarget.identifier?.accept(this)) {
|
|
||||||
is IdentifierReference -> assignTarget.identifier = ident
|
|
||||||
null -> assignTarget.identifier = null
|
|
||||||
else -> throw FatalAstException("can't change class of assign target identifier")
|
|
||||||
}
|
|
||||||
assignTarget.arrayindexed = assignTarget.arrayindexed?.accept(this)
|
|
||||||
assignTarget.memoryAddress?.let { visit(it) }
|
|
||||||
return assignTarget
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(scope: AnonymousScope): Statement {
|
|
||||||
scope.statements = scope.statements.map { it.accept(this) }.toMutableList()
|
|
||||||
return scope
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(typecast: TypecastExpression): Expression {
|
|
||||||
typecast.expression = typecast.expression.accept(this)
|
|
||||||
return typecast
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(memread: DirectMemoryRead): Expression {
|
|
||||||
memread.addressExpression = memread.addressExpression.accept(this)
|
|
||||||
return memread
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(memwrite: DirectMemoryWrite) {
|
|
||||||
memwrite.addressExpression = memwrite.addressExpression.accept(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(addressOf: AddressOf): Expression {
|
|
||||||
val ident = addressOf.identifier.accept(this)
|
|
||||||
if(ident is IdentifierReference)
|
|
||||||
addressOf.identifier = ident
|
|
||||||
else
|
|
||||||
throw FatalAstException("can't change class of addressof identifier")
|
|
||||||
return addressOf
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(inlineAssembly: InlineAssembly): Statement {
|
|
||||||
return inlineAssembly
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(registerExpr: RegisterExpr): Expression {
|
|
||||||
return registerExpr
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder): Statement {
|
|
||||||
return builtinFunctionStatementPlaceholder
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(nopStatement: NopStatement): Statement {
|
|
||||||
return nopStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(whenStatement: WhenStatement): Statement {
|
|
||||||
whenStatement.condition = whenStatement.condition.accept(this)
|
|
||||||
whenStatement.choices.forEach { it.accept(this) }
|
|
||||||
return whenStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(whenChoice: WhenChoice) {
|
|
||||||
whenChoice.values = whenChoice.values?.map { it.accept(this) }
|
|
||||||
val stmt = whenChoice.statements.accept(this)
|
|
||||||
if(stmt is AnonymousScope)
|
|
||||||
whenChoice.statements = stmt
|
|
||||||
else {
|
|
||||||
whenChoice.statements = AnonymousScope(mutableListOf(stmt), stmt.position)
|
|
||||||
whenChoice.statements.linkParents(whenChoice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(structDecl: StructDecl): Statement {
|
|
||||||
structDecl.statements = structDecl.statements.map{ it.accept(this) }.toMutableList()
|
|
||||||
return structDecl
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(structLv: StructLiteralValue): Expression {
|
|
||||||
structLv.values = structLv.values.map { it.accept(this) }
|
|
||||||
return structLv
|
|
||||||
}
|
|
||||||
}
|
|
@ -102,7 +102,7 @@ interface IAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun visit(forLoop: ForLoop) {
|
fun visit(forLoop: ForLoop) {
|
||||||
forLoop.loopVar?.accept(this)
|
forLoop.loopVar.accept(this)
|
||||||
forLoop.iterable.accept(this)
|
forLoop.iterable.accept(this)
|
||||||
forLoop.body.accept(this)
|
forLoop.body.accept(this)
|
||||||
}
|
}
|
||||||
@ -112,13 +112,14 @@ interface IAstVisitor {
|
|||||||
whileLoop.body.accept(this)
|
whileLoop.body.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(foreverLoop: ForeverLoop) {
|
fun visit(repeatLoop: RepeatLoop) {
|
||||||
foreverLoop.body.accept(this)
|
repeatLoop.iterations?.accept(this)
|
||||||
|
repeatLoop.body.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(repeatLoop: RepeatLoop) {
|
fun visit(untilLoop: UntilLoop) {
|
||||||
repeatLoop.untilCondition.accept(this)
|
untilLoop.untilCondition.accept(this)
|
||||||
repeatLoop.body.accept(this)
|
untilLoop.body.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(returnStmt: Return) {
|
fun visit(returnStmt: Return) {
|
||||||
@ -159,9 +160,6 @@ interface IAstVisitor {
|
|||||||
fun visit(inlineAssembly: InlineAssembly) {
|
fun visit(inlineAssembly: InlineAssembly) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(registerExpr: RegisterExpr) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
|
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,8 +179,4 @@ interface IAstVisitor {
|
|||||||
fun visit(structDecl: StructDecl) {
|
fun visit(structDecl: StructDecl) {
|
||||||
structDecl.statements.forEach { it.accept(this) }
|
structDecl.statements.forEach { it.accept(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(structLv: StructLiteralValue) {
|
|
||||||
structLv.values.forEach { it.accept(this) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,12 @@ internal class ImportedModuleDirectiveRemover: AstWalker() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address")
|
private val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address")
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
override fun before(directive: Directive, parent: Node): Iterable<IAstModification> {
|
override fun before(directive: Directive, parent: Node): Iterable<IAstModification> {
|
||||||
if(directive.directive in moduleLevelDirectives) {
|
if(directive.directive in moduleLevelDirectives) {
|
||||||
return listOf(IAstModification.Remove(directive, parent))
|
return listOf(IAstModification.Remove(directive, parent))
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
// - sorts the choices in when statement.
|
// - sorts the choices in when statement.
|
||||||
// - insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
// - insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
||||||
|
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
|
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
|
||||||
|
|
||||||
override fun after(module: Module, parent: Node): Iterable<IAstModification> {
|
override fun after(module: Module, parent: Node): Iterable<IAstModification> {
|
||||||
@ -33,7 +33,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reorderVardeclsAndDirectives(module.statements)
|
reorderVardeclsAndDirectives(module.statements)
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reorderVardeclsAndDirectives(statements: MutableList<Statement>) {
|
private fun reorderVardeclsAndDirectives(statements: MutableList<Statement>) {
|
||||||
@ -56,7 +56,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reorderVardeclsAndDirectives(block.statements)
|
reorderVardeclsAndDirectives(block.statements)
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
override fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
@ -68,7 +68,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
@ -78,7 +78,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
if(declConstValue==null) {
|
if(declConstValue==null) {
|
||||||
// move the vardecl (without value) to the scope and replace this with a regular assignment
|
// move the vardecl (without value) to the scope and replace this with a regular assignment
|
||||||
decl.value = null
|
decl.value = null
|
||||||
val target = AssignTarget(null, IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
||||||
val assign = Assignment(target, null, declValue, decl.position)
|
val assign = Assignment(target, null, declValue, decl.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.ReplaceNode(decl, assign, parent),
|
IAstModification.ReplaceNode(decl, assign, parent),
|
||||||
@ -86,7 +86,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
|
||||||
@ -95,7 +95,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
}
|
}
|
||||||
whenStatement.choices.clear()
|
whenStatement.choices.clear()
|
||||||
choices.mapTo(whenStatement.choices) { it.second }
|
choices.mapTo(whenStatement.choices) { it.second }
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
@ -106,8 +106,8 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
val valueType = assignment.value.inferType(program)
|
val valueType = assignment.value.inferType(program)
|
||||||
val targetType = assignment.target.inferType(program, assignment)
|
val targetType = assignment.target.inferType(program, assignment)
|
||||||
if(valueType.istype(DataType.STRUCT) && targetType.istype(DataType.STRUCT)) {
|
if(valueType.istype(DataType.STRUCT) && targetType.istype(DataType.STRUCT)) {
|
||||||
val assignments = if (assignment.value is StructLiteralValue) {
|
val assignments = if (assignment.value is ArrayLiteralValue) {
|
||||||
flattenStructAssignmentFromStructLiteral(assignment, program) // 'structvar = { ..... } '
|
flattenStructAssignmentFromStructLiteral(assignment, program) // 'structvar = [ ..... ] '
|
||||||
} else {
|
} else {
|
||||||
flattenStructAssignmentFromIdentifier(assignment, program) // 'structvar1 = structvar2'
|
flattenStructAssignmentFromIdentifier(assignment, program) // 'structvar1 = structvar2'
|
||||||
}
|
}
|
||||||
@ -119,7 +119,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun flattenStructAssignmentFromStructLiteral(structAssignment: Assignment, program: Program): List<Assignment> {
|
private fun flattenStructAssignmentFromStructLiteral(structAssignment: Assignment, program: Program): List<Assignment> {
|
||||||
@ -128,15 +128,15 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
||||||
val struct = targetVar.struct!!
|
val struct = targetVar.struct!!
|
||||||
|
|
||||||
val slv = structAssignment.value as? StructLiteralValue
|
val slv = structAssignment.value as? ArrayLiteralValue
|
||||||
if(slv==null || slv.values.size != struct.numberOfElements)
|
if(slv==null || slv.value.size != struct.numberOfElements)
|
||||||
throw FatalAstException("element count mismatch")
|
throw FatalAstException("element count mismatch")
|
||||||
|
|
||||||
return struct.statements.zip(slv.values).map { (targetDecl, sourceValue) ->
|
return struct.statements.zip(slv.value).map { (targetDecl, sourceValue) ->
|
||||||
targetDecl as VarDecl
|
targetDecl as VarDecl
|
||||||
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
|
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
|
||||||
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
||||||
val assign = Assignment(AssignTarget(null, idref, null, null, structAssignment.position),
|
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position),
|
||||||
null, sourceValue, sourceValue.position)
|
null, sourceValue, sourceValue.position)
|
||||||
assign.linkParents(structAssignment)
|
assign.linkParents(structAssignment)
|
||||||
assign
|
assign
|
||||||
@ -168,13 +168,13 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
||||||
val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name)
|
val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name)
|
||||||
val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position)
|
val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position)
|
||||||
val assign = Assignment(AssignTarget(null, idref, null, null, structAssignment.position),
|
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position),
|
||||||
null, sourceIdref, member.second.position)
|
null, sourceIdref, member.second.position)
|
||||||
assign.linkParents(structAssignment)
|
assign.linkParents(structAssignment)
|
||||||
assign
|
assign
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is StructLiteralValue -> {
|
is ArrayLiteralValue -> {
|
||||||
throw IllegalArgumentException("not going to flatten a structLv assignment here")
|
throw IllegalArgumentException("not going to flatten a structLv assignment here")
|
||||||
}
|
}
|
||||||
else -> throw FatalAstException("strange struct value")
|
else -> throw FatalAstException("strange struct value")
|
||||||
|
@ -16,6 +16,8 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
* (this includes function call arguments)
|
* (this includes function call arguments)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
val leftDt = expr.left.inferType(program)
|
val leftDt = expr.left.inferType(program)
|
||||||
val rightDt = expr.right.inferType(program)
|
val rightDt = expr.right.inferType(program)
|
||||||
@ -32,7 +34,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
@ -72,7 +74,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
@ -85,7 +87,9 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
|
|
||||||
private fun afterFunctionCallArgs(call: IFunctionCall, scope: INameScope): Iterable<IAstModification> {
|
private fun afterFunctionCallArgs(call: IFunctionCall, scope: INameScope): Iterable<IAstModification> {
|
||||||
// see if a typecast is needed to convert the arguments into the required parameter's type
|
// see if a typecast is needed to convert the arguments into the required parameter's type
|
||||||
return when(val sub = call.target.targetStatement(scope)) {
|
val modifications = mutableListOf<IAstModification>()
|
||||||
|
|
||||||
|
when(val sub = call.target.targetStatement(scope)) {
|
||||||
is Subroutine -> {
|
is Subroutine -> {
|
||||||
for(arg in sub.parameters.zip(call.args.withIndex())) {
|
for(arg in sub.parameters.zip(call.args.withIndex())) {
|
||||||
val argItype = arg.second.value.inferType(program)
|
val argItype = arg.second.value.inferType(program)
|
||||||
@ -94,26 +98,33 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
val requiredType = arg.first.type
|
val requiredType = arg.first.type
|
||||||
if (requiredType != argtype) {
|
if (requiredType != argtype) {
|
||||||
if (argtype isAssignableTo requiredType) {
|
if (argtype isAssignableTo requiredType) {
|
||||||
return listOf(IAstModification.ReplaceNode(
|
modifications += IAstModification.ReplaceNode(
|
||||||
call.args[arg.second.index],
|
call.args[arg.second.index],
|
||||||
TypecastExpression(arg.second.value, requiredType, true, arg.second.value.position),
|
TypecastExpression(arg.second.value, requiredType, true, arg.second.value.position),
|
||||||
call as Node))
|
call as Node)
|
||||||
} else if(requiredType == DataType.UWORD && argtype in PassByReferenceDatatypes) {
|
} else if(requiredType == DataType.UWORD && argtype in PassByReferenceDatatypes) {
|
||||||
// we allow STR/ARRAY values in place of UWORD parameters. Take their address instead.
|
// we allow STR/ARRAY values in place of UWORD parameters. Take their address instead.
|
||||||
return listOf(IAstModification.ReplaceNode(
|
modifications += IAstModification.ReplaceNode(
|
||||||
call.args[arg.second.index],
|
call.args[arg.second.index],
|
||||||
AddressOf(arg.second.value as IdentifierReference, arg.second.value.position),
|
AddressOf(arg.second.value as IdentifierReference, arg.second.value.position),
|
||||||
call as Node))
|
call as Node)
|
||||||
|
} else if(arg.second.value is NumericLiteralValue) {
|
||||||
|
try {
|
||||||
|
val castedValue = (arg.second.value as NumericLiteralValue).cast(requiredType)
|
||||||
|
modifications += IAstModification.ReplaceNode(
|
||||||
|
call.args[arg.second.index],
|
||||||
|
castedValue,
|
||||||
|
call as Node)
|
||||||
|
} catch (x: ExpressionError) {
|
||||||
|
// no cast possible
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emptyList()
|
|
||||||
}
|
}
|
||||||
is BuiltinFunctionStatementPlaceholder -> {
|
is BuiltinFunctionStatementPlaceholder -> {
|
||||||
val func = BuiltinFunctions.getValue(sub.name)
|
val func = BuiltinFunctions.getValue(sub.name)
|
||||||
if(func.pure) {
|
|
||||||
// non-pure functions don't get automatic typecasts because sometimes they act directly on their parameters
|
|
||||||
for (arg in func.parameters.zip(call.args.withIndex())) {
|
for (arg in func.parameters.zip(call.args.withIndex())) {
|
||||||
val argItype = arg.second.value.inferType(program)
|
val argItype = arg.second.value.inferType(program)
|
||||||
if (argItype.isKnown) {
|
if (argItype.isKnown) {
|
||||||
@ -122,20 +133,20 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
continue
|
continue
|
||||||
for (possibleType in arg.first.possibleDatatypes) {
|
for (possibleType in arg.first.possibleDatatypes) {
|
||||||
if (argtype isAssignableTo possibleType) {
|
if (argtype isAssignableTo possibleType) {
|
||||||
return listOf(IAstModification.ReplaceNode(
|
modifications += IAstModification.ReplaceNode(
|
||||||
call.args[arg.second.index],
|
call.args[arg.second.index],
|
||||||
TypecastExpression(arg.second.value, possibleType, true, arg.second.value.position),
|
TypecastExpression(arg.second.value, possibleType, true, arg.second.value.position),
|
||||||
call as Node))
|
call as Node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emptyList()
|
null -> { }
|
||||||
}
|
|
||||||
null -> emptyList()
|
|
||||||
else -> throw FatalAstException("call to something weird $sub ${call.target}")
|
else -> throw FatalAstException("call to something weird $sub ${call.target}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return modifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||||
@ -143,7 +154,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
if(typecast.implicit && typecast.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
if(typecast.implicit && typecast.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
||||||
errors.warn("byte or word value implicitly converted to float. Suggestion: use explicit cast as float, a float number, or revert to integer arithmetic", typecast.position)
|
errors.warn("byte or word value implicitly converted to float. Suggestion: use explicit cast as float, a float number, or revert to integer arithmetic", typecast.position)
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
override fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||||
@ -154,7 +165,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
?: TypecastExpression(memread.addressExpression, DataType.UWORD, true, memread.addressExpression.position)
|
?: TypecastExpression(memread.addressExpression, DataType.UWORD, true, memread.addressExpression.position)
|
||||||
return listOf(IAstModification.ReplaceNode(memread.addressExpression, typecast, memread))
|
return listOf(IAstModification.ReplaceNode(memread.addressExpression, typecast, memread))
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> {
|
override fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> {
|
||||||
@ -165,52 +176,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
?: TypecastExpression(memwrite.addressExpression, DataType.UWORD, true, memwrite.addressExpression.position)
|
?: TypecastExpression(memwrite.addressExpression, DataType.UWORD, true, memwrite.addressExpression.position)
|
||||||
return listOf(IAstModification.ReplaceNode(memwrite.addressExpression, typecast, memwrite))
|
return listOf(IAstModification.ReplaceNode(memwrite.addressExpression, typecast, memwrite))
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(structLv: StructLiteralValue, parent: Node): Iterable<IAstModification> {
|
|
||||||
// assignment of a struct literal value, some member values may need proper typecast
|
|
||||||
|
|
||||||
fun addTypecastsIfNeeded(struct: StructDecl): Iterable<IAstModification> {
|
|
||||||
val newValues = struct.statements.zip(structLv.values).map { (structMemberDecl, memberValue) ->
|
|
||||||
val memberDt = (structMemberDecl as VarDecl).datatype
|
|
||||||
val valueDt = memberValue.inferType(program)
|
|
||||||
if (valueDt.typeOrElse(memberDt) != memberDt)
|
|
||||||
TypecastExpression(memberValue, memberDt, true, memberValue.position)
|
|
||||||
else
|
|
||||||
memberValue
|
|
||||||
}
|
|
||||||
|
|
||||||
class StructLvValueReplacer(val targetStructLv: StructLiteralValue, val typecastValues: List<Expression>) : IAstModification {
|
|
||||||
override fun perform() {
|
|
||||||
targetStructLv.values = typecastValues
|
|
||||||
typecastValues.forEach { it.linkParents(targetStructLv) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return if(structLv.values.zip(newValues).any { (v1, v2) -> v1 !== v2})
|
|
||||||
listOf(StructLvValueReplacer(structLv, newValues))
|
|
||||||
else
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
val decl = structLv.parent as? VarDecl
|
|
||||||
if(decl != null) {
|
|
||||||
val struct = decl.struct
|
|
||||||
if(struct != null)
|
|
||||||
return addTypecastsIfNeeded(struct)
|
|
||||||
} else {
|
|
||||||
val assign = structLv.parent as? Assignment
|
|
||||||
if (assign != null) {
|
|
||||||
val decl2 = assign.target.identifier?.targetVarDecl(program.namespace)
|
|
||||||
if(decl2 != null) {
|
|
||||||
val struct = decl2.struct
|
|
||||||
if(struct != null)
|
|
||||||
return addTypecastsIfNeeded(struct)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
override fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
||||||
@ -221,7 +187,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
if(subroutine.returntypes.size==1) {
|
if(subroutine.returntypes.size==1) {
|
||||||
val subReturnType = subroutine.returntypes.first()
|
val subReturnType = subroutine.returntypes.first()
|
||||||
if (returnValue.inferType(program).istype(subReturnType))
|
if (returnValue.inferType(program).istype(subReturnType))
|
||||||
return emptyList()
|
return noModifications
|
||||||
if (returnValue is NumericLiteralValue) {
|
if (returnValue is NumericLiteralValue) {
|
||||||
returnStmt.value = returnValue.cast(subroutine.returntypes.single())
|
returnStmt.value = returnValue.cast(subroutine.returntypes.single())
|
||||||
} else {
|
} else {
|
||||||
@ -232,6 +198,6 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
43
compiler/src/prog8/ast/processing/VariousCleanups.kt
Normal file
43
compiler/src/prog8/ast/processing/VariousCleanups.kt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package prog8.ast.processing
|
||||||
|
|
||||||
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
|
import prog8.ast.expressions.TypecastExpression
|
||||||
|
import prog8.ast.statements.AnonymousScope
|
||||||
|
import prog8.ast.statements.NopStatement
|
||||||
|
|
||||||
|
|
||||||
|
internal class VariousCleanups: AstWalker() {
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
|
override fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> {
|
||||||
|
return listOf(IAstModification.Remove(nopStatement, parent))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||||
|
return if(parent is INameScope)
|
||||||
|
listOf(ScopeFlatten(scope, parent as INameScope))
|
||||||
|
else
|
||||||
|
noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScopeFlatten(val scope: AnonymousScope, val into: INameScope) : IAstModification {
|
||||||
|
override fun perform() {
|
||||||
|
val idx = into.statements.indexOf(scope)
|
||||||
|
if(idx>=0) {
|
||||||
|
into.statements.addAll(idx+1, scope.statements)
|
||||||
|
into.statements.remove(scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(typecast.expression is NumericLiteralValue) {
|
||||||
|
val value = (typecast.expression as NumericLiteralValue).cast(typecast.type)
|
||||||
|
return listOf(IAstModification.ReplaceNode(typecast, value, parent))
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
}
|
42
compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt
Normal file
42
compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package prog8.ast.processing
|
||||||
|
|
||||||
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.expressions.FunctionCall
|
||||||
|
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
|
||||||
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
|
import prog8.compiler.CompilerException
|
||||||
|
import prog8.functions.BuiltinFunctions
|
||||||
|
|
||||||
|
class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
|
||||||
|
|
||||||
|
override fun visit(functionCall: FunctionCall)
|
||||||
|
= checkTypes(functionCall as IFunctionCall, functionCall.definingScope())
|
||||||
|
|
||||||
|
override fun visit(functionCallStatement: FunctionCallStatement)
|
||||||
|
= checkTypes(functionCallStatement as IFunctionCall, functionCallStatement.definingScope())
|
||||||
|
|
||||||
|
private fun checkTypes(call: IFunctionCall, scope: INameScope) {
|
||||||
|
val argtypes = call.args.map { it.inferType(program).typeOrElse(DataType.STRUCT) }
|
||||||
|
val target = call.target.targetStatement(scope)
|
||||||
|
when(target) {
|
||||||
|
is Subroutine -> {
|
||||||
|
val paramtypes = target.parameters.map { it.type }
|
||||||
|
if(argtypes!=paramtypes)
|
||||||
|
throw CompilerException("parameter type mismatch $call")
|
||||||
|
}
|
||||||
|
is BuiltinFunctionStatementPlaceholder -> {
|
||||||
|
val func = BuiltinFunctions.getValue(target.name)
|
||||||
|
val paramtypes = func.parameters.map { it.possibleDatatypes }
|
||||||
|
for(x in argtypes.zip(paramtypes)) {
|
||||||
|
if(x.first !in x.second)
|
||||||
|
throw CompilerException("parameter type mismatch $call")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,12 +4,10 @@ import prog8.ast.*
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.AstWalker
|
import prog8.ast.processing.AstWalker
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
|
|
||||||
|
|
||||||
sealed class Statement : Node {
|
sealed class Statement : Node {
|
||||||
abstract fun accept(visitor: IAstModifyingVisitor) : Statement
|
|
||||||
abstract fun accept(visitor: IAstVisitor)
|
abstract fun accept(visitor: IAstVisitor)
|
||||||
abstract fun accept(visitor: AstWalker, parent: Node)
|
abstract fun accept(visitor: AstWalker, parent: Node)
|
||||||
|
|
||||||
@ -31,8 +29,6 @@ sealed class Statement : Node {
|
|||||||
return scope.joinToString(".")
|
return scope.joinToString(".")
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract val expensiveToInline: Boolean
|
|
||||||
|
|
||||||
fun definingBlock(): Block {
|
fun definingBlock(): Block {
|
||||||
if(this is Block)
|
if(this is Block)
|
||||||
return this
|
return this
|
||||||
@ -44,14 +40,12 @@ sealed class Statement : Node {
|
|||||||
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : Statement() {
|
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : Statement() {
|
||||||
override var parent: Node = ParentSentinel
|
override var parent: Node = ParentSentinel
|
||||||
override fun linkParents(parent: Node) {}
|
override fun linkParents(parent: Node) {}
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
override fun definingScope(): INameScope = BuiltinFunctionScopePlaceholder
|
override fun definingScope(): INameScope = BuiltinFunctionScopePlaceholder
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
override val expensiveToInline = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?, val stack: Boolean)
|
data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?, val stack: Boolean)
|
||||||
@ -62,8 +56,6 @@ class Block(override val name: String,
|
|||||||
val isInLibrary: Boolean,
|
val isInLibrary: Boolean,
|
||||||
override val position: Position) : Statement(), INameScope {
|
override val position: Position) : Statement(), INameScope {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
|
||||||
get() = statements.any { it.expensiveToInline }
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -72,12 +64,11 @@ class Block(override val name: String,
|
|||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Statement)
|
require(replacement is Statement)
|
||||||
val idx = statements.indexOf(node)
|
val idx = statements.indexOfFirst { it ===node }
|
||||||
statements[idx] = replacement
|
statements[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -90,7 +81,6 @@ class Block(override val name: String,
|
|||||||
|
|
||||||
data class Directive(val directive: String, val args: List<DirectiveArg>, override val position: Position) : Statement() {
|
data class Directive(val directive: String, val args: List<DirectiveArg>, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -98,7 +88,6 @@ data class Directive(val directive: String, val args: List<DirectiveArg>, overri
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
@ -114,14 +103,12 @@ data class DirectiveArg(val str: String?, val name: String?, val int: Int?, over
|
|||||||
|
|
||||||
data class Label(val name: String, override val position: Position) : Statement() {
|
data class Label(val name: String, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -132,7 +119,6 @@ data class Label(val name: String, override val position: Position) : Statement(
|
|||||||
|
|
||||||
open class Return(var value: Expression?, override val position: Position) : Statement() {
|
open class Return(var value: Expression?, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = value!=null && value !is NumericLiteralValue
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -145,7 +131,6 @@ open class Return(var value: Expression?, override val position: Position) : Sta
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -155,7 +140,6 @@ open class Return(var value: Expression?, override val position: Position) : Sta
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ReturnFromIrq(override val position: Position) : Return(null, position) {
|
class ReturnFromIrq(override val position: Position) : Return(null, position) {
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
@ -166,28 +150,24 @@ class ReturnFromIrq(override val position: Position) : Return(null, position) {
|
|||||||
|
|
||||||
class Continue(override val position: Position) : Statement() {
|
class Continue(override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent=parent
|
this.parent=parent
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Break(override val position: Position) : Statement() {
|
class Break(override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent=parent
|
this.parent=parent
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
@ -217,9 +197,6 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
var structHasBeenFlattened = false // set later
|
var structHasBeenFlattened = false // set later
|
||||||
private set
|
private set
|
||||||
|
|
||||||
override val expensiveToInline
|
|
||||||
get() = value!=null && value !is NumericLiteralValue
|
|
||||||
|
|
||||||
// prefix for literal values that are turned into a variable on the heap
|
// prefix for literal values that are turned into a variable on the heap
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -281,7 +258,6 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -294,7 +270,7 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
fun flattenStructMembers(): MutableList<Statement> {
|
fun flattenStructMembers(): MutableList<Statement> {
|
||||||
val result = struct!!.statements.withIndex().map {
|
val result = struct!!.statements.withIndex().map {
|
||||||
val member = it.value as VarDecl
|
val member = it.value as VarDecl
|
||||||
val initvalue = if(value!=null) (value as StructLiteralValue).values[it.index] else null
|
val initvalue = if(value!=null) (value as ArrayLiteralValue).value[it.index] else null
|
||||||
VarDecl(
|
VarDecl(
|
||||||
VarDeclType.VAR,
|
VarDeclType.VAR,
|
||||||
member.datatype,
|
member.datatype,
|
||||||
@ -338,11 +314,8 @@ class ArrayIndex(var index: Expression, override val position: Position) : Node
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun accept(visitor: IAstModifyingVisitor) {
|
|
||||||
index = index.accept(visitor)
|
|
||||||
}
|
|
||||||
fun accept(visitor: IAstVisitor) = index.accept(visitor)
|
fun accept(visitor: IAstVisitor) = index.accept(visitor)
|
||||||
fun accept(visitor: AstWalker, parent: Node) = index.accept(visitor, parent)
|
fun accept(visitor: AstWalker, parent: Node) = index.accept(visitor, this)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return("ArrayIndex($index, pos=$position)")
|
return("ArrayIndex($index, pos=$position)")
|
||||||
@ -353,8 +326,6 @@ class ArrayIndex(var index: Expression, override val position: Position) : Node
|
|||||||
|
|
||||||
open class Assignment(var target: AssignTarget, var aug_op : String?, var value: Expression, override val position: Position) : Statement() {
|
open class Assignment(var target: AssignTarget, var aug_op : String?, var value: Expression, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
|
||||||
get() = value !is NumericLiteralValue
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -371,7 +342,6 @@ open class Assignment(var target: AssignTarget, var aug_op : String?, var value:
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -384,7 +354,6 @@ open class Assignment(var target: AssignTarget, var aug_op : String?, var value:
|
|||||||
|
|
||||||
val leftOperand: Expression =
|
val leftOperand: Expression =
|
||||||
when {
|
when {
|
||||||
target.register != null -> RegisterExpr(target.register!!, target.position)
|
|
||||||
target.identifier != null -> target.identifier!!
|
target.identifier != null -> target.identifier!!
|
||||||
target.arrayindexed != null -> target.arrayindexed!!
|
target.arrayindexed != null -> target.arrayindexed!!
|
||||||
target.memoryAddress != null -> DirectMemoryRead(target.memoryAddress!!.addressExpression, value.position)
|
target.memoryAddress != null -> DirectMemoryRead(target.memoryAddress!!.addressExpression, value.position)
|
||||||
@ -404,8 +373,7 @@ open class Assignment(var target: AssignTarget, var aug_op : String?, var value:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class AssignTarget(val register: Register?,
|
data class AssignTarget(var identifier: IdentifierReference?,
|
||||||
var identifier: IdentifierReference?,
|
|
||||||
var arrayindexed: ArrayIndexedExpression?,
|
var arrayindexed: ArrayIndexedExpression?,
|
||||||
val memoryAddress: DirectMemoryWrite?,
|
val memoryAddress: DirectMemoryWrite?,
|
||||||
override val position: Position) : Node {
|
override val position: Position) : Node {
|
||||||
@ -427,26 +395,21 @@ data class AssignTarget(val register: Register?,
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromExpr(expr: Expression): AssignTarget {
|
fun fromExpr(expr: Expression): AssignTarget {
|
||||||
return when (expr) {
|
return when (expr) {
|
||||||
is RegisterExpr -> AssignTarget(expr.register, null, null, null, expr.position)
|
is IdentifierReference -> AssignTarget(expr, null, null, expr.position)
|
||||||
is IdentifierReference -> AssignTarget(null, expr, null, null, expr.position)
|
is ArrayIndexedExpression -> AssignTarget(null, expr, null, expr.position)
|
||||||
is ArrayIndexedExpression -> AssignTarget(null, null, expr, null, expr.position)
|
is DirectMemoryRead -> AssignTarget(null, null, DirectMemoryWrite(expr.addressExpression, expr.position), expr.position)
|
||||||
is DirectMemoryRead -> AssignTarget(null, null, null, DirectMemoryWrite(expr.addressExpression, expr.position), expr.position)
|
|
||||||
else -> throw FatalAstException("invalid expression object $expr")
|
else -> throw FatalAstException("invalid expression object $expr")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun inferType(program: Program, stmt: Statement): InferredTypes.InferredType {
|
fun inferType(program: Program, stmt: Statement): InferredTypes.InferredType {
|
||||||
if(register!=null)
|
|
||||||
return InferredTypes.knownFor(DataType.UBYTE)
|
|
||||||
|
|
||||||
if(identifier!=null) {
|
if(identifier!=null) {
|
||||||
val symbol = program.namespace.lookup(identifier!!.nameInSource, stmt) ?: return InferredTypes.unknown()
|
val symbol = program.namespace.lookup(identifier!!.nameInSource, stmt) ?: return InferredTypes.unknown()
|
||||||
if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype)
|
if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype)
|
||||||
@ -471,7 +434,6 @@ data class AssignTarget(val register: Register?,
|
|||||||
else
|
else
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
this.register!=null -> value is RegisterExpr && value.register==register
|
|
||||||
this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier!!.nameInSource
|
this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier!!.nameInSource
|
||||||
this.arrayindexed!=null -> value is ArrayIndexedExpression &&
|
this.arrayindexed!=null -> value is ArrayIndexedExpression &&
|
||||||
value.identifier.nameInSource==arrayindexed!!.identifier.nameInSource &&
|
value.identifier.nameInSource==arrayindexed!!.identifier.nameInSource &&
|
||||||
@ -485,8 +447,6 @@ data class AssignTarget(val register: Register?,
|
|||||||
fun isSameAs(other: AssignTarget, program: Program): Boolean {
|
fun isSameAs(other: AssignTarget, program: Program): Boolean {
|
||||||
if(this===other)
|
if(this===other)
|
||||||
return true
|
return true
|
||||||
if(this.register!=null && other.register!=null)
|
|
||||||
return this.register==other.register
|
|
||||||
if(this.identifier!=null && other.identifier!=null)
|
if(this.identifier!=null && other.identifier!=null)
|
||||||
return this.identifier!!.nameInSource==other.identifier!!.nameInSource
|
return this.identifier!!.nameInSource==other.identifier!!.nameInSource
|
||||||
if(this.memoryAddress!=null && other.memoryAddress!=null) {
|
if(this.memoryAddress!=null && other.memoryAddress!=null) {
|
||||||
@ -505,8 +465,6 @@ data class AssignTarget(val register: Register?,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun isNotMemory(namespace: INameScope): Boolean {
|
fun isNotMemory(namespace: INameScope): Boolean {
|
||||||
if(this.register!=null)
|
|
||||||
return true
|
|
||||||
if(this.memoryAddress!=null)
|
if(this.memoryAddress!=null)
|
||||||
return false
|
return false
|
||||||
if(this.arrayindexed!=null) {
|
if(this.arrayindexed!=null) {
|
||||||
@ -525,7 +483,6 @@ data class AssignTarget(val register: Register?,
|
|||||||
|
|
||||||
class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : Statement() {
|
class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -538,7 +495,6 @@ class PostIncrDecr(var target: AssignTarget, val operator: String, override val
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -552,7 +508,6 @@ class Jump(val address: Int?,
|
|||||||
val generatedLabel: String?, // used in code generation scenarios
|
val generatedLabel: String?, // used in code generation scenarios
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -560,7 +515,6 @@ class Jump(val address: Int?,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -574,8 +528,6 @@ class FunctionCallStatement(override var target: IdentifierReference,
|
|||||||
val void: Boolean,
|
val void: Boolean,
|
||||||
override val position: Position) : Statement(), IFunctionCall {
|
override val position: Position) : Statement(), IFunctionCall {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
|
||||||
get() = args.any { it !is NumericLiteralValue }
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -587,13 +539,12 @@ class FunctionCallStatement(override var target: IdentifierReference,
|
|||||||
if(node===target)
|
if(node===target)
|
||||||
target = replacement as IdentifierReference
|
target = replacement as IdentifierReference
|
||||||
else {
|
else {
|
||||||
val idx = args.indexOf(node)
|
val idx = args.indexOfFirst { it===node }
|
||||||
args[idx] = replacement as Expression
|
args[idx] = replacement as Expression
|
||||||
}
|
}
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -604,14 +555,12 @@ class FunctionCallStatement(override var target: IdentifierReference,
|
|||||||
|
|
||||||
class InlineAssembly(val assembly: String, override val position: Position) : Statement() {
|
class InlineAssembly(val assembly: String, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
@ -620,8 +569,6 @@ class AnonymousScope(override var statements: MutableList<Statement>,
|
|||||||
override val position: Position) : INameScope, Statement() {
|
override val position: Position) : INameScope, Statement() {
|
||||||
override val name: String
|
override val name: String
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
|
||||||
get() = statements.any { it.expensiveToInline }
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private var sequenceNumber = 1
|
private var sequenceNumber = 1
|
||||||
@ -639,36 +586,25 @@ class AnonymousScope(override var statements: MutableList<Statement>,
|
|||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Statement)
|
require(replacement is Statement)
|
||||||
val idx = statements.indexOf(node)
|
val idx = statements.indexOfFirst { it===node }
|
||||||
statements[idx] = replacement
|
statements[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
class NopStatement(override val position: Position): Statement() {
|
class NopStatement(override val position: Position): Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun insteadOf(stmt: Statement): NopStatement {
|
|
||||||
val nop = NopStatement(stmt.position)
|
|
||||||
nop.parent = stmt.parent
|
|
||||||
return nop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// the subroutine class covers both the normal user-defined subroutines,
|
// the subroutine class covers both the normal user-defined subroutines,
|
||||||
@ -679,20 +615,14 @@ class Subroutine(override val name: String,
|
|||||||
val returntypes: List<DataType>,
|
val returntypes: List<DataType>,
|
||||||
val asmParameterRegisters: List<RegisterOrStatusflag>,
|
val asmParameterRegisters: List<RegisterOrStatusflag>,
|
||||||
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
||||||
val asmClobbers: Set<Register>,
|
val asmClobbers: Set<CpuRegister>,
|
||||||
val asmAddress: Int?,
|
val asmAddress: Int?,
|
||||||
val isAsmSubroutine: Boolean,
|
val isAsmSubroutine: Boolean,
|
||||||
override var statements: MutableList<Statement>,
|
override var statements: MutableList<Statement>,
|
||||||
override val position: Position) : Statement(), INameScope {
|
override val position: Position) : Statement(), INameScope {
|
||||||
|
|
||||||
var keepAlways: Boolean = false
|
var keepAlways: Boolean = false
|
||||||
override val expensiveToInline
|
|
||||||
get() = statements.any { it.expensiveToInline }
|
|
||||||
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
val calledBy = mutableListOf<Node>()
|
|
||||||
val calls = mutableSetOf<Subroutine>()
|
|
||||||
|
|
||||||
val scopedname: String by lazy { makeScopedName(name) }
|
val scopedname: String by lazy { makeScopedName(name) }
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -703,12 +633,11 @@ class Subroutine(override val name: String,
|
|||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Statement)
|
require(replacement is Statement)
|
||||||
val idx = statements.indexOf(node)
|
val idx = statements.indexOfFirst { it===node }
|
||||||
statements[idx] = replacement
|
statements[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -745,8 +674,6 @@ class IfStatement(var condition: Expression,
|
|||||||
var elsepart: AnonymousScope,
|
var elsepart: AnonymousScope,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean
|
|
||||||
get() = truepart.expensiveToInline || elsepart.expensiveToInline
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -765,7 +692,6 @@ class IfStatement(var condition: Expression,
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -776,8 +702,6 @@ class BranchStatement(var condition: BranchCondition,
|
|||||||
var elsepart: AnonymousScope,
|
var elsepart: AnonymousScope,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean
|
|
||||||
get() = truepart.expensiveToInline || elsepart.expensiveToInline
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -794,23 +718,20 @@ class BranchStatement(var condition: BranchCondition,
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ForLoop(val loopRegister: Register?,
|
class ForLoop(var loopVar: IdentifierReference,
|
||||||
var loopVar: IdentifierReference?,
|
|
||||||
var iterable: Expression,
|
var iterable: Expression,
|
||||||
var body: AnonymousScope,
|
var body: AnonymousScope,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent=parent
|
this.parent=parent
|
||||||
loopVar?.linkParents(this)
|
loopVar.linkParents(this)
|
||||||
iterable.linkParents(this)
|
iterable.linkParents(this)
|
||||||
body.linkParents(this)
|
body.linkParents(this)
|
||||||
}
|
}
|
||||||
@ -825,26 +746,20 @@ class ForLoop(val loopRegister: Register?,
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "ForLoop(loopVar: $loopVar, loopReg: $loopRegister, iterable: $iterable, pos=$position)"
|
return "ForLoop(loopVar: $loopVar, iterable: $iterable, pos=$position)"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loopVarDt(program: Program): InferredTypes.InferredType {
|
fun loopVarDt(program: Program) = loopVar.inferType(program)
|
||||||
val lv = loopVar
|
|
||||||
return if(loopRegister!=null) InferredTypes.InferredType.known(DataType.UBYTE)
|
|
||||||
else lv?.inferType(program) ?: InferredTypes.InferredType.unknown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class WhileLoop(var condition: Expression,
|
class WhileLoop(var condition: Expression,
|
||||||
var body: AnonymousScope,
|
var body: AnonymousScope,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -861,36 +776,36 @@ class WhileLoop(var condition: Expression,
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ForeverLoop(var body: AnonymousScope, override val position: Position) : Statement() {
|
class RepeatLoop(var iterations: Expression?, var body: AnonymousScope, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
|
iterations?.linkParents(this)
|
||||||
body.linkParents(this)
|
body.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is AnonymousScope && node===body)
|
when {
|
||||||
body = replacement
|
node===iterations -> iterations = replacement as Expression
|
||||||
|
node===body -> body = replacement as AnonymousScope
|
||||||
|
else -> throw FatalAstException("invalid replace")
|
||||||
|
}
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
class RepeatLoop(var body: AnonymousScope,
|
class UntilLoop(var body: AnonymousScope,
|
||||||
var untilCondition: Expression,
|
var untilCondition: Expression,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -907,7 +822,6 @@ class RepeatLoop(var body: AnonymousScope,
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
@ -916,7 +830,6 @@ class WhenStatement(var condition: Expression,
|
|||||||
var choices: MutableList<WhenChoice>,
|
var choices: MutableList<WhenChoice>,
|
||||||
override val position: Position): Statement() {
|
override val position: Position): Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -928,7 +841,7 @@ class WhenStatement(var condition: Expression,
|
|||||||
if(node===condition)
|
if(node===condition)
|
||||||
condition = replacement as Expression
|
condition = replacement as Expression
|
||||||
else {
|
else {
|
||||||
val idx = choices.indexOf(node)
|
val idx = choices.withIndex().find { it.value===node }!!.index
|
||||||
choices[idx] = replacement as WhenChoice
|
choices[idx] = replacement as WhenChoice
|
||||||
}
|
}
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
@ -952,7 +865,6 @@ class WhenStatement(var condition: Expression,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -978,7 +890,6 @@ class WhenChoice(var values: List<Expression>?, // if null, this is t
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -988,7 +899,6 @@ class StructDecl(override val name: String,
|
|||||||
override val position: Position): Statement(), INameScope {
|
override val position: Position): Statement(), INameScope {
|
||||||
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -997,7 +907,7 @@ class StructDecl(override val name: String,
|
|||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Statement)
|
require(replacement is Statement)
|
||||||
val idx = statements.indexOf(node)
|
val idx = statements.indexOfFirst { it===node }
|
||||||
statements[idx] = replacement
|
statements[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
@ -1006,7 +916,6 @@ class StructDecl(override val name: String,
|
|||||||
get() = this.statements.size
|
get() = this.statements.size
|
||||||
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
fun nameOfFirstMember() = (statements.first() as VarDecl).name
|
fun nameOfFirstMember() = (statements.first() as VarDecl).name
|
||||||
@ -1031,6 +940,5 @@ class DirectMemoryWrite(var addressExpression: Expression, override val position
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,14 @@ import prog8.ast.statements.*
|
|||||||
|
|
||||||
internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: ErrorReporter) : AstWalker() {
|
internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: ErrorReporter) : AstWalker() {
|
||||||
|
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
if (decl.value == null && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
if (decl.value == null && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
||||||
// a numeric vardecl without an initial value is initialized with zero.
|
// a numeric vardecl without an initial value is initialized with zero.
|
||||||
decl.value = decl.zeroElementValue()
|
decl.value = decl.zeroElementValue()
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||||
@ -37,7 +39,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
|||||||
return numericVarsWithValue.map {
|
return numericVarsWithValue.map {
|
||||||
val initValue = it.value!! // assume here that value has always been set by now
|
val initValue = it.value!! // assume here that value has always been set by now
|
||||||
it.value = null // make sure no value init assignment for this vardecl will be created later (would be superfluous)
|
it.value = null // make sure no value init assignment for this vardecl will be created later (would be superfluous)
|
||||||
val target = AssignTarget(null, IdentifierReference(listOf(it.name), it.position), null, null, it.position)
|
val target = AssignTarget(IdentifierReference(listOf(it.name), it.position), null, null, it.position)
|
||||||
val assign = Assignment(target, null, initValue, it.position)
|
val assign = Assignment(target, null, initValue, it.position)
|
||||||
initValue.parent = assign
|
initValue.parent = assign
|
||||||
IAstModification.InsertFirst(assign, scope)
|
IAstModification.InsertFirst(assign, scope)
|
||||||
@ -45,7 +47,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
|||||||
decls.map { IAstModification.InsertFirst(it, sub) } // move it up to the subroutine
|
decls.map { IAstModification.InsertFirst(it, sub) } // move it up to the subroutine
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
@ -99,6 +101,6 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ fun compileProgram(filepath: Path,
|
|||||||
optimizeAst(programAst, errors)
|
optimizeAst(programAst, errors)
|
||||||
postprocessAst(programAst, errors, compilationOptions)
|
postprocessAst(programAst, errors, compilationOptions)
|
||||||
|
|
||||||
// printAst(programAst) // TODO
|
// printAst(programAst)
|
||||||
|
|
||||||
if(writeAssembly)
|
if(writeAssembly)
|
||||||
programName = writeAssembly(programAst, errors, outputDir, optimize, compilationOptions)
|
programName = writeAssembly(programAst, errors, outputDir, optimize, compilationOptions)
|
||||||
@ -144,13 +144,12 @@ private fun processAst(programAst: Program, errors: ErrorReporter, compilerOptio
|
|||||||
println("Processing...")
|
println("Processing...")
|
||||||
programAst.checkIdentifiers(errors)
|
programAst.checkIdentifiers(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.makeForeverLoops()
|
|
||||||
programAst.constantFold(errors)
|
programAst.constantFold(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.removeNopsFlattenAnonScopes()
|
|
||||||
programAst.reorderStatements()
|
programAst.reorderStatements()
|
||||||
programAst.addTypecasts(errors)
|
programAst.addTypecasts(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
|
programAst.variousCleanups()
|
||||||
programAst.checkValid(compilerOptions, errors)
|
programAst.checkValid(compilerOptions, errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.checkIdentifiers(errors)
|
programAst.checkIdentifiers(errors)
|
||||||
@ -180,11 +179,12 @@ private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerO
|
|||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.addTypecasts(errors)
|
programAst.addTypecasts(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.removeNopsFlattenAnonScopes()
|
programAst.variousCleanups()
|
||||||
programAst.checkValid(compilerOptions, errors) // check if final tree is still valid
|
programAst.checkValid(compilerOptions, errors) // check if final tree is still valid
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.checkRecursion(errors) // check if there are recursive subroutine calls
|
programAst.checkRecursion(errors) // check if there are recursive subroutine calls
|
||||||
errors.handle()
|
errors.handle()
|
||||||
|
programAst.verifyFunctionArgTypes()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
|
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
|
||||||
@ -194,7 +194,7 @@ private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir:
|
|||||||
programAst.processAstBeforeAsmGeneration(errors)
|
programAst.processAstBeforeAsmGeneration(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
|
|
||||||
// printAst(programAst) // TODO
|
// printAst(programAst)
|
||||||
|
|
||||||
val assembly = CompilationTarget.asmGenerator(
|
val assembly = CompilationTarget.asmGenerator(
|
||||||
programAst,
|
programAst,
|
||||||
|
@ -5,9 +5,6 @@ import prog8.compiler.CompilerException
|
|||||||
import prog8.compiler.Zeropage
|
import prog8.compiler.Zeropage
|
||||||
import prog8.compiler.ZeropageType
|
import prog8.compiler.ZeropageType
|
||||||
import prog8.compiler.target.IMachineDefinition
|
import prog8.compiler.target.IMachineDefinition
|
||||||
import java.awt.Color
|
|
||||||
import java.awt.image.BufferedImage
|
|
||||||
import javax.imageio.ImageIO
|
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
@ -177,90 +174,4 @@ object C64MachineDefinition: IMachineDefinition {
|
|||||||
return if (sign) -result else result
|
return if (sign) -result else result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object Charset {
|
|
||||||
private val normalImg = ImageIO.read(javaClass.getResource("/charset/c64/charset-normal.png"))
|
|
||||||
private val shiftedImg = ImageIO.read(javaClass.getResource("/charset/c64/charset-shifted.png"))
|
|
||||||
|
|
||||||
private fun scanChars(img: BufferedImage): Array<BufferedImage> {
|
|
||||||
|
|
||||||
val transparent = BufferedImage(img.width, img.height, BufferedImage.TYPE_INT_ARGB)
|
|
||||||
transparent.createGraphics().drawImage(img, 0, 0, null)
|
|
||||||
|
|
||||||
val black = Color(0, 0, 0).rgb
|
|
||||||
val nopixel = Color(0, 0, 0, 0).rgb
|
|
||||||
for (y in 0 until transparent.height) {
|
|
||||||
for (x in 0 until transparent.width) {
|
|
||||||
val col = transparent.getRGB(x, y)
|
|
||||||
if (col == black)
|
|
||||||
transparent.setRGB(x, y, nopixel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val numColumns = transparent.width / 8
|
|
||||||
val charImages = (0..255).map {
|
|
||||||
val charX = it % numColumns
|
|
||||||
val charY = it / numColumns
|
|
||||||
transparent.getSubimage(charX * 8, charY * 8, 8, 8)
|
|
||||||
}
|
|
||||||
return charImages.toTypedArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
val normalChars = scanChars(normalImg)
|
|
||||||
val shiftedChars = scanChars(shiftedImg)
|
|
||||||
|
|
||||||
private val coloredNormalChars = mutableMapOf<Short, Array<BufferedImage>>()
|
|
||||||
|
|
||||||
fun getColoredChar(screenCode: Short, color: Short): BufferedImage {
|
|
||||||
val colorIdx = (color % colorPalette.size).toShort()
|
|
||||||
val chars = coloredNormalChars[colorIdx]
|
|
||||||
if (chars != null)
|
|
||||||
return chars[screenCode.toInt()]
|
|
||||||
|
|
||||||
val coloredChars = mutableListOf<BufferedImage>()
|
|
||||||
val transparent = Color(0, 0, 0, 0).rgb
|
|
||||||
val rgb = colorPalette[colorIdx.toInt()].rgb
|
|
||||||
for (c in normalChars) {
|
|
||||||
val colored = c.copy()
|
|
||||||
for (y in 0 until colored.height)
|
|
||||||
for (x in 0 until colored.width) {
|
|
||||||
if (colored.getRGB(x, y) != transparent) {
|
|
||||||
colored.setRGB(x, y, rgb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
coloredChars.add(colored)
|
|
||||||
}
|
|
||||||
coloredNormalChars[colorIdx] = coloredChars.toTypedArray()
|
|
||||||
return coloredNormalChars.getValue(colorIdx)[screenCode.toInt()]
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun BufferedImage.copy(): BufferedImage {
|
|
||||||
val bcopy = BufferedImage(this.width, this.height, this.type)
|
|
||||||
val g = bcopy.graphics
|
|
||||||
g.drawImage(this, 0, 0, null)
|
|
||||||
g.dispose()
|
|
||||||
return bcopy
|
|
||||||
}
|
|
||||||
|
|
||||||
val colorPalette = listOf( // this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
|
|
||||||
Color(0x000000), // 0 = black
|
|
||||||
Color(0xFFFFFF), // 1 = white
|
|
||||||
Color(0x813338), // 2 = red
|
|
||||||
Color(0x75cec8), // 3 = cyan
|
|
||||||
Color(0x8e3c97), // 4 = purple
|
|
||||||
Color(0x56ac4d), // 5 = green
|
|
||||||
Color(0x2e2c9b), // 6 = blue
|
|
||||||
Color(0xedf171), // 7 = yellow
|
|
||||||
Color(0x8e5029), // 8 = orange
|
|
||||||
Color(0x553800), // 9 = brown
|
|
||||||
Color(0xc46c71), // 10 = light red
|
|
||||||
Color(0x4a4a4a), // 11 = dark grey
|
|
||||||
Color(0x7b7b7b), // 12 = medium grey
|
|
||||||
Color(0xa9ff9f), // 13 = light green
|
|
||||||
Color(0x706deb), // 14 = light blue
|
|
||||||
Color(0xb2b2b2) // 15 = light grey
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
|
import prog8.ast.INameScope
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.antlr.escape
|
import prog8.ast.antlr.escape
|
||||||
@ -183,7 +184,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
blockLevelVarInits.getValue(block).forEach { decl ->
|
blockLevelVarInits.getValue(block).forEach { decl ->
|
||||||
val scopedFullName = decl.makeScopedName(decl.name).split('.')
|
val scopedFullName = decl.makeScopedName(decl.name).split('.')
|
||||||
require(scopedFullName.first()==block.name)
|
require(scopedFullName.first()==block.name)
|
||||||
val target = AssignTarget(null, IdentifierReference(scopedFullName.drop(1), decl.position), null, null, decl.position)
|
val target = AssignTarget(IdentifierReference(scopedFullName.drop(1), decl.position), null, null, decl.position)
|
||||||
val assign = Assignment(target, null, decl.value!!, decl.position)
|
val assign = Assignment(target, null, decl.value!!, decl.position)
|
||||||
assign.linkParents(decl.parent)
|
assign.linkParents(decl.parent)
|
||||||
assignmentAsmGen.translate(assign)
|
assignmentAsmGen.translate(assign)
|
||||||
@ -561,19 +562,19 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun saveRegister(register: Register) {
|
internal fun saveRegister(register: CpuRegister) {
|
||||||
when(register) {
|
when(register) {
|
||||||
Register.A -> out(" pha")
|
CpuRegister.A -> out(" pha")
|
||||||
Register.X -> out(" txa | pha")
|
CpuRegister.X -> out(" txa | pha")
|
||||||
Register.Y -> out(" tya | pha")
|
CpuRegister.Y -> out(" tya | pha")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun restoreRegister(register: Register) {
|
internal fun restoreRegister(register: CpuRegister) {
|
||||||
when(register) {
|
when(register) {
|
||||||
Register.A -> out(" pla")
|
CpuRegister.A -> out(" pla")
|
||||||
Register.X -> out(" pla | tax")
|
CpuRegister.X -> out(" pla | tax")
|
||||||
Register.Y -> out(" pla | tay")
|
CpuRegister.Y -> out(" pla | tay")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -640,8 +641,8 @@ internal class AsmGen(private val program: Program,
|
|||||||
is Continue -> out(" jmp ${loopContinueLabels.peek()}")
|
is Continue -> out(" jmp ${loopContinueLabels.peek()}")
|
||||||
is Break -> out(" jmp ${loopEndLabels.peek()}")
|
is Break -> out(" jmp ${loopEndLabels.peek()}")
|
||||||
is WhileLoop -> translate(stmt)
|
is WhileLoop -> translate(stmt)
|
||||||
is ForeverLoop -> translate(stmt)
|
|
||||||
is RepeatLoop -> translate(stmt)
|
is RepeatLoop -> translate(stmt)
|
||||||
|
is UntilLoop -> translate(stmt)
|
||||||
is WhenStatement -> translate(stmt)
|
is WhenStatement -> translate(stmt)
|
||||||
is BuiltinFunctionStatementPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore?")
|
is BuiltinFunctionStatementPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore?")
|
||||||
is AnonymousScope -> translate(stmt)
|
is AnonymousScope -> translate(stmt)
|
||||||
@ -672,26 +673,114 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(stmt: ForeverLoop) {
|
private fun translate(stmt: RepeatLoop) {
|
||||||
val foreverLabel = makeLabel("forever")
|
val repeatLabel = makeLabel("repeat")
|
||||||
val endLabel = makeLabel("foreverend")
|
val endLabel = makeLabel("repeatend")
|
||||||
|
val counterLabel = makeLabel("repeatcounter")
|
||||||
loopEndLabels.push(endLabel)
|
loopEndLabels.push(endLabel)
|
||||||
loopContinueLabels.push(foreverLabel)
|
loopContinueLabels.push(repeatLabel)
|
||||||
out(foreverLabel)
|
|
||||||
|
when (stmt.iterations) {
|
||||||
|
null -> {
|
||||||
|
// endless loop
|
||||||
|
out(repeatLabel)
|
||||||
translate(stmt.body)
|
translate(stmt.body)
|
||||||
out(" jmp $foreverLabel")
|
out(" jmp $repeatLabel")
|
||||||
out(endLabel)
|
out(endLabel)
|
||||||
|
}
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val iterations = (stmt.iterations as NumericLiteralValue).number.toInt()
|
||||||
|
if(iterations<0 || iterations > 65536)
|
||||||
|
throw AssemblyError("invalid number of iterations")
|
||||||
|
when {
|
||||||
|
iterations == 0 -> {}
|
||||||
|
iterations <= 255 -> {
|
||||||
|
out(" lda #${iterations}")
|
||||||
|
repeatByteCountInA(counterLabel, repeatLabel, endLabel, stmt.body)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
out(" lda #<${iterations} | ldy #>${iterations}")
|
||||||
|
repeatWordCountInAY(counterLabel, repeatLabel, endLabel, stmt.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val vardecl = (stmt.iterations as IdentifierReference).targetStatement(program.namespace) as VarDecl
|
||||||
|
val name = asmIdentifierName(stmt.iterations as IdentifierReference)
|
||||||
|
when(vardecl.datatype) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
out(" lda $name")
|
||||||
|
repeatByteCountInA(counterLabel, repeatLabel, endLabel, stmt.body)
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
out(" lda $name | ldy $name+1")
|
||||||
|
repeatWordCountInAY(counterLabel, repeatLabel, endLabel, stmt.body)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid loop variable datatype $vardecl")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
translateExpression(stmt.iterations!!)
|
||||||
|
val dt = stmt.iterations!!.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
when (dt) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
out(" inx | lda ${ESTACK_LO_HEX},x")
|
||||||
|
repeatByteCountInA(counterLabel, repeatLabel, endLabel, stmt.body)
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
out(" inx | lda ${ESTACK_LO_HEX},x | ldy ${ESTACK_HI_HEX},x")
|
||||||
|
repeatWordCountInAY(counterLabel, repeatLabel, endLabel, stmt.body)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid loop expression datatype $dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loopEndLabels.pop()
|
loopEndLabels.pop()
|
||||||
loopContinueLabels.pop()
|
loopContinueLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun repeatWordCountInAY(counterLabel: String, repeatLabel: String, endLabel: String, body: AnonymousScope) {
|
||||||
|
// note: A/Y must have been loaded with the number of iterations already!
|
||||||
|
out("""
|
||||||
|
sta $counterLabel
|
||||||
|
sty $counterLabel+1
|
||||||
|
$repeatLabel lda $counterLabel
|
||||||
|
bne +
|
||||||
|
lda $counterLabel+1
|
||||||
|
beq $endLabel
|
||||||
|
+ lda $counterLabel
|
||||||
|
bne +
|
||||||
|
dec $counterLabel+1
|
||||||
|
+ dec $counterLabel
|
||||||
|
""")
|
||||||
|
translate(body)
|
||||||
|
out("""
|
||||||
|
jmp $repeatLabel
|
||||||
|
$counterLabel .word 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun repeatByteCountInA(counterLabel: String, repeatLabel: String, endLabel: String, body: AnonymousScope) {
|
||||||
|
// note: A must have been loaded with the number of iterations already!
|
||||||
|
out("""
|
||||||
|
sta $counterLabel
|
||||||
|
$repeatLabel lda $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
dec $counterLabel""")
|
||||||
|
translate(body)
|
||||||
|
out("""
|
||||||
|
jmp $repeatLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
|
||||||
private fun translate(stmt: WhileLoop) {
|
private fun translate(stmt: WhileLoop) {
|
||||||
val whileLabel = makeLabel("while")
|
val whileLabel = makeLabel("while")
|
||||||
val endLabel = makeLabel("whileend")
|
val endLabel = makeLabel("whileend")
|
||||||
loopEndLabels.push(endLabel)
|
loopEndLabels.push(endLabel)
|
||||||
loopContinueLabels.push(whileLabel)
|
loopContinueLabels.push(whileLabel)
|
||||||
out(whileLabel)
|
out(whileLabel)
|
||||||
// TODO optimize for the simple cases, can we avoid stack use?
|
|
||||||
expressionsAsmGen.translateExpression(stmt.condition)
|
expressionsAsmGen.translateExpression(stmt.condition)
|
||||||
val conditionDt = stmt.condition.inferType(program)
|
val conditionDt = stmt.condition.inferType(program)
|
||||||
if(!conditionDt.isKnown)
|
if(!conditionDt.isKnown)
|
||||||
@ -714,13 +803,12 @@ internal class AsmGen(private val program: Program,
|
|||||||
loopContinueLabels.pop()
|
loopContinueLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(stmt: RepeatLoop) {
|
private fun translate(stmt: UntilLoop) {
|
||||||
val repeatLabel = makeLabel("repeat")
|
val repeatLabel = makeLabel("repeat")
|
||||||
val endLabel = makeLabel("repeatend")
|
val endLabel = makeLabel("repeatend")
|
||||||
loopEndLabels.push(endLabel)
|
loopEndLabels.push(endLabel)
|
||||||
loopContinueLabels.push(repeatLabel)
|
loopContinueLabels.push(repeatLabel)
|
||||||
out(repeatLabel)
|
out(repeatLabel)
|
||||||
// TODO optimize this for the simple cases, can we avoid stack use?
|
|
||||||
translate(stmt.body)
|
translate(stmt.body)
|
||||||
expressionsAsmGen.translateExpression(stmt.untilCondition)
|
expressionsAsmGen.translateExpression(stmt.untilCondition)
|
||||||
val conditionDt = stmt.untilCondition.inferType(program)
|
val conditionDt = stmt.untilCondition.inferType(program)
|
||||||
@ -838,13 +926,16 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
inits.add(stmt)
|
inits.add(stmt)
|
||||||
} else {
|
} else {
|
||||||
val target = AssignTarget(null, IdentifierReference(listOf(stmt.name), stmt.position), null, null, stmt.position)
|
val next = (stmt.parent as INameScope).nextSibling(stmt)
|
||||||
|
if (next !is ForLoop || next.loopVar.nameInSource.single() != stmt.name) {
|
||||||
|
val target = AssignTarget(IdentifierReference(listOf(stmt.name), stmt.position), null, null, stmt.position)
|
||||||
val assign = Assignment(target, null, stmt.value!!, stmt.position)
|
val assign = Assignment(target, null, stmt.value!!, stmt.position)
|
||||||
assign.linkParents(stmt.parent)
|
assign.linkParents(stmt.parent)
|
||||||
translate(assign)
|
translate(assign)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun translate(stmt: Directive) {
|
private fun translate(stmt: Directive) {
|
||||||
when(stmt.directive) {
|
when(stmt.directive) {
|
||||||
@ -903,13 +994,6 @@ internal class AsmGen(private val program: Program,
|
|||||||
internal fun translateArrayIndexIntoA(expr: ArrayIndexedExpression) {
|
internal fun translateArrayIndexIntoA(expr: ArrayIndexedExpression) {
|
||||||
when (val index = expr.arrayspec.index) {
|
when (val index = expr.arrayspec.index) {
|
||||||
is NumericLiteralValue -> throw AssemblyError("this should be optimized directly")
|
is NumericLiteralValue -> throw AssemblyError("this should be optimized directly")
|
||||||
is RegisterExpr -> {
|
|
||||||
when (index.register) {
|
|
||||||
Register.A -> {}
|
|
||||||
Register.X -> out(" txa")
|
|
||||||
Register.Y -> out(" tya")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val indexName = asmIdentifierName(index)
|
val indexName = asmIdentifierName(index)
|
||||||
out(" lda $indexName")
|
out(" lda $indexName")
|
||||||
@ -925,13 +1009,6 @@ internal class AsmGen(private val program: Program,
|
|||||||
internal fun translateArrayIndexIntoY(expr: ArrayIndexedExpression) {
|
internal fun translateArrayIndexIntoY(expr: ArrayIndexedExpression) {
|
||||||
when (val index = expr.arrayspec.index) {
|
when (val index = expr.arrayspec.index) {
|
||||||
is NumericLiteralValue -> throw AssemblyError("this should be optimized directly")
|
is NumericLiteralValue -> throw AssemblyError("this should be optimized directly")
|
||||||
is RegisterExpr -> {
|
|
||||||
when (index.register) {
|
|
||||||
Register.A -> out(" tay")
|
|
||||||
Register.X -> out(" txa | tay")
|
|
||||||
Register.Y -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val indexName = asmIdentifierName(index)
|
val indexName = asmIdentifierName(index)
|
||||||
out(" ldy $indexName")
|
out(" ldy $indexName")
|
||||||
@ -974,9 +1051,12 @@ internal class AsmGen(private val program: Program,
|
|||||||
fun assignFromFloatVariable(target: AssignTarget, variable: IdentifierReference) =
|
fun assignFromFloatVariable(target: AssignTarget, variable: IdentifierReference) =
|
||||||
assignmentAsmGen.assignFromFloatVariable(target, variable)
|
assignmentAsmGen.assignFromFloatVariable(target, variable)
|
||||||
|
|
||||||
fun assignFromRegister(target: AssignTarget, register: Register) =
|
fun assignFromRegister(target: AssignTarget, register: CpuRegister) =
|
||||||
assignmentAsmGen.assignFromRegister(target, register)
|
assignmentAsmGen.assignFromRegister(target, register)
|
||||||
|
|
||||||
fun assignFromMemoryByte(target: AssignTarget, address: Int?, identifier: IdentifierReference?) =
|
fun assignFromMemoryByte(target: AssignTarget, address: Int?, identifier: IdentifierReference?) =
|
||||||
assignmentAsmGen.assignFromMemoryByte(target, address, identifier)
|
assignmentAsmGen.assignFromMemoryByte(target, address, identifier)
|
||||||
|
|
||||||
|
fun assignToRegister(reg: CpuRegister, value: Short?, identifier: IdentifierReference?) =
|
||||||
|
assignmentAsmGen.assignToRegister(reg, value, identifier)
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -2,12 +2,8 @@ package prog8.compiler.target.c64.codegen
|
|||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.ByteDatatypes
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.DataType
|
|
||||||
import prog8.ast.base.Register
|
|
||||||
import prog8.ast.base.WordDatatypes
|
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.AssignTarget
|
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
||||||
@ -176,13 +172,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.ror2_mem_ub")
|
asmgen.out(" jsr prog8_lib.ror2_mem_ub")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is RegisterExpr -> {
|
|
||||||
when (what.register) {
|
|
||||||
Register.A -> asmgen.out(" lsr a | bcc + | ora #\$80 |+ ")
|
|
||||||
Register.X -> asmgen.out(" txa | lsr a | bcc + | ora #\$80 |+ tax ")
|
|
||||||
Register.Y -> asmgen.out(" tya | lsr a | bcc + | ora #\$80 |+ tay ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable")
|
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable")
|
||||||
@ -235,13 +224,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is RegisterExpr -> {
|
|
||||||
when (what.register) {
|
|
||||||
Register.A -> asmgen.out(" ror a")
|
|
||||||
Register.X -> asmgen.out(" txa | ror a | tax")
|
|
||||||
Register.Y -> asmgen.out(" tya | ror a | tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
asmgen.out(" ror $variable")
|
asmgen.out(" ror $variable")
|
||||||
@ -287,13 +269,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.rol2_mem_ub")
|
asmgen.out(" jsr prog8_lib.rol2_mem_ub")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is RegisterExpr -> {
|
|
||||||
when (what.register) {
|
|
||||||
Register.A -> asmgen.out(" cmp #\$80 | rol a ")
|
|
||||||
Register.X -> asmgen.out(" txa | cmp #\$80 | rol a | tax")
|
|
||||||
Register.Y -> asmgen.out(" tya | cmp #\$80 | rol a | tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable")
|
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable")
|
||||||
@ -346,13 +321,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is RegisterExpr -> {
|
|
||||||
when (what.register) {
|
|
||||||
Register.A -> asmgen.out(" rol a")
|
|
||||||
Register.X -> asmgen.out(" txa | rol a | tax")
|
|
||||||
Register.Y -> asmgen.out(" tya | rol a | tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
asmgen.out(" rol $variable")
|
asmgen.out(" rol $variable")
|
||||||
@ -384,13 +352,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is RegisterExpr -> {
|
|
||||||
when (what.register) {
|
|
||||||
Register.A -> asmgen.out(" lsr a")
|
|
||||||
Register.X -> asmgen.out(" txa | lsr a | tax")
|
|
||||||
Register.Y -> asmgen.out(" tya | lsr a | tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> asmgen.out(" lsr ${asmgen.asmIdentifierName(what)}")
|
is IdentifierReference -> asmgen.out(" lsr ${asmgen.asmIdentifierName(what)}")
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
if (what.addressExpression is NumericLiteralValue) {
|
if (what.addressExpression is NumericLiteralValue) {
|
||||||
@ -468,13 +429,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is RegisterExpr -> {
|
|
||||||
when (what.register) {
|
|
||||||
Register.A -> asmgen.out(" asl a")
|
|
||||||
Register.X -> asmgen.out(" txa | asl a | tax")
|
|
||||||
Register.Y -> asmgen.out(" tya | asl a | tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> asmgen.out(" asl ${asmgen.asmIdentifierName(what)}")
|
is IdentifierReference -> asmgen.out(" asl ${asmgen.asmIdentifierName(what)}")
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
if (what.addressExpression is NumericLiteralValue) {
|
if (what.addressExpression is NumericLiteralValue) {
|
||||||
@ -581,32 +535,32 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
ldy $firstName
|
ldy $firstName
|
||||||
lda $secondName
|
lda $secondName
|
||||||
sta $firstName
|
sta $firstName
|
||||||
tya
|
sty $secondName
|
||||||
sta $secondName
|
|
||||||
ldy $firstName+1
|
ldy $firstName+1
|
||||||
lda $secondName+1
|
lda $secondName+1
|
||||||
sta $firstName+1
|
sta $firstName+1
|
||||||
tya
|
sty $secondName+1
|
||||||
sta $secondName+1
|
|
||||||
""")
|
""")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if(dt.istype(DataType.FLOAT)) {
|
if(dt.istype(DataType.FLOAT)) {
|
||||||
TODO("optimized case for swapping 2 float vars-- asm subroutine")
|
asmgen.out("""
|
||||||
|
lda #<$firstName
|
||||||
|
sta ${C64Zeropage.SCRATCH_W1}
|
||||||
|
lda #>$firstName
|
||||||
|
sta ${C64Zeropage.SCRATCH_W1+1}
|
||||||
|
lda #<$secondName
|
||||||
|
sta ${C64Zeropage.SCRATCH_W2}
|
||||||
|
lda #>$secondName
|
||||||
|
sta ${C64Zeropage.SCRATCH_W2+1}
|
||||||
|
jsr c64flt.swap_floats
|
||||||
|
""")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO more optimized cases? for instance swapping elements of array vars?
|
// other types of swap() calls should have been replaced by a different statement sequence involving a temp variable
|
||||||
|
throw AssemblyError("no asm generation for swap funccall $fcall")
|
||||||
// suboptimal code via the evaluation stack...
|
|
||||||
asmgen.translateExpression(first)
|
|
||||||
asmgen.translateExpression(second)
|
|
||||||
// pop in reverse order
|
|
||||||
val firstTarget = AssignTarget.fromExpr(first)
|
|
||||||
val secondTarget = AssignTarget.fromExpr(second)
|
|
||||||
asmgen.assignFromEvalResult(firstTarget)
|
|
||||||
asmgen.assignFromEvalResult(secondTarget)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcAbs(fcall: IFunctionCall, func: FSignature) {
|
private fun funcAbs(fcall: IFunctionCall, func: FSignature) {
|
||||||
|
@ -25,11 +25,9 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
is AddressOf -> translateExpression(expression)
|
is AddressOf -> translateExpression(expression)
|
||||||
is DirectMemoryRead -> translateExpression(expression)
|
is DirectMemoryRead -> translateExpression(expression)
|
||||||
is NumericLiteralValue -> translateExpression(expression)
|
is NumericLiteralValue -> translateExpression(expression)
|
||||||
is RegisterExpr -> translateExpression(expression)
|
|
||||||
is IdentifierReference -> translateExpression(expression)
|
is IdentifierReference -> translateExpression(expression)
|
||||||
is FunctionCall -> translateExpression(expression)
|
is FunctionCall -> translateExpression(expression)
|
||||||
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array assignment")
|
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
|
||||||
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened")
|
|
||||||
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,14 +173,6 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: RegisterExpr) {
|
|
||||||
when(expr.register) {
|
|
||||||
Register.A -> asmgen.out(" sta $ESTACK_LO_HEX,x | dex")
|
|
||||||
Register.X -> asmgen.out(" txa | sta $ESTACK_LO_HEX,x | dex")
|
|
||||||
Register.Y -> asmgen.out(" tya | sta $ESTACK_LO_HEX,x | dex")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translateExpression(expr: IdentifierReference) {
|
private fun translateExpression(expr: IdentifierReference) {
|
||||||
val varname = asmgen.asmIdentifierName(expr)
|
val varname = asmgen.asmIdentifierName(expr)
|
||||||
when(expr.inferType(program).typeOrElse(DataType.STRUCT)) {
|
when(expr.inferType(program).typeOrElse(DataType.STRUCT)) {
|
||||||
@ -240,16 +230,26 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
if(amount<=2)
|
var left = amount
|
||||||
repeat(amount) { asmgen.out(" lsr $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") }
|
while(left>=7) {
|
||||||
|
asmgen.out(" jsr math.shift_right_uw_7")
|
||||||
|
left -= 7
|
||||||
|
}
|
||||||
|
if (left in 0..2)
|
||||||
|
repeat(left) { asmgen.out(" lsr $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") }
|
||||||
else
|
else
|
||||||
asmgen.out(" jsr math.shift_right_uw_$amount") // 3-7 (8+ is done via other optimizations)
|
asmgen.out(" jsr math.shift_right_uw_$left")
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
if(amount<=2)
|
var left = amount
|
||||||
repeat(amount) { asmgen.out(" lda $ESTACK_HI_PLUS1_HEX,x | asl a | ror $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") }
|
while(left>=7) {
|
||||||
|
asmgen.out(" jsr math.shift_right_w_7")
|
||||||
|
left -= 7
|
||||||
|
}
|
||||||
|
if (left in 0..2)
|
||||||
|
repeat(left) { asmgen.out(" lda $ESTACK_HI_PLUS1_HEX,x | asl a | ror $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") }
|
||||||
else
|
else
|
||||||
asmgen.out(" jsr math.shift_right_w_$amount") // 3-7 (8+ is done via other optimizations)
|
asmgen.out(" jsr math.shift_right_w_$left")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
@ -269,11 +269,15 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(amount<=2) {
|
var left=amount
|
||||||
repeat(amount) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x | rol $ESTACK_HI_PLUS1_HEX,x") }
|
while(left>=7) {
|
||||||
} else {
|
asmgen.out(" jsr math.shift_left_w_7")
|
||||||
asmgen.out(" jsr math.shift_left_w_$amount") // 3-7 (8+ is done via other optimizations)
|
left -= 7
|
||||||
}
|
}
|
||||||
|
if (left in 0..2)
|
||||||
|
repeat(left) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x | rol $ESTACK_HI_PLUS1_HEX,x") }
|
||||||
|
else
|
||||||
|
asmgen.out(" jsr math.shift_left_w_$left")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package prog8.compiler.target.c64.codegen
|
|||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.Register
|
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.RangeExpr
|
import prog8.ast.expressions.RangeExpr
|
||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
@ -16,7 +15,7 @@ import prog8.compiler.toHex
|
|||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
// todo choose more efficient comparisons to avoid needless lda's
|
// todo choose more efficient comparisons to avoid needless lda's
|
||||||
// todo optimize common case step == 2 / -2
|
// todo optimize common case when step == 2 or -2
|
||||||
|
|
||||||
|
|
||||||
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
@ -37,7 +36,7 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
|||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
translateForOverIterableVar(stmt, iterableDt.typeOrElse(DataType.STRUCT), stmt.iterable as IdentifierReference)
|
translateForOverIterableVar(stmt, iterableDt.typeOrElse(DataType.STRUCT), stmt.iterable as IdentifierReference)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("can't iterate over ${stmt.iterable}")
|
else -> throw AssemblyError("can't iterate over ${stmt.iterable.javaClass} - should have been replaced by a variable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,28 +54,8 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
|||||||
// bytes, step 1 or -1
|
// bytes, step 1 or -1
|
||||||
|
|
||||||
val incdec = if(stepsize==1) "inc" else "dec"
|
val incdec = if(stepsize==1) "inc" else "dec"
|
||||||
if (stmt.loopRegister != null) {
|
|
||||||
// loop register over range
|
|
||||||
if(stmt.loopRegister!= Register.A)
|
|
||||||
throw AssemblyError("can only use A")
|
|
||||||
asmgen.translateExpression(range.to)
|
|
||||||
asmgen.translateExpression(range.from)
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
lda ${ESTACK_LO_HEX},x
|
|
||||||
sta $loopLabel+1
|
|
||||||
$loopLabel lda #0 ; modified""")
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel lda $loopLabel+1
|
|
||||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
|
||||||
beq $endLabel
|
|
||||||
$incdec $loopLabel+1
|
|
||||||
jmp $loopLabel
|
|
||||||
$endLabel inx""")
|
|
||||||
} else {
|
|
||||||
// loop over byte range via loopvar
|
// loop over byte range via loopvar
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||||
asmgen.translateExpression(range.to)
|
asmgen.translateExpression(range.to)
|
||||||
asmgen.translateExpression(range.from)
|
asmgen.translateExpression(range.from)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -93,46 +72,12 @@ $continueLabel lda $varname
|
|||||||
jmp $loopLabel
|
jmp $loopLabel
|
||||||
$endLabel inx""")
|
$endLabel inx""")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
|
|
||||||
// bytes, step >= 2 or <= -2
|
// bytes, step >= 2 or <= -2
|
||||||
|
|
||||||
if (stmt.loopRegister != null) {
|
|
||||||
// loop register over range
|
|
||||||
if(stmt.loopRegister!= Register.A)
|
|
||||||
throw AssemblyError("can only use A")
|
|
||||||
asmgen.translateExpression(range.to)
|
|
||||||
asmgen.translateExpression(range.from)
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
lda ${ESTACK_LO_HEX},x
|
|
||||||
sta $loopLabel+1
|
|
||||||
$loopLabel lda #0 ; modified""")
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel lda $loopLabel+1""")
|
|
||||||
if(stepsize>0) {
|
|
||||||
asmgen.out("""
|
|
||||||
clc
|
|
||||||
adc #$stepsize
|
|
||||||
sta $loopLabel+1
|
|
||||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
|
||||||
bcc $loopLabel
|
|
||||||
beq $loopLabel""")
|
|
||||||
} else {
|
|
||||||
asmgen.out("""
|
|
||||||
sec
|
|
||||||
sbc #${stepsize.absoluteValue}
|
|
||||||
sta $loopLabel+1
|
|
||||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
|
||||||
bcs $loopLabel""")
|
|
||||||
}
|
|
||||||
asmgen.out("""
|
|
||||||
$endLabel inx""")
|
|
||||||
} else {
|
|
||||||
// loop over byte range via loopvar
|
// loop over byte range via loopvar
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||||
asmgen.translateExpression(range.to)
|
asmgen.translateExpression(range.to)
|
||||||
asmgen.translateExpression(range.from)
|
asmgen.translateExpression(range.from)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -163,7 +108,6 @@ $continueLabel lda $varname""")
|
|||||||
$endLabel inx""")
|
$endLabel inx""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
when {
|
when {
|
||||||
|
|
||||||
@ -171,8 +115,8 @@ $endLabel inx""")
|
|||||||
|
|
||||||
stepsize == 1 || stepsize == -1 -> {
|
stepsize == 1 || stepsize == -1 -> {
|
||||||
asmgen.translateExpression(range.to)
|
asmgen.translateExpression(range.to)
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||||
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position),
|
val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position),
|
||||||
null, range.from, range.position)
|
null, range.from, range.position)
|
||||||
assignLoopvar.linkParents(stmt)
|
assignLoopvar.linkParents(stmt)
|
||||||
asmgen.translate(assignLoopvar)
|
asmgen.translate(assignLoopvar)
|
||||||
@ -207,8 +151,8 @@ $endLabel inx""")
|
|||||||
// (u)words, step >= 2
|
// (u)words, step >= 2
|
||||||
|
|
||||||
asmgen.translateExpression(range.to)
|
asmgen.translateExpression(range.to)
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||||
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position),
|
val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position),
|
||||||
null, range.from, range.position)
|
null, range.from, range.position)
|
||||||
assignLoopvar.linkParents(stmt)
|
assignLoopvar.linkParents(stmt)
|
||||||
asmgen.translate(assignLoopvar)
|
asmgen.translate(assignLoopvar)
|
||||||
@ -256,8 +200,8 @@ $endLabel inx""")
|
|||||||
|
|
||||||
// (u)words, step <= -2
|
// (u)words, step <= -2
|
||||||
asmgen.translateExpression(range.to)
|
asmgen.translateExpression(range.to)
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||||
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position),
|
val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position),
|
||||||
null, range.from, range.position)
|
null, range.from, range.position)
|
||||||
assignLoopvar.linkParents(stmt)
|
assignLoopvar.linkParents(stmt)
|
||||||
asmgen.translate(assignLoopvar)
|
asmgen.translate(assignLoopvar)
|
||||||
@ -319,8 +263,6 @@ $endLabel inx""")
|
|||||||
val decl = ident.targetVarDecl(program.namespace)!!
|
val decl = ident.targetVarDecl(program.namespace)!!
|
||||||
when(iterableDt) {
|
when(iterableDt) {
|
||||||
DataType.STR -> {
|
DataType.STR -> {
|
||||||
if(stmt.loopRegister!=null && stmt.loopRegister!= Register.A)
|
|
||||||
throw AssemblyError("can only use A")
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$iterableName
|
lda #<$iterableName
|
||||||
ldy #>$iterableName
|
ldy #>$iterableName
|
||||||
@ -328,8 +270,7 @@ $endLabel inx""")
|
|||||||
sty $loopLabel+2
|
sty $loopLabel+2
|
||||||
$loopLabel lda ${65535.toHex()} ; modified
|
$loopLabel lda ${65535.toHex()} ; modified
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
if(stmt.loopVar!=null)
|
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar)}")
|
||||||
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar!!)}")
|
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
$continueLabel inc $loopLabel+1
|
$continueLabel inc $loopLabel+1
|
||||||
@ -339,10 +280,8 @@ $continueLabel inc $loopLabel+1
|
|||||||
$endLabel""")
|
$endLabel""")
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
// TODO: optimize loop code when the length of the array is < 256, don't need a separate counter in such cases
|
// TODO: optimize loop code when the length of the array is < 256, don't need a separate counter var in such cases
|
||||||
val length = decl.arraysize!!.size()!!
|
val length = decl.arraysize!!.size()!!
|
||||||
if(stmt.loopRegister!=null && stmt.loopRegister!= Register.A)
|
|
||||||
throw AssemblyError("can only use A")
|
|
||||||
val counterLabel = asmgen.makeLabel("for_counter")
|
val counterLabel = asmgen.makeLabel("for_counter")
|
||||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -353,8 +292,7 @@ $endLabel""")
|
|||||||
ldy #0
|
ldy #0
|
||||||
$loopLabel sty $counterLabel
|
$loopLabel sty $counterLabel
|
||||||
$modifiedLabel lda ${65535.toHex()},y ; modified""")
|
$modifiedLabel lda ${65535.toHex()},y ; modified""")
|
||||||
if(stmt.loopVar!=null)
|
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar)}")
|
||||||
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar!!)}")
|
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
$continueLabel ldy $counterLabel
|
$continueLabel ldy $counterLabel
|
||||||
@ -366,14 +304,12 @@ $counterLabel .byte 0
|
|||||||
$endLabel""")
|
$endLabel""")
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
// TODO: optimize loop code when the length of the array is < 256, don't need a separate counter in such cases
|
// TODO: optimize loop code when the length of the array is < 256, don't need a separate counter var in such cases
|
||||||
val length = decl.arraysize!!.size()!! * 2
|
val length = decl.arraysize!!.size()!! * 2
|
||||||
if(stmt.loopRegister!=null)
|
|
||||||
throw AssemblyError("can't use register to loop over words")
|
|
||||||
val counterLabel = asmgen.makeLabel("for_counter")
|
val counterLabel = asmgen.makeLabel("for_counter")
|
||||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
val modifiedLabel2 = asmgen.makeLabel("for_modified2")
|
val modifiedLabel2 = asmgen.makeLabel("for_modified2")
|
||||||
val loopvarName = asmgen.asmIdentifierName(stmt.loopVar!!)
|
val loopvarName = asmgen.asmIdentifierName(stmt.loopVar)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$iterableName
|
lda #<$iterableName
|
||||||
ldy #>$iterableName
|
ldy #>$iterableName
|
||||||
@ -410,7 +346,7 @@ $endLabel""")
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForOverConstRange(stmt: ForLoop, iterableDt: DataType, range: IntProgression) {
|
private fun translateForOverConstRange(stmt: ForLoop, iterableDt: DataType, range: IntProgression) {
|
||||||
// TODO: optimize loop code when the range is < 256 iterations, don't need a separate counter in such cases
|
// TODO: optimize loop code when the range is < 256 iterations, don't need a separate counter var in such cases
|
||||||
if (range.isEmpty())
|
if (range.isEmpty())
|
||||||
throw AssemblyError("empty range")
|
throw AssemblyError("empty range")
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
@ -421,89 +357,8 @@ $endLabel""")
|
|||||||
when(iterableDt) {
|
when(iterableDt) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
val counterLabel = asmgen.makeLabel("for_counter")
|
val counterLabel = asmgen.makeLabel("for_counter")
|
||||||
if(stmt.loopRegister!=null) {
|
|
||||||
|
|
||||||
// loop register over range
|
|
||||||
|
|
||||||
if(stmt.loopRegister!= Register.A)
|
|
||||||
throw AssemblyError("can only use A")
|
|
||||||
when {
|
|
||||||
range.step==1 -> {
|
|
||||||
// step = 1
|
|
||||||
asmgen.out("""
|
|
||||||
lda #${range.first}
|
|
||||||
sta $loopLabel+1
|
|
||||||
lda #${range.last-range.first+1 and 255}
|
|
||||||
sta $counterLabel
|
|
||||||
$loopLabel lda #0 ; modified""")
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel dec $counterLabel
|
|
||||||
beq $endLabel
|
|
||||||
inc $loopLabel+1
|
|
||||||
jmp $loopLabel
|
|
||||||
$counterLabel .byte 0
|
|
||||||
$endLabel""")
|
|
||||||
}
|
|
||||||
range.step==-1 -> {
|
|
||||||
// step = -1
|
|
||||||
asmgen.out("""
|
|
||||||
lda #${range.first}
|
|
||||||
sta $loopLabel+1
|
|
||||||
lda #${range.first-range.last+1 and 255}
|
|
||||||
sta $counterLabel
|
|
||||||
$loopLabel lda #0 ; modified """)
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel dec $counterLabel
|
|
||||||
beq $endLabel
|
|
||||||
dec $loopLabel+1
|
|
||||||
jmp $loopLabel
|
|
||||||
$counterLabel .byte 0
|
|
||||||
$endLabel""")
|
|
||||||
}
|
|
||||||
range.step >= 2 -> {
|
|
||||||
// step >= 2
|
|
||||||
asmgen.out("""
|
|
||||||
lda #${(range.last-range.first) / range.step + 1}
|
|
||||||
sta $counterLabel
|
|
||||||
lda #${range.first}
|
|
||||||
$loopLabel pha""")
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel pla
|
|
||||||
dec $counterLabel
|
|
||||||
beq $endLabel
|
|
||||||
clc
|
|
||||||
adc #${range.step}
|
|
||||||
jmp $loopLabel
|
|
||||||
$counterLabel .byte 0
|
|
||||||
$endLabel""")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
// step <= -2
|
|
||||||
asmgen.out("""
|
|
||||||
lda #${(range.first-range.last) / range.step.absoluteValue + 1}
|
|
||||||
sta $counterLabel
|
|
||||||
lda #${range.first}
|
|
||||||
$loopLabel pha""")
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel pla
|
|
||||||
dec $counterLabel
|
|
||||||
beq $endLabel
|
|
||||||
sec
|
|
||||||
sbc #${range.step.absoluteValue}
|
|
||||||
jmp $loopLabel
|
|
||||||
$counterLabel .byte 0
|
|
||||||
$endLabel""")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// loop over byte range via loopvar
|
// loop over byte range via loopvar
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||||
when {
|
when {
|
||||||
range.step==1 -> {
|
range.step==1 -> {
|
||||||
// step = 1
|
// step = 1
|
||||||
@ -581,10 +436,9 @@ $endLabel""")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
// loop over word range via loopvar
|
// loop over word range via loopvar
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||||
when {
|
when {
|
||||||
range.step == 1 -> {
|
range.step == 1 -> {
|
||||||
// word, step = 1
|
// word, step = 1
|
||||||
|
@ -19,14 +19,42 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
// output the code to setup the parameters and perform the actual call
|
// output the code to setup the parameters and perform the actual call
|
||||||
// does NOT output the code to deal with the result values!
|
// does NOT output the code to deal with the result values!
|
||||||
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||||
val saveX = Register.X in sub.asmClobbers || sub.regXasResult()
|
val saveX = CpuRegister.X in sub.asmClobbers || sub.regXasResult()
|
||||||
if(saveX)
|
if(saveX)
|
||||||
asmgen.out(" stx c64.SCRATCH_ZPREGX") // we only save X for now (required! is the eval stack pointer), screw A and Y...
|
asmgen.out(" stx c64.SCRATCH_ZPREGX") // we only save X for now (required! is the eval stack pointer), screw A and Y...
|
||||||
|
|
||||||
val subName = asmgen.asmIdentifierName(stmt.target)
|
val subName = asmgen.asmIdentifierName(stmt.target)
|
||||||
if(stmt.args.isNotEmpty()) {
|
if(stmt.args.isNotEmpty()) {
|
||||||
|
if(sub.asmParameterRegisters.isEmpty()) {
|
||||||
|
// via variables
|
||||||
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
||||||
translateFuncArguments(arg.first, arg.second, sub)
|
argumentViaVariable(sub, arg.first, arg.second)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// via registers
|
||||||
|
if(sub.parameters.size==1) {
|
||||||
|
// just a single parameter, no risk of clobbering registers
|
||||||
|
argumentViaRegister(sub, sub.parameters.withIndex().single(), stmt.args[0])
|
||||||
|
} else {
|
||||||
|
// multiple register arguments, risk of register clobbering.
|
||||||
|
// evaluate arguments onto the stack, and load the registers from the evaluated values on the stack.
|
||||||
|
when {
|
||||||
|
stmt.args.all {it is AddressOf ||
|
||||||
|
it is NumericLiteralValue ||
|
||||||
|
it is StringLiteralValue ||
|
||||||
|
it is ArrayLiteralValue ||
|
||||||
|
it is IdentifierReference} -> {
|
||||||
|
// no risk of clobbering for these simple argument types. Optimize the register loading.
|
||||||
|
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
||||||
|
argumentViaRegister(sub, arg.first, arg.second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// Risk of clobbering due to complex expression args. Work via the stack.
|
||||||
|
argsViaStackEvaluation(stmt, sub)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
asmgen.out(" jsr $subName")
|
asmgen.out(" jsr $subName")
|
||||||
@ -35,18 +63,50 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
asmgen.out(" ldx c64.SCRATCH_ZPREGX") // restore X again
|
asmgen.out(" ldx c64.SCRATCH_ZPREGX") // restore X again
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateFuncArguments(parameter: IndexedValue<SubroutineParameter>, value: Expression, sub: Subroutine) {
|
private fun argsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {
|
||||||
val sourceIDt = value.inferType(program)
|
for (arg in stmt.args.reversed())
|
||||||
if(!sourceIDt.isKnown)
|
asmgen.translateExpression(arg)
|
||||||
throw AssemblyError("arg type unknown")
|
for (regparam in sub.asmParameterRegisters) {
|
||||||
val sourceDt = sourceIDt.typeOrElse(DataType.STRUCT)
|
when (regparam.registerOrPair) {
|
||||||
if(!argumentTypeCompatible(sourceDt, parameter.value.type))
|
RegisterOrPair.A -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
|
||||||
throw AssemblyError("argument type incompatible")
|
RegisterOrPair.X -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||||
if(sub.asmParameterRegisters.isEmpty()) {
|
RegisterOrPair.Y -> asmgen.out(" inx | ldy $ESTACK_LO_HEX,x")
|
||||||
|
RegisterOrPair.AX -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x")
|
||||||
|
RegisterOrPair.XY -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||||
|
null -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
when (regparam.statusflag) {
|
||||||
|
Statusflag.Pc -> asmgen.out("""
|
||||||
|
inx
|
||||||
|
pha
|
||||||
|
lda $ESTACK_LO_HEX,x
|
||||||
|
beq +
|
||||||
|
sec
|
||||||
|
bcs ++
|
||||||
|
+ clc
|
||||||
|
+ pla
|
||||||
|
""")
|
||||||
|
null -> {
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("can only use Carry as status flag parameter")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun argumentViaVariable(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {
|
||||||
// pass parameter via a regular variable (not via registers)
|
// pass parameter via a regular variable (not via registers)
|
||||||
|
val valueIDt = value.inferType(program)
|
||||||
|
if(!valueIDt.isKnown)
|
||||||
|
throw AssemblyError("arg type unknown")
|
||||||
|
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
if(!argumentTypeCompatible(valueDt, parameter.value.type))
|
||||||
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
val paramVar = parameter.value
|
val paramVar = parameter.value
|
||||||
val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
|
val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
|
||||||
val target = AssignTarget(null, IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
|
val target = AssignTarget(IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
|
||||||
target.linkParents(value.parent)
|
target.linkParents(value.parent)
|
||||||
when (value) {
|
when (value) {
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteralValue -> {
|
||||||
@ -69,9 +129,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
else -> throw AssemblyError("weird parameter datatype")
|
else -> throw AssemblyError("weird parameter datatype")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is RegisterExpr -> {
|
|
||||||
asmgen.assignFromRegister(target, value.register)
|
|
||||||
}
|
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
when(value.addressExpression) {
|
when(value.addressExpression) {
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteralValue -> {
|
||||||
@ -84,7 +141,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
else -> {
|
else -> {
|
||||||
asmgen.translateExpression(value.addressExpression)
|
asmgen.translateExpression(value.addressExpression)
|
||||||
asmgen.out(" jsr prog8_lib.read_byte_from_address | inx")
|
asmgen.out(" jsr prog8_lib.read_byte_from_address | inx")
|
||||||
asmgen.assignFromRegister(target, Register.A)
|
asmgen.assignFromRegister(target, CpuRegister.A)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,8 +150,17 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
asmgen.assignFromEvalResult(target)
|
asmgen.assignFromEvalResult(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// pass parameter via a register parameter
|
|
||||||
|
private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {
|
||||||
|
// pass argument via a register parameter
|
||||||
|
val valueIDt = value.inferType(program)
|
||||||
|
if(!valueIDt.isKnown)
|
||||||
|
throw AssemblyError("arg type unknown")
|
||||||
|
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
if(!argumentTypeCompatible(valueDt, parameter.value.type))
|
||||||
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
val paramRegister = sub.asmParameterRegisters[parameter.index]
|
val paramRegister = sub.asmParameterRegisters[parameter.index]
|
||||||
val statusflag = paramRegister.statusflag
|
val statusflag = paramRegister.statusflag
|
||||||
val register = paramRegister.registerOrPair
|
val register = paramRegister.registerOrPair
|
||||||
@ -123,32 +189,19 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
bcs ++
|
bcs ++
|
||||||
+ clc
|
+ clc
|
||||||
+
|
+
|
||||||
""")
|
|
||||||
}
|
|
||||||
is RegisterExpr -> {
|
|
||||||
when(value.register) {
|
|
||||||
Register.A -> asmgen.out(" cmp #0")
|
|
||||||
Register.X -> asmgen.out(" txa")
|
|
||||||
Register.Y -> asmgen.out(" tya")
|
|
||||||
}
|
|
||||||
asmgen.out("""
|
|
||||||
beq +
|
|
||||||
sec
|
|
||||||
bcs ++
|
|
||||||
+ clc
|
|
||||||
+
|
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
asmgen.translateExpression(value)
|
asmgen.translateExpression(value)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
|
pha
|
||||||
lda $ESTACK_LO_HEX,x
|
lda $ESTACK_LO_HEX,x
|
||||||
beq +
|
beq +
|
||||||
sec
|
sec
|
||||||
bcs ++
|
bcs ++
|
||||||
+ clc
|
+ clc
|
||||||
+
|
+ pla
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,14 +211,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
register!=null && register.name.length==1 -> {
|
register!=null && register.name.length==1 -> {
|
||||||
when (value) {
|
when (value) {
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteralValue -> {
|
||||||
val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position)
|
asmgen.assignToRegister(CpuRegister.valueOf(register.name), value.number.toShort(), null)
|
||||||
target.linkParents(value.parent)
|
|
||||||
asmgen.assignFromByteConstant(target, value.number.toShort())
|
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position)
|
asmgen.assignToRegister(CpuRegister.valueOf(register.name), null, value)
|
||||||
target.linkParents(value.parent)
|
|
||||||
asmgen.assignFromByteVariable(target, value)
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
asmgen.translateExpression(value)
|
asmgen.translateExpression(value)
|
||||||
@ -203,7 +252,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val sourceName = asmgen.asmIdentifierName(value)
|
val sourceName = asmgen.asmIdentifierName(value)
|
||||||
if(sourceDt in PassByReferenceDatatypes) {
|
if(valueDt in PassByReferenceDatatypes) {
|
||||||
when (register) {
|
when (register) {
|
||||||
RegisterOrPair.AX -> asmgen.out(" lda #<$sourceName | ldx #>$sourceName")
|
RegisterOrPair.AX -> asmgen.out(" lda #<$sourceName | ldx #>$sourceName")
|
||||||
RegisterOrPair.AY -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
RegisterOrPair.AY -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
||||||
@ -230,7 +279,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun argumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
private fun argumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
||||||
if(argType isAssignableTo paramType)
|
if(argType isAssignableTo paramType)
|
||||||
|
@ -4,7 +4,6 @@ import prog8.ast.Program
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.expressions.RegisterExpr
|
|
||||||
import prog8.ast.statements.PostIncrDecr
|
import prog8.ast.statements.PostIncrDecr
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
||||||
@ -17,28 +16,10 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
val targetIdent = stmt.target.identifier
|
val targetIdent = stmt.target.identifier
|
||||||
val targetMemory = stmt.target.memoryAddress
|
val targetMemory = stmt.target.memoryAddress
|
||||||
val targetArrayIdx = stmt.target.arrayindexed
|
val targetArrayIdx = stmt.target.arrayindexed
|
||||||
val targetRegister = stmt.target.register
|
|
||||||
when {
|
when {
|
||||||
targetRegister!=null -> {
|
|
||||||
when(targetRegister) {
|
|
||||||
Register.A -> {
|
|
||||||
if(incr)
|
|
||||||
asmgen.out(" clc | adc #1 ")
|
|
||||||
else
|
|
||||||
asmgen.out(" sec | sbc #1 ")
|
|
||||||
}
|
|
||||||
Register.X -> {
|
|
||||||
if(incr) asmgen.out(" inx") else asmgen.out(" dex")
|
|
||||||
}
|
|
||||||
Register.Y -> {
|
|
||||||
if(incr) asmgen.out(" iny") else asmgen.out(" dey")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
targetIdent!=null -> {
|
targetIdent!=null -> {
|
||||||
val what = asmgen.asmIdentifierName(targetIdent)
|
val what = asmgen.asmIdentifierName(targetIdent)
|
||||||
val dt = stmt.target.inferType(program, stmt).typeOrElse(DataType.STRUCT)
|
when (stmt.target.inferType(program, stmt).typeOrElse(DataType.STRUCT)) {
|
||||||
when (dt) {
|
|
||||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
if(incr)
|
if(incr)
|
||||||
@ -103,10 +84,6 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
else -> throw AssemblyError("need numeric type")
|
else -> throw AssemblyError("need numeric type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is RegisterExpr -> {
|
|
||||||
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
|
||||||
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
||||||
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
||||||
|
@ -87,7 +87,20 @@ val BuiltinFunctions = mapOf(
|
|||||||
FParam("address", IterableDatatypes + DataType.UWORD),
|
FParam("address", IterableDatatypes + DataType.UWORD),
|
||||||
FParam("numwords", setOf(DataType.UWORD)),
|
FParam("numwords", setOf(DataType.UWORD)),
|
||||||
FParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
|
FParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
|
||||||
"strlen" to FSignature(true, listOf(FParam("string", setOf(DataType.STR))), DataType.UBYTE, ::builtinStrlen)
|
"strlen" to FSignature(true, listOf(FParam("string", setOf(DataType.STR))), DataType.UBYTE, ::builtinStrlen),
|
||||||
|
"substr" to FSignature(false, listOf(
|
||||||
|
FParam("source", IterableDatatypes + DataType.UWORD),
|
||||||
|
FParam("target", IterableDatatypes + DataType.UWORD),
|
||||||
|
FParam("start", setOf(DataType.UBYTE)),
|
||||||
|
FParam("length", setOf(DataType.UBYTE))), null),
|
||||||
|
"leftstr" to FSignature(false, listOf(
|
||||||
|
FParam("source", IterableDatatypes + DataType.UWORD),
|
||||||
|
FParam("target", IterableDatatypes + DataType.UWORD),
|
||||||
|
FParam("length", setOf(DataType.UBYTE))), null),
|
||||||
|
"rightstr" to FSignature(false, listOf(
|
||||||
|
FParam("source", IterableDatatypes + DataType.UWORD),
|
||||||
|
FParam("target", IterableDatatypes + DataType.UWORD),
|
||||||
|
FParam("length", setOf(DataType.UBYTE))), null)
|
||||||
)
|
)
|
||||||
|
|
||||||
fun builtinMax(array: List<Number>): Number = array.maxBy { it.toDouble() }!!
|
fun builtinMax(array: List<Number>): Number = array.maxBy { it.toDouble() }!!
|
||||||
@ -172,6 +185,7 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
|
|||||||
|
|
||||||
|
|
||||||
class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
||||||
|
class CannotEvaluateException(func:String, msg: String): FatalAstException("cannot evaluate built-in function $func: $msg")
|
||||||
|
|
||||||
|
|
||||||
private fun oneDoubleArg(args: List<Expression>, position: Position, program: Program, function: (arg: Double)->Number): NumericLiteralValue {
|
private fun oneDoubleArg(args: List<Expression>, position: Position, program: Program, function: (arg: Double)->Number): NumericLiteralValue {
|
||||||
@ -252,17 +266,22 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
|||||||
return NumericLiteralValue.optimalInteger((args[0] as ArrayLiteralValue).value.size, position)
|
return NumericLiteralValue.optimalInteger((args[0] as ArrayLiteralValue).value.size, position)
|
||||||
if(args[0] !is IdentifierReference)
|
if(args[0] !is IdentifierReference)
|
||||||
throw SyntaxError("len argument should be an identifier, but is ${args[0]}", position)
|
throw SyntaxError("len argument should be an identifier, but is ${args[0]}", position)
|
||||||
val target = (args[0] as IdentifierReference).targetVarDecl(program.namespace)!!
|
val target = (args[0] as IdentifierReference).targetVarDecl(program.namespace)
|
||||||
|
?: throw CannotEvaluateException("len", "no target vardecl")
|
||||||
|
|
||||||
return when(target.datatype) {
|
return when(target.datatype) {
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
arraySize = target.arraysize!!.size()!!
|
arraySize = target.arraysize?.size()
|
||||||
|
if(arraySize==null)
|
||||||
|
throw CannotEvaluateException("len", "arraysize unknown")
|
||||||
if(arraySize>256)
|
if(arraySize>256)
|
||||||
throw CompilerException("array length exceeds byte limit ${target.position}")
|
throw CompilerException("array length exceeds byte limit ${target.position}")
|
||||||
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
arraySize = target.arraysize!!.size()!!
|
arraySize = target.arraysize?.size()
|
||||||
|
if(arraySize==null)
|
||||||
|
throw CannotEvaluateException("len", "arraysize unknown")
|
||||||
if(arraySize>256)
|
if(arraySize>256)
|
||||||
throw CompilerException("array length exceeds byte limit ${target.position}")
|
throw CompilerException("array length exceeds byte limit ${target.position}")
|
||||||
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
||||||
|
@ -14,6 +14,7 @@ import prog8.ast.statements.PostIncrDecr
|
|||||||
internal class AssignmentTransformer(val program: Program, val errors: ErrorReporter) : AstWalker() {
|
internal class AssignmentTransformer(val program: Program, val errors: ErrorReporter) : AstWalker() {
|
||||||
|
|
||||||
var optimizationsDone: Int = 0
|
var optimizationsDone: Int = 0
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
// modify A = A + 5 back into augmented form A += 5 for easier code generation for optimized in-place assignments
|
// modify A = A + 5 back into augmented form A += 5 for easier code generation for optimized in-place assignments
|
||||||
@ -27,7 +28,7 @@ internal class AssignmentTransformer(val program: Program, val errors: ErrorRepo
|
|||||||
assignment.aug_op = binExpr.operator + "="
|
assignment.aug_op = binExpr.operator + "="
|
||||||
assignment.value.parent = assignment
|
assignment.value.parent = assignment
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assignment.aug_op = "setvalue"
|
assignment.aug_op = "setvalue"
|
||||||
@ -151,6 +152,6 @@ internal class AssignmentTransformer(val program: Program, val errors: ErrorRepo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,10 +24,10 @@ private val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexO
|
|||||||
|
|
||||||
class CallGraph(private val program: Program) : IAstVisitor {
|
class CallGraph(private val program: Program) : IAstVisitor {
|
||||||
|
|
||||||
val modulesImporting = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
val imports = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||||
val modulesImportedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
val importedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||||
val subroutinesCalling = mutableMapOf<INameScope, List<Subroutine>>().withDefault { mutableListOf() }
|
val calls = mutableMapOf<INameScope, List<Subroutine>>().withDefault { mutableListOf() }
|
||||||
val subroutinesCalledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
|
val calledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
|
||||||
|
|
||||||
// TODO add dataflow graph: what statements use what variables - can be used to eliminate unused vars
|
// TODO add dataflow graph: what statements use what variables - can be used to eliminate unused vars
|
||||||
val usedSymbols = mutableSetOf<Statement>()
|
val usedSymbols = mutableSetOf<Statement>()
|
||||||
@ -55,17 +55,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
it.importedBy.clear()
|
it.importedBy.clear()
|
||||||
it.imports.clear()
|
it.imports.clear()
|
||||||
|
|
||||||
it.importedBy.addAll(modulesImportedBy.getValue(it))
|
it.importedBy.addAll(importedBy.getValue(it))
|
||||||
it.imports.addAll(modulesImporting.getValue(it))
|
it.imports.addAll(imports.getValue(it))
|
||||||
|
|
||||||
forAllSubroutines(it) { sub ->
|
|
||||||
sub.calledBy.clear()
|
|
||||||
sub.calls.clear()
|
|
||||||
|
|
||||||
sub.calledBy.addAll(subroutinesCalledBy.getValue(sub))
|
|
||||||
sub.calls.addAll(subroutinesCalling.getValue(sub))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val rootmodule = program.modules.first()
|
val rootmodule = program.modules.first()
|
||||||
@ -85,8 +76,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
val thisModule = directive.definingModule()
|
val thisModule = directive.definingModule()
|
||||||
if (directive.directive == "%import") {
|
if (directive.directive == "%import") {
|
||||||
val importedModule: Module = program.modules.single { it.name == directive.args[0].name }
|
val importedModule: Module = program.modules.single { it.name == directive.args[0].name }
|
||||||
modulesImporting[thisModule] = modulesImporting.getValue(thisModule).plus(importedModule)
|
imports[thisModule] = imports.getValue(thisModule).plus(importedModule)
|
||||||
modulesImportedBy[importedModule] = modulesImportedBy.getValue(importedModule).plus(thisModule)
|
importedBy[importedModule] = importedBy.getValue(importedModule).plus(thisModule)
|
||||||
} else if (directive.directive == "%asminclude") {
|
} else if (directive.directive == "%asminclude") {
|
||||||
val asm = loadAsmIncludeFile(directive.args[0].str!!, thisModule.source)
|
val asm = loadAsmIncludeFile(directive.args[0].str!!, thisModule.source)
|
||||||
val scope = directive.definingScope()
|
val scope = directive.definingScope()
|
||||||
@ -141,8 +132,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
val otherSub = functionCall.target.targetSubroutine(program.namespace)
|
val otherSub = functionCall.target.targetSubroutine(program.namespace)
|
||||||
if (otherSub != null) {
|
if (otherSub != null) {
|
||||||
functionCall.definingSubroutine()?.let { thisSub ->
|
functionCall.definingSubroutine()?.let { thisSub ->
|
||||||
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
|
calls[thisSub] = calls.getValue(thisSub).plus(otherSub)
|
||||||
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(functionCall)
|
calledBy[otherSub] = calledBy.getValue(otherSub).plus(functionCall)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.visit(functionCall)
|
super.visit(functionCall)
|
||||||
@ -152,8 +143,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
val otherSub = functionCallStatement.target.targetSubroutine(program.namespace)
|
val otherSub = functionCallStatement.target.targetSubroutine(program.namespace)
|
||||||
if (otherSub != null) {
|
if (otherSub != null) {
|
||||||
functionCallStatement.definingSubroutine()?.let { thisSub ->
|
functionCallStatement.definingSubroutine()?.let { thisSub ->
|
||||||
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
|
calls[thisSub] = calls.getValue(thisSub).plus(otherSub)
|
||||||
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(functionCallStatement)
|
calledBy[otherSub] = calledBy.getValue(otherSub).plus(functionCallStatement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.visit(functionCallStatement)
|
super.visit(functionCallStatement)
|
||||||
@ -163,8 +154,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
val otherSub = jump.identifier?.targetSubroutine(program.namespace)
|
val otherSub = jump.identifier?.targetSubroutine(program.namespace)
|
||||||
if (otherSub != null) {
|
if (otherSub != null) {
|
||||||
jump.definingSubroutine()?.let { thisSub ->
|
jump.definingSubroutine()?.let { thisSub ->
|
||||||
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
|
calls[thisSub] = calls.getValue(thisSub).plus(otherSub)
|
||||||
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(jump)
|
calledBy[otherSub] = calledBy.getValue(otherSub).plus(jump)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.visit(jump)
|
super.visit(jump)
|
||||||
@ -190,14 +181,14 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
if (jumptarget != null && (jumptarget[0].isLetter() || jumptarget[0] == '_')) {
|
if (jumptarget != null && (jumptarget[0].isLetter() || jumptarget[0] == '_')) {
|
||||||
val node = program.namespace.lookup(jumptarget.split('.'), context)
|
val node = program.namespace.lookup(jumptarget.split('.'), context)
|
||||||
if (node is Subroutine) {
|
if (node is Subroutine) {
|
||||||
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node)
|
calls[scope] = calls.getValue(scope).plus(node)
|
||||||
subroutinesCalledBy[node] = subroutinesCalledBy.getValue(node).plus(context)
|
calledBy[node] = calledBy.getValue(node).plus(context)
|
||||||
} else if (jumptarget.contains('.')) {
|
} else if (jumptarget.contains('.')) {
|
||||||
// maybe only the first part already refers to a subroutine
|
// maybe only the first part already refers to a subroutine
|
||||||
val node2 = program.namespace.lookup(listOf(jumptarget.substringBefore('.')), context)
|
val node2 = program.namespace.lookup(listOf(jumptarget.substringBefore('.')), context)
|
||||||
if (node2 is Subroutine) {
|
if (node2 is Subroutine) {
|
||||||
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node2)
|
calls[scope] = calls.getValue(scope).plus(node2)
|
||||||
subroutinesCalledBy[node2] = subroutinesCalledBy.getValue(node2).plus(context)
|
calledBy[node2] = calledBy.getValue(node2).plus(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,8 +200,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
if (target.contains('.')) {
|
if (target.contains('.')) {
|
||||||
val node = program.namespace.lookup(listOf(target.substringBefore('.')), context)
|
val node = program.namespace.lookup(listOf(target.substringBefore('.')), context)
|
||||||
if (node is Subroutine) {
|
if (node is Subroutine) {
|
||||||
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node)
|
calls[scope] = calls.getValue(scope).plus(node)
|
||||||
subroutinesCalledBy[node] = subroutinesCalledBy.getValue(node).plus(context)
|
calledBy[node] = calledBy.getValue(node).plus(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,46 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.AstWalker
|
||||||
|
import prog8.ast.processing.IAstModification
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.functions.BuiltinFunctions
|
|
||||||
|
|
||||||
|
|
||||||
// TODO implement using AstWalker instead of IAstModifyingVisitor
|
// First thing to do is replace all constant identifiers with their actual value,
|
||||||
internal class ConstantFoldingOptimizer(private val program: Program, private val errors: ErrorReporter) : IAstModifyingVisitor {
|
// and the array var initializer values and sizes.
|
||||||
var optimizationsDone: Int = 0
|
// This is needed because further constant optimizations depend on those.
|
||||||
|
internal class ConstantIdentifierReplacer(private val program: Program, private val errors: ErrorReporter) : AstWalker() {
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
override fun visit(decl: VarDecl): Statement {
|
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
||||||
|
// replace identifiers that refer to const value, with the value itself
|
||||||
|
// if it's a simple type and if it's not a left hand side variable
|
||||||
|
if(identifier.parent is AssignTarget)
|
||||||
|
return noModifications
|
||||||
|
var forloop = identifier.parent as? ForLoop
|
||||||
|
if(forloop==null)
|
||||||
|
forloop = identifier.parent.parent as? ForLoop
|
||||||
|
if(forloop!=null && identifier===forloop.loopVar)
|
||||||
|
return noModifications
|
||||||
|
|
||||||
|
val cval = identifier.constValue(program) ?: return noModifications
|
||||||
|
return when (cval.type) {
|
||||||
|
in NumericDatatypes -> listOf(IAstModification.ReplaceNode(identifier, NumericLiteralValue(cval.type, cval.number, identifier.position), identifier.parent))
|
||||||
|
in PassByReferenceDatatypes -> throw FatalAstException("pass-by-reference type should not be considered a constant")
|
||||||
|
else -> noModifications
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
// the initializer value can't refer to the variable itself (recursive definition)
|
// the initializer value can't refer to the variable itself (recursive definition)
|
||||||
// TODO: use call graph for this?
|
// TODO: use call graph for this?
|
||||||
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) {
|
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) {
|
||||||
errors.err("recursive var declaration", decl.position)
|
errors.err("recursive var declaration", decl.position)
|
||||||
return decl
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
if(decl.type==VarDeclType.CONST || decl.type==VarDeclType.VAR) {
|
if(decl.type==VarDeclType.CONST || decl.type==VarDeclType.VAR) {
|
||||||
@ -28,15 +49,20 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
// for arrays that have no size specifier (or a non-constant one) attempt to deduce the size
|
// for arrays that have no size specifier (or a non-constant one) attempt to deduce the size
|
||||||
val arrayval = decl.value as? ArrayLiteralValue
|
val arrayval = decl.value as? ArrayLiteralValue
|
||||||
if(arrayval!=null) {
|
if(arrayval!=null) {
|
||||||
decl.arraysize = ArrayIndex(NumericLiteralValue.optimalInteger(arrayval.value.size, decl.position), decl.position)
|
return listOf(IAstModification.SetExpression(
|
||||||
optimizationsDone++
|
{ decl.arraysize = ArrayIndex(it, decl.position) },
|
||||||
|
NumericLiteralValue.optimalInteger(arrayval.value.size, decl.position),
|
||||||
|
decl
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(decl.arraysize?.size()==null) {
|
else if(decl.arraysize?.size()==null) {
|
||||||
val size = decl.arraysize!!.index.accept(this)
|
val size = decl.arraysize!!.index.constValue(program)
|
||||||
if(size is NumericLiteralValue) {
|
if(size!=null) {
|
||||||
decl.arraysize = ArrayIndex(size, decl.position)
|
return listOf(IAstModification.SetExpression(
|
||||||
optimizationsDone++
|
{ decl.arraysize = ArrayIndex(it, decl.position) },
|
||||||
|
size, decl
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,9 +73,7 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
val litval = decl.value as? NumericLiteralValue
|
val litval = decl.value as? NumericLiteralValue
|
||||||
if (litval!=null && litval.type in IntegerDatatypes) {
|
if (litval!=null && litval.type in IntegerDatatypes) {
|
||||||
val newValue = NumericLiteralValue(DataType.FLOAT, litval.number.toDouble(), litval.position)
|
val newValue = NumericLiteralValue(DataType.FLOAT, litval.number.toDouble(), litval.position)
|
||||||
decl.value = newValue
|
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||||
optimizationsDone++
|
|
||||||
return super.visit(decl)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
@ -63,23 +87,21 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
val constRange = rangeExpr.toConstantIntegerRange()
|
val constRange = rangeExpr.toConstantIntegerRange()
|
||||||
if(constRange!=null) {
|
if(constRange!=null) {
|
||||||
val eltType = rangeExpr.inferType(program).typeOrElse(DataType.UBYTE)
|
val eltType = rangeExpr.inferType(program).typeOrElse(DataType.UBYTE)
|
||||||
if(eltType in ByteDatatypes) {
|
val newValue = if(eltType in ByteDatatypes) {
|
||||||
decl.value = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
||||||
constRange.map { NumericLiteralValue(eltType, it.toShort(), decl.value!!.position) }.toTypedArray(),
|
constRange.map { NumericLiteralValue(eltType, it.toShort(), decl.value!!.position) }.toTypedArray(),
|
||||||
position = decl.value!!.position)
|
position = decl.value!!.position)
|
||||||
} else {
|
} else {
|
||||||
decl.value = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
||||||
constRange.map { NumericLiteralValue(eltType, it, decl.value!!.position) }.toTypedArray(),
|
constRange.map { NumericLiteralValue(eltType, it, decl.value!!.position) }.toTypedArray(),
|
||||||
position = decl.value!!.position)
|
position = decl.value!!.position)
|
||||||
}
|
}
|
||||||
decl.value!!.linkParents(decl)
|
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||||
optimizationsDone++
|
|
||||||
return super.visit(decl)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(numericLv!=null && numericLv.type== DataType.FLOAT)
|
if(numericLv!=null && numericLv.type==DataType.FLOAT)
|
||||||
errors.err("arraysize requires only integers here", numericLv.position)
|
errors.err("arraysize requires only integers here", numericLv.position)
|
||||||
val size = decl.arraysize?.size() ?: return decl
|
val size = decl.arraysize?.size() ?: return noModifications
|
||||||
if (rangeExpr==null && numericLv!=null) {
|
if (rangeExpr==null && numericLv!=null) {
|
||||||
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
||||||
val fillvalue = numericLv.number.toInt()
|
val fillvalue = numericLv.number.toInt()
|
||||||
@ -105,19 +127,27 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
// create the array itself, filled with the fillvalue.
|
// create the array itself, filled with the fillvalue.
|
||||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(ArrayElementTypes.getValue(decl.datatype), it, numericLv.position) as Expression}.toTypedArray()
|
val array = Array(size) {fillvalue}.map { NumericLiteralValue(ArrayElementTypes.getValue(decl.datatype), it, numericLv.position) as Expression}.toTypedArray()
|
||||||
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
||||||
decl.value = refValue
|
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||||
refValue.parent=decl
|
|
||||||
optimizationsDone++
|
|
||||||
return super.visit(decl)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
val size = decl.arraysize?.size() ?: return decl
|
val size = decl.arraysize?.size() ?: return noModifications
|
||||||
val litval = decl.value as? NumericLiteralValue
|
val litval = decl.value as? NumericLiteralValue
|
||||||
if(litval==null) {
|
val rangeExpr = decl.value as? RangeExpr
|
||||||
// there's no initialization value, but the size is known, so we're ok.
|
if(rangeExpr!=null) {
|
||||||
return super.visit(decl)
|
// convert the initializer range expression to an actual array of floats
|
||||||
} else {
|
val declArraySize = decl.arraysize?.size()
|
||||||
|
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||||
|
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
||||||
|
val constRange = rangeExpr.toConstantIntegerRange()
|
||||||
|
if(constRange!=null) {
|
||||||
|
val newValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F),
|
||||||
|
constRange.map { NumericLiteralValue(DataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||||
|
position = decl.value!!.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(rangeExpr==null && litval!=null) {
|
||||||
// arraysize initializer is a single int, and we know the size.
|
// arraysize initializer is a single int, and we know the size.
|
||||||
val fillvalue = litval.number.toDouble()
|
val fillvalue = litval.number.toDouble()
|
||||||
if (fillvalue < CompilationTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > CompilationTarget.machine.FLOAT_MAX_POSITIVE)
|
if (fillvalue < CompilationTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > CompilationTarget.machine.FLOAT_MAX_POSITIVE)
|
||||||
@ -126,10 +156,7 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
// create the array itself, filled with the fillvalue.
|
// create the array itself, filled with the fillvalue.
|
||||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) as Expression}.toTypedArray()
|
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) as Expression}.toTypedArray()
|
||||||
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = litval.position)
|
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = litval.position)
|
||||||
decl.value = refValue
|
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||||
refValue.parent=decl
|
|
||||||
optimizationsDone++
|
|
||||||
return super.visit(decl)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,135 +171,69 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
if(declValue!=null && decl.type==VarDeclType.VAR
|
if(declValue!=null && decl.type==VarDeclType.VAR
|
||||||
&& declValue is NumericLiteralValue && !declValue.inferType(program).istype(decl.datatype)) {
|
&& declValue is NumericLiteralValue && !declValue.inferType(program).istype(decl.datatype)) {
|
||||||
// cast the numeric literal to the appropriate datatype of the variable
|
// cast the numeric literal to the appropriate datatype of the variable
|
||||||
decl.value = declValue.cast(decl.datatype)
|
return listOf(IAstModification.ReplaceNode(decl.value!!, declValue.cast(decl.datatype), decl))
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.visit(decl)
|
return noModifications
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* replace identifiers that refer to const value, with the value itself (if it's a simple type)
|
|
||||||
*/
|
|
||||||
override fun visit(identifier: IdentifierReference): Expression {
|
|
||||||
// don't replace when it's an assignment target or loop variable
|
|
||||||
if(identifier.parent is AssignTarget)
|
|
||||||
return identifier
|
|
||||||
var forloop = identifier.parent as? ForLoop
|
|
||||||
if(forloop==null)
|
|
||||||
forloop = identifier.parent.parent as? ForLoop
|
|
||||||
if(forloop!=null && identifier===forloop.loopVar)
|
|
||||||
return identifier
|
|
||||||
|
|
||||||
val cval = identifier.constValue(program) ?: return identifier
|
internal class ConstantFoldingOptimizer(private val program: Program, private val errors: ErrorReporter) : AstWalker() {
|
||||||
return when (cval.type) {
|
private val noModifications = emptyList<IAstModification>()
|
||||||
in NumericDatatypes -> {
|
|
||||||
val copy = NumericLiteralValue(cval.type, cval.number, identifier.position)
|
|
||||||
copy.parent = identifier.parent
|
|
||||||
copy
|
|
||||||
}
|
|
||||||
in PassByReferenceDatatypes -> throw FatalAstException("pass-by-reference type should not be considered a constant")
|
|
||||||
else -> identifier
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(functionCall: FunctionCall): Expression {
|
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||||
super.visit(functionCall)
|
|
||||||
typeCastConstArguments(functionCall)
|
|
||||||
return functionCall.constValue(program) ?: functionCall
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
|
||||||
super.visit(functionCallStatement)
|
|
||||||
typeCastConstArguments(functionCallStatement)
|
|
||||||
return functionCallStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun typeCastConstArguments(functionCall: IFunctionCall) {
|
|
||||||
if(functionCall.target.nameInSource.size==1) {
|
|
||||||
val builtinFunction = BuiltinFunctions[functionCall.target.nameInSource.single()]
|
|
||||||
if(builtinFunction!=null) {
|
|
||||||
// match the arguments of a builtin function signature.
|
|
||||||
for(arg in functionCall.args.withIndex().zip(builtinFunction.parameters)) {
|
|
||||||
val possibleDts = arg.second.possibleDatatypes
|
|
||||||
val argConst = arg.first.value.constValue(program)
|
|
||||||
if(argConst!=null && argConst.type !in possibleDts) {
|
|
||||||
val convertedValue = argConst.cast(possibleDts.first())
|
|
||||||
functionCall.args[arg.first.index] = convertedValue
|
|
||||||
optimizationsDone++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// match the arguments of a subroutine.
|
|
||||||
val subroutine = functionCall.target.targetSubroutine(program.namespace)
|
|
||||||
if(subroutine!=null) {
|
|
||||||
// if types differ, try to typecast constant arguments to the function call to the desired data type of the parameter
|
|
||||||
for(arg in functionCall.args.withIndex().zip(subroutine.parameters)) {
|
|
||||||
val expectedDt = arg.second.type
|
|
||||||
val argConst = arg.first.value.constValue(program)
|
|
||||||
if(argConst!=null && argConst.type!=expectedDt) {
|
|
||||||
val convertedValue = argConst.cast(expectedDt)
|
|
||||||
functionCall.args[arg.first.index] = convertedValue
|
|
||||||
optimizationsDone++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(memread: DirectMemoryRead): Expression {
|
|
||||||
// @( &thing ) --> thing
|
// @( &thing ) --> thing
|
||||||
val addrOf = memread.addressExpression as? AddressOf
|
val addrOf = memread.addressExpression as? AddressOf
|
||||||
if(addrOf!=null)
|
return if(addrOf!=null)
|
||||||
return super.visit(addrOf.identifier)
|
listOf(IAstModification.ReplaceNode(memread, addrOf.identifier, parent))
|
||||||
return super.visit(memread)
|
else
|
||||||
|
noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
||||||
* Try to accept a unary prefix expression.
|
// 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.
|
||||||
* For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
|
// For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
|
||||||
*/
|
val subexpr = expr.expression
|
||||||
override fun visit(expr: PrefixExpression): Expression {
|
|
||||||
val prefixExpr=super.visit(expr)
|
|
||||||
if(prefixExpr !is PrefixExpression)
|
|
||||||
return prefixExpr
|
|
||||||
|
|
||||||
val subexpr = prefixExpr.expression
|
|
||||||
if (subexpr is NumericLiteralValue) {
|
if (subexpr is NumericLiteralValue) {
|
||||||
// accept prefixed literal values (such as -3, not true)
|
// accept prefixed literal values (such as -3, not true)
|
||||||
return when (prefixExpr.operator) {
|
return when (expr.operator) {
|
||||||
"+" -> subexpr
|
"+" -> listOf(IAstModification.ReplaceNode(expr, subexpr, parent))
|
||||||
"-" -> when (subexpr.type) {
|
"-" -> when (subexpr.type) {
|
||||||
in IntegerDatatypes -> {
|
in IntegerDatatypes -> {
|
||||||
optimizationsDone++
|
listOf(IAstModification.ReplaceNode(expr,
|
||||||
NumericLiteralValue.optimalNumeric(-subexpr.number.toInt(), subexpr.position)
|
NumericLiteralValue.optimalNumeric(-subexpr.number.toInt(), subexpr.position),
|
||||||
|
parent))
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
optimizationsDone++
|
listOf(IAstModification.ReplaceNode(expr,
|
||||||
NumericLiteralValue(DataType.FLOAT, -subexpr.number.toDouble(), subexpr.position)
|
NumericLiteralValue(DataType.FLOAT, -subexpr.number.toDouble(), subexpr.position),
|
||||||
|
parent))
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
|
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
|
||||||
}
|
}
|
||||||
"~" -> when (subexpr.type) {
|
"~" -> when (subexpr.type) {
|
||||||
in IntegerDatatypes -> {
|
in IntegerDatatypes -> {
|
||||||
optimizationsDone++
|
listOf(IAstModification.ReplaceNode(expr,
|
||||||
NumericLiteralValue.optimalNumeric(subexpr.number.toInt().inv(), subexpr.position)
|
NumericLiteralValue.optimalNumeric(subexpr.number.toInt().inv(), subexpr.position),
|
||||||
|
parent))
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
|
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
|
||||||
}
|
}
|
||||||
"not" -> {
|
"not" -> {
|
||||||
optimizationsDone++
|
listOf(IAstModification.ReplaceNode(expr,
|
||||||
NumericLiteralValue.fromBoolean(subexpr.number.toDouble() == 0.0, subexpr.position)
|
NumericLiteralValue.fromBoolean(subexpr.number.toDouble() == 0.0, subexpr.position),
|
||||||
|
parent))
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError(prefixExpr.operator, subexpr.position)
|
else -> throw ExpressionError(expr.operator, subexpr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return prefixExpr
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to accept a binary expression.
|
* Try to constfold a binary expression.
|
||||||
* Compile-time constant sub expressions will be evaluated on the spot.
|
* Compile-time constant sub expressions will be evaluated on the spot.
|
||||||
* For instance, "9 * (4 + 2)" will be optimized into the integer literal 54.
|
* For instance, "9 * (4 + 2)" will be optimized into the integer literal 54.
|
||||||
*
|
*
|
||||||
@ -288,13 +249,7 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
* (X / c1) * c2 -> X / (c2/c1)
|
* (X / c1) * c2 -> X / (c2/c1)
|
||||||
* (X + c1) - c2 -> X + (c1-c2)
|
* (X + c1) - c2 -> X + (c1-c2)
|
||||||
*/
|
*/
|
||||||
override fun visit(expr: BinaryExpression): Expression {
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
super.visit(expr)
|
|
||||||
|
|
||||||
if(expr.left is StringLiteralValue || expr.left is ArrayLiteralValue
|
|
||||||
|| expr.right is StringLiteralValue || expr.right is ArrayLiteralValue)
|
|
||||||
throw FatalAstException("binexpr with reference litval instead of numeric")
|
|
||||||
|
|
||||||
val leftconst = expr.left.constValue(program)
|
val leftconst = expr.left.constValue(program)
|
||||||
val rightconst = expr.right.constValue(program)
|
val rightconst = expr.right.constValue(program)
|
||||||
|
|
||||||
@ -308,218 +263,62 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
val subrightconst = subExpr.right.constValue(program)
|
val subrightconst = subExpr.right.constValue(program)
|
||||||
if ((subleftconst != null && subrightconst == null) || (subleftconst==null && subrightconst!=null)) {
|
if ((subleftconst != null && subrightconst == null) || (subleftconst==null && subrightconst!=null)) {
|
||||||
// try reordering.
|
// try reordering.
|
||||||
return groupTwoConstsTogether(expr, subExpr,
|
val change = groupTwoConstsTogether(expr, subExpr,
|
||||||
leftconst != null, rightconst != null,
|
leftconst != null, rightconst != null,
|
||||||
subleftconst != null, subrightconst != null)
|
subleftconst != null, subrightconst != null)
|
||||||
|
return change?.let { listOf(it) } ?: noModifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// const fold when both operands are a const
|
// const fold when both operands are a const
|
||||||
return when {
|
if(leftconst != null && rightconst != null) {
|
||||||
leftconst != null && rightconst != null -> {
|
|
||||||
optimizationsDone++
|
|
||||||
val evaluator = ConstExprEvaluator()
|
val evaluator = ConstExprEvaluator()
|
||||||
evaluator.evaluate(leftconst, expr.operator, rightconst)
|
return listOf(IAstModification.ReplaceNode(
|
||||||
|
expr,
|
||||||
|
evaluator.evaluate(leftconst, expr.operator, rightconst),
|
||||||
|
parent
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> expr
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {
|
||||||
|
// because constant folding can result in arrays that are now suddenly capable
|
||||||
|
// of telling the type of all their elements (for instance, when they contained -2 which
|
||||||
|
// was a prefix expression earlier), we recalculate the array's datatype.
|
||||||
|
if(array.type.isKnown)
|
||||||
|
return noModifications
|
||||||
|
|
||||||
|
// if the array literalvalue is inside an array vardecl, take the type from that
|
||||||
|
// otherwise infer it from the elements of the array
|
||||||
|
val vardeclType = (array.parent as? VarDecl)?.datatype
|
||||||
|
if(vardeclType!=null) {
|
||||||
|
val newArray = array.cast(vardeclType)
|
||||||
|
if (newArray != null && newArray != array)
|
||||||
|
return listOf(IAstModification.ReplaceNode(array, newArray, parent))
|
||||||
|
} else {
|
||||||
|
val arrayDt = array.guessDatatype(program)
|
||||||
|
if (arrayDt.isKnown) {
|
||||||
|
val newArray = array.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
||||||
|
if (newArray != null && newArray != array)
|
||||||
|
return listOf(IAstModification.ReplaceNode(array, newArray, parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun groupTwoConstsTogether(expr: BinaryExpression,
|
return noModifications
|
||||||
subExpr: BinaryExpression,
|
}
|
||||||
leftIsConst: Boolean,
|
|
||||||
rightIsConst: Boolean,
|
override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||||
subleftIsConst: Boolean,
|
// the args of a fuction are constfolded via recursion already.
|
||||||
subrightIsConst: Boolean): Expression
|
val constvalue = functionCall.constValue(program)
|
||||||
{
|
return if(constvalue!=null)
|
||||||
// todo: this implements only a small set of possible reorderings at this time
|
listOf(IAstModification.ReplaceNode(functionCall, constvalue, parent))
|
||||||
if(expr.operator==subExpr.operator) {
|
|
||||||
// both operators are the isSameAs.
|
|
||||||
// If + or *, we can simply swap the const of expr and Var in subexpr.
|
|
||||||
if(expr.operator=="+" || expr.operator=="*") {
|
|
||||||
if(leftIsConst) {
|
|
||||||
if(subleftIsConst)
|
|
||||||
expr.left = subExpr.right.also { subExpr.right = expr.left }
|
|
||||||
else
|
else
|
||||||
expr.left = subExpr.left.also { subExpr.left = expr.left }
|
noModifications
|
||||||
} else {
|
|
||||||
if(subleftIsConst)
|
|
||||||
expr.right = subExpr.right.also {subExpr.right = expr.right }
|
|
||||||
else
|
|
||||||
expr.right = subExpr.left.also { subExpr.left = expr.right }
|
|
||||||
}
|
|
||||||
optimizationsDone++
|
|
||||||
return expr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If - or /, we simetimes must reorder more, and flip operators (- -> +, / -> *)
|
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
||||||
if(expr.operator=="-" || expr.operator=="/") {
|
|
||||||
optimizationsDone++
|
|
||||||
if(leftIsConst) {
|
|
||||||
return if(subleftIsConst) {
|
|
||||||
val tmp = subExpr.right
|
|
||||||
subExpr.right = subExpr.left
|
|
||||||
subExpr.left = expr.left
|
|
||||||
expr.left = tmp
|
|
||||||
expr.operator = if(expr.operator=="-") "+" else "*"
|
|
||||||
expr
|
|
||||||
} else
|
|
||||||
BinaryExpression(
|
|
||||||
BinaryExpression(expr.left, if (expr.operator == "-") "+" else "*", subExpr.right, subExpr.position),
|
|
||||||
expr.operator, subExpr.left, expr.position)
|
|
||||||
} else {
|
|
||||||
return if(subleftIsConst) {
|
|
||||||
expr.right = subExpr.right.also { subExpr.right = expr.right }
|
|
||||||
expr
|
|
||||||
} else
|
|
||||||
BinaryExpression(
|
|
||||||
subExpr.left, expr.operator,
|
|
||||||
BinaryExpression(expr.right, if (expr.operator == "-") "+" else "*", subExpr.right, subExpr.position),
|
|
||||||
expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return expr
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
if(expr.operator=="/" && subExpr.operator=="*") {
|
|
||||||
optimizationsDone++
|
|
||||||
if(leftIsConst) {
|
|
||||||
return if(subleftIsConst) {
|
|
||||||
// C1/(C2*V) -> (C1/C2)/V
|
|
||||||
BinaryExpression(
|
|
||||||
BinaryExpression(expr.left, "/", subExpr.left, subExpr.position),
|
|
||||||
"/",
|
|
||||||
subExpr.right, expr.position)
|
|
||||||
} else {
|
|
||||||
// C1/(V*C2) -> (C1/C2)/V
|
|
||||||
BinaryExpression(
|
|
||||||
BinaryExpression(expr.left, "/", subExpr.right, subExpr.position),
|
|
||||||
"/",
|
|
||||||
subExpr.left, expr.position)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return if(subleftIsConst) {
|
|
||||||
// (C1*V)/C2 -> (C1/C2)*V
|
|
||||||
BinaryExpression(
|
|
||||||
BinaryExpression(subExpr.left, "/", expr.right, subExpr.position),
|
|
||||||
"*",
|
|
||||||
subExpr.right, expr.position)
|
|
||||||
} else {
|
|
||||||
// (V*C1)/C2 -> (C1/C2)*V
|
|
||||||
BinaryExpression(
|
|
||||||
BinaryExpression(subExpr.right, "/", expr.right, subExpr.position),
|
|
||||||
"*",
|
|
||||||
subExpr.left, expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(expr.operator=="*" && subExpr.operator=="/") {
|
|
||||||
optimizationsDone++
|
|
||||||
if(leftIsConst) {
|
|
||||||
return if(subleftIsConst) {
|
|
||||||
// C1*(C2/V) -> (C1*C2)/V
|
|
||||||
BinaryExpression(
|
|
||||||
BinaryExpression(expr.left, "*", subExpr.left, subExpr.position),
|
|
||||||
"/",
|
|
||||||
subExpr.right, expr.position)
|
|
||||||
} else {
|
|
||||||
// C1*(V/C2) -> (C1/C2)*V
|
|
||||||
BinaryExpression(
|
|
||||||
BinaryExpression(expr.left, "/", subExpr.right, subExpr.position),
|
|
||||||
"*",
|
|
||||||
subExpr.left, expr.position)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return if(subleftIsConst) {
|
|
||||||
// (C1/V)*C2 -> (C1*C2)/V
|
|
||||||
BinaryExpression(
|
|
||||||
BinaryExpression(subExpr.left, "*", expr.right, subExpr.position),
|
|
||||||
"/",
|
|
||||||
subExpr.right, expr.position)
|
|
||||||
} else {
|
|
||||||
// (V/C1)*C2 -> (C1/C2)*V
|
|
||||||
BinaryExpression(
|
|
||||||
BinaryExpression(expr.right, "/", subExpr.right, subExpr.position),
|
|
||||||
"*",
|
|
||||||
subExpr.left, expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(expr.operator=="+" && subExpr.operator=="-") {
|
|
||||||
optimizationsDone++
|
|
||||||
if(leftIsConst){
|
|
||||||
return if(subleftIsConst){
|
|
||||||
// c1+(c2-v) -> (c1+c2)-v
|
|
||||||
BinaryExpression(
|
|
||||||
BinaryExpression(expr.left, "+", subExpr.left, subExpr.position),
|
|
||||||
"-",
|
|
||||||
subExpr.right, expr.position)
|
|
||||||
} else {
|
|
||||||
// c1+(v-c2) -> v+(c1-c2)
|
|
||||||
BinaryExpression(
|
|
||||||
BinaryExpression(expr.left, "-", subExpr.right, subExpr.position),
|
|
||||||
"+",
|
|
||||||
subExpr.left, expr.position)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return if(subleftIsConst) {
|
|
||||||
// (c1-v)+c2 -> (c1+c2)-v
|
|
||||||
BinaryExpression(
|
|
||||||
BinaryExpression(subExpr.left, "+", expr.right, subExpr.position),
|
|
||||||
"-",
|
|
||||||
subExpr.right, expr.position)
|
|
||||||
} else {
|
|
||||||
// (v-c1)+c2 -> v+(c2-c1)
|
|
||||||
BinaryExpression(
|
|
||||||
BinaryExpression(expr.right, "-", subExpr.right, subExpr.position),
|
|
||||||
"+",
|
|
||||||
subExpr.left, expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(expr.operator=="-" && subExpr.operator=="+") {
|
|
||||||
optimizationsDone++
|
|
||||||
if(leftIsConst) {
|
|
||||||
return if(subleftIsConst) {
|
|
||||||
// c1-(c2+v) -> (c1-c2)-v
|
|
||||||
BinaryExpression(
|
|
||||||
BinaryExpression(expr.left, "-", subExpr.left, subExpr.position),
|
|
||||||
"-",
|
|
||||||
subExpr.right, expr.position)
|
|
||||||
} else {
|
|
||||||
// c1-(v+c2) -> (c1-c2)-v
|
|
||||||
BinaryExpression(
|
|
||||||
BinaryExpression(expr.left, "-", subExpr.right, subExpr.position),
|
|
||||||
"-",
|
|
||||||
subExpr.left, expr.position)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return if(subleftIsConst) {
|
|
||||||
// (c1+v)-c2 -> v+(c1-c2)
|
|
||||||
BinaryExpression(
|
|
||||||
BinaryExpression(subExpr.left, "-", expr.right, subExpr.position),
|
|
||||||
"+",
|
|
||||||
subExpr.right, expr.position)
|
|
||||||
} else {
|
|
||||||
// (v+c1)-c2 -> v+(c1-c2)
|
|
||||||
BinaryExpression(
|
|
||||||
BinaryExpression(subExpr.right, "-", expr.right, subExpr.position),
|
|
||||||
"+",
|
|
||||||
subExpr.left, expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(forLoop: ForLoop): Statement {
|
|
||||||
|
|
||||||
fun adjustRangeDt(rangeFrom: NumericLiteralValue, targetDt: DataType, rangeTo: NumericLiteralValue, stepLiteral: NumericLiteralValue?, range: RangeExpr): RangeExpr {
|
fun adjustRangeDt(rangeFrom: NumericLiteralValue, targetDt: DataType, rangeTo: NumericLiteralValue, stepLiteral: NumericLiteralValue?, range: RangeExpr): RangeExpr {
|
||||||
val newFrom: NumericLiteralValue
|
val newFrom: NumericLiteralValue
|
||||||
val newTo: NumericLiteralValue
|
val newTo: NumericLiteralValue
|
||||||
@ -537,87 +336,257 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
return RangeExpr(newFrom, newTo, newStep, range.position)
|
return RangeExpr(newFrom, newTo, newStep, range.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
val forLoop2 = super.visit(forLoop) as ForLoop
|
|
||||||
|
|
||||||
// check if we need to adjust an array literal to the loop variable's datatype
|
|
||||||
val array = forLoop2.iterable as? ArrayLiteralValue
|
|
||||||
if(array!=null) {
|
|
||||||
val loopvarDt: DataType = when {
|
|
||||||
forLoop.loopVar!=null -> forLoop.loopVar!!.inferType(program).typeOrElse(DataType.UBYTE)
|
|
||||||
forLoop.loopRegister!=null -> DataType.UBYTE
|
|
||||||
else -> throw FatalAstException("weird for loop")
|
|
||||||
}
|
|
||||||
|
|
||||||
val arrayType = when(loopvarDt) {
|
|
||||||
DataType.UBYTE -> DataType.ARRAY_UB
|
|
||||||
DataType.BYTE -> DataType.ARRAY_B
|
|
||||||
DataType.UWORD -> DataType.ARRAY_UW
|
|
||||||
DataType.WORD -> DataType.ARRAY_W
|
|
||||||
DataType.FLOAT -> DataType.ARRAY_F
|
|
||||||
else -> throw FatalAstException("invalid array elt type")
|
|
||||||
}
|
|
||||||
val array2 = array.cast(arrayType)
|
|
||||||
if(array2!=null && array2!==array) {
|
|
||||||
forLoop2.iterable = array2
|
|
||||||
array2.linkParents(forLoop2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// adjust the datatype of a range expression in for loops to the loop variable.
|
// adjust the datatype of a range expression in for loops to the loop variable.
|
||||||
val iterableRange = forLoop2.iterable as? RangeExpr ?: return forLoop2
|
val iterableRange = forLoop.iterable as? RangeExpr ?: return noModifications
|
||||||
val rangeFrom = iterableRange.from as? NumericLiteralValue
|
val rangeFrom = iterableRange.from as? NumericLiteralValue
|
||||||
val rangeTo = iterableRange.to as? NumericLiteralValue
|
val rangeTo = iterableRange.to as? NumericLiteralValue
|
||||||
if(rangeFrom==null || rangeTo==null) return forLoop2
|
if(rangeFrom==null || rangeTo==null) return noModifications
|
||||||
|
|
||||||
val loopvar = forLoop2.loopVar?.targetVarDecl(program.namespace)
|
val loopvar = forLoop.loopVar.targetVarDecl(program.namespace)!!
|
||||||
if(loopvar!=null) {
|
|
||||||
val stepLiteral = iterableRange.step as? NumericLiteralValue
|
val stepLiteral = iterableRange.step as? NumericLiteralValue
|
||||||
when(loopvar.datatype) {
|
when(loopvar.datatype) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if(rangeFrom.type!= DataType.UBYTE) {
|
if(rangeFrom.type!= DataType.UBYTE) {
|
||||||
// attempt to translate the iterable into ubyte values
|
// attempt to translate the iterable into ubyte values
|
||||||
forLoop2.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||||
|
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
if(rangeFrom.type!= DataType.BYTE) {
|
if(rangeFrom.type!= DataType.BYTE) {
|
||||||
// attempt to translate the iterable into byte values
|
// attempt to translate the iterable into byte values
|
||||||
forLoop2.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||||
|
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
if(rangeFrom.type!= DataType.UWORD) {
|
if(rangeFrom.type!= DataType.UWORD) {
|
||||||
// attempt to translate the iterable into uword values
|
// attempt to translate the iterable into uword values
|
||||||
forLoop2.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||||
|
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
if(rangeFrom.type!= DataType.WORD) {
|
if(rangeFrom.type!= DataType.WORD) {
|
||||||
// attempt to translate the iterable into word values
|
// attempt to translate the iterable into word values
|
||||||
forLoop2.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||||
|
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw FatalAstException("invalid loopvar datatype $loopvar")
|
else -> throw FatalAstException("invalid loopvar datatype $loopvar")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return forLoop2
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(arrayLiteral: ArrayLiteralValue): Expression {
|
private class ShuffleOperands(val expr: BinaryExpression,
|
||||||
// because constant folding can result in arrays that are now suddenly capable
|
val exprOperator: String?,
|
||||||
// of telling the type of all their elements (for instance, when they contained -2 which
|
val subExpr: BinaryExpression,
|
||||||
// was a prefix expression earlier), we recalculate the array's datatype.
|
val newExprLeft: Expression?,
|
||||||
val array = super.visit(arrayLiteral)
|
val newExprRight: Expression?,
|
||||||
if(array is ArrayLiteralValue) {
|
val newSubexprLeft: Expression?,
|
||||||
if(array.type.isKnown)
|
val newSubexprRight: Expression?
|
||||||
return array
|
): IAstModification {
|
||||||
val arrayDt = array.guessDatatype(program)
|
override fun perform() {
|
||||||
if(arrayDt.isKnown) {
|
if(exprOperator!=null) expr.operator = exprOperator
|
||||||
val newArray = arrayLiteral.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
if(newExprLeft!=null) expr.left = newExprLeft
|
||||||
if(newArray!=null)
|
if(newExprRight!=null) expr.right = newExprRight
|
||||||
return newArray
|
if(newSubexprLeft!=null) subExpr.left = newSubexprLeft
|
||||||
|
if(newSubexprRight!=null) subExpr.right = newSubexprRight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return array
|
|
||||||
|
private fun groupTwoConstsTogether(expr: BinaryExpression,
|
||||||
|
subExpr: BinaryExpression,
|
||||||
|
leftIsConst: Boolean,
|
||||||
|
rightIsConst: Boolean,
|
||||||
|
subleftIsConst: Boolean,
|
||||||
|
subrightIsConst: Boolean): IAstModification?
|
||||||
|
{
|
||||||
|
// todo: this implements only a small set of possible reorderings at this time
|
||||||
|
if(expr.operator==subExpr.operator) {
|
||||||
|
// both operators are the same.
|
||||||
|
// If + or *, we can simply shuffle the const operands around to optimize.
|
||||||
|
if(expr.operator=="+" || expr.operator=="*") {
|
||||||
|
return if(leftIsConst) {
|
||||||
|
if(subleftIsConst)
|
||||||
|
ShuffleOperands(expr, null, subExpr, subExpr.right, null, null, expr.left)
|
||||||
|
else
|
||||||
|
ShuffleOperands(expr, null, subExpr, subExpr.left, null, expr.left, null)
|
||||||
|
} else {
|
||||||
|
if(subleftIsConst)
|
||||||
|
ShuffleOperands(expr, null, subExpr, null, subExpr.right, null, expr.right)
|
||||||
|
else
|
||||||
|
ShuffleOperands(expr, null, subExpr, null, subExpr.left, expr.right, null)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If - or /, we simetimes must reorder more, and flip operators (- -> +, / -> *)
|
||||||
|
if(expr.operator=="-" || expr.operator=="/") {
|
||||||
|
if(leftIsConst) {
|
||||||
|
return if (subleftIsConst) {
|
||||||
|
ShuffleOperands(expr, if (expr.operator == "-") "+" else "*", subExpr, subExpr.right, null, expr.left, subExpr.left)
|
||||||
|
} else {
|
||||||
|
IAstModification.ReplaceNode(expr,
|
||||||
|
BinaryExpression(
|
||||||
|
BinaryExpression(expr.left, if (expr.operator == "-") "+" else "*", subExpr.right, subExpr.position),
|
||||||
|
expr.operator, subExpr.left, expr.position),
|
||||||
|
expr.parent)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return if(subleftIsConst) {
|
||||||
|
return ShuffleOperands(expr, null, subExpr, null, subExpr.right, null, expr.right)
|
||||||
|
} else {
|
||||||
|
IAstModification.ReplaceNode(expr,
|
||||||
|
BinaryExpression(
|
||||||
|
subExpr.left, expr.operator,
|
||||||
|
BinaryExpression(expr.right, if (expr.operator == "-") "+" else "*", subExpr.right, subExpr.position),
|
||||||
|
expr.position),
|
||||||
|
expr.parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
if(expr.operator=="/" && subExpr.operator=="*") {
|
||||||
|
if(leftIsConst) {
|
||||||
|
val change = if(subleftIsConst) {
|
||||||
|
// C1/(C2*V) -> (C1/C2)/V
|
||||||
|
BinaryExpression(
|
||||||
|
BinaryExpression(expr.left, "/", subExpr.left, subExpr.position),
|
||||||
|
"/",
|
||||||
|
subExpr.right, expr.position)
|
||||||
|
} else {
|
||||||
|
// C1/(V*C2) -> (C1/C2)/V
|
||||||
|
BinaryExpression(
|
||||||
|
BinaryExpression(expr.left, "/", subExpr.right, subExpr.position),
|
||||||
|
"/",
|
||||||
|
subExpr.left, expr.position)
|
||||||
|
}
|
||||||
|
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||||
|
} else {
|
||||||
|
val change = if(subleftIsConst) {
|
||||||
|
// (C1*V)/C2 -> (C1/C2)*V
|
||||||
|
BinaryExpression(
|
||||||
|
BinaryExpression(subExpr.left, "/", expr.right, subExpr.position),
|
||||||
|
"*",
|
||||||
|
subExpr.right, expr.position)
|
||||||
|
} else {
|
||||||
|
// (V*C1)/C2 -> (C1/C2)*V
|
||||||
|
BinaryExpression(
|
||||||
|
BinaryExpression(subExpr.right, "/", expr.right, subExpr.position),
|
||||||
|
"*",
|
||||||
|
subExpr.left, expr.position)
|
||||||
|
}
|
||||||
|
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(expr.operator=="*" && subExpr.operator=="/") {
|
||||||
|
if(leftIsConst) {
|
||||||
|
val change = if(subleftIsConst) {
|
||||||
|
// C1*(C2/V) -> (C1*C2)/V
|
||||||
|
BinaryExpression(
|
||||||
|
BinaryExpression(expr.left, "*", subExpr.left, subExpr.position),
|
||||||
|
"/",
|
||||||
|
subExpr.right, expr.position)
|
||||||
|
} else {
|
||||||
|
// C1*(V/C2) -> (C1/C2)*V
|
||||||
|
BinaryExpression(
|
||||||
|
BinaryExpression(expr.left, "/", subExpr.right, subExpr.position),
|
||||||
|
"*",
|
||||||
|
subExpr.left, expr.position)
|
||||||
|
}
|
||||||
|
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||||
|
} else {
|
||||||
|
val change = if(subleftIsConst) {
|
||||||
|
// (C1/V)*C2 -> (C1*C2)/V
|
||||||
|
BinaryExpression(
|
||||||
|
BinaryExpression(subExpr.left, "*", expr.right, subExpr.position),
|
||||||
|
"/",
|
||||||
|
subExpr.right, expr.position)
|
||||||
|
} else {
|
||||||
|
// (V/C1)*C2 -> (C1/C2)*V
|
||||||
|
BinaryExpression(
|
||||||
|
BinaryExpression(expr.right, "/", subExpr.right, subExpr.position),
|
||||||
|
"*",
|
||||||
|
subExpr.left, expr.position)
|
||||||
|
}
|
||||||
|
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(expr.operator=="+" && subExpr.operator=="-") {
|
||||||
|
if(leftIsConst){
|
||||||
|
val change = if(subleftIsConst){
|
||||||
|
// c1+(c2-v) -> (c1+c2)-v
|
||||||
|
BinaryExpression(
|
||||||
|
BinaryExpression(expr.left, "+", subExpr.left, subExpr.position),
|
||||||
|
"-",
|
||||||
|
subExpr.right, expr.position)
|
||||||
|
} else {
|
||||||
|
// c1+(v-c2) -> v+(c1-c2)
|
||||||
|
BinaryExpression(
|
||||||
|
BinaryExpression(expr.left, "-", subExpr.right, subExpr.position),
|
||||||
|
"+",
|
||||||
|
subExpr.left, expr.position)
|
||||||
|
}
|
||||||
|
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||||
|
} else {
|
||||||
|
val change = if(subleftIsConst) {
|
||||||
|
// (c1-v)+c2 -> (c1+c2)-v
|
||||||
|
BinaryExpression(
|
||||||
|
BinaryExpression(subExpr.left, "+", expr.right, subExpr.position),
|
||||||
|
"-",
|
||||||
|
subExpr.right, expr.position)
|
||||||
|
} else {
|
||||||
|
// (v-c1)+c2 -> v+(c2-c1)
|
||||||
|
BinaryExpression(
|
||||||
|
BinaryExpression(expr.right, "-", subExpr.right, subExpr.position),
|
||||||
|
"+",
|
||||||
|
subExpr.left, expr.position)
|
||||||
|
}
|
||||||
|
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(expr.operator=="-" && subExpr.operator=="+") {
|
||||||
|
if(leftIsConst) {
|
||||||
|
val change = if(subleftIsConst) {
|
||||||
|
// c1-(c2+v) -> (c1-c2)-v
|
||||||
|
BinaryExpression(
|
||||||
|
BinaryExpression(expr.left, "-", subExpr.left, subExpr.position),
|
||||||
|
"-",
|
||||||
|
subExpr.right, expr.position)
|
||||||
|
} else {
|
||||||
|
// c1-(v+c2) -> (c1-c2)-v
|
||||||
|
BinaryExpression(
|
||||||
|
BinaryExpression(expr.left, "-", subExpr.right, subExpr.position),
|
||||||
|
"-",
|
||||||
|
subExpr.left, expr.position)
|
||||||
|
}
|
||||||
|
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||||
|
} else {
|
||||||
|
val change = if(subleftIsConst) {
|
||||||
|
// (c1+v)-c2 -> v+(c1-c2)
|
||||||
|
BinaryExpression(
|
||||||
|
BinaryExpression(subExpr.left, "-", expr.right, subExpr.position),
|
||||||
|
"+",
|
||||||
|
subExpr.right, expr.position)
|
||||||
|
} else {
|
||||||
|
// (v+c1)-c2 -> v+(c1-c2)
|
||||||
|
BinaryExpression(
|
||||||
|
BinaryExpression(subExpr.right, "-", expr.right, subExpr.position),
|
||||||
|
"+",
|
||||||
|
subExpr.left, expr.position)
|
||||||
|
}
|
||||||
|
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,12 @@ import kotlin.math.pow
|
|||||||
internal class ExpressionSimplifier(private val program: Program) : AstWalker() {
|
internal class ExpressionSimplifier(private val program: Program) : AstWalker() {
|
||||||
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
|
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
|
||||||
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
if (assignment.aug_op != null)
|
if (assignment.aug_op != null)
|
||||||
throw FatalAstException("augmented assignments should have been converted to normal assignments before this optimizer: $assignment")
|
throw FatalAstException("augmented assignments should have been converted to normal assignments before this optimizer: $assignment")
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||||
@ -82,10 +83,10 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
if (newExpr != null)
|
if (newExpr != null)
|
||||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||||
}
|
}
|
||||||
else -> return emptyList()
|
else -> return noModifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
@ -297,7 +298,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
if(newExpr != null)
|
if(newExpr != null)
|
||||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||||
|
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun determineY(x: Expression, subBinExpr: BinaryExpression): Expression? {
|
private fun determineY(x: Expression, subBinExpr: BinaryExpression): Expression? {
|
||||||
@ -609,8 +610,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
if (amount == 0) {
|
if (amount == 0) {
|
||||||
return expr.left
|
return expr.left
|
||||||
}
|
}
|
||||||
val targetDt = expr.left.inferType(program).typeOrElse(DataType.STRUCT)
|
when (expr.left.inferType(program).typeOrElse(DataType.STRUCT)) {
|
||||||
when (targetDt) {
|
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if (amount >= 8) {
|
if (amount >= 8) {
|
||||||
return NumericLiteralValue.optimalInteger(0, expr.position)
|
return NumericLiteralValue.optimalInteger(0, expr.position)
|
||||||
|
@ -5,14 +5,23 @@ import prog8.ast.base.ErrorReporter
|
|||||||
|
|
||||||
|
|
||||||
internal fun Program.constantFold(errors: ErrorReporter) {
|
internal fun Program.constantFold(errors: ErrorReporter) {
|
||||||
|
val replacer = ConstantIdentifierReplacer(this, errors)
|
||||||
|
replacer.visit(this)
|
||||||
|
if(errors.isEmpty()) {
|
||||||
|
replacer.applyModifications()
|
||||||
|
|
||||||
val optimizer = ConstantFoldingOptimizer(this, errors)
|
val optimizer = ConstantFoldingOptimizer(this, errors)
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
|
while (errors.isEmpty() && optimizer.applyModifications() > 0) {
|
||||||
while(errors.isEmpty() && optimizer.optimizationsDone>0) {
|
|
||||||
optimizer.optimizationsDone = 0
|
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(errors.isEmpty()) {
|
||||||
|
replacer.visit(this)
|
||||||
|
replacer.applyModifications()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(errors.isEmpty())
|
if(errors.isEmpty())
|
||||||
modules.forEach { it.linkParents(namespace) } // re-link in final configuration
|
modules.forEach { it.linkParents(namespace) } // re-link in final configuration
|
||||||
}
|
}
|
||||||
@ -21,9 +30,11 @@ internal fun Program.constantFold(errors: ErrorReporter) {
|
|||||||
internal fun Program.optimizeStatements(errors: ErrorReporter): Int {
|
internal fun Program.optimizeStatements(errors: ErrorReporter): Int {
|
||||||
val optimizer = StatementOptimizer(this, errors)
|
val optimizer = StatementOptimizer(this, errors)
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
|
val optimizationCount = optimizer.applyModifications()
|
||||||
|
|
||||||
modules.forEach { it.linkParents(this.namespace) } // re-link in final configuration
|
modules.forEach { it.linkParents(this.namespace) } // re-link in final configuration
|
||||||
|
|
||||||
return optimizer.optimizationsDone
|
return optimizationCount
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.simplifyExpressions() : Int {
|
internal fun Program.simplifyExpressions() : Int {
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
package prog8.optimizer
|
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.processing.IAstVisitor
|
|
||||||
import prog8.ast.statements.AnonymousScope
|
|
||||||
import prog8.ast.statements.NopStatement
|
|
||||||
import prog8.ast.statements.Statement
|
|
||||||
|
|
||||||
internal class FlattenAnonymousScopesAndNopRemover: IAstVisitor {
|
|
||||||
private var scopesToFlatten = mutableListOf<INameScope>()
|
|
||||||
private val nopStatements = mutableListOf<NopStatement>()
|
|
||||||
|
|
||||||
override fun visit(program: Program) {
|
|
||||||
super.visit(program)
|
|
||||||
for(scope in scopesToFlatten.reversed()) {
|
|
||||||
val namescope = scope.parent as INameScope
|
|
||||||
val idx = namescope.statements.indexOf(scope as Statement)
|
|
||||||
if(idx>=0) {
|
|
||||||
val nop = NopStatement.insteadOf(namescope.statements[idx])
|
|
||||||
nop.parent = namescope as Node
|
|
||||||
namescope.statements[idx] = nop
|
|
||||||
namescope.statements.addAll(idx, scope.statements)
|
|
||||||
scope.statements.forEach { it.parent = namescope }
|
|
||||||
visit(nop)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.nopStatements.forEach {
|
|
||||||
it.definingScope().remove(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(scope: AnonymousScope) {
|
|
||||||
if(scope.parent is INameScope) {
|
|
||||||
scopesToFlatten.add(scope) // get rid of the anonymous scope
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.visit(scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(nopStatement: NopStatement) {
|
|
||||||
nopStatements.add(nopStatement)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +1,12 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.AstWalker
|
||||||
|
import prog8.ast.processing.IAstModification
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
@ -14,54 +16,41 @@ import kotlin.math.floor
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: remove unreachable code after return and exit()
|
TODO: remove unreachable code after return and exit()
|
||||||
TODO: proper inlining of tiny subroutines (at first, restrict to subs without parameters and variables in them, and build it up from there: correctly renaming/relocating all variables in them and refs to those as well)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
// TODO implement using AstWalker instead of IAstModifyingVisitor
|
|
||||||
internal class StatementOptimizer(private val program: Program,
|
internal class StatementOptimizer(private val program: Program,
|
||||||
private val errors: ErrorReporter) : IAstModifyingVisitor {
|
private val errors: ErrorReporter) : AstWalker() {
|
||||||
var optimizationsDone: Int = 0
|
|
||||||
private set
|
|
||||||
|
|
||||||
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
|
private val noModifications = emptyList<IAstModification>()
|
||||||
private val callgraph = CallGraph(program)
|
private val callgraph = CallGraph(program)
|
||||||
private val vardeclsToRemove = mutableListOf<VarDecl>()
|
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
|
||||||
|
|
||||||
override fun visit(program: Program) {
|
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
||||||
super.visit(program)
|
|
||||||
|
|
||||||
for(decl in vardeclsToRemove) {
|
|
||||||
decl.definingScope().remove(decl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(block: Block): Statement {
|
|
||||||
if("force_output" !in block.options()) {
|
if("force_output" !in block.options()) {
|
||||||
if (block.containsNoCodeNorVars()) {
|
if (block.containsNoCodeNorVars()) {
|
||||||
optimizationsDone++
|
|
||||||
errors.warn("removing empty block '${block.name}'", block.position)
|
errors.warn("removing empty block '${block.name}'", block.position)
|
||||||
return NopStatement.insteadOf(block)
|
return listOf(IAstModification.Remove(block, parent))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block !in callgraph.usedSymbols) {
|
if (block !in callgraph.usedSymbols) {
|
||||||
optimizationsDone++
|
|
||||||
errors.warn("removing unused block '${block.name}'", block.position)
|
errors.warn("removing unused block '${block.name}'", block.position)
|
||||||
return NopStatement.insteadOf(block) // remove unused block
|
return listOf(IAstModification.Remove(block, parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
return super.visit(block)
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(subroutine: Subroutine): Statement {
|
|
||||||
super.visit(subroutine)
|
|
||||||
val forceOutput = "force_output" in subroutine.definingBlock().options()
|
val forceOutput = "force_output" in subroutine.definingBlock().options()
|
||||||
if(subroutine.asmAddress==null && !forceOutput) {
|
if(subroutine.asmAddress==null && !forceOutput) {
|
||||||
if(subroutine.containsNoCodeNorVars()) {
|
if(subroutine.containsNoCodeNorVars()) {
|
||||||
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
||||||
optimizationsDone++
|
val removals = callgraph.calledBy.getValue(subroutine).map {
|
||||||
return NopStatement.insteadOf(subroutine)
|
IAstModification.Remove(it, it.parent)
|
||||||
|
}.toMutableList()
|
||||||
|
removals += IAstModification.Remove(subroutine, parent)
|
||||||
|
return removals
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,23 +61,359 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
|
|
||||||
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
||||||
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
||||||
optimizationsDone++
|
return listOf(IAstModification.Remove(subroutine, parent))
|
||||||
return NopStatement.insteadOf(subroutine)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return subroutine
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(decl: VarDecl): Statement {
|
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||||
|
val linesToRemove = deduplicateAssignments(scope.statements)
|
||||||
|
return linesToRemove.reversed().map { IAstModification.Remove(scope.statements[it], scope) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
val forceOutput = "force_output" in decl.definingBlock().options()
|
val forceOutput = "force_output" in decl.definingBlock().options()
|
||||||
if(decl !in callgraph.usedSymbols && !forceOutput) {
|
if(decl !in callgraph.usedSymbols && !forceOutput) {
|
||||||
if(decl.type == VarDeclType.VAR)
|
if(decl.type == VarDeclType.VAR)
|
||||||
errors.warn("removing unused variable ${decl.type} '${decl.name}'", decl.position)
|
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(decl)
|
return listOf(IAstModification.Remove(decl, parent))
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.visit(decl)
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) {
|
||||||
|
val functionName = functionCallStatement.target.nameInSource[0]
|
||||||
|
if (functionName in pureBuiltinFunctions) {
|
||||||
|
errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
||||||
|
return listOf(IAstModification.Remove(functionCallStatement, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
|
||||||
|
// this is a C-64 specific optimization
|
||||||
|
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print")) {
|
||||||
|
val arg = functionCallStatement.args.single()
|
||||||
|
val stringVar: IdentifierReference?
|
||||||
|
stringVar = if(arg is AddressOf) {
|
||||||
|
arg.identifier
|
||||||
|
} else {
|
||||||
|
arg as? IdentifierReference
|
||||||
|
}
|
||||||
|
if(stringVar!=null) {
|
||||||
|
val vardecl = stringVar.targetVarDecl(program.namespace)!!
|
||||||
|
val string = vardecl.value!! as StringLiteralValue
|
||||||
|
val pos = functionCallStatement.position
|
||||||
|
if(string.value.length==1) {
|
||||||
|
val firstCharEncoded = CompilationTarget.encodeString(string.value, string.altEncoding)[0]
|
||||||
|
val chrout = FunctionCallStatement(
|
||||||
|
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
||||||
|
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
|
||||||
|
functionCallStatement.void, pos
|
||||||
|
)
|
||||||
|
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
|
||||||
|
} else if(string.value.length==2) {
|
||||||
|
val firstTwoCharsEncoded = CompilationTarget.encodeString(string.value.take(2), string.altEncoding)
|
||||||
|
val chrout1 = FunctionCallStatement(
|
||||||
|
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
||||||
|
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
|
||||||
|
functionCallStatement.void, pos
|
||||||
|
)
|
||||||
|
val chrout2 = FunctionCallStatement(
|
||||||
|
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
||||||
|
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)),
|
||||||
|
functionCallStatement.void, pos
|
||||||
|
)
|
||||||
|
val anonscope = AnonymousScope(mutableListOf(), pos)
|
||||||
|
anonscope.statements.add(chrout1)
|
||||||
|
anonscope.statements.add(chrout2)
|
||||||
|
return listOf(IAstModification.ReplaceNode(functionCallStatement, anonscope, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the first instruction in the called subroutine is a return statement, remove the jump altogeter
|
||||||
|
val subroutine = functionCallStatement.target.targetSubroutine(program.namespace)
|
||||||
|
if(subroutine!=null) {
|
||||||
|
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||||
|
if(first is ReturnFromIrq || first is Return)
|
||||||
|
return listOf(IAstModification.Remove(functionCallStatement, parent))
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||||
|
// if the first instruction in the called subroutine is a return statement with constant value, replace with the constant value
|
||||||
|
val subroutine = functionCall.target.targetSubroutine(program.namespace)
|
||||||
|
if(subroutine!=null) {
|
||||||
|
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||||
|
if(first is Return && first.value!=null) {
|
||||||
|
val constval = first.value?.constValue(program)
|
||||||
|
if(constval!=null)
|
||||||
|
return listOf(IAstModification.ReplaceNode(functionCall, constval, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> {
|
||||||
|
// remove empty if statements
|
||||||
|
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsNoCodeNorVars())
|
||||||
|
return listOf(IAstModification.Remove(ifStatement, parent))
|
||||||
|
|
||||||
|
// empty true part? switch with the else part
|
||||||
|
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsCodeOrVars()) {
|
||||||
|
val invertedCondition = PrefixExpression("not", ifStatement.condition, ifStatement.condition.position)
|
||||||
|
val emptyscope = AnonymousScope(mutableListOf(), ifStatement.elsepart.position)
|
||||||
|
val truepart = AnonymousScope(ifStatement.elsepart.statements, ifStatement.truepart.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.ReplaceNode(ifStatement.condition, invertedCondition, ifStatement),
|
||||||
|
IAstModification.ReplaceNode(ifStatement.truepart, truepart, ifStatement),
|
||||||
|
IAstModification.ReplaceNode(ifStatement.elsepart, emptyscope, ifStatement)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val constvalue = ifStatement.condition.constValue(program)
|
||||||
|
if(constvalue!=null) {
|
||||||
|
return if(constvalue.asBooleanValue){
|
||||||
|
// always true -> keep only if-part
|
||||||
|
errors.warn("condition is always true", ifStatement.position)
|
||||||
|
listOf(IAstModification.ReplaceNode(ifStatement, ifStatement.truepart, parent))
|
||||||
|
} else {
|
||||||
|
// always false -> keep only else-part
|
||||||
|
errors.warn("condition is always false", ifStatement.position)
|
||||||
|
listOf(IAstModification.ReplaceNode(ifStatement, ifStatement.elsepart, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(forLoop.body.containsNoCodeNorVars()) {
|
||||||
|
errors.warn("removing empty for loop", forLoop.position)
|
||||||
|
return listOf(IAstModification.Remove(forLoop, parent))
|
||||||
|
} else if(forLoop.body.statements.size==1) {
|
||||||
|
val loopvar = forLoop.body.statements[0] as? VarDecl
|
||||||
|
if(loopvar!=null && loopvar.name==forLoop.loopVar.nameInSource.singleOrNull()) {
|
||||||
|
// remove empty for loop (only loopvar decl in it)
|
||||||
|
return listOf(IAstModification.Remove(forLoop, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val range = forLoop.iterable as? RangeExpr
|
||||||
|
if(range!=null) {
|
||||||
|
if(range.size()==1) {
|
||||||
|
// for loop over a (constant) range of just a single value-- optimize the loop away
|
||||||
|
// loopvar/reg = range value , follow by block
|
||||||
|
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||||
|
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), null, range.from, forLoop.position))
|
||||||
|
scope.statements.addAll(forLoop.body.statements)
|
||||||
|
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl(program.namespace)
|
||||||
|
if(iterable!=null) {
|
||||||
|
if(iterable.datatype==DataType.STR) {
|
||||||
|
val sv = iterable.value as StringLiteralValue
|
||||||
|
val size = sv.value.length
|
||||||
|
if(size==1) {
|
||||||
|
// loop over string of length 1 -> just assign the single character
|
||||||
|
val character = CompilationTarget.encodeString(sv.value, sv.altEncoding)[0]
|
||||||
|
val byte = NumericLiteralValue(DataType.UBYTE, character, iterable.position)
|
||||||
|
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||||
|
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), null, byte, forLoop.position))
|
||||||
|
scope.statements.addAll(forLoop.body.statements)
|
||||||
|
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(iterable.datatype in ArrayDatatypes) {
|
||||||
|
val size = iterable.arraysize!!.size()
|
||||||
|
if(size==1) {
|
||||||
|
// loop over array of length 1 -> just assign the single value
|
||||||
|
val av = (iterable.value as ArrayLiteralValue).value[0].constValue(program)?.number
|
||||||
|
if(av!=null) {
|
||||||
|
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||||
|
scope.statements.add(Assignment(
|
||||||
|
AssignTarget(forLoop.loopVar, null, null, forLoop.position), null, NumericLiteralValue.optimalInteger(av.toInt(), iterable.position),
|
||||||
|
forLoop.position))
|
||||||
|
scope.statements.addAll(forLoop.body.statements)
|
||||||
|
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
|
||||||
|
val constvalue = untilLoop.untilCondition.constValue(program)
|
||||||
|
if(constvalue!=null) {
|
||||||
|
if(constvalue.asBooleanValue) {
|
||||||
|
// always true -> keep only the statement block (if there are no continue and break statements)
|
||||||
|
errors.warn("condition is always true", untilLoop.untilCondition.position)
|
||||||
|
if(!hasContinueOrBreak(untilLoop.body))
|
||||||
|
return listOf(IAstModification.ReplaceNode(untilLoop, untilLoop.body, parent))
|
||||||
|
} else {
|
||||||
|
// always false
|
||||||
|
val forever = RepeatLoop(null, untilLoop.body, untilLoop.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(untilLoop, forever, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
|
||||||
|
val constvalue = whileLoop.condition.constValue(program)
|
||||||
|
if(constvalue!=null) {
|
||||||
|
return if(constvalue.asBooleanValue) {
|
||||||
|
// always true
|
||||||
|
val forever = RepeatLoop(null, whileLoop.body, whileLoop.position)
|
||||||
|
listOf(IAstModification.ReplaceNode(whileLoop, forever, parent))
|
||||||
|
} else {
|
||||||
|
// always false -> remove the while statement altogether
|
||||||
|
errors.warn("condition is always false", whileLoop.condition.position)
|
||||||
|
listOf(IAstModification.Remove(whileLoop, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
|
||||||
|
val iter = repeatLoop.iterations
|
||||||
|
if(iter!=null) {
|
||||||
|
if(repeatLoop.body.containsNoCodeNorVars()) {
|
||||||
|
errors.warn("empty loop removed", repeatLoop.position)
|
||||||
|
return listOf(IAstModification.Remove(repeatLoop, parent))
|
||||||
|
}
|
||||||
|
val iterations = iter.constValue(program)?.number?.toInt()
|
||||||
|
if (iterations == 0) {
|
||||||
|
errors.warn("iterations is always 0, removed loop", iter.position)
|
||||||
|
return listOf(IAstModification.Remove(repeatLoop, parent))
|
||||||
|
}
|
||||||
|
if (iterations == 1)
|
||||||
|
errors.warn("iterations is always 1", iter.position)
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
|
||||||
|
// remove empty choices
|
||||||
|
class ChoiceRemover(val choice: WhenChoice) : IAstModification {
|
||||||
|
override fun perform() {
|
||||||
|
whenStatement.choices.remove(choice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return whenStatement.choices
|
||||||
|
.filter { !it.statements.containsCodeOrVars() }
|
||||||
|
.map { ChoiceRemover(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(jump: Jump, parent: Node): Iterable<IAstModification> {
|
||||||
|
// if the jump is to the next statement, remove the jump
|
||||||
|
val scope = jump.definingScope()
|
||||||
|
val label = jump.identifier?.targetStatement(scope)
|
||||||
|
if(label!=null && scope.statements.indexOf(label) == scope.statements.indexOf(jump)+1)
|
||||||
|
return listOf(IAstModification.Remove(jump, parent))
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(assignment.aug_op!=null)
|
||||||
|
throw FatalAstException("augmented assignments should have been converted to normal assignments before this optimizer: $assignment")
|
||||||
|
|
||||||
|
// remove assignments to self
|
||||||
|
if(assignment.target isSameAs assignment.value) {
|
||||||
|
if(assignment.target.isNotMemory(program.namespace))
|
||||||
|
return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
}
|
||||||
|
|
||||||
|
val targetIDt = assignment.target.inferType(program, assignment)
|
||||||
|
if(!targetIDt.isKnown)
|
||||||
|
throw FatalAstException("can't infer type of assignment target")
|
||||||
|
|
||||||
|
|
||||||
|
// optimize binary expressions a bit
|
||||||
|
val targetDt = targetIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
val bexpr=assignment.value as? BinaryExpression
|
||||||
|
if(bexpr!=null) {
|
||||||
|
val cv = bexpr.right.constValue(program)?.number?.toDouble()
|
||||||
|
if (cv != null && assignment.target isSameAs bexpr.left) {
|
||||||
|
// assignments of the form: X = X <operator> <expr>
|
||||||
|
// remove assignments that have no effect (such as X=X+0)
|
||||||
|
// optimize/rewrite some other expressions
|
||||||
|
val vardeclDt = (assignment.target.identifier?.targetVarDecl(program.namespace))?.type
|
||||||
|
when (bexpr.operator) {
|
||||||
|
"+" -> {
|
||||||
|
if (cv == 0.0) {
|
||||||
|
return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
} else if (targetDt in IntegerDatatypes && floor(cv) == cv) {
|
||||||
|
if ((vardeclDt == VarDeclType.MEMORY && cv in 1.0..3.0) || (vardeclDt != VarDeclType.MEMORY && cv in 1.0..8.0)) {
|
||||||
|
// replace by several INCs (a bit less when dealing with memory targets)
|
||||||
|
val incs = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
|
repeat(cv.toInt()) {
|
||||||
|
incs.statements.add(PostIncrDecr(assignment.target, "++", assignment.position))
|
||||||
|
}
|
||||||
|
return listOf(IAstModification.ReplaceNode(assignment, incs, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"-" -> {
|
||||||
|
if (cv == 0.0) {
|
||||||
|
return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
} else if (targetDt in IntegerDatatypes && floor(cv) == cv) {
|
||||||
|
if ((vardeclDt == VarDeclType.MEMORY && cv in 1.0..3.0) || (vardeclDt != VarDeclType.MEMORY && cv in 1.0..8.0)) {
|
||||||
|
// replace by several DECs (a bit less when dealing with memory targets)
|
||||||
|
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
|
repeat(cv.toInt()) {
|
||||||
|
decs.statements.add(PostIncrDecr(assignment.target, "--", assignment.position))
|
||||||
|
}
|
||||||
|
return listOf(IAstModification.ReplaceNode(assignment, decs, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"*" -> if (cv == 1.0) return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
"/" -> if (cv == 1.0) return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
"**" -> if (cv == 1.0) return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
"|" -> if (cv == 0.0) return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
"^" -> if (cv == 0.0) return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
"<<" -> {
|
||||||
|
if (cv == 0.0)
|
||||||
|
return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
// replace by in-place lsl(...) call
|
||||||
|
val scope = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
|
var numshifts = cv.toInt()
|
||||||
|
while (numshifts > 0) {
|
||||||
|
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("lsl"), assignment.position),
|
||||||
|
mutableListOf(bexpr.left), true, assignment.position))
|
||||||
|
numshifts--
|
||||||
|
}
|
||||||
|
return listOf(IAstModification.ReplaceNode(assignment, scope, parent))
|
||||||
|
}
|
||||||
|
">>" -> {
|
||||||
|
if (cv == 0.0)
|
||||||
|
return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
// replace by in-place lsr(...) call
|
||||||
|
val scope = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
|
var numshifts = cv.toInt()
|
||||||
|
while (numshifts > 0) {
|
||||||
|
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("lsr"), assignment.position),
|
||||||
|
mutableListOf(bexpr.left), true, assignment.position))
|
||||||
|
numshifts--
|
||||||
|
}
|
||||||
|
return listOf(IAstModification.ReplaceNode(assignment, scope, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deduplicateAssignments(statements: List<Statement>): MutableList<Int> {
|
private fun deduplicateAssignments(statements: List<Statement>): MutableList<Int> {
|
||||||
@ -116,207 +441,6 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
return linesToRemove
|
return linesToRemove
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
|
||||||
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) {
|
|
||||||
val functionName = functionCallStatement.target.nameInSource[0]
|
|
||||||
if (functionName in pureBuiltinFunctions) {
|
|
||||||
errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(functionCallStatement)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print") ||
|
|
||||||
functionCallStatement.target.nameInSource==listOf("c64scr", "print_p")) {
|
|
||||||
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
|
|
||||||
val arg = functionCallStatement.args.single()
|
|
||||||
val stringVar: IdentifierReference?
|
|
||||||
stringVar = if(arg is AddressOf) {
|
|
||||||
arg.identifier
|
|
||||||
} else {
|
|
||||||
arg as? IdentifierReference
|
|
||||||
}
|
|
||||||
if(stringVar!=null) {
|
|
||||||
val vardecl = stringVar.targetVarDecl(program.namespace)!!
|
|
||||||
val string = vardecl.value!! as StringLiteralValue
|
|
||||||
if(string.value.length==1) {
|
|
||||||
val firstCharEncoded = CompilationTarget.encodeString(string.value, string.altEncoding)[0]
|
|
||||||
functionCallStatement.args.clear()
|
|
||||||
functionCallStatement.args.add(NumericLiteralValue.optimalInteger(firstCharEncoded.toInt(), functionCallStatement.position))
|
|
||||||
functionCallStatement.target = IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position)
|
|
||||||
vardeclsToRemove.add(vardecl)
|
|
||||||
optimizationsDone++
|
|
||||||
return functionCallStatement
|
|
||||||
} else if(string.value.length==2) {
|
|
||||||
val firstTwoCharsEncoded = CompilationTarget.encodeString(string.value.take(2), string.altEncoding)
|
|
||||||
val scope = AnonymousScope(mutableListOf(), functionCallStatement.position)
|
|
||||||
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
|
|
||||||
mutableListOf(NumericLiteralValue.optimalInteger(firstTwoCharsEncoded[0].toInt(), functionCallStatement.position)),
|
|
||||||
functionCallStatement.void, functionCallStatement.position))
|
|
||||||
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
|
|
||||||
mutableListOf(NumericLiteralValue.optimalInteger(firstTwoCharsEncoded[1].toInt(), functionCallStatement.position)),
|
|
||||||
functionCallStatement.void, functionCallStatement.position))
|
|
||||||
vardeclsToRemove.add(vardecl)
|
|
||||||
optimizationsDone++
|
|
||||||
return scope
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if it calls a subroutine,
|
|
||||||
// and the first instruction in the subroutine is a jump, call that jump target instead
|
|
||||||
// if the first instruction in the subroutine is a return statement, replace with a nop instruction
|
|
||||||
val subroutine = functionCallStatement.target.targetSubroutine(program.namespace)
|
|
||||||
if(subroutine!=null) {
|
|
||||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
|
||||||
if(first is Jump && first.identifier!=null) {
|
|
||||||
optimizationsDone++
|
|
||||||
return FunctionCallStatement(first.identifier, functionCallStatement.args, functionCallStatement.void, functionCallStatement.position)
|
|
||||||
}
|
|
||||||
if(first is ReturnFromIrq || first is Return) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(functionCallStatement)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.visit(functionCallStatement)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(functionCall: FunctionCall): Expression {
|
|
||||||
// if it calls a subroutine,
|
|
||||||
// and the first instruction in the subroutine is a jump, call that jump target instead
|
|
||||||
// if the first instruction in the subroutine is a return statement with constant value, replace with the constant value
|
|
||||||
val subroutine = functionCall.target.targetSubroutine(program.namespace)
|
|
||||||
if(subroutine!=null) {
|
|
||||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
|
||||||
if(first is Jump && first.identifier!=null) {
|
|
||||||
optimizationsDone++
|
|
||||||
return FunctionCall(first.identifier, functionCall.args, functionCall.position)
|
|
||||||
}
|
|
||||||
if(first is Return && first.value!=null) {
|
|
||||||
val constval = first.value?.constValue(program)
|
|
||||||
if(constval!=null)
|
|
||||||
return constval
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.visit(functionCall)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(ifStatement: IfStatement): Statement {
|
|
||||||
super.visit(ifStatement)
|
|
||||||
|
|
||||||
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsNoCodeNorVars()) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(ifStatement)
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsCodeOrVars()) {
|
|
||||||
// invert the condition and move else part to true part
|
|
||||||
ifStatement.truepart = ifStatement.elsepart
|
|
||||||
ifStatement.elsepart = AnonymousScope(mutableListOf(), ifStatement.elsepart.position)
|
|
||||||
ifStatement.condition = PrefixExpression("not", ifStatement.condition, ifStatement.condition.position)
|
|
||||||
optimizationsDone++
|
|
||||||
return ifStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
val constvalue = ifStatement.condition.constValue(program)
|
|
||||||
if(constvalue!=null) {
|
|
||||||
return if(constvalue.asBooleanValue){
|
|
||||||
// always true -> keep only if-part
|
|
||||||
errors.warn("condition is always true", ifStatement.position)
|
|
||||||
optimizationsDone++
|
|
||||||
ifStatement.truepart
|
|
||||||
} else {
|
|
||||||
// always false -> keep only else-part
|
|
||||||
errors.warn("condition is always false", ifStatement.position)
|
|
||||||
optimizationsDone++
|
|
||||||
ifStatement.elsepart
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ifStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(forLoop: ForLoop): Statement {
|
|
||||||
super.visit(forLoop)
|
|
||||||
if(forLoop.body.containsNoCodeNorVars()) {
|
|
||||||
// remove empty for loop
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(forLoop)
|
|
||||||
} else if(forLoop.body.statements.size==1) {
|
|
||||||
val loopvar = forLoop.body.statements[0] as? VarDecl
|
|
||||||
if(loopvar!=null && loopvar.name==forLoop.loopVar?.nameInSource?.singleOrNull()) {
|
|
||||||
// remove empty for loop
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(forLoop)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val range = forLoop.iterable as? RangeExpr
|
|
||||||
if(range!=null) {
|
|
||||||
if(range.size()==1) {
|
|
||||||
// for loop over a (constant) range of just a single value-- optimize the loop away
|
|
||||||
// loopvar/reg = range value , follow by block
|
|
||||||
val assignment = Assignment(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, null, forLoop.position), null, range.from, forLoop.position)
|
|
||||||
forLoop.body.statements.add(0, assignment)
|
|
||||||
optimizationsDone++
|
|
||||||
return forLoop.body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return forLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(whileLoop: WhileLoop): Statement {
|
|
||||||
super.visit(whileLoop)
|
|
||||||
val constvalue = whileLoop.condition.constValue(program)
|
|
||||||
if(constvalue!=null) {
|
|
||||||
return if(constvalue.asBooleanValue){
|
|
||||||
// always true -> print a warning, and optimize into a forever-loop
|
|
||||||
errors.warn("condition is always true", whileLoop.condition.position)
|
|
||||||
optimizationsDone++
|
|
||||||
ForeverLoop(whileLoop.body, whileLoop.position)
|
|
||||||
} else {
|
|
||||||
// always false -> remove the while statement altogether
|
|
||||||
errors.warn("condition is always false", whileLoop.condition.position)
|
|
||||||
optimizationsDone++
|
|
||||||
NopStatement.insteadOf(whileLoop)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return whileLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(repeatLoop: RepeatLoop): Statement {
|
|
||||||
super.visit(repeatLoop)
|
|
||||||
val constvalue = repeatLoop.untilCondition.constValue(program)
|
|
||||||
if(constvalue!=null) {
|
|
||||||
return if(constvalue.asBooleanValue){
|
|
||||||
// always true -> keep only the statement block (if there are no continue and break statements)
|
|
||||||
errors.warn("condition is always true", repeatLoop.untilCondition.position)
|
|
||||||
if(hasContinueOrBreak(repeatLoop.body))
|
|
||||||
repeatLoop
|
|
||||||
else {
|
|
||||||
optimizationsDone++
|
|
||||||
repeatLoop.body
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// always false -> print a warning, and optimize into a forever loop
|
|
||||||
errors.warn("condition is always false", repeatLoop.untilCondition.position)
|
|
||||||
optimizationsDone++
|
|
||||||
ForeverLoop(repeatLoop.body, repeatLoop.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return repeatLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(whenStatement: WhenStatement): Statement {
|
|
||||||
val choices = whenStatement.choices.toList()
|
|
||||||
for(choice in choices) {
|
|
||||||
if(choice.statements.containsNoCodeNorVars())
|
|
||||||
whenStatement.choices.remove(choice)
|
|
||||||
}
|
|
||||||
return super.visit(whenStatement)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun hasContinueOrBreak(scope: INameScope): Boolean {
|
private fun hasContinueOrBreak(scope: INameScope): Boolean {
|
||||||
|
|
||||||
class Searcher: IAstVisitor
|
class Searcher: IAstVisitor
|
||||||
@ -341,185 +465,4 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
return s.count > 0
|
return s.count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(jump: Jump): Statement {
|
|
||||||
val subroutine = jump.identifier?.targetSubroutine(program.namespace)
|
|
||||||
if(subroutine!=null) {
|
|
||||||
// if the first instruction in the subroutine is another jump, shortcut this one
|
|
||||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
|
||||||
if(first is Jump) {
|
|
||||||
optimizationsDone++
|
|
||||||
return first
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the jump is to the next statement, remove the jump
|
|
||||||
val scope = jump.definingScope()
|
|
||||||
val label = jump.identifier?.targetStatement(scope)
|
|
||||||
if(label!=null) {
|
|
||||||
if(scope.statements.indexOf(label) == scope.statements.indexOf(jump)+1) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(jump)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return jump
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(assignment: Assignment): Statement {
|
|
||||||
if(assignment.aug_op!=null)
|
|
||||||
throw FatalAstException("augmented assignments should have been converted to normal assignments before this optimizer: $assignment")
|
|
||||||
|
|
||||||
if(assignment.target isSameAs assignment.value) {
|
|
||||||
if(assignment.target.isNotMemory(program.namespace)) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val targetIDt = assignment.target.inferType(program, assignment)
|
|
||||||
if(!targetIDt.isKnown)
|
|
||||||
throw FatalAstException("can't infer type of assignment target")
|
|
||||||
val targetDt = targetIDt.typeOrElse(DataType.STRUCT)
|
|
||||||
val bexpr=assignment.value as? BinaryExpression
|
|
||||||
if(bexpr!=null) {
|
|
||||||
val cv = bexpr.right.constValue(program)?.number?.toDouble()
|
|
||||||
if (cv == null) {
|
|
||||||
if (bexpr.operator == "+" && targetDt != DataType.FLOAT) {
|
|
||||||
if (bexpr.left isSameAs bexpr.right && assignment.target isSameAs bexpr.left) {
|
|
||||||
bexpr.operator = "*"
|
|
||||||
bexpr.right = NumericLiteralValue.optimalInteger(2, assignment.value.position)
|
|
||||||
optimizationsDone++
|
|
||||||
return assignment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (assignment.target isSameAs bexpr.left) {
|
|
||||||
// remove assignments that have no effect X=X , X+=0, X-=0, X*=1, X/=1, X//=1, A |= 0, A ^= 0, A<<=0, etc etc
|
|
||||||
// A = A <operator> B
|
|
||||||
val vardeclDt = (assignment.target.identifier?.targetVarDecl(program.namespace))?.type
|
|
||||||
|
|
||||||
when (bexpr.operator) {
|
|
||||||
"+" -> {
|
|
||||||
if (cv == 0.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
} else if (targetDt in IntegerDatatypes && floor(cv) == cv) {
|
|
||||||
if ((vardeclDt == VarDeclType.MEMORY && cv in 1.0..3.0) || (vardeclDt != VarDeclType.MEMORY && cv in 1.0..8.0)) {
|
|
||||||
// replace by several INCs (a bit less when dealing with memory targets)
|
|
||||||
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
|
||||||
repeat(cv.toInt()) {
|
|
||||||
decs.statements.add(PostIncrDecr(assignment.target, "++", assignment.position))
|
|
||||||
}
|
|
||||||
return decs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"-" -> {
|
|
||||||
if (cv == 0.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
} else if (targetDt in IntegerDatatypes && floor(cv) == cv) {
|
|
||||||
if ((vardeclDt == VarDeclType.MEMORY && cv in 1.0..3.0) || (vardeclDt != VarDeclType.MEMORY && cv in 1.0..8.0)) {
|
|
||||||
// replace by several DECs (a bit less when dealing with memory targets)
|
|
||||||
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
|
||||||
repeat(cv.toInt()) {
|
|
||||||
decs.statements.add(PostIncrDecr(assignment.target, "--", assignment.position))
|
|
||||||
}
|
|
||||||
return decs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"*" -> if (cv == 1.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
}
|
|
||||||
"/" -> if (cv == 1.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
}
|
|
||||||
"**" -> if (cv == 1.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
}
|
|
||||||
"|" -> if (cv == 0.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
}
|
|
||||||
"^" -> if (cv == 0.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
}
|
|
||||||
"<<" -> {
|
|
||||||
if (cv == 0.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
}
|
|
||||||
if (((targetDt == DataType.UWORD || targetDt == DataType.WORD) && cv > 15.0) ||
|
|
||||||
((targetDt == DataType.UBYTE || targetDt == DataType.BYTE) && cv > 7.0)) {
|
|
||||||
assignment.value = NumericLiteralValue.optimalInteger(0, assignment.value.position)
|
|
||||||
assignment.value.linkParents(assignment)
|
|
||||||
optimizationsDone++
|
|
||||||
} else {
|
|
||||||
// replace by in-place lsl(...) call
|
|
||||||
val scope = AnonymousScope(mutableListOf(), assignment.position)
|
|
||||||
var numshifts = cv.toInt()
|
|
||||||
while (numshifts > 0) {
|
|
||||||
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("lsl"), assignment.position),
|
|
||||||
mutableListOf(bexpr.left), true, assignment.position))
|
|
||||||
numshifts--
|
|
||||||
}
|
|
||||||
optimizationsDone++
|
|
||||||
return scope
|
|
||||||
}
|
|
||||||
}
|
|
||||||
">>" -> {
|
|
||||||
if (cv == 0.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
}
|
|
||||||
if ((targetDt == DataType.UWORD && cv > 15.0) || (targetDt == DataType.UBYTE && cv > 7.0)) {
|
|
||||||
assignment.value = NumericLiteralValue.optimalInteger(0, assignment.value.position)
|
|
||||||
assignment.value.linkParents(assignment)
|
|
||||||
optimizationsDone++
|
|
||||||
} else {
|
|
||||||
// replace by in-place lsr(...) call
|
|
||||||
val scope = AnonymousScope(mutableListOf(), assignment.position)
|
|
||||||
var numshifts = cv.toInt()
|
|
||||||
while (numshifts > 0) {
|
|
||||||
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("lsr"), assignment.position),
|
|
||||||
mutableListOf(bexpr.left), true, assignment.position))
|
|
||||||
numshifts--
|
|
||||||
}
|
|
||||||
optimizationsDone++
|
|
||||||
return scope
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return super.visit(assignment)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(scope: AnonymousScope): Statement {
|
|
||||||
val linesToRemove = deduplicateAssignments(scope.statements)
|
|
||||||
if(linesToRemove.isNotEmpty()) {
|
|
||||||
linesToRemove.reversed().forEach{scope.statements.removeAt(it)}
|
|
||||||
}
|
|
||||||
return super.visit(scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(label: Label): Statement {
|
|
||||||
// remove duplicate labels
|
|
||||||
val stmts = label.definingScope().statements
|
|
||||||
val startIdx = stmts.indexOf(label)
|
|
||||||
if(startIdx< stmts.lastIndex && stmts[startIdx+1] == label)
|
|
||||||
return NopStatement.insteadOf(label)
|
|
||||||
|
|
||||||
return super.visit(label)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ internal class UnusedCodeRemover: AstWalker() {
|
|||||||
val entrypoint = program.entrypoint()
|
val entrypoint = program.entrypoint()
|
||||||
program.modules.forEach {
|
program.modules.forEach {
|
||||||
callgraph.forAllSubroutines(it) { sub ->
|
callgraph.forAllSubroutines(it) { sub ->
|
||||||
if (sub !== entrypoint && !sub.keepAlways && (sub.calledBy.isEmpty() || (sub.containsNoCodeNorVars() && !sub.isAsmSubroutine)))
|
if (sub !== entrypoint && !sub.keepAlways && (callgraph.calledBy[sub].isNullOrEmpty() || (sub.containsNoCodeNorVars() && !sub.isAsmSubroutine)))
|
||||||
removals.add(IAstModification.Remove(sub, sub.definingScope() as Node))
|
removals.add(IAstModification.Remove(sub, sub.definingScope() as Node))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -30,7 +30,7 @@ internal class UnusedCodeRemover: AstWalker() {
|
|||||||
// remove modules that are not imported, or are empty (unless it's a library modules)
|
// remove modules that are not imported, or are empty (unless it's a library modules)
|
||||||
program.modules.forEach {
|
program.modules.forEach {
|
||||||
if (!it.isLibraryModule && (it.importedBy.isEmpty() || it.containsNoCodeNorVars()))
|
if (!it.isLibraryModule && (it.importedBy.isEmpty() || it.containsNoCodeNorVars()))
|
||||||
removals.add(IAstModification.Remove(it, it.parent)) // TODO does removing modules work like this?
|
removals.add(IAstModification.Remove(it, it.parent))
|
||||||
}
|
}
|
||||||
|
|
||||||
return removals
|
return removals
|
||||||
|
@ -139,8 +139,8 @@ Design principles and features
|
|||||||
- 'One statement per line' code, resulting in clear readable programs.
|
- 'One statement per line' code, resulting in clear readable programs.
|
||||||
- Modular programming and scoping via modules, code blocks, and subroutines.
|
- Modular programming and scoping via modules, code blocks, and subroutines.
|
||||||
- Provide high level programming constructs but at the same time stay close to the metal;
|
- Provide high level programming constructs but at the same time stay close to the metal;
|
||||||
still able to directly use memory addresses, CPU registers and ROM subroutines,
|
still able to directly use memory addresses and ROM subroutines,
|
||||||
and inline assembly to have full control when every cycle or byte matters
|
and inline assembly to have full control when every register, cycle or byte matters
|
||||||
- Arbitrary number of subroutine parameters
|
- Arbitrary number of subroutine parameters
|
||||||
- Complex nested expressions are possible
|
- Complex nested expressions are possible
|
||||||
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
|
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
|
||||||
@ -156,7 +156,7 @@ Design principles and features
|
|||||||
- The compiler tries to optimize the program and generated code a bit, but hand-tuning of the
|
- The compiler tries to optimize the program and generated code a bit, but hand-tuning of the
|
||||||
performance or space-critical parts will likely still be required. This is supported by
|
performance or space-critical parts will likely still be required. This is supported by
|
||||||
the ability to easily write embedded assembly code directly in the program source code.
|
the ability to easily write embedded assembly code directly in the program source code.
|
||||||
- There are many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse``
|
- There are many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``substr``, ``sort`` and ``reverse`` (and others)
|
||||||
- Assembling the generated code into a program wil be done by an external cross-assembler tool.
|
- Assembling the generated code into a program wil be done by an external cross-assembler tool.
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ Code
|
|||||||
There are different kinds of instructions ('statements' is a better name) such as:
|
There are different kinds of instructions ('statements' is a better name) such as:
|
||||||
|
|
||||||
- value assignment
|
- value assignment
|
||||||
- looping (for, while, repeat, unconditional jumps)
|
- looping (for, while, do-until, repeat, unconditional jumps)
|
||||||
- conditional execution (if - then - else, when, and conditional jumps)
|
- conditional execution (if - then - else, when, and conditional jumps)
|
||||||
- subroutine calls
|
- subroutine calls
|
||||||
- label definition
|
- label definition
|
||||||
@ -137,7 +137,7 @@ Scopes are created using either of these two statements:
|
|||||||
|
|
||||||
.. important::
|
.. important::
|
||||||
Unlike most other programming languages, a new scope is *not* created inside
|
Unlike most other programming languages, a new scope is *not* created inside
|
||||||
for, while and repeat statements, the if statement, and the branching conditionals.
|
for, while, repeat, and do-until statements, the if statement, and the branching conditionals.
|
||||||
These all share the same scope from the subroutine they're defined in.
|
These all share the same scope from the subroutine they're defined in.
|
||||||
You can define variables in these blocks, but these will be treated as if they
|
You can define variables in these blocks, but these will be treated as if they
|
||||||
were defined in the subroutine instead.
|
were defined in the subroutine instead.
|
||||||
@ -204,13 +204,6 @@ Example::
|
|||||||
byte @zp zeropageCounter = 42
|
byte @zp zeropageCounter = 42
|
||||||
|
|
||||||
|
|
||||||
Variables that represent CPU hardware registers
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
The following variables are reserved
|
|
||||||
and map directly (read/write) to a CPU hardware register: ``A``, ``X``, ``Y``.
|
|
||||||
|
|
||||||
|
|
||||||
Integers
|
Integers
|
||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
|
|
||||||
@ -279,16 +272,23 @@ This @-prefix can also be used for character byte values.
|
|||||||
|
|
||||||
|
|
||||||
You can concatenate two string literals using '+' (not very useful though) or repeat
|
You can concatenate two string literals using '+' (not very useful though) or repeat
|
||||||
a string literal a given number of times using '*'::
|
a string literal a given number of times using '*'. You can also assign a new string
|
||||||
|
value to another string. No bounds check is done so be sure the destination string is
|
||||||
|
large enough to contain the new value::
|
||||||
|
|
||||||
str string1 = "first part" + "second part"
|
str string1 = "first part" + "second part"
|
||||||
str string2 = "hello!" * 10
|
str string2 = "hello!" * 10
|
||||||
|
|
||||||
|
string1 = string2
|
||||||
|
string1 = "new value"
|
||||||
|
|
||||||
|
|
||||||
.. caution::
|
.. caution::
|
||||||
Avoid changing strings after they've been created.
|
It's probably best to avoid changing strings after they've been created. This
|
||||||
|
includes changing certain letters by index, or by assigning a new value, or by
|
||||||
|
modifying the string via other means for example ``substr`` function and its cousins.
|
||||||
This is because if your program exits and is restarted (without loading it again),
|
This is because if your program exits and is restarted (without loading it again),
|
||||||
it will then start working with the changed strings instead of the original ones.
|
it will then start working with the changed strings instead of the original ones!
|
||||||
The same is true for arrays.
|
The same is true for arrays.
|
||||||
|
|
||||||
|
|
||||||
@ -386,21 +386,19 @@ expected when the program is restarted.
|
|||||||
Loops
|
Loops
|
||||||
-----
|
-----
|
||||||
|
|
||||||
The *for*-loop is used to let a variable (or register) iterate over a range of values. Iteration is done in steps of 1, but you can change this.
|
The *for*-loop is used to let a variable iterate over a range of values. Iteration is done in steps of 1, but you can change this.
|
||||||
The loop variable must be declared as byte or word earlier so you can reuse it for multiple occasions.
|
The loop variable must be declared as byte or word earlier so you can reuse it for multiple occasions.
|
||||||
Iterating with a floating point variable is not supported. If you want to loop over a floating-point array, use a loop with an integer index variable instead.
|
Iterating with a floating point variable is not supported. If you want to loop over a floating-point array, use a loop with an integer index variable instead.
|
||||||
|
|
||||||
The *while*-loop is used to repeat a piece of code while a certain condition is still true.
|
The *while*-loop is used to repeat a piece of code while a certain condition is still true.
|
||||||
The *repeat--until* loop is used to repeat a piece of code until a certain condition is true.
|
The *do--until* loop is used to repeat a piece of code until a certain condition is true.
|
||||||
|
The *repeat* loop is used as a short notation of a for loop where the loop variable doesn't matter and you're only interested in the number of iterations.
|
||||||
The *forever*-loop is used to simply run a piece of code in a loop, forever. You can still
|
(without iteration count specified it simply loops forever).
|
||||||
break out of this loop if desired. A "while true" or "until false" loop is equivalent to
|
|
||||||
a forever-loop.
|
|
||||||
|
|
||||||
You can also create loops by using the ``goto`` statement, but this should usually be avoided.
|
You can also create loops by using the ``goto`` statement, but this should usually be avoided.
|
||||||
|
|
||||||
.. attention::
|
.. attention::
|
||||||
The value of the loop variable or register after executing the loop *is undefined*. Don't use it immediately
|
The value of the loop variable after executing the loop *is undefined*. Don't use it immediately
|
||||||
after the loop without first assigning a new value to it!
|
after the loop without first assigning a new value to it!
|
||||||
(this is an optimization issue to avoid having to deal with mostly useless post-loop logic to adjust the loop variable's value)
|
(this is an optimization issue to avoid having to deal with mostly useless post-loop logic to adjust the loop variable's value)
|
||||||
|
|
||||||
@ -414,15 +412,15 @@ if statements
|
|||||||
Conditional execution means that the flow of execution changes based on certiain conditions,
|
Conditional execution means that the flow of execution changes based on certiain conditions,
|
||||||
rather than having fixed gotos or subroutine calls::
|
rather than having fixed gotos or subroutine calls::
|
||||||
|
|
||||||
if A>4 goto overflow
|
if aa>4 goto overflow
|
||||||
|
|
||||||
if X==3 Y = 4
|
if xx==3 yy = 4
|
||||||
if X==3 Y = 4 else A = 2
|
if xx==3 yy = 4 else aa = 2
|
||||||
|
|
||||||
if X==5 {
|
if xx==5 {
|
||||||
Y = 99
|
yy = 99
|
||||||
} else {
|
} else {
|
||||||
A = 3
|
aa = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -486,16 +484,16 @@ Assignments
|
|||||||
-----------
|
-----------
|
||||||
|
|
||||||
Assignment statements assign a single value to a target variable or memory location.
|
Assignment statements assign a single value to a target variable or memory location.
|
||||||
Augmented assignments (such as ``A += X``) are also available, but these are just shorthands
|
Augmented assignments (such as ``aa += xx``) are also available, but these are just shorthands
|
||||||
for normal assignments (``A = A + X``).
|
for normal assignments (``aa = aa + xx``).
|
||||||
|
|
||||||
Only register variables and variables of type byte, word and float can be assigned a new value.
|
Only variables of type byte, word and float can be assigned a new value.
|
||||||
It's not possible to set a new value to string or array variables etc, because they get allocated
|
It's not possible to set a new value to string or array variables etc, because they get allocated
|
||||||
a fixed amount of memory which will not change.
|
a fixed amount of memory which will not change. (You *can* change the value of elements in a string or array though).
|
||||||
|
|
||||||
.. attention::
|
.. attention::
|
||||||
**Data type conversion (in assignments):**
|
**Data type conversion (in assignments):**
|
||||||
When assigning a value with a 'smaller' datatype to a register or variable with a 'larger' datatype,
|
When assigning a value with a 'smaller' datatype to variable with a 'larger' datatype,
|
||||||
the value will be automatically converted to the target datatype: byte --> word --> float.
|
the value will be automatically converted to the target datatype: byte --> word --> float.
|
||||||
So assigning a byte to a word variable, or a word to a floating point variable, is fine.
|
So assigning a byte to a word variable, or a word to a floating point variable, is fine.
|
||||||
The reverse is *not* true: it is *not* possible to assign a value of a 'larger' datatype to
|
The reverse is *not* true: it is *not* possible to assign a value of a 'larger' datatype to
|
||||||
@ -511,7 +509,7 @@ as the memory mapped address $d021.
|
|||||||
If you want to access a memory location directly (by using the address itself), without defining
|
If you want to access a memory location directly (by using the address itself), without defining
|
||||||
a memory mapped location, you can do so by enclosing the address in ``@(...)``::
|
a memory mapped location, you can do so by enclosing the address in ``@(...)``::
|
||||||
|
|
||||||
A = @($d020) ; set the A register to the current c64 screen border color ("peek(53280)")
|
color = @($d020) ; set the variable 'color' to the current c64 screen border color ("peek(53280)")
|
||||||
@($d020) = 0 ; set the c64 screen border to black ("poke 53280,0")
|
@($d020) = 0 ; set the c64 screen border to black ("poke 53280,0")
|
||||||
@(vic+$20) = 6 ; you can also use expressions to 'calculate' the address
|
@(vic+$20) = 6 ; you can also use expressions to 'calculate' the address
|
||||||
|
|
||||||
@ -802,6 +800,22 @@ memsetw(address, numwords, wordvalue)
|
|||||||
Efficiently set a part of memory to the given (u)word value.
|
Efficiently set a part of memory to the given (u)word value.
|
||||||
But the most efficient will always be to write a specialized fill routine in assembly yourself!
|
But the most efficient will always be to write a specialized fill routine in assembly yourself!
|
||||||
|
|
||||||
|
leftstr(source, target, length)
|
||||||
|
Copies the left side of the source string of the given length to target string.
|
||||||
|
It is assumed the target string buffer is large enough to contain the result.
|
||||||
|
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||||
|
|
||||||
|
rightstr(source, target, length)
|
||||||
|
Copies the right side of the source string of the given length to target string.
|
||||||
|
It is assumed the target string buffer is large enough to contain the result.
|
||||||
|
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||||
|
|
||||||
|
substr(source, target, start, length)
|
||||||
|
Copies a segment from the source string, starting at the given index,
|
||||||
|
and of the given length to target string.
|
||||||
|
It is assumed the target string buffer is large enough to contain the result.
|
||||||
|
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||||
|
|
||||||
swap(x, y)
|
swap(x, y)
|
||||||
Swap the values of numerical variables (or memory locations) x and y in a fast way.
|
Swap the values of numerical variables (or memory locations) x and y in a fast way.
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ Everything after a semicolon ``;`` is a comment and is ignored.
|
|||||||
If the whole line is just a comment, it will be copied into the resulting assembly source code.
|
If the whole line is just a comment, it will be copied into the resulting assembly source code.
|
||||||
This makes it easier to understand and relate the generated code. Examples::
|
This makes it easier to understand and relate the generated code. Examples::
|
||||||
|
|
||||||
A = 42 ; set the initial value to 42
|
counter = 42 ; set the initial value to 42
|
||||||
; next is the code that...
|
; next is the code that...
|
||||||
|
|
||||||
|
|
||||||
@ -313,7 +313,7 @@ Direct access to memory locations
|
|||||||
Instead of defining a memory mapped name for a specific memory location, you can also
|
Instead of defining a memory mapped name for a specific memory location, you can also
|
||||||
directly access the memory. Enclose a numeric expression or literal with ``@(...)`` to do that::
|
directly access the memory. Enclose a numeric expression or literal with ``@(...)`` to do that::
|
||||||
|
|
||||||
A = @($d020) ; set the A register to the current c64 screen border color ("peek(53280)")
|
color = @($d020) ; set the variable 'color' to the current c64 screen border color ("peek(53280)")
|
||||||
@($d020) = 0 ; set the c64 screen border to black ("poke 53280,0")
|
@($d020) = 0 ; set the c64 screen border to black ("poke 53280,0")
|
||||||
@(vic+$20) = 6 ; a dynamic expression to 'calculate' the address
|
@(vic+$20) = 6 ; a dynamic expression to 'calculate' the address
|
||||||
|
|
||||||
@ -333,8 +333,6 @@ Reserved names
|
|||||||
|
|
||||||
The following names are reserved, they have a special meaning::
|
The following names are reserved, they have a special meaning::
|
||||||
|
|
||||||
A X Y ; 6502 hardware registers
|
|
||||||
Pc Pz Pn Pv ; 6502 status register flags
|
|
||||||
true false ; boolean values 1 and 0
|
true false ; boolean values 1 and 0
|
||||||
|
|
||||||
|
|
||||||
@ -406,10 +404,10 @@ assignment: ``=``
|
|||||||
Note that an assignment sometimes is not possible or supported.
|
Note that an assignment sometimes is not possible or supported.
|
||||||
|
|
||||||
augmented assignment: ``+=`` ``-=`` ``*=`` ``/=`` ``**=`` ``&=`` ``|=`` ``^=`` ``<<=`` ``>>=``
|
augmented assignment: ``+=`` ``-=`` ``*=`` ``/=`` ``**=`` ``&=`` ``|=`` ``^=`` ``<<=`` ``>>=``
|
||||||
Syntactic sugar; ``A += X`` is equivalent to ``A = A + X``
|
Syntactic sugar; ``aa += xx`` is equivalent to ``aa = aa + xx``
|
||||||
|
|
||||||
postfix increment and decrement: ``++`` ``--``
|
postfix increment and decrement: ``++`` ``--``
|
||||||
Syntactic sugar; ``A++`` is equivalent to ``A = A + 1``, and ``A--`` is equivalent to ``A = A - 1``.
|
Syntactic sugar; ``aa++`` is equivalent to ``aa = aa + 1``, and ``aa--`` is equivalent to ``aa = aa - 1``.
|
||||||
Because these operations are so common, we have these short forms.
|
Because these operations are so common, we have these short forms.
|
||||||
|
|
||||||
comparison: ``!=`` ``<`` ``>`` ``<=`` ``>=``
|
comparison: ``!=`` ``<`` ``>`` ``<=`` ``>=``
|
||||||
@ -427,9 +425,9 @@ range creation: ``to``
|
|||||||
|
|
||||||
0 to 7 ; range of values 0, 1, 2, 3, 4, 5, 6, 7 (constant)
|
0 to 7 ; range of values 0, 1, 2, 3, 4, 5, 6, 7 (constant)
|
||||||
|
|
||||||
A = 5
|
aa = 5
|
||||||
X = 10
|
aa = 10
|
||||||
A to X ; range of 5, 6, 7, 8, 9, 10
|
aa to xx ; range of 5, 6, 7, 8, 9, 10
|
||||||
|
|
||||||
byte[] array = 10 to 13 ; sets the array to [1, 2, 3, 4]
|
byte[] array = 10 to 13 ; sets the array to [1, 2, 3, 4]
|
||||||
|
|
||||||
@ -551,7 +549,7 @@ Loops
|
|||||||
for loop
|
for loop
|
||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
|
|
||||||
The loop variable must be a register or a byte/word variable,
|
The loop variable must be a byte or word variable,
|
||||||
and must be defined first in the local scope of the for loop.
|
and must be defined first in the local scope of the for loop.
|
||||||
The expression that you loop over can be anything that supports iteration (such as ranges like ``0 to 100``,
|
The expression that you loop over can be anything that supports iteration (such as ranges like ``0 to 100``,
|
||||||
array variables and strings) *except* floating-point arrays (because a floating-point loop variable is not supported).
|
array variables and strings) *except* floating-point arrays (because a floating-point loop variable is not supported).
|
||||||
@ -598,31 +596,33 @@ You can use a single statement, or a statement block like in the example below::
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
repeat-until loop
|
do-until loop
|
||||||
^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
Until the given condition is true (1), repeat the given statement(s).
|
Until the given condition is true (1), repeat the given statement(s).
|
||||||
You can use a single statement, or a statement block like in the example below::
|
You can use a single statement, or a statement block like in the example below::
|
||||||
|
|
||||||
repeat {
|
do {
|
||||||
; do something...
|
; do something...
|
||||||
break ; break out of the loop
|
break ; break out of the loop
|
||||||
continue ; immediately enter next iteration
|
continue ; immediately enter next iteration
|
||||||
} until <condition>
|
} until <condition>
|
||||||
|
|
||||||
|
|
||||||
forever loop
|
repeat loop
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|
|
||||||
Simply run the code in a loop, forever. It's the same as a while true or until false loop,
|
When you're only interested in repeating something a given number of times.
|
||||||
or just a jump back to a previous label. You can still break out of this loop as well, if you want::
|
It's a short hand for a for loop without an explicit loop variable::
|
||||||
|
|
||||||
forever {
|
repeat 15 {
|
||||||
; .. do stuff
|
; do something...
|
||||||
if something
|
break ; you can break out of the loop
|
||||||
break ; you can exit the loop if you want
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
If you omit the iteration count, it simply loops forever.
|
||||||
|
You can still ``break`` out of such a loop if you want though.
|
||||||
|
|
||||||
|
|
||||||
Conditional Execution and Jumps
|
Conditional Execution and Jumps
|
||||||
-------------------------------
|
-------------------------------
|
||||||
@ -702,3 +702,4 @@ case you have to use { } to enclose them::
|
|||||||
}
|
}
|
||||||
else -> c64scr.print("don't know")
|
else -> c64scr.print("don't know")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,22 +113,14 @@ CPU
|
|||||||
Directly Usable Registers
|
Directly Usable Registers
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
The following 6502 CPU hardware registers are directly usable in program code (and are reserved symbols):
|
The hardware CPU registers are not directly accessible from regular Prog8 code.
|
||||||
|
If you need to mess with them, you'll have to use inline assembly.
|
||||||
|
Be extra wary of the ``X`` register because it is used as an evaluation stack pointer and
|
||||||
|
changing its value you will destroy the evaluation stack and likely crash the program.
|
||||||
|
|
||||||
- ``A``, ``X``, ``Y`` the three main cpu registers (8 bits)
|
The status register (P) carry flag and interrupt disable flag can be written via a couple of special
|
||||||
- the status register (P) carry flag and interrupt disable flag can be written via a couple of special
|
builtin functions (``set_carry()``, ``clear_carry()``, ``set_irqd()``, ``clear_irqd()``),
|
||||||
builtin functions (``set_carry()``, ``clear_carry()``, ``set_irqd()``, ``clear_irqd()``),
|
and read via the ``read_flags()`` function.
|
||||||
and read via the ``read_flags()`` function.
|
|
||||||
|
|
||||||
However, you must assume that the 3 hardware registers ``A``, ``X`` and ``Y``
|
|
||||||
are volatile. Their values cannot be depended upon, the compiler will use them as required.
|
|
||||||
Even simple assignments may require modification of one or more of the registers (for instance, when using arrays).
|
|
||||||
|
|
||||||
Even more important, the ``X`` register is used as an evaluation stack pointer.
|
|
||||||
If you mess with it, you will destroy the evaluation stack and likely crash your program.
|
|
||||||
In some cases the compiler will warn you about this, but you should really avoid to use
|
|
||||||
this register. It's possible to store/restore the register's value (using special built in functions)
|
|
||||||
for the cases you really really need to use it directly.
|
|
||||||
|
|
||||||
|
|
||||||
Subroutine Calling Conventions
|
Subroutine Calling Conventions
|
||||||
@ -173,3 +165,4 @@ as a subroutine ``irq`` in the module ``irq`` so like this::
|
|||||||
; ... irq handling here ...
|
; ... irq handling here ...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,10 +3,10 @@ TODO
|
|||||||
====
|
====
|
||||||
|
|
||||||
- finalize (most) of the still missing "new" assignment asm code generation
|
- finalize (most) of the still missing "new" assignment asm code generation
|
||||||
|
|
||||||
- aliases for imported symbols for example perhaps '%alias print = c64scr.print'
|
- aliases for imported symbols for example perhaps '%alias print = c64scr.print'
|
||||||
- option to load library files from a directory instead of the embedded ones (easier library development/debugging)
|
- option to load library files from a directory instead of the embedded ones (easier library development/debugging)
|
||||||
- investigate support for 8bitguy's Commander X16 platform https://murray2.com/forums/commander-x16.9/ and https://github.com/commanderx16/x16-docs
|
- investigate support for 8bitguy's Commander X16 platform https://www.commanderx16.com and https://github.com/commanderx16/x16-docs
|
||||||
|
- see if we can group some errors together for instance the (now single) errors about unidentified symbols
|
||||||
|
|
||||||
|
|
||||||
More optimizations
|
More optimizations
|
||||||
@ -17,13 +17,13 @@ Add more compiler optimizations to the existing ones.
|
|||||||
- more targeted optimizations for assigment asm code, such as the following:
|
- more targeted optimizations for assigment asm code, such as the following:
|
||||||
- subroutine calling convention? like: 1 byte arg -> pass in A, 2 bytes -> pass in A+Y, return value likewise.
|
- subroutine calling convention? like: 1 byte arg -> pass in A, 2 bytes -> pass in A+Y, return value likewise.
|
||||||
- remove unreachable code after an exit(), return or goto
|
- remove unreachable code after an exit(), return or goto
|
||||||
- working subroutine inlining (start with trivial routines, grow to taking care of vars and identifier refs to them)
|
|
||||||
- add a compiler option to not include variable initialization code (useful if the program is expected to run only once, such as a game)
|
- add a compiler option to not include variable initialization code (useful if the program is expected to run only once, such as a game)
|
||||||
the program will then rely solely on the values as they are in memory at the time of program startup.
|
the program will then rely solely on the values as they are in memory at the time of program startup.
|
||||||
- Also some library routines and code patterns could perhaps be optimized further
|
- Also some library routines and code patterns could perhaps be optimized further
|
||||||
- can the parameter passing to subroutines be optimized to avoid copying?
|
- can the parameter passing to subroutines be optimized to avoid copying?
|
||||||
- more optimizations on the language AST level
|
- more optimizations on the language AST level
|
||||||
- more optimizations on the final assembly source level
|
- more optimizations on the final assembly source level
|
||||||
|
- note: abandoned subroutine inlining because of problems referencing non-local stuff. Can't move everything around.
|
||||||
|
|
||||||
|
|
||||||
Eval stack redesign? (lot of work)
|
Eval stack redesign? (lot of work)
|
||||||
|
@ -104,17 +104,6 @@ main {
|
|||||||
ub = all(farr)
|
ub = all(farr)
|
||||||
if ub==0 c64scr.print("error all10\n")
|
if ub==0 c64scr.print("error all10\n")
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
|
|
||||||
c64scr.print("\nyou should see no errors printed above (only at first run).")
|
c64scr.print("\nyou should see no errors printed above (only at first run).")
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
%import c64utils
|
%import c64utils
|
||||||
;%import c64flt
|
|
||||||
;%option enable_floats
|
|
||||||
%zeropage dontuse
|
%zeropage dontuse
|
||||||
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
ubyte A
|
||||||
|
|
||||||
c64scr.print("ubyte shift left\n")
|
c64scr.print("ubyte shift left\n")
|
||||||
A = shiftlb0()
|
A = shiftlb0()
|
||||||
c64scr.print_ubbin(A, true)
|
c64scr.print_ubbin(A, true)
|
||||||
|
@ -24,8 +24,6 @@ main {
|
|||||||
|
|
||||||
div_float(0,1,0)
|
div_float(0,1,0)
|
||||||
div_float(999.9,111.0,9.008108108108107)
|
div_float(999.9,111.0,9.008108108108107)
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub div_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub div_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
@ -103,12 +101,4 @@ main {
|
|||||||
c64flt.print_f(r)
|
c64flt.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,6 @@ main {
|
|||||||
minus_float(0,0,0)
|
minus_float(0,0,0)
|
||||||
minus_float(2.5,1.5,1.0)
|
minus_float(2.5,1.5,1.0)
|
||||||
minus_float(-1.5,3.5,-5.0)
|
minus_float(-1.5,3.5,-5.0)
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub minus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub minus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
@ -111,13 +109,4 @@ main {
|
|||||||
c64flt.print_f(r)
|
c64flt.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,6 @@ main {
|
|||||||
mul_float(0,0,0)
|
mul_float(0,0,0)
|
||||||
mul_float(2.5,10,25)
|
mul_float(2.5,10,25)
|
||||||
mul_float(-1.5,10,-15)
|
mul_float(-1.5,10,-15)
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub mul_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub mul_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
@ -105,12 +103,4 @@ main {
|
|||||||
c64flt.print_f(r)
|
c64flt.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,6 @@ main {
|
|||||||
plus_float(1.5,2.5,4.0)
|
plus_float(1.5,2.5,4.0)
|
||||||
plus_float(-1.5,3.5,2.0)
|
plus_float(-1.5,3.5,2.0)
|
||||||
plus_float(-1.1,3.3,2.2)
|
plus_float(-1.1,3.3,2.2)
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub plus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub plus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
@ -109,13 +107,4 @@ main {
|
|||||||
c64flt.print_f(r)
|
c64flt.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ main {
|
|||||||
|
|
||||||
c64scr.plot(0,24)
|
c64scr.plot(0,24)
|
||||||
|
|
||||||
|
ubyte Y
|
||||||
ubyte ub=200
|
ubyte ub=200
|
||||||
byte bb=-100
|
byte bb=-100
|
||||||
uword uw = 2000
|
uword uw = 2000
|
||||||
@ -76,8 +77,6 @@ main {
|
|||||||
check_b(barr[1], -100)
|
check_b(barr[1], -100)
|
||||||
check_uw(uwarr[1], 2000)
|
check_uw(uwarr[1], 2000)
|
||||||
check_w(warr[1], -1000)
|
check_w(warr[1], -1000)
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_ub(ubyte value, ubyte expected) {
|
sub check_ub(ubyte value, ubyte expected) {
|
||||||
@ -139,13 +138,4 @@ main {
|
|||||||
c64flt.print_f(expected)
|
c64flt.print_f(expected)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,6 @@ main {
|
|||||||
remainder_uword(40000,511,142)
|
remainder_uword(40000,511,142)
|
||||||
remainder_uword(40000,500,0)
|
remainder_uword(40000,500,0)
|
||||||
remainder_uword(43211,12,11)
|
remainder_uword(43211,12,11)
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub remainder_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub remainder_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
@ -48,12 +46,4 @@ main {
|
|||||||
c64scr.print_uw(r)
|
c64scr.print_uw(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ main {
|
|||||||
ubyte active_height = 24
|
ubyte active_height = 24
|
||||||
ubyte upwards = true
|
ubyte upwards = true
|
||||||
|
|
||||||
forever {
|
repeat {
|
||||||
ubyte mountain = 223 ; slope upwards
|
ubyte mountain = 223 ; slope upwards
|
||||||
if active_height < target_height {
|
if active_height < target_height {
|
||||||
active_height++
|
active_height++
|
||||||
|
@ -16,7 +16,7 @@ sub start() {
|
|||||||
void c64.CHRIN()
|
void c64.CHRIN()
|
||||||
c64.CLEARSCR()
|
c64.CLEARSCR()
|
||||||
|
|
||||||
forever {
|
repeat {
|
||||||
uword note
|
uword note
|
||||||
for note in notes {
|
for note in notes {
|
||||||
ubyte note1 = lsb(note)
|
ubyte note1 = lsb(note)
|
||||||
@ -37,10 +37,8 @@ sub start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub delay() {
|
sub delay() {
|
||||||
ubyte d
|
repeat 32 {
|
||||||
for d in 0 to 12 {
|
while c64.RASTER {
|
||||||
while c64.RASTER!=0 {
|
|
||||||
; tempo delay synced to screen refresh
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ graphics {
|
|||||||
|
|
||||||
if dx >= dy {
|
if dx >= dy {
|
||||||
if positive_ix {
|
if positive_ix {
|
||||||
forever {
|
repeat {
|
||||||
plot(y1)
|
plot(y1)
|
||||||
if plotx==x2
|
if plotx==x2
|
||||||
return
|
return
|
||||||
@ -51,7 +51,7 @@ graphics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
forever {
|
repeat {
|
||||||
plot(y1)
|
plot(y1)
|
||||||
if plotx==x2
|
if plotx==x2
|
||||||
return
|
return
|
||||||
@ -66,7 +66,7 @@ graphics {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if positive_ix {
|
if positive_ix {
|
||||||
forever {
|
repeat {
|
||||||
plot(y1)
|
plot(y1)
|
||||||
if y1 == y2
|
if y1 == y2
|
||||||
return
|
return
|
||||||
@ -78,7 +78,7 @@ graphics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
forever {
|
repeat {
|
||||||
plot(y1)
|
plot(y1)
|
||||||
if y1 == y2
|
if y1 == y2
|
||||||
return
|
return
|
||||||
|
@ -105,16 +105,5 @@ main {
|
|||||||
c64scr.print("ok: 22 >= 22\n")
|
c64scr.print("ok: 22 >= 22\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 22>=22!\n")
|
c64scr.print("error in 22>=22!\n")
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,16 +105,5 @@ main {
|
|||||||
c64scr.print("ok: -22.2 >= -22.2\n")
|
c64scr.print("ok: -22.2 >= -22.2\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in -22.2>=-22.2!\n")
|
c64scr.print("error in -22.2>=-22.2!\n")
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -105,16 +105,5 @@ main {
|
|||||||
c64scr.print("ok: 22 >= 22\n")
|
c64scr.print("ok: 22 >= 22\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 22>=22!\n")
|
c64scr.print("error in 22>=22!\n")
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -105,16 +105,5 @@ main {
|
|||||||
c64scr.print("ok: 322 >= 322\n")
|
c64scr.print("ok: 322 >= 322\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 322>=322!\n")
|
c64scr.print("error in 322>=322!\n")
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -137,16 +137,5 @@ main {
|
|||||||
c64scr.print("ok: 1000 >= 1000\n")
|
c64scr.print("ok: 1000 >= 1000\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 1000>=1000!\n")
|
c64scr.print("error in 1000>=1000!\n")
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,6 @@ main {
|
|||||||
c64scr.print("v1=20, v2=-111\n")
|
c64scr.print("v1=20, v2=-111\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
sub compare() {
|
sub compare() {
|
||||||
@ -91,13 +90,4 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,6 @@ main {
|
|||||||
c64scr.print("v1 = v2 = 0\n")
|
c64scr.print("v1 = v2 = 0\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
sub compare() {
|
sub compare() {
|
||||||
@ -108,11 +107,4 @@ main {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,6 @@ main {
|
|||||||
c64scr.print("v1=220, v2=10\n")
|
c64scr.print("v1=220, v2=10\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
sub compare() {
|
sub compare() {
|
||||||
@ -92,12 +91,4 @@ main {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,6 @@ main {
|
|||||||
c64scr.print("v1 = v2 = aa\n")
|
c64scr.print("v1 = v2 = aa\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
sub compare() {
|
sub compare() {
|
||||||
@ -121,13 +120,4 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,6 @@ main {
|
|||||||
c64scr.print("v1 = v2 = aa\n")
|
c64scr.print("v1 = v2 = aa\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
sub compare() {
|
sub compare() {
|
||||||
@ -157,13 +156,4 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
BIN
examples/compiled/mandelbrot-gfx.prg
Normal file
BIN
examples/compiled/mandelbrot-gfx.prg
Normal file
Binary file not shown.
Binary file not shown.
@ -19,7 +19,7 @@ main {
|
|||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
float time=0.0
|
float time=0.0
|
||||||
forever {
|
repeat {
|
||||||
rotate_vertices(time)
|
rotate_vertices(time)
|
||||||
c64scr.clear_screenchars(32)
|
c64scr.clear_screenchars(32)
|
||||||
draw_edges()
|
draw_edges()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
%import c64lib
|
%import c64lib
|
||||||
%import c64utils
|
%import c64utils
|
||||||
|
|
||||||
|
|
||||||
spritedata $2000 {
|
spritedata $2000 {
|
||||||
; this memory block contains the sprite data
|
; this memory block contains the sprite data
|
||||||
; it must start on an address aligned to 64 bytes.
|
; it must start on an address aligned to 64 bytes.
|
||||||
@ -81,7 +82,7 @@ main {
|
|||||||
uword anglex
|
uword anglex
|
||||||
uword angley
|
uword angley
|
||||||
uword anglez
|
uword anglez
|
||||||
forever {
|
repeat {
|
||||||
c64.TIME_LO=0
|
c64.TIME_LO=0
|
||||||
rotate_vertices(msb(anglex), msb(angley), msb(anglez))
|
rotate_vertices(msb(anglex), msb(angley), msb(anglez))
|
||||||
position_sprites()
|
position_sprites()
|
||||||
|
@ -21,7 +21,7 @@ main {
|
|||||||
uword anglex
|
uword anglex
|
||||||
uword angley
|
uword angley
|
||||||
uword anglez
|
uword anglez
|
||||||
forever {
|
repeat {
|
||||||
rotate_vertices(msb(anglex), msb(angley), msb(anglez))
|
rotate_vertices(msb(anglex), msb(angley), msb(anglez))
|
||||||
c64scr.clear_screenchars(32)
|
c64scr.clear_screenchars(32)
|
||||||
draw_edges()
|
draw_edges()
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
c64scr.print("fibonacci sequence\n")
|
c64scr.print("fibonacci sequence\n")
|
||||||
for A in 0 to 20 {
|
|
||||||
|
repeat 21 {
|
||||||
c64scr.print_uw(fib_next())
|
c64scr.print_uw(fib_next())
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
@ -42,17 +42,5 @@ main {
|
|||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
c64scr.print("bye!\n")
|
c64scr.print("bye!\n")
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("stack x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
%import c64lib
|
%import c64lib
|
||||||
%import c64graphics
|
%import c64graphics
|
||||||
|
|
||||||
|
; TODO fix compiler errors when compiling without optimizations
|
||||||
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
graphics.enable_bitmap_mode()
|
graphics.enable_bitmap_mode()
|
||||||
draw_lines()
|
draw_lines()
|
||||||
draw_circles()
|
draw_circles()
|
||||||
forever {
|
repeat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ main {
|
|||||||
ubyte y = y1
|
ubyte y = y1
|
||||||
|
|
||||||
if dx >= dy {
|
if dx >= dy {
|
||||||
forever {
|
repeat {
|
||||||
c64scr.setcc(x, y, 42, 5)
|
c64scr.setcc(x, y, 42, 5)
|
||||||
if x==x2
|
if x==x2
|
||||||
return
|
return
|
||||||
@ -89,7 +89,7 @@ main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
forever {
|
repeat {
|
||||||
c64scr.setcc(x, y, 42, 5)
|
c64scr.setcc(x, y, 42, 5)
|
||||||
if y == y2
|
if y == y2
|
||||||
return
|
return
|
||||||
|
54
examples/mandelbrot-gfx.p8
Normal file
54
examples/mandelbrot-gfx.p8
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
%import c64lib
|
||||||
|
%import c64flt
|
||||||
|
%import c64graphics
|
||||||
|
%zeropage floatsafe
|
||||||
|
|
||||||
|
; Draw a mandelbrot in graphics mode (the image will be 256 x 200 pixels).
|
||||||
|
; NOTE: this will take an eternity to draw on a real c64.
|
||||||
|
; even in Vice in warp mode (700% speed on my machine) it's slow, but you can see progress
|
||||||
|
|
||||||
|
; TODO fix compiler errors when compiling without optimizations
|
||||||
|
|
||||||
|
|
||||||
|
main {
|
||||||
|
const ubyte width = 255
|
||||||
|
const ubyte height = 200
|
||||||
|
const ubyte max_iter = 16
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
graphics.enable_bitmap_mode()
|
||||||
|
|
||||||
|
ubyte pixelx
|
||||||
|
ubyte pixely
|
||||||
|
|
||||||
|
for pixely in 0 to height-1 {
|
||||||
|
float yy = (pixely as float)/0.4/height - 1.0
|
||||||
|
|
||||||
|
for pixelx in 0 to width-1 {
|
||||||
|
float xx = (pixelx as float)/0.3/width - 2.2
|
||||||
|
|
||||||
|
float xsquared = 0.0
|
||||||
|
float ysquared = 0.0
|
||||||
|
float x = 0.0
|
||||||
|
float y = 0.0
|
||||||
|
ubyte iter = 0
|
||||||
|
|
||||||
|
while iter<max_iter and xsquared+ysquared<4.0 {
|
||||||
|
y = x*y*2.0 + yy
|
||||||
|
x = xsquared - ysquared + xx
|
||||||
|
xsquared = x*x
|
||||||
|
ysquared = y*y
|
||||||
|
iter++
|
||||||
|
}
|
||||||
|
|
||||||
|
if iter & 1 {
|
||||||
|
graphics.plotx = pixelx
|
||||||
|
graphics.plot(pixely)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repeat {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,7 +30,7 @@ main {
|
|||||||
float y = 0.0
|
float y = 0.0
|
||||||
ubyte iter = 0
|
ubyte iter = 0
|
||||||
|
|
||||||
while (iter<max_iter and xsquared+ysquared<4.0) {
|
while iter<max_iter and xsquared+ysquared<4.0 {
|
||||||
y = x*y*2.0 + yy
|
y = x*y*2.0 + yy
|
||||||
x = xsquared - ysquared + xx
|
x = xsquared - ysquared + xx
|
||||||
xsquared = x*x
|
xsquared = x*x
|
||||||
@ -48,15 +48,5 @@ main {
|
|||||||
c64scr.print("finished in ")
|
c64scr.print("finished in ")
|
||||||
c64flt.print_f(duration)
|
c64flt.print_f(duration)
|
||||||
c64scr.print(" seconds!\n")
|
c64scr.print(" seconds!\n")
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("stack x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -57,18 +57,6 @@ main {
|
|||||||
c64scr.print("Thanks for playing, ")
|
c64scr.print("Thanks for playing, ")
|
||||||
c64scr.print(name)
|
c64scr.print(name)
|
||||||
c64scr.print(".\n")
|
c64scr.print(".\n")
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("stack x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ main {
|
|||||||
; calculate primes
|
; calculate primes
|
||||||
c64scr.print("prime numbers up to 255:\n\n")
|
c64scr.print("prime numbers up to 255:\n\n")
|
||||||
ubyte amount=0
|
ubyte amount=0
|
||||||
forever {
|
repeat {
|
||||||
ubyte prime = find_next_prime()
|
ubyte prime = find_next_prime()
|
||||||
if prime==0
|
if prime==0
|
||||||
break
|
break
|
||||||
@ -24,8 +24,6 @@ main {
|
|||||||
c64scr.print("number of primes (expected 54): ")
|
c64scr.print("number of primes (expected 54): ")
|
||||||
c64scr.print_ub(amount)
|
c64scr.print_ub(amount)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -48,14 +46,4 @@ main {
|
|||||||
}
|
}
|
||||||
return candidate_prime
|
return candidate_prime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("stack x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ main {
|
|||||||
c64.SCROLY &= %11101111 ; blank the screen
|
c64.SCROLY &= %11101111 ; blank the screen
|
||||||
c64utils.set_rasterirq_excl(40) ; register exclusive raster irq handler
|
c64utils.set_rasterirq_excl(40) ; register exclusive raster irq handler
|
||||||
|
|
||||||
forever {
|
repeat {
|
||||||
; enjoy the moving bars :)
|
; enjoy the moving bars :)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,17 +52,5 @@ main {
|
|||||||
|
|
||||||
c64flt.print_f(0.0)
|
c64flt.print_f(0.0)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("stack x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -34,17 +34,5 @@ main {
|
|||||||
c64scr.print("\nscreencode z=")
|
c64scr.print("\nscreencode z=")
|
||||||
c64scr.print_ub(c2)
|
c64scr.print_ub(c2)
|
||||||
c64scr.print("\n")
|
c64scr.print("\n")
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("stack x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ main {
|
|||||||
c64scr.print("reversed\n")
|
c64scr.print("reversed\n")
|
||||||
print_arrays()
|
print_arrays()
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@ -65,14 +64,4 @@ main {
|
|||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("stack x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
%import c64lib
|
|
||||||
%zeropage basicsafe
|
|
||||||
|
|
||||||
main {
|
|
||||||
|
|
||||||
sub start() {
|
|
||||||
|
|
||||||
str s1 = "apple"
|
|
||||||
str s2 = "banana"
|
|
||||||
byte[] a1 = [66,77,88,0]
|
|
||||||
ubyte i1 = 101
|
|
||||||
uword w1 = 000
|
|
||||||
|
|
||||||
c64.STROUT(s1)
|
|
||||||
c64.CHROUT('\n')
|
|
||||||
c64.STROUT(a1)
|
|
||||||
c64.CHROUT('\n')
|
|
||||||
|
|
||||||
c64scr.print("bla\n")
|
|
||||||
|
|
||||||
; c64scr.print_uwhex(s1, true)
|
|
||||||
; w1 = &s1
|
|
||||||
; c64scr.print_uwhex(w1, true)
|
|
||||||
;
|
|
||||||
; c64scr.print_uwhex(a1, true)
|
|
||||||
; w1 = &a1
|
|
||||||
; c64scr.print_uwhex(w1, true)
|
|
||||||
;
|
|
||||||
; s1 = s1
|
|
||||||
; s1 = s2
|
|
||||||
; s2 = "zzz"
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
|||||||
%import c64utils
|
%import c64utils
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
; TODO fix compiler errors when compiling without optimization ( /= )
|
||||||
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
struct Color {
|
struct Color {
|
||||||
@ -11,7 +14,7 @@ main {
|
|||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
|
||||||
Color purple = {255, 0, 255}
|
Color purple = [255, 0, 255]
|
||||||
|
|
||||||
Color other
|
Color other
|
||||||
|
|
||||||
@ -27,17 +30,5 @@ main {
|
|||||||
c64.CHROUT(',')
|
c64.CHROUT(',')
|
||||||
c64scr.print_ub(other.blue)
|
c64scr.print_ub(other.blue)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("stack x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,9 @@ main {
|
|||||||
float t
|
float t
|
||||||
ubyte color
|
ubyte color
|
||||||
|
|
||||||
forever {
|
repeat {
|
||||||
float x = sin(t)
|
ubyte xx=(sin(t) * width/2.2) + width/2.0 as ubyte
|
||||||
float y = cos(t*1.1356)
|
ubyte yy=(cos(t*1.1356) * height/2.2) + height/2.0 as ubyte
|
||||||
ubyte xx=(x * width/2.2) + width/2.0 as ubyte
|
|
||||||
ubyte yy=(y * height/2.2) + height/2.0 as ubyte
|
|
||||||
c64scr.setcc(xx, yy, 81, color)
|
c64scr.setcc(xx, yy, 81, color)
|
||||||
t += 0.08
|
t += 0.08
|
||||||
color++
|
color++
|
||||||
|
@ -15,7 +15,7 @@ main {
|
|||||||
|
|
||||||
Ball ball
|
Ball ball
|
||||||
|
|
||||||
forever {
|
repeat {
|
||||||
ubyte x = msb(sin8u(msb(ball.anglex)) as uword * width)
|
ubyte x = msb(sin8u(msb(ball.anglex)) as uword * width)
|
||||||
ubyte y = msb(cos8u(msb(ball.angley)) as uword * height)
|
ubyte y = msb(cos8u(msb(ball.angley)) as uword * height)
|
||||||
c64scr.setcc(x, y, 81, ball.color)
|
c64scr.setcc(x, y, 81, ball.color)
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
; staged speed increase
|
; staged speed increase
|
||||||
; some simple sound effects
|
; some simple sound effects
|
||||||
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
const ubyte boardOffsetX = 14
|
const ubyte boardOffsetX = 14
|
||||||
@ -38,8 +39,6 @@ newgame:
|
|||||||
spawnNextBlock()
|
spawnNextBlock()
|
||||||
|
|
||||||
waitkey:
|
waitkey:
|
||||||
check_eval_stack()
|
|
||||||
|
|
||||||
if c64.TIME_LO>=(60-4*speedlevel) {
|
if c64.TIME_LO>=(60-4*speedlevel) {
|
||||||
c64.TIME_LO = 0
|
c64.TIME_LO = 0
|
||||||
|
|
||||||
@ -107,6 +106,7 @@ waitkey:
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if dropypos>ypos {
|
if dropypos>ypos {
|
||||||
ypos = dropypos
|
ypos = dropypos
|
||||||
sound.blockdrop()
|
sound.blockdrop()
|
||||||
@ -389,16 +389,6 @@ waitkey:
|
|||||||
c64scr.setcc((i&3)+x, (i/4)+y, character, c)
|
c64scr.setcc((i&3)+x, (i/4)+y, character, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("stack x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,45 +2,45 @@
|
|||||||
%import c64utils
|
%import c64utils
|
||||||
%import c64flt
|
%import c64flt
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
%option enable_floats
|
||||||
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
|
||||||
A += 50
|
repeat 10 {
|
||||||
|
c64.CHROUT('*')
|
||||||
|
}
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
A += Y + 1
|
ubyte ub = 9
|
||||||
A -= Y + 1
|
repeat ub {
|
||||||
A += Y - 1
|
c64.CHROUT('*')
|
||||||
A -= Y - 1
|
}
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
A += Y + 2
|
repeat 320 {
|
||||||
A -= Y + 2
|
c64.CHROUT('+')
|
||||||
A += Y - 2
|
}
|
||||||
A -= Y - 2
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
; ubyte ubb
|
uword uw = 320
|
||||||
; byte bb
|
repeat uw {
|
||||||
; uword uww
|
c64.CHROUT('-')
|
||||||
; word ww
|
}
|
||||||
; word ww2
|
c64.CHROUT('\n')
|
||||||
;
|
|
||||||
; A = ubb*0
|
|
||||||
; Y = ubb*1
|
|
||||||
; A = ubb*2
|
|
||||||
; Y = ubb*4
|
|
||||||
; A = ubb*8
|
|
||||||
; Y = ubb*16
|
|
||||||
; A = ubb*32
|
|
||||||
; Y = ubb*64
|
|
||||||
; A = ubb*128
|
|
||||||
; Y = ubb+ubb+ubb
|
|
||||||
; A = ubb+ubb+ubb+ubb
|
|
||||||
; ww = ww2+ww2
|
|
||||||
; ww = ww2+ww2+ww2
|
|
||||||
; ww = ww2+ww2+ww2+ww2
|
|
||||||
|
|
||||||
|
ub = 7
|
||||||
|
repeat ub+2 {
|
||||||
|
c64.CHROUT('*')
|
||||||
|
}
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
uw = 318
|
||||||
|
repeat uw+2 {
|
||||||
|
c64.CHROUT('*')
|
||||||
|
}
|
||||||
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ main {
|
|||||||
&uword[4] muwarray = $c000
|
&uword[4] muwarray = $c000
|
||||||
&float[4] mflarray = $c000
|
&float[4] mflarray = $c000
|
||||||
|
|
||||||
|
ubyte A
|
||||||
byte bb
|
byte bb
|
||||||
ubyte ub
|
ubyte ub
|
||||||
word ww
|
word ww
|
||||||
|
@ -12,6 +12,7 @@ main {
|
|||||||
ubyte ub
|
ubyte ub
|
||||||
byte bb
|
byte bb
|
||||||
word total
|
word total
|
||||||
|
ubyte A
|
||||||
|
|
||||||
c64scr.plot(0,24)
|
c64scr.plot(0,24)
|
||||||
|
|
||||||
@ -959,8 +960,6 @@ main {
|
|||||||
c64scr.print("ok\n")
|
c64scr.print("ok\n")
|
||||||
else
|
else
|
||||||
c64scr.print("fail!!!\n")
|
c64scr.print("fail!!!\n")
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub wait_input() {
|
sub wait_input() {
|
||||||
@ -969,13 +968,4 @@ main {
|
|||||||
void c64scr.input_chars(input)
|
void c64scr.input_chars(input)
|
||||||
c64scr.print("\n\n")
|
c64scr.print("\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("stack x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,25 +3,24 @@
|
|||||||
%import c64graphics
|
%import c64graphics
|
||||||
%option enable_floats
|
%option enable_floats
|
||||||
|
|
||||||
|
; TODO fix compiler errors when compiling without optimizations
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
graphics.enable_bitmap_mode()
|
graphics.enable_bitmap_mode()
|
||||||
turtle.init()
|
turtle.init()
|
||||||
|
; turtle.pu()
|
||||||
|
; turtle.pos(150, 110)
|
||||||
|
; turtle.pd()
|
||||||
|
|
||||||
ubyte i
|
ubyte i
|
||||||
for i in 0 to 255 {
|
|
||||||
while c64.RASTER {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in 0 to 100 {
|
for i in 0 to 100 {
|
||||||
turtle.fd(i+20)
|
turtle.fd(i+20)
|
||||||
turtle.rt(94)
|
turtle.rt(94)
|
||||||
}
|
}
|
||||||
|
|
||||||
forever {
|
repeat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,26 +41,32 @@ turtle {
|
|||||||
c64.SPENA = 1
|
c64.SPENA = 1
|
||||||
c64.SP0COL = 5
|
c64.SP0COL = 5
|
||||||
|
|
||||||
turtlepos()
|
update_turtle_sprite()
|
||||||
}
|
}
|
||||||
|
|
||||||
sub turtlepos() {
|
sub update_turtle_sprite() {
|
||||||
uword xx = xpos as uword
|
uword xx = xpos as uword
|
||||||
c64.SPXY[0] = lsb(xx) + 12
|
c64.SPXY[0] = lsb(xx) + 12
|
||||||
if msb(xx)
|
c64.MSIGX = msb(xx) > 0
|
||||||
c64.MSIGX = 1
|
|
||||||
else
|
|
||||||
c64.MSIGX = 0
|
|
||||||
c64.SPXY[1] = lsb(ypos) + 40
|
c64.SPXY[1] = lsb(ypos) + 40
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub pos(float x, float y) {
|
||||||
|
if pendown {
|
||||||
|
graphics.line(xpos as uword, ypos as ubyte, x as uword, y as ubyte)
|
||||||
|
}
|
||||||
|
xpos = x
|
||||||
|
ypos = y
|
||||||
|
update_turtle_sprite()
|
||||||
|
}
|
||||||
|
|
||||||
sub fd(uword length) {
|
sub fd(uword length) {
|
||||||
float flen = length as float
|
float flen = length as float
|
||||||
float sx = xpos
|
float sx = xpos
|
||||||
float sy = ypos
|
float sy = ypos
|
||||||
xpos += flen * sin(angle)
|
xpos += flen * sin(angle)
|
||||||
ypos -= flen * cos(angle)
|
ypos -= flen * cos(angle)
|
||||||
turtlepos()
|
update_turtle_sprite()
|
||||||
if pendown {
|
if pendown {
|
||||||
graphics.line(sx as uword, lsb(sy), xpos as uword, lsb(ypos))
|
graphics.line(sx as uword, lsb(sy), xpos as uword, lsb(ypos))
|
||||||
}
|
}
|
||||||
|
@ -94,8 +94,8 @@ statement :
|
|||||||
| returnstmt
|
| returnstmt
|
||||||
| forloop
|
| forloop
|
||||||
| whileloop
|
| whileloop
|
||||||
|
| untilloop
|
||||||
| repeatloop
|
| repeatloop
|
||||||
| foreverloop
|
|
||||||
| whenstmt
|
| whenstmt
|
||||||
| breakstmt
|
| breakstmt
|
||||||
| continuestmt
|
| continuestmt
|
||||||
@ -158,8 +158,7 @@ augassignment :
|
|||||||
;
|
;
|
||||||
|
|
||||||
assign_target:
|
assign_target:
|
||||||
register
|
scoped_identifier
|
||||||
| scoped_identifier
|
|
||||||
| arrayindexed
|
| arrayindexed
|
||||||
| directmemory
|
| directmemory
|
||||||
;
|
;
|
||||||
@ -184,7 +183,6 @@ expression :
|
|||||||
| left = expression EOL? bop = 'xor' EOL? right = expression
|
| left = expression EOL? bop = 'xor' EOL? right = expression
|
||||||
| prefix = 'not' expression
|
| prefix = 'not' expression
|
||||||
| literalvalue
|
| literalvalue
|
||||||
| register
|
|
||||||
| scoped_identifier
|
| scoped_identifier
|
||||||
| arrayindexed
|
| arrayindexed
|
||||||
| directmemory
|
| directmemory
|
||||||
@ -222,12 +220,6 @@ identifier : NAME ;
|
|||||||
|
|
||||||
scoped_identifier : NAME ('.' NAME)* ;
|
scoped_identifier : NAME ('.' NAME)* ;
|
||||||
|
|
||||||
register : 'A' | 'X' | 'Y' ;
|
|
||||||
|
|
||||||
registerorpair : 'A' | 'X' | 'Y' | 'AX' | 'AY' | 'XY' ; // pairs can only be used in subroutine params and returnvalues
|
|
||||||
|
|
||||||
statusregister : 'Pc' | 'Pz' | 'Pn' | 'Pv' ;
|
|
||||||
|
|
||||||
integerliteral : intpart=(DEC_INTEGER | HEX_INTEGER | BIN_INTEGER) wordsuffix? ;
|
integerliteral : intpart=(DEC_INTEGER | HEX_INTEGER | BIN_INTEGER) wordsuffix? ;
|
||||||
|
|
||||||
wordsuffix : '.w' ;
|
wordsuffix : '.w' ;
|
||||||
@ -236,8 +228,6 @@ booleanliteral : 'true' | 'false' ;
|
|||||||
|
|
||||||
arrayliteral : '[' EOL? expression (',' EOL? expression)* EOL? ']' ; // you can split the values over several lines
|
arrayliteral : '[' EOL? expression (',' EOL? expression)* EOL? ']' ; // you can split the values over several lines
|
||||||
|
|
||||||
structliteral : '{' EOL? expression (',' EOL? expression)* EOL? '}' ; // you can split the values over several lines
|
|
||||||
|
|
||||||
stringliteral : ALT_STRING_ENCODING? STRING ;
|
stringliteral : ALT_STRING_ENCODING? STRING ;
|
||||||
|
|
||||||
charliteral : ALT_STRING_ENCODING? SINGLECHAR ;
|
charliteral : ALT_STRING_ENCODING? SINGLECHAR ;
|
||||||
@ -252,7 +242,6 @@ literalvalue :
|
|||||||
| stringliteral
|
| stringliteral
|
||||||
| charliteral
|
| charliteral
|
||||||
| floatliteral
|
| floatliteral
|
||||||
| structliteral
|
|
||||||
;
|
;
|
||||||
|
|
||||||
inlineasm : '%asm' INLINEASMBLOCK;
|
inlineasm : '%asm' INLINEASMBLOCK;
|
||||||
@ -287,15 +276,15 @@ asmsub_decl : identifier '(' asmsub_params? ')' asmsub_clobbers? asmsub_returns?
|
|||||||
|
|
||||||
asmsub_params : asmsub_param (',' EOL? asmsub_param)* ;
|
asmsub_params : asmsub_param (',' EOL? asmsub_param)* ;
|
||||||
|
|
||||||
asmsub_param : vardecl '@' (registerorpair | statusregister | stack='stack') ;
|
asmsub_param : vardecl '@' (identifier | stack='stack') ; // A,X,Y,AX,AY,XY,Pc,Pz,Pn,Pv allowed
|
||||||
|
|
||||||
asmsub_clobbers : 'clobbers' '(' clobber? ')' ;
|
asmsub_clobbers : 'clobbers' '(' clobber? ')' ;
|
||||||
|
|
||||||
clobber : register (',' register)* ;
|
clobber : identifier (',' identifier)* ; // A,X,Y allowed
|
||||||
|
|
||||||
asmsub_returns : '->' asmsub_return (',' EOL? asmsub_return)* ;
|
asmsub_returns : '->' asmsub_return (',' EOL? asmsub_return)* ;
|
||||||
|
|
||||||
asmsub_return : datatype '@' (registerorpair | statusregister | stack='stack') ;
|
asmsub_return : datatype '@' (identifier | stack='stack') ; // A,X,Y,AX,AY,XY,Pc,Pz,Pn,Pv allowed
|
||||||
|
|
||||||
|
|
||||||
if_stmt : 'if' expression EOL? (statement | statement_block) EOL? else_part? ; // statement is constrained later
|
if_stmt : 'if' expression EOL? (statement | statement_block) EOL? else_part? ; // statement is constrained later
|
||||||
@ -308,13 +297,13 @@ branch_stmt : branchcondition EOL? (statement | statement_block) EOL? else_part?
|
|||||||
branchcondition: 'if_cs' | 'if_cc' | 'if_eq' | 'if_z' | 'if_ne' | 'if_nz' | 'if_pl' | 'if_pos' | 'if_mi' | 'if_neg' | 'if_vs' | 'if_vc' ;
|
branchcondition: 'if_cs' | 'if_cc' | 'if_eq' | 'if_z' | 'if_ne' | 'if_nz' | 'if_pl' | 'if_pos' | 'if_mi' | 'if_neg' | 'if_vs' | 'if_vc' ;
|
||||||
|
|
||||||
|
|
||||||
forloop : 'for' (register | identifier) 'in' expression EOL? (statement | statement_block) ;
|
forloop : 'for' identifier 'in' expression EOL? (statement | statement_block) ;
|
||||||
|
|
||||||
whileloop: 'while' expression EOL? (statement | statement_block) ;
|
whileloop: 'while' expression EOL? (statement | statement_block) ;
|
||||||
|
|
||||||
repeatloop: 'repeat' (statement | statement_block) EOL? 'until' expression ;
|
untilloop: 'do' (statement | statement_block) EOL? 'until' expression ;
|
||||||
|
|
||||||
foreverloop: 'forever' EOL? (statement | statement_block) ;
|
repeatloop: 'repeat' expression? EOL? (statement | statement_block) ;
|
||||||
|
|
||||||
whenstmt: 'when' expression '{' EOL (when_choice | EOL) * '}' EOL? ;
|
whenstmt: 'when' expression '{' EOL (when_choice | EOL) * '}' EOL? ;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user