mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 16:29:21 +00:00
for, while, repeat, if, branch bodies are now (anonymous) symbol scopes
This commit is contained in:
parent
7b650ffa18
commit
1d37841575
@ -29,9 +29,6 @@
|
||||
float[len(ycoor)] rotatedy
|
||||
float[len(zcoor)] rotatedz
|
||||
|
||||
; general index var
|
||||
byte i
|
||||
|
||||
sub start() {
|
||||
if irq.time_changed {
|
||||
irq.time_changed = 0
|
||||
@ -39,6 +36,7 @@
|
||||
_vm_gfx_text(8, 6, 1, "Spin")
|
||||
_vm_gfx_text(29, 11, 1, "to Win !")
|
||||
|
||||
byte i
|
||||
for i in 0 to width//10 {
|
||||
_vm_gfx_line(i*2+width//2-width//10, 130, i*10.w, 199, 6)
|
||||
}
|
||||
@ -71,6 +69,7 @@
|
||||
float Azy = cosb*sinc
|
||||
float Azz = cosb*cosc
|
||||
|
||||
byte i
|
||||
for i in 0 to len(xcoor)-1 {
|
||||
rotatedx[i] = Axx*xcoor[i] + Axy*ycoor[i] + Axz*zcoor[i]
|
||||
rotatedy[i] = Ayx*xcoor[i] + Ayy*ycoor[i] + Ayz*zcoor[i]
|
||||
@ -80,9 +79,6 @@
|
||||
|
||||
|
||||
sub draw_edges() {
|
||||
word edge
|
||||
byte e_from
|
||||
byte e_to
|
||||
|
||||
sub toscreenx(x: float, z: float) -> word {
|
||||
return floor(x/(4.2+z) * flt(height)) + width // 2
|
||||
@ -93,35 +89,34 @@
|
||||
}
|
||||
|
||||
; draw all edges of the object
|
||||
word edge
|
||||
for edge in edges {
|
||||
e_from = msb(edge)
|
||||
e_to = lsb(edge)
|
||||
byte e_from = msb(edge)
|
||||
byte e_to = lsb(edge)
|
||||
_vm_gfx_line(toscreenx(rotatedx[e_from], rotatedz[e_from]), toscreeny(rotatedy[e_from], rotatedz[e_from]),
|
||||
toscreenx(rotatedx[e_to], rotatedz[e_to]), toscreeny(rotatedy[e_to], rotatedz[e_to]), e_from+e_to)
|
||||
}
|
||||
|
||||
; accentuate the vertices a bit with small boxes
|
||||
word sx
|
||||
word sy
|
||||
byte col
|
||||
byte i
|
||||
for i in 0 to len(xcoor)-1 {
|
||||
sx = toscreenx(rotatedx[i], rotatedz[i])
|
||||
sy = toscreeny(rotatedy[i], rotatedz[i])
|
||||
col=i+2
|
||||
_vm_gfx_pixel(sx-1, sy-1, col)
|
||||
_vm_gfx_pixel(sx, sy-1, col)
|
||||
_vm_gfx_pixel(sx+1, sy-1, col)
|
||||
_vm_gfx_pixel(sx-1, sy, col)
|
||||
_vm_gfx_pixel(sx, sy, col)
|
||||
_vm_gfx_pixel(sx+1, sy, col)
|
||||
_vm_gfx_pixel(sx-1, sy+1, col)
|
||||
_vm_gfx_pixel(sx, sy+1, col)
|
||||
_vm_gfx_pixel(sx+1, sy+1, col)
|
||||
word sx = toscreenx(rotatedx[i], rotatedz[i])
|
||||
word sy = toscreeny(rotatedy[i], rotatedz[i])
|
||||
byte color=i+2
|
||||
_vm_gfx_pixel(sx-1, sy-1, color)
|
||||
_vm_gfx_pixel(sx, sy-1, color)
|
||||
_vm_gfx_pixel(sx+1, sy-1, color)
|
||||
_vm_gfx_pixel(sx-1, sy, color)
|
||||
_vm_gfx_pixel(sx, sy, color)
|
||||
_vm_gfx_pixel(sx+1, sy, color)
|
||||
_vm_gfx_pixel(sx-1, sy+1, color)
|
||||
_vm_gfx_pixel(sx, sy+1, color)
|
||||
_vm_gfx_pixel(sx+1, sy+1, color)
|
||||
|
||||
_vm_gfx_pixel(sx, sy-2, col)
|
||||
_vm_gfx_pixel(sx+2, sy, col)
|
||||
_vm_gfx_pixel(sx, sy+2, col)
|
||||
_vm_gfx_pixel(sx-2, sy, col)
|
||||
_vm_gfx_pixel(sx, sy-2, color)
|
||||
_vm_gfx_pixel(sx+2, sy, color)
|
||||
_vm_gfx_pixel(sx, sy+2, color)
|
||||
_vm_gfx_pixel(sx-2, sy, color)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,33 +1,27 @@
|
||||
%option enable_floats
|
||||
|
||||
~ main {
|
||||
const word width = 320 // 2
|
||||
const word height = 256 // 2
|
||||
const word xoffset = 40
|
||||
const word yoffset = 30
|
||||
|
||||
sub start() {
|
||||
|
||||
const word width = 320 // 2
|
||||
const word height = 256 // 2
|
||||
const word xoffset = 40
|
||||
const word yoffset = 30
|
||||
word pixelx
|
||||
byte pixely
|
||||
float xx
|
||||
float yy
|
||||
float x
|
||||
float y
|
||||
float xsquared
|
||||
float ysquared
|
||||
byte iter
|
||||
word plotx
|
||||
byte ploty
|
||||
|
||||
_vm_gfx_clearscr(11)
|
||||
_vm_gfx_text(2, 1, 1, "Calculating Mandelbrot Fractal...")
|
||||
|
||||
byte pixely ; @todo allow defining loopvar INSIDE loop scope ("for byte pixely in ...")
|
||||
for pixely in yoffset to yoffset+height-1 {
|
||||
yy = flt((pixely-yoffset))/height/3.6+0.4
|
||||
float yy = flt((pixely-yoffset))/height/3.6+0.4
|
||||
|
||||
word pixelx ; @todo allow defining loopvar INSIDE loop scope ("for word pixelx in ...")
|
||||
for pixelx in xoffset to xoffset+width-1 {
|
||||
xx = flt((pixelx-xoffset))/width/3.0+0.2
|
||||
float xx = flt((pixelx-xoffset))/width/3.0+0.2
|
||||
float xsquared
|
||||
float ysquared
|
||||
float x
|
||||
float y
|
||||
byte iter ; @todo re-initialize variable when entering scope
|
||||
|
||||
x = 0.0
|
||||
y = 0.0
|
||||
|
@ -1,11 +1,8 @@
|
||||
%output prg
|
||||
|
||||
~ main {
|
||||
sub start() {
|
||||
str name = " "
|
||||
str guess = "000000"
|
||||
byte guessednumber
|
||||
byte attempts_left
|
||||
byte secretnumber = rnd() % 100
|
||||
|
||||
_vm_write_str("Let's play a number guessing game!\n")
|
||||
_vm_write_str("Enter your name: ")
|
||||
@ -14,8 +11,7 @@
|
||||
_vm_write_str(name)
|
||||
_vm_write_str(".\nI am thinking of a number from 1 to 100! You'll have to guess it!\n")
|
||||
|
||||
byte secretnumber = rnd() % 100
|
||||
|
||||
byte attempts_left
|
||||
for attempts_left in 10 to 1 step -1 {
|
||||
_vm_write_str("\nYou have ")
|
||||
_vm_write_num(attempts_left)
|
||||
@ -23,7 +19,7 @@
|
||||
if attempts_left>1 _vm_write_str("es")
|
||||
_vm_write_str(" left. What is your next guess? ")
|
||||
_vm_input_str(guess)
|
||||
guessednumber = str2byte(guess)
|
||||
byte guessednumber = str2byte(guess)
|
||||
if guessednumber==secretnumber {
|
||||
_vm_write_str("\nYou guessed it, impressive!\n")
|
||||
_vm_write_str("Thanks for playing.\n")
|
||||
|
@ -9,14 +9,12 @@
|
||||
|
||||
_vm_gfx_clearscr(0)
|
||||
|
||||
float x
|
||||
float y
|
||||
float t
|
||||
byte color
|
||||
|
||||
while(1) {
|
||||
x = sin(t*1.01) + cos(t*1.1234)
|
||||
y = cos(t) + sin(t*0.03456)
|
||||
float x = sin(t*1.01) + cos(t*1.1234)
|
||||
float y = cos(t) + sin(t*0.03456)
|
||||
_vm_gfx_pixel(screenx(x), screeny(y), color//16)
|
||||
t += 0.01
|
||||
color++
|
||||
|
@ -5,19 +5,42 @@
|
||||
~ main {
|
||||
|
||||
sub start() {
|
||||
|
||||
const float c1 = 11.11
|
||||
const float c2 = 22.22
|
||||
float v
|
||||
float r
|
||||
byte x
|
||||
word w
|
||||
|
||||
r=flt(x)
|
||||
w=wrd(x)
|
||||
w=wrdhi(x)
|
||||
for x in 0 to 20 {
|
||||
float xx = sin(flt(x))
|
||||
float yy = cos(flt(x))
|
||||
}
|
||||
|
||||
while(1) {
|
||||
float xx = sin(flt(x))
|
||||
float yy = cos(flt(x))
|
||||
}
|
||||
|
||||
repeat {
|
||||
float xx = sin(flt(x))
|
||||
float yy = cos(flt(x))
|
||||
} until(1)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
; for x in 0 to 30 {
|
||||
; float xx = 2123.33
|
||||
; float yy = 2444.55
|
||||
; xx = sin(flt(x))
|
||||
; yy = cos(flt(x))
|
||||
; r = xx*yy
|
||||
; }
|
||||
;
|
||||
; for x in 0 to 40 {
|
||||
; float xx = 3123.33
|
||||
; float yy = 3444.55
|
||||
; xx = sin(flt(x))
|
||||
; yy = cos(flt(x))
|
||||
; r = xx*yy
|
||||
; }
|
||||
return
|
||||
|
||||
}
|
||||
|
@ -148,14 +148,14 @@ interface IAstProcessor {
|
||||
|
||||
fun process(ifStatement: IfStatement): IStatement {
|
||||
ifStatement.condition = ifStatement.condition.process(this)
|
||||
ifStatement.statements = ifStatement.statements.map { it.process(this) }
|
||||
ifStatement.elsepart = ifStatement.elsepart.map { it.process(this) }
|
||||
ifStatement.truepart = ifStatement.truepart.process(this)
|
||||
ifStatement.elsepart = ifStatement.elsepart.process(this)
|
||||
return ifStatement
|
||||
}
|
||||
|
||||
fun process(branchStatement: BranchStatement): IStatement {
|
||||
branchStatement.statements = branchStatement.statements.map { it.process(this) }
|
||||
branchStatement.elsepart = branchStatement.elsepart.map { it.process(this) }
|
||||
branchStatement.truepart = branchStatement.truepart.process(this)
|
||||
branchStatement.elsepart = branchStatement.elsepart.process(this)
|
||||
return branchStatement
|
||||
}
|
||||
|
||||
@ -195,19 +195,19 @@ interface IAstProcessor {
|
||||
fun process(forLoop: ForLoop): IStatement {
|
||||
forLoop.loopVar?.process(this)
|
||||
forLoop.iterable = forLoop.iterable.process(this)
|
||||
forLoop.body = forLoop.body.asSequence().map {it.process(this)}.toMutableList()
|
||||
forLoop.body = forLoop.body.process(this)
|
||||
return forLoop
|
||||
}
|
||||
|
||||
fun process(whileLoop: WhileLoop): IStatement {
|
||||
whileLoop.condition = whileLoop.condition.process(this)
|
||||
whileLoop.statements = whileLoop.statements.map { it.process(this) }
|
||||
whileLoop.body = whileLoop.body.process(this)
|
||||
return whileLoop
|
||||
}
|
||||
|
||||
fun process(repeatLoop: RepeatLoop): IStatement {
|
||||
repeatLoop.untilCondition = repeatLoop.untilCondition.process(this)
|
||||
repeatLoop.statements = repeatLoop.statements.map { it.process(this) }
|
||||
repeatLoop.body = repeatLoop.body.process(this)
|
||||
return repeatLoop
|
||||
}
|
||||
|
||||
@ -227,6 +227,11 @@ interface IAstProcessor {
|
||||
assignTarget.identifier?.process(this)
|
||||
return assignTarget
|
||||
}
|
||||
|
||||
fun process(scope: AnonymousScope): AnonymousScope {
|
||||
scope.statements = scope.statements.asSequence().map { it.process(this) }.toMutableList()
|
||||
return scope
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -288,10 +293,32 @@ interface INameScope {
|
||||
val name: String
|
||||
val position: Position
|
||||
var statements: MutableList<IStatement>
|
||||
val parent: Node
|
||||
|
||||
fun registerUsedName(name: String)
|
||||
fun linkParents(parent: Node)
|
||||
|
||||
fun subScopes() = statements.asSequence().filter { it is INameScope }.map { it as INameScope }.associate { it.name to it }
|
||||
fun subScopes(): Map<String, INameScope> {
|
||||
val subscopes = mutableMapOf<String, INameScope>()
|
||||
for(stmt in statements) {
|
||||
when(stmt) {
|
||||
is INameScope -> subscopes[stmt.name] = stmt
|
||||
is ForLoop -> subscopes[stmt.body.name] = stmt.body
|
||||
is RepeatLoop -> subscopes[stmt.body.name] = stmt.body
|
||||
is WhileLoop -> subscopes[stmt.body.name] = stmt.body
|
||||
is BranchStatement -> {
|
||||
subscopes[stmt.truepart.name] = stmt.truepart
|
||||
if(!stmt.elsepart.isEmpty())
|
||||
subscopes[stmt.elsepart.name] = stmt.elsepart
|
||||
}
|
||||
is IfStatement -> {
|
||||
subscopes[stmt.truepart.name] = stmt.truepart
|
||||
if(!stmt.elsepart.isEmpty())
|
||||
subscopes[stmt.elsepart.name] = stmt.elsepart
|
||||
}
|
||||
}
|
||||
}
|
||||
return subscopes
|
||||
}
|
||||
|
||||
fun labelsAndVariables() = statements.asSequence().filter { it is Label || it is VarDecl }
|
||||
.associate {((it as? Label)?.name ?: (it as? VarDecl)?.name)!! to it }
|
||||
@ -345,30 +372,8 @@ interface INameScope {
|
||||
val removed = statements.remove(statement)
|
||||
if(!removed) throw AstException("node to remove wasn't found")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inserted into the Ast in place of modified nodes (not inserted directly as a parser result)
|
||||
* It can hold zero or more replacement statements that have to be inserted at that point.
|
||||
*/
|
||||
class AnonymousStatementList(override var parent: Node,
|
||||
var statements: List<IStatement>,
|
||||
override val position: Position) : IStatement {
|
||||
|
||||
init {
|
||||
linkParents(parent)
|
||||
}
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
statements.forEach { it.linkParents(this) }
|
||||
}
|
||||
|
||||
override fun process(processor: IAstProcessor): IStatement {
|
||||
statements = statements.map { it.process(processor) }
|
||||
return this
|
||||
}
|
||||
fun isEmpty() = statements.isEmpty()
|
||||
}
|
||||
|
||||
|
||||
@ -382,7 +387,8 @@ object BuiltinFunctionScopePlaceholder : INameScope {
|
||||
override val name = "<<builtin-functions-scope-placeholder>>"
|
||||
override val position = Position("<<placeholder>>", 0, 0, 0)
|
||||
override var statements = mutableListOf<IStatement>()
|
||||
override fun registerUsedName(name: String) = throw NotImplementedError("not implemented on sub-scopes")
|
||||
override var parent: Node = ParentSentinel
|
||||
override fun linkParents(parent: Node) {}
|
||||
}
|
||||
|
||||
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : IStatement {
|
||||
@ -411,15 +417,14 @@ class Module(override val name: String,
|
||||
}
|
||||
|
||||
override fun definingScope(): INameScope = GlobalNamespace("<<<global>>>", statements, position)
|
||||
override fun registerUsedName(name: String) = throw NotImplementedError("not implemented on sub-scopes")
|
||||
}
|
||||
|
||||
|
||||
private class GlobalNamespace(override val name: String,
|
||||
override var statements: MutableList<IStatement>,
|
||||
override val position: Position) : INameScope {
|
||||
|
||||
private val scopedNamesUsed: MutableSet<String> = mutableSetOf("main", "main.start") // main and main.start are always used
|
||||
override var parent = ParentSentinel
|
||||
override fun linkParents(parent: Node) {}
|
||||
|
||||
override fun lookup(scopedName: List<String>, statement: Node): IStatement? {
|
||||
if(scopedName.last() in BuiltinFunctions) {
|
||||
@ -429,24 +434,11 @@ private class GlobalNamespace(override val name: String,
|
||||
return builtinPlaceholder
|
||||
}
|
||||
val stmt = super.lookup(scopedName, statement)
|
||||
if(stmt!=null) {
|
||||
val targetScopedName = when(stmt) {
|
||||
is Label -> stmt.scopedname
|
||||
is VarDecl -> stmt.scopedname
|
||||
is Block -> stmt.scopedname
|
||||
is Subroutine -> stmt.scopedname
|
||||
else -> throw NameError("wrong identifier target: $stmt", stmt.position)
|
||||
}
|
||||
registerUsedName(targetScopedName)
|
||||
return when (stmt) {
|
||||
is Label, is VarDecl, is Block, is Subroutine -> stmt
|
||||
null -> null
|
||||
else -> throw NameError("wrong identifier target: $stmt", stmt.position)
|
||||
}
|
||||
return stmt
|
||||
}
|
||||
|
||||
override fun registerUsedName(name: String) {
|
||||
// make sure to also register each scope separately
|
||||
scopedNamesUsed.add(name)
|
||||
if('.' in name)
|
||||
registerUsedName(name.substringBeforeLast('.'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -469,8 +461,6 @@ class Block(override val name: String,
|
||||
override fun toString(): String {
|
||||
return "Block(name=$name, address=$address, ${statements.size} statements)"
|
||||
}
|
||||
|
||||
override fun registerUsedName(name: String) = throw NotImplementedError("not implemented on sub-scopes")
|
||||
}
|
||||
|
||||
|
||||
@ -669,7 +659,7 @@ data class AssignTarget(val register: Register?,
|
||||
|
||||
fun process(processor: IAstProcessor) = processor.process(this)
|
||||
|
||||
fun determineDatatype(namespace: INameScope, heap: HeapValues, stmt: IStatement): DataType {
|
||||
fun determineDatatype(namespace: INameScope, heap: HeapValues, stmt: IStatement): DataType? {
|
||||
if(register!=null)
|
||||
return when(register){
|
||||
Register.A, Register.X, Register.Y -> DataType.BYTE
|
||||
@ -677,8 +667,7 @@ data class AssignTarget(val register: Register?,
|
||||
}
|
||||
|
||||
if(identifier!=null) {
|
||||
val symbol = namespace.lookup(identifier.nameInSource, stmt)
|
||||
?: throw FatalAstException("symbol lookup failed: ${identifier.nameInSource}")
|
||||
val symbol = namespace.lookup(identifier.nameInSource, stmt) ?: return null
|
||||
if (symbol is VarDecl) return symbol.datatype
|
||||
}
|
||||
|
||||
@ -1291,6 +1280,27 @@ class InlineAssembly(val assembly: String, override val position: Position) : IS
|
||||
|
||||
class RegisterOrStatusflag(val register: Register?, val statusflag: Statusflag?)
|
||||
|
||||
class AnonymousScope(override var statements: MutableList<IStatement>,
|
||||
override val position: Position) : INameScope, IStatement {
|
||||
override val name: String
|
||||
override lateinit var parent: Node
|
||||
|
||||
init {
|
||||
name = "<<<anonymous-$sequenceNumber>>>"
|
||||
sequenceNumber++
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var sequenceNumber = 1
|
||||
}
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
statements.forEach { it.linkParents(this) }
|
||||
}
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
}
|
||||
|
||||
class Subroutine(override val name: String,
|
||||
val parameters: List<SubroutineParameter>,
|
||||
val returnvalues: List<DataType>,
|
||||
@ -1314,8 +1324,6 @@ class Subroutine(override val name: String,
|
||||
override fun toString(): String {
|
||||
return "Subroutine(name=$name, parameters=$parameters, returnvalues=$returnvalues, ${statements.size} statements, address=$asmAddress)"
|
||||
}
|
||||
|
||||
override fun registerUsedName(name: String) = throw NotImplementedError("not implemented on sub-scopes")
|
||||
}
|
||||
|
||||
|
||||
@ -1330,16 +1338,16 @@ open class SubroutineParameter(val name: String,
|
||||
}
|
||||
|
||||
class IfStatement(var condition: IExpression,
|
||||
var statements: List<IStatement>,
|
||||
var elsepart: List<IStatement>,
|
||||
var truepart: AnonymousScope,
|
||||
var elsepart: AnonymousScope,
|
||||
override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
condition.linkParents(this)
|
||||
statements.forEach { it.linkParents(this) }
|
||||
elsepart.forEach { it.linkParents(this) }
|
||||
truepart.linkParents(this)
|
||||
elsepart.linkParents(this)
|
||||
}
|
||||
|
||||
override fun process(processor: IAstProcessor): IStatement = processor.process(this)
|
||||
@ -1347,29 +1355,25 @@ class IfStatement(var condition: IExpression,
|
||||
|
||||
|
||||
class BranchStatement(var condition: BranchCondition,
|
||||
var statements: List<IStatement>,
|
||||
var elsepart: List<IStatement>,
|
||||
var truepart: AnonymousScope,
|
||||
var elsepart: AnonymousScope,
|
||||
override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
statements.forEach { it.linkParents(this) }
|
||||
elsepart.forEach { it.linkParents(this) }
|
||||
truepart.linkParents(this)
|
||||
elsepart.linkParents(this)
|
||||
}
|
||||
|
||||
override fun process(processor: IAstProcessor): IStatement = processor.process(this)
|
||||
|
||||
override fun toString(): String {
|
||||
return "Branch(cond: $condition, ${statements.size} stmts, ${elsepart.size} else-stmts, pos=$position)"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ForLoop(val loopRegister: Register?,
|
||||
val loopVar: IdentifierReference?,
|
||||
var iterable: IExpression,
|
||||
var body: MutableList<IStatement>,
|
||||
var body: AnonymousScope,
|
||||
override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
|
||||
@ -1377,7 +1381,7 @@ class ForLoop(val loopRegister: Register?,
|
||||
this.parent=parent
|
||||
loopVar?.linkParents(this)
|
||||
iterable.linkParents(this)
|
||||
body.forEach { it.linkParents(this) }
|
||||
body.linkParents(this)
|
||||
}
|
||||
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
@ -1389,21 +1393,21 @@ class ForLoop(val loopRegister: Register?,
|
||||
|
||||
|
||||
class WhileLoop(var condition: IExpression,
|
||||
var statements: List<IStatement>,
|
||||
var body: AnonymousScope,
|
||||
override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
condition.linkParents(this)
|
||||
statements.forEach { it.linkParents(this) }
|
||||
body.linkParents(this)
|
||||
}
|
||||
|
||||
override fun process(processor: IAstProcessor): IStatement = processor.process(this)
|
||||
}
|
||||
|
||||
|
||||
class RepeatLoop(var statements: List<IStatement>,
|
||||
class RepeatLoop(var body: AnonymousScope,
|
||||
var untilCondition: IExpression,
|
||||
override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
@ -1411,7 +1415,7 @@ class RepeatLoop(var statements: List<IStatement>,
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
untilCondition.linkParents(this)
|
||||
statements.forEach { it.linkParents(this) }
|
||||
body.linkParents(this)
|
||||
}
|
||||
|
||||
override fun process(processor: IAstProcessor): IStatement = processor.process(this)
|
||||
@ -1826,21 +1830,25 @@ private fun prog8Parser.ArrayliteralContext.toAst() : Array<IExpression> =
|
||||
|
||||
private fun prog8Parser.If_stmtContext.toAst(): IfStatement {
|
||||
val condition = expression().toAst()
|
||||
val statements = statement_block()?.toAst() ?: listOf(statement().toAst())
|
||||
val elsepart = else_part()?.toAst() ?: emptyList()
|
||||
return IfStatement(condition, statements, elsepart, toPosition())
|
||||
val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
val elseStatements = else_part()?.toAst() ?: mutableListOf()
|
||||
val trueScope = AnonymousScope(trueStatements, statement_block()?.toPosition() ?: statement().toPosition())
|
||||
val elseScope = AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition())
|
||||
return IfStatement(condition, trueScope, elseScope, toPosition())
|
||||
}
|
||||
|
||||
private fun prog8Parser.Else_partContext.toAst(): List<IStatement> {
|
||||
return statement_block()?.toAst() ?: listOf(statement().toAst())
|
||||
private fun prog8Parser.Else_partContext.toAst(): MutableList<IStatement> {
|
||||
return statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.Branch_stmtContext.toAst(): BranchStatement {
|
||||
val branchcondition = branchcondition().toAst()
|
||||
val statements = statement_block()?.toAst() ?: listOf(statement().toAst())
|
||||
val elsepart = else_part()?.toAst() ?: emptyList()
|
||||
return BranchStatement(branchcondition, statements, elsepart, toPosition())
|
||||
val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
val elseStatements = else_part()?.toAst() ?: mutableListOf()
|
||||
val trueScope = AnonymousScope(trueStatements, statement_block()?.toPosition() ?: statement().toPosition())
|
||||
val elseScope = AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition())
|
||||
return BranchStatement(branchcondition, trueScope, elseScope, toPosition())
|
||||
}
|
||||
|
||||
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase())
|
||||
@ -1850,8 +1858,8 @@ private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
||||
val loopregister = register()?.toAst()
|
||||
val loopvar = identifier()?.toAst()
|
||||
val iterable = expression()!!.toAst()
|
||||
val body = statement_block().toAst()
|
||||
return ForLoop(loopregister, loopvar, iterable, body, toPosition())
|
||||
val scope = AnonymousScope(statement_block().toAst(), statement_block().toPosition())
|
||||
return ForLoop(loopregister, loopvar, iterable, scope, toPosition())
|
||||
}
|
||||
|
||||
|
||||
@ -1862,13 +1870,15 @@ private fun prog8Parser.BreakstmtContext.toAst() = Break(toPosition())
|
||||
|
||||
private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {
|
||||
val condition = expression().toAst()
|
||||
val statements = statement_block()?.toAst() ?: listOf(statement().toAst())
|
||||
return WhileLoop(condition, statements, toPosition())
|
||||
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
val scope = AnonymousScope(statements, statement_block()?.toPosition() ?: statement().toPosition())
|
||||
return WhileLoop(condition, scope, toPosition())
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop {
|
||||
val untilCondition = expression().toAst()
|
||||
val statements = statement_block()?.toAst() ?: listOf(statement().toAst())
|
||||
return RepeatLoop(statements, untilCondition, toPosition())
|
||||
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
val scope = AnonymousScope(statements, statement_block()?.toPosition() ?: statement().toPosition())
|
||||
return RepeatLoop(scope, untilCondition, toPosition())
|
||||
}
|
||||
|
@ -275,19 +275,21 @@ class AstChecker(private val namespace: INameScope,
|
||||
}
|
||||
|
||||
val targetDatatype = assignment.target.determineDatatype(namespace, heap, assignment)
|
||||
val constVal = assignment.value.constValue(namespace, heap)
|
||||
if(constVal!=null) {
|
||||
checkValueTypeAndRange(targetDatatype, null, constVal, heap)
|
||||
} else {
|
||||
val sourceDatatype: DataType? = assignment.value.resultingDatatype(namespace, heap)
|
||||
if(sourceDatatype==null) {
|
||||
if(assignment.value is FunctionCall)
|
||||
checkResult.add(ExpressionError("function call doesn't return a value to use in assignment", assignment.value.position))
|
||||
else
|
||||
checkResult.add(ExpressionError("assignment value is invalid or has no proper datatype", assignment.value.position))
|
||||
}
|
||||
else {
|
||||
checkAssignmentCompatible(targetDatatype, sourceDatatype, assignment.value, assignment.position)
|
||||
if(targetDatatype!=null) {
|
||||
val constVal = assignment.value.constValue(namespace, heap)
|
||||
if(constVal!=null) {
|
||||
checkValueTypeAndRange(targetDatatype, null, constVal, heap)
|
||||
} else {
|
||||
val sourceDatatype: DataType? = assignment.value.resultingDatatype(namespace, heap)
|
||||
if(sourceDatatype==null) {
|
||||
if(assignment.value is FunctionCall)
|
||||
checkResult.add(ExpressionError("function call doesn't return a value to use in assignment", assignment.value.position))
|
||||
else
|
||||
checkResult.add(ExpressionError("assignment value is invalid or has no proper datatype", assignment.value.position))
|
||||
}
|
||||
else {
|
||||
checkAssignmentCompatible(targetDatatype, sourceDatatype, assignment.value, assignment.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,11 +312,6 @@ class AstChecker(private val namespace: INameScope,
|
||||
err("recursive var declaration")
|
||||
}
|
||||
|
||||
// for now, variables can only be declared in a block or subroutine (not in a loop statement block) @todo fix this (anonymous namescope)
|
||||
if(decl.parent !is Block && decl.parent !is Subroutine) {
|
||||
err ("variables must be declared at block or subroutine level")
|
||||
}
|
||||
|
||||
when(decl.type) {
|
||||
VarDeclType.VAR, VarDeclType.CONST -> {
|
||||
if (decl.value == null) {
|
||||
|
@ -387,7 +387,6 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||
for (stmt: IStatement in statements) {
|
||||
stmtUniqueSequenceNr++
|
||||
when (stmt) {
|
||||
is AnonymousStatementList -> translate(stmt.statements)
|
||||
is Label -> translate(stmt)
|
||||
is Return -> translate(stmt)
|
||||
is Assignment -> translate(stmt) // normal and augmented assignments
|
||||
@ -401,6 +400,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||
is ForLoop -> translate(stmt)
|
||||
is WhileLoop -> translate(stmt)
|
||||
is RepeatLoop -> translate(stmt)
|
||||
is AnonymousScope -> translate(stmt)
|
||||
is Directive, is VarDecl, is Subroutine -> {} // skip this, already processed these.
|
||||
is InlineAssembly -> throw CompilerException("inline assembly is not supported by the StackVM")
|
||||
else -> TODO("translate statement $stmt to stackvm")
|
||||
@ -574,11 +574,11 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||
}
|
||||
if(branch.elsepart.isEmpty()) {
|
||||
stackvmProg.instr(opcode, callLabel = labelEnd)
|
||||
translate(branch.statements)
|
||||
translate(branch.truepart)
|
||||
stackvmProg.label(labelEnd)
|
||||
} else {
|
||||
stackvmProg.instr(opcode, callLabel = labelElse)
|
||||
translate(branch.statements)
|
||||
translate(branch.truepart)
|
||||
stackvmProg.instr(Opcode.JUMP, callLabel = labelEnd)
|
||||
stackvmProg.label(labelElse)
|
||||
translate(branch.elsepart)
|
||||
@ -616,12 +616,12 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||
val labelEnd = makeLabel("end")
|
||||
if(stmt.elsepart.isEmpty()) {
|
||||
stackvmProg.instr(Opcode.BZ, callLabel = labelEnd)
|
||||
translate(stmt.statements)
|
||||
translate(stmt.truepart)
|
||||
stackvmProg.label(labelEnd)
|
||||
} else {
|
||||
val labelElse = makeLabel("else")
|
||||
stackvmProg.instr(Opcode.BZ, callLabel = labelElse)
|
||||
translate(stmt.statements)
|
||||
translate(stmt.truepart)
|
||||
stackvmProg.instr(Opcode.JUMP, callLabel = labelEnd)
|
||||
stackvmProg.label(labelElse)
|
||||
translate(stmt.elsepart)
|
||||
@ -1209,7 +1209,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||
val target = stmt.target.identifier!!.targetStatement(namespace)!!
|
||||
when(target) {
|
||||
is VarDecl -> {
|
||||
val opcode = opcodePushvar(stmt.target.determineDatatype(namespace, heap, stmt))
|
||||
val opcode = opcodePushvar(stmt.target.determineDatatype(namespace, heap, stmt)!!)
|
||||
stackvmProg.instr(opcode, callLabel = target.scopedname)
|
||||
}
|
||||
else -> throw CompilerException("invalid assignment target type ${target::class}")
|
||||
@ -1231,7 +1231,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||
val target = stmt.target.identifier!!.targetStatement(namespace)!!
|
||||
when(target) {
|
||||
is VarDecl -> {
|
||||
val opcode = opcodePopvar(stmt.target.determineDatatype(namespace, heap, stmt))
|
||||
val opcode = opcodePopvar(stmt.target.determineDatatype(namespace, heap, stmt)!!)
|
||||
stackvmProg.instr(opcode, callLabel = target.scopedname)
|
||||
}
|
||||
else -> throw CompilerException("invalid assignment target type ${target::class}")
|
||||
@ -1383,10 +1383,10 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||
} else {
|
||||
// loop over a range where one or more of the start, last or step values is not a constant
|
||||
if(loop.loopRegister!=null) {
|
||||
translateForOverVariableRange(null, loop.loopRegister, loopVarDt, loop.iterable as RangeExpr, loop.body)
|
||||
translateForOverVariableRange(null, loop.loopRegister, loop.iterable as RangeExpr, loop.body)
|
||||
}
|
||||
else {
|
||||
translateForOverVariableRange(loop.loopVar!!.nameInSource, null, loopVarDt, loop.iterable as RangeExpr, loop.body)
|
||||
translateForOverVariableRange(loop.loopVar!!.nameInSource, null, loop.iterable as RangeExpr, loop.body)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -1496,7 +1496,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||
continueStmtLabelStack.pop()
|
||||
}
|
||||
|
||||
private fun translateForOverConstantRange(varname: String, varDt: DataType, range: IntProgression, body: MutableList<IStatement>) {
|
||||
private fun translateForOverConstantRange(varname: String, varDt: DataType, range: IntProgression, body: AnonymousScope) {
|
||||
/**
|
||||
* for LV in start..last { body }
|
||||
* (and we already know that the range is not empty, and first and last are exactly inclusive.)
|
||||
@ -1559,7 +1559,8 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||
continueStmtLabelStack.pop()
|
||||
}
|
||||
|
||||
private fun translateForOverVariableRange(varname: List<String>?, register: Register?, varDt: DataType, range: RangeExpr, body: MutableList<IStatement>) {
|
||||
private fun translateForOverVariableRange(varname: List<String>?, register: Register?,
|
||||
range: RangeExpr, body: AnonymousScope) {
|
||||
/*
|
||||
* for LV in start..last { body }
|
||||
* (where at least one of the start, last, step values is not a constant)
|
||||
@ -1628,8 +1629,8 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||
BinaryExpression(loopVar,"<", range.to, range.position)
|
||||
}
|
||||
val ifstmt = IfStatement(condition,
|
||||
listOf(Jump(null, null, breakLabel, range.position)),
|
||||
emptyList(),
|
||||
AnonymousScope(mutableListOf(Jump(null, null, breakLabel, range.position)), range.position),
|
||||
AnonymousScope(mutableListOf(), range.position),
|
||||
range.position)
|
||||
ifstmt.linkParents(range.parent)
|
||||
translate(ifstmt)
|
||||
@ -1665,8 +1666,9 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||
}
|
||||
val branch = BranchStatement(
|
||||
BranchCondition.NZ,
|
||||
listOf(Jump(null, null, loopLabel, range.position)),
|
||||
emptyList(), range.position)
|
||||
AnonymousScope(mutableListOf(Jump(null, null, loopLabel, range.position)), range.position),
|
||||
AnonymousScope(mutableListOf(), range.position),
|
||||
range.position)
|
||||
branch.linkParents(range.parent)
|
||||
translate(branch)
|
||||
}
|
||||
@ -1690,6 +1692,8 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||
continueStmtLabelStack.pop()
|
||||
}
|
||||
|
||||
private fun translate(scope: AnonymousScope) = translate(scope.statements)
|
||||
|
||||
private fun translate(stmt: WhileLoop)
|
||||
{
|
||||
/*
|
||||
@ -1714,7 +1718,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||
continueStmtLabelStack.push(continueLabel)
|
||||
stackvmProg.instr(Opcode.JUMP, callLabel = continueLabel)
|
||||
stackvmProg.label(loopLabel)
|
||||
translate(stmt.statements)
|
||||
translate(stmt.body)
|
||||
stackvmProg.label(continueLabel)
|
||||
translate(stmt.condition)
|
||||
stackvmProg.instr(Opcode.BNZ, callLabel = loopLabel)
|
||||
@ -1747,7 +1751,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||
breakStmtLabelStack.push(breakLabel)
|
||||
continueStmtLabelStack.push(continueLabel)
|
||||
stackvmProg.label(loopLabel)
|
||||
translate(stmt.statements)
|
||||
translate(stmt.body)
|
||||
stackvmProg.label(continueLabel)
|
||||
translate(stmt.untilCondition)
|
||||
stackvmProg.instr(Opcode.BZ, callLabel = loopLabel)
|
||||
|
@ -50,11 +50,11 @@ class StatementOptimizer(private val globalNamespace: INameScope, private val he
|
||||
return if(constvalue.asBooleanValue){
|
||||
// always true -> keep only if-part
|
||||
printWarning("condition is always true", ifStatement.position)
|
||||
AnonymousStatementList(ifStatement.parent, ifStatement.statements, ifStatement.position)
|
||||
ifStatement.truepart
|
||||
} else {
|
||||
// always false -> keep only else-part
|
||||
printWarning("condition is always false", ifStatement.position)
|
||||
AnonymousStatementList(ifStatement.parent, ifStatement.elsepart, ifStatement.position)
|
||||
ifStatement.elsepart
|
||||
}
|
||||
}
|
||||
return ifStatement
|
||||
@ -68,8 +68,8 @@ class StatementOptimizer(private val globalNamespace: INameScope, private val he
|
||||
// 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, forLoop.position), null, range.from, forLoop.position)
|
||||
forLoop.body.add(0, assignment)
|
||||
return AnonymousStatementList(forLoop.parent, forLoop.body, forLoop.position)
|
||||
forLoop.body.statements.add(0, assignment)
|
||||
return forLoop.body
|
||||
}
|
||||
}
|
||||
return forLoop
|
||||
@ -86,7 +86,7 @@ class StatementOptimizer(private val globalNamespace: INameScope, private val he
|
||||
} else {
|
||||
// always false -> ditch whole statement
|
||||
printWarning("condition is always false", whileLoop.position)
|
||||
AnonymousStatementList(whileLoop.parent, emptyList(), whileLoop.position)
|
||||
AnonymousScope(mutableListOf(), whileLoop.position)
|
||||
}
|
||||
}
|
||||
return whileLoop
|
||||
@ -99,7 +99,7 @@ class StatementOptimizer(private val globalNamespace: INameScope, private val he
|
||||
return if(constvalue.asBooleanValue){
|
||||
// always true -> keep only the statement block
|
||||
printWarning("condition is always true", repeatLoop.position)
|
||||
AnonymousStatementList(repeatLoop.parent, repeatLoop.statements, repeatLoop.position)
|
||||
repeatLoop.body
|
||||
} else {
|
||||
// always false
|
||||
printWarning("condition is always false", repeatLoop.position)
|
||||
|
@ -90,7 +90,7 @@ Scope
|
||||
Blocks, Scopes, and accessing Symbols
|
||||
-------------------------------------
|
||||
|
||||
Blocks are the separate pieces of code and data of your program. They are combined
|
||||
**Blocks** are the top level separate pieces of code and data of your program. They are combined
|
||||
into a single output program. No code or data can occur outside a block. Here's an example::
|
||||
|
||||
~ main $c000 {
|
||||
@ -111,6 +111,18 @@ Usually it is omitted, and the compiler will automatically choose the location (
|
||||
the previous block in memory).
|
||||
The address must be >= ``$0200`` (because ``$00``--``$ff`` is the ZP and ``$100``--``$200`` is the cpu stack).
|
||||
|
||||
**The special "ZP" ZeroPage block**
|
||||
|
||||
Blocks named "ZP" are treated a bit differently: they refer to the ZeroPage.
|
||||
The contents of every block with that name (this one may occur multiple times) are merged into one.
|
||||
Its start address is always set to ``$04``, because ``$00 - $01`` are used by the hardware
|
||||
and ``$02 - $03`` are reserved as general purpose scratch registers.
|
||||
|
||||
|
||||
.. _scopes:
|
||||
|
||||
**Scopes**
|
||||
|
||||
.. sidebar::
|
||||
Scoped access to symbols / "dotted names"
|
||||
|
||||
@ -118,21 +130,16 @@ The address must be >= ``$0200`` (because ``$00``--``$ff`` is the ZP and ``$100`
|
||||
So, accessing a variable ``counter`` defined in subroutine ``worker`` in block ``main``,
|
||||
can be done from anywhere by using ``main.worker.counter``.
|
||||
|
||||
A block is also a *scope* in your program so the symbols in the block don't clash with
|
||||
symbols of the same name defined elsewhere in the same file or in another file.
|
||||
You can refer to the symbols in a particular block by using a *dotted name*: ``blockname.symbolname``.
|
||||
Labels inside a subroutine are appended again to that; ``blockname.subroutinename.label``.
|
||||
A symbol name that's not a dotted name is searched for in the current scope, if it's not found there,
|
||||
one scope higher, and so on until it is found.
|
||||
*Symbols* are names defined in a certain *scope*. Inside the same scope, you can refer
|
||||
to them by their 'short' name directly. If the symbol is not found in the same scope,
|
||||
the enclosing scope is searched for it, and so on, until the symbol is found.
|
||||
|
||||
Scopes are created using several statements:
|
||||
|
||||
|
||||
**The special "ZP" ZeroPage block**
|
||||
|
||||
Blocks named "ZP" are treated a bit differently: they refer to the ZeroPage.
|
||||
The contents of every block with that name (this one may occur multiple times) are merged into one.
|
||||
Its start address is always set to ``$04``, because ``$00 - $01`` are used by the hardware
|
||||
and ``$02 - $03`` are reserved as general purpose scratch registers.
|
||||
- blocks (top-level named scope)
|
||||
- subroutines (nested named scopes)
|
||||
- for, while, repeat loops (anonymous scope)
|
||||
- if statements and branching conditionals (anonymous scope)
|
||||
|
||||
|
||||
Program Start and Entry Point
|
||||
@ -172,6 +179,7 @@ Variables and values
|
||||
--------------------
|
||||
|
||||
Variables are named values that can change during the execution of the program.
|
||||
They can be defined inside any scope (blocks, subroutines, for loops, etc.) See :ref:`Scopes <scopes>`.
|
||||
When declaring a numeric variable it is possible to specify the initial value, if you don't want it to be zero.
|
||||
For other data types it is required to specify that initial value it should get.
|
||||
Values will usually be part of an expression or assignment statement::
|
||||
|
@ -236,8 +236,7 @@ type identifier type storage size example var declara
|
||||
``byte[x]`` unsigned byte array x bytes ``byte[4] myvar = [1, 2, 3, 4]``
|
||||
``word[x]`` unsigned word array 2*x bytes ``word[4] myvar = [1, 2, 3, 4]``
|
||||
``float[x]`` floating-point array 5*x bytes ``float[4] myvar = [1.1, 2.2, 3.3, 4.4]``
|
||||
``byte[x,y]`` unsigned byte matrix x*y bytes ``byte[40,25] myvar = @todo``
|
||||
word-matrix not supported
|
||||
``byte[x,y]`` unsigned byte matrix x*y bytes ``byte[40,25] myvar = 255``
|
||||
``str`` string (petscii) varies ``str myvar = "hello."``
|
||||
implicitly terminated by a 0-byte
|
||||
``str_p`` pascal-string (petscii) varies ``str_p myvar = "hello."``
|
||||
|
Loading…
Reference in New Issue
Block a user