for, while, repeat, if, branch bodies are now (anonymous) symbol scopes

This commit is contained in:
Irmen de Jong 2018-10-06 17:21:34 +02:00
parent 7b650ffa18
commit 1d37841575
11 changed files with 239 additions and 215 deletions

View File

@ -29,9 +29,6 @@
float[len(ycoor)] rotatedy float[len(ycoor)] rotatedy
float[len(zcoor)] rotatedz float[len(zcoor)] rotatedz
; general index var
byte i
sub start() { sub start() {
if irq.time_changed { if irq.time_changed {
irq.time_changed = 0 irq.time_changed = 0
@ -39,6 +36,7 @@
_vm_gfx_text(8, 6, 1, "Spin") _vm_gfx_text(8, 6, 1, "Spin")
_vm_gfx_text(29, 11, 1, "to Win !") _vm_gfx_text(29, 11, 1, "to Win !")
byte i
for i in 0 to width//10 { for i in 0 to width//10 {
_vm_gfx_line(i*2+width//2-width//10, 130, i*10.w, 199, 6) _vm_gfx_line(i*2+width//2-width//10, 130, i*10.w, 199, 6)
} }
@ -71,6 +69,7 @@
float Azy = cosb*sinc float Azy = cosb*sinc
float Azz = cosb*cosc float Azz = cosb*cosc
byte i
for i in 0 to len(xcoor)-1 { for i in 0 to len(xcoor)-1 {
rotatedx[i] = Axx*xcoor[i] + Axy*ycoor[i] + Axz*zcoor[i] rotatedx[i] = Axx*xcoor[i] + Axy*ycoor[i] + Axz*zcoor[i]
rotatedy[i] = Ayx*xcoor[i] + Ayy*ycoor[i] + Ayz*zcoor[i] rotatedy[i] = Ayx*xcoor[i] + Ayy*ycoor[i] + Ayz*zcoor[i]
@ -80,9 +79,6 @@
sub draw_edges() { sub draw_edges() {
word edge
byte e_from
byte e_to
sub toscreenx(x: float, z: float) -> word { sub toscreenx(x: float, z: float) -> word {
return floor(x/(4.2+z) * flt(height)) + width // 2 return floor(x/(4.2+z) * flt(height)) + width // 2
@ -93,35 +89,34 @@
} }
; draw all edges of the object ; draw all edges of the object
word edge
for edge in edges { for edge in edges {
e_from = msb(edge) byte e_from = msb(edge)
e_to = lsb(edge) byte e_to = lsb(edge)
_vm_gfx_line(toscreenx(rotatedx[e_from], rotatedz[e_from]), toscreeny(rotatedy[e_from], rotatedz[e_from]), _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) 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 ; accentuate the vertices a bit with small boxes
word sx byte i
word sy
byte col
for i in 0 to len(xcoor)-1 { for i in 0 to len(xcoor)-1 {
sx = toscreenx(rotatedx[i], rotatedz[i]) word sx = toscreenx(rotatedx[i], rotatedz[i])
sy = toscreeny(rotatedy[i], rotatedz[i]) word sy = toscreeny(rotatedy[i], rotatedz[i])
col=i+2 byte color=i+2
_vm_gfx_pixel(sx-1, sy-1, col) _vm_gfx_pixel(sx-1, sy-1, color)
_vm_gfx_pixel(sx, sy-1, col) _vm_gfx_pixel(sx, sy-1, color)
_vm_gfx_pixel(sx+1, sy-1, col) _vm_gfx_pixel(sx+1, sy-1, color)
_vm_gfx_pixel(sx-1, sy, col) _vm_gfx_pixel(sx-1, sy, color)
_vm_gfx_pixel(sx, sy, col) _vm_gfx_pixel(sx, sy, color)
_vm_gfx_pixel(sx+1, sy, col) _vm_gfx_pixel(sx+1, sy, color)
_vm_gfx_pixel(sx-1, sy+1, col) _vm_gfx_pixel(sx-1, sy+1, color)
_vm_gfx_pixel(sx, sy+1, col) _vm_gfx_pixel(sx, sy+1, color)
_vm_gfx_pixel(sx+1, sy+1, col) _vm_gfx_pixel(sx+1, sy+1, color)
_vm_gfx_pixel(sx, sy-2, col) _vm_gfx_pixel(sx, sy-2, color)
_vm_gfx_pixel(sx+2, sy, col) _vm_gfx_pixel(sx+2, sy, color)
_vm_gfx_pixel(sx, sy+2, col) _vm_gfx_pixel(sx, sy+2, color)
_vm_gfx_pixel(sx-2, sy, col) _vm_gfx_pixel(sx-2, sy, color)
} }
} }
} }

View File

@ -1,33 +1,27 @@
%option enable_floats %option enable_floats
~ main { ~ main {
const word width = 320 // 2
const word height = 256 // 2
const word xoffset = 40
const word yoffset = 30
sub start() { 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_clearscr(11)
_vm_gfx_text(2, 1, 1, "Calculating Mandelbrot Fractal...") _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 { 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 { 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 x = 0.0
y = 0.0 y = 0.0

View File

@ -1,11 +1,8 @@
%output prg
~ main { ~ main {
sub start() { sub start() {
str name = " " str name = " "
str guess = "000000" str guess = "000000"
byte guessednumber byte secretnumber = rnd() % 100
byte attempts_left
_vm_write_str("Let's play a number guessing game!\n") _vm_write_str("Let's play a number guessing game!\n")
_vm_write_str("Enter your name: ") _vm_write_str("Enter your name: ")
@ -14,8 +11,7 @@
_vm_write_str(name) _vm_write_str(name)
_vm_write_str(".\nI am thinking of a number from 1 to 100! You'll have to guess it!\n") _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 { for attempts_left in 10 to 1 step -1 {
_vm_write_str("\nYou have ") _vm_write_str("\nYou have ")
_vm_write_num(attempts_left) _vm_write_num(attempts_left)
@ -23,7 +19,7 @@
if attempts_left>1 _vm_write_str("es") if attempts_left>1 _vm_write_str("es")
_vm_write_str(" left. What is your next guess? ") _vm_write_str(" left. What is your next guess? ")
_vm_input_str(guess) _vm_input_str(guess)
guessednumber = str2byte(guess) byte guessednumber = str2byte(guess)
if guessednumber==secretnumber { if guessednumber==secretnumber {
_vm_write_str("\nYou guessed it, impressive!\n") _vm_write_str("\nYou guessed it, impressive!\n")
_vm_write_str("Thanks for playing.\n") _vm_write_str("Thanks for playing.\n")

View File

@ -9,14 +9,12 @@
_vm_gfx_clearscr(0) _vm_gfx_clearscr(0)
float x
float y
float t float t
byte color byte color
while(1) { while(1) {
x = sin(t*1.01) + cos(t*1.1234) float x = sin(t*1.01) + cos(t*1.1234)
y = cos(t) + sin(t*0.03456) float y = cos(t) + sin(t*0.03456)
_vm_gfx_pixel(screenx(x), screeny(y), color//16) _vm_gfx_pixel(screenx(x), screeny(y), color//16)
t += 0.01 t += 0.01
color++ color++

View File

@ -5,19 +5,42 @@
~ main { ~ main {
sub start() { sub start() {
const float c1 = 11.11
const float c2 = 22.22
float v
float r
byte x byte x
word w
r=flt(x) for x in 0 to 20 {
w=wrd(x) float xx = sin(flt(x))
w=wrdhi(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 return
} }

View File

@ -148,14 +148,14 @@ interface IAstProcessor {
fun process(ifStatement: IfStatement): IStatement { fun process(ifStatement: IfStatement): IStatement {
ifStatement.condition = ifStatement.condition.process(this) ifStatement.condition = ifStatement.condition.process(this)
ifStatement.statements = ifStatement.statements.map { it.process(this) } ifStatement.truepart = ifStatement.truepart.process(this)
ifStatement.elsepart = ifStatement.elsepart.map { it.process(this) } ifStatement.elsepart = ifStatement.elsepart.process(this)
return ifStatement return ifStatement
} }
fun process(branchStatement: BranchStatement): IStatement { fun process(branchStatement: BranchStatement): IStatement {
branchStatement.statements = branchStatement.statements.map { it.process(this) } branchStatement.truepart = branchStatement.truepart.process(this)
branchStatement.elsepart = branchStatement.elsepart.map { it.process(this) } branchStatement.elsepart = branchStatement.elsepart.process(this)
return branchStatement return branchStatement
} }
@ -195,19 +195,19 @@ interface IAstProcessor {
fun process(forLoop: ForLoop): IStatement { fun process(forLoop: ForLoop): IStatement {
forLoop.loopVar?.process(this) forLoop.loopVar?.process(this)
forLoop.iterable = forLoop.iterable.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 return forLoop
} }
fun process(whileLoop: WhileLoop): IStatement { fun process(whileLoop: WhileLoop): IStatement {
whileLoop.condition = whileLoop.condition.process(this) whileLoop.condition = whileLoop.condition.process(this)
whileLoop.statements = whileLoop.statements.map { it.process(this) } whileLoop.body = whileLoop.body.process(this)
return whileLoop return whileLoop
} }
fun process(repeatLoop: RepeatLoop): IStatement { fun process(repeatLoop: RepeatLoop): IStatement {
repeatLoop.untilCondition = repeatLoop.untilCondition.process(this) repeatLoop.untilCondition = repeatLoop.untilCondition.process(this)
repeatLoop.statements = repeatLoop.statements.map { it.process(this) } repeatLoop.body = repeatLoop.body.process(this)
return repeatLoop return repeatLoop
} }
@ -227,6 +227,11 @@ interface IAstProcessor {
assignTarget.identifier?.process(this) assignTarget.identifier?.process(this)
return assignTarget 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 name: String
val position: Position val position: Position
var statements: MutableList<IStatement> 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 } fun labelsAndVariables() = statements.asSequence().filter { it is Label || it is VarDecl }
.associate {((it as? Label)?.name ?: (it as? VarDecl)?.name)!! to it } .associate {((it as? Label)?.name ?: (it as? VarDecl)?.name)!! to it }
@ -345,30 +372,8 @@ interface INameScope {
val removed = statements.remove(statement) val removed = statements.remove(statement)
if(!removed) throw AstException("node to remove wasn't found") if(!removed) throw AstException("node to remove wasn't found")
} }
}
fun isEmpty() = statements.isEmpty()
/**
* 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
}
} }
@ -382,7 +387,8 @@ object BuiltinFunctionScopePlaceholder : INameScope {
override val name = "<<builtin-functions-scope-placeholder>>" override val name = "<<builtin-functions-scope-placeholder>>"
override val position = Position("<<placeholder>>", 0, 0, 0) override val position = Position("<<placeholder>>", 0, 0, 0)
override var statements = mutableListOf<IStatement>() 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 { 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 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, private class GlobalNamespace(override val name: String,
override var statements: MutableList<IStatement>, override var statements: MutableList<IStatement>,
override val position: Position) : INameScope { override val position: Position) : INameScope {
override var parent = ParentSentinel
private val scopedNamesUsed: MutableSet<String> = mutableSetOf("main", "main.start") // main and main.start are always used override fun linkParents(parent: Node) {}
override fun lookup(scopedName: List<String>, statement: Node): IStatement? { override fun lookup(scopedName: List<String>, statement: Node): IStatement? {
if(scopedName.last() in BuiltinFunctions) { if(scopedName.last() in BuiltinFunctions) {
@ -429,24 +434,11 @@ private class GlobalNamespace(override val name: String,
return builtinPlaceholder return builtinPlaceholder
} }
val stmt = super.lookup(scopedName, statement) val stmt = super.lookup(scopedName, statement)
if(stmt!=null) { return when (stmt) {
val targetScopedName = when(stmt) { is Label, is VarDecl, is Block, is Subroutine -> stmt
is Label -> stmt.scopedname null -> null
is VarDecl -> stmt.scopedname else -> throw NameError("wrong identifier target: $stmt", stmt.position)
is Block -> stmt.scopedname
is Subroutine -> stmt.scopedname
else -> throw NameError("wrong identifier target: $stmt", stmt.position)
}
registerUsedName(targetScopedName)
} }
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 { override fun toString(): String {
return "Block(name=$name, address=$address, ${statements.size} statements)" 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 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) if(register!=null)
return when(register){ return when(register){
Register.A, Register.X, Register.Y -> DataType.BYTE Register.A, Register.X, Register.Y -> DataType.BYTE
@ -677,8 +667,7 @@ data class AssignTarget(val register: Register?,
} }
if(identifier!=null) { if(identifier!=null) {
val symbol = namespace.lookup(identifier.nameInSource, stmt) val symbol = namespace.lookup(identifier.nameInSource, stmt) ?: return null
?: throw FatalAstException("symbol lookup failed: ${identifier.nameInSource}")
if (symbol is VarDecl) return symbol.datatype 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 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, class Subroutine(override val name: String,
val parameters: List<SubroutineParameter>, val parameters: List<SubroutineParameter>,
val returnvalues: List<DataType>, val returnvalues: List<DataType>,
@ -1314,8 +1324,6 @@ class Subroutine(override val name: String,
override fun toString(): String { override fun toString(): String {
return "Subroutine(name=$name, parameters=$parameters, returnvalues=$returnvalues, ${statements.size} statements, address=$asmAddress)" 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, class IfStatement(var condition: IExpression,
var statements: List<IStatement>, var truepart: AnonymousScope,
var elsepart: List<IStatement>, var elsepart: AnonymousScope,
override val position: Position) : IStatement { override val position: Position) : IStatement {
override lateinit var parent: Node override lateinit var parent: Node
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
condition.linkParents(this) condition.linkParents(this)
statements.forEach { it.linkParents(this) } truepart.linkParents(this)
elsepart.forEach { it.linkParents(this) } elsepart.linkParents(this)
} }
override fun process(processor: IAstProcessor): IStatement = processor.process(this) override fun process(processor: IAstProcessor): IStatement = processor.process(this)
@ -1347,29 +1355,25 @@ class IfStatement(var condition: IExpression,
class BranchStatement(var condition: BranchCondition, class BranchStatement(var condition: BranchCondition,
var statements: List<IStatement>, var truepart: AnonymousScope,
var elsepart: List<IStatement>, var elsepart: AnonymousScope,
override val position: Position) : IStatement { override val position: Position) : IStatement {
override lateinit var parent: Node override lateinit var parent: Node
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
statements.forEach { it.linkParents(this) } truepart.linkParents(this)
elsepart.forEach { it.linkParents(this) } elsepart.linkParents(this)
} }
override fun process(processor: IAstProcessor): IStatement = processor.process(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?, class ForLoop(val loopRegister: Register?,
val loopVar: IdentifierReference?, val loopVar: IdentifierReference?,
var iterable: IExpression, var iterable: IExpression,
var body: MutableList<IStatement>, var body: AnonymousScope,
override val position: Position) : IStatement { override val position: Position) : IStatement {
override lateinit var parent: Node override lateinit var parent: Node
@ -1377,7 +1381,7 @@ class ForLoop(val loopRegister: Register?,
this.parent=parent this.parent=parent
loopVar?.linkParents(this) loopVar?.linkParents(this)
iterable.linkParents(this) iterable.linkParents(this)
body.forEach { it.linkParents(this) } body.linkParents(this)
} }
override fun process(processor: IAstProcessor) = processor.process(this) override fun process(processor: IAstProcessor) = processor.process(this)
@ -1389,21 +1393,21 @@ class ForLoop(val loopRegister: Register?,
class WhileLoop(var condition: IExpression, class WhileLoop(var condition: IExpression,
var statements: List<IStatement>, var body: AnonymousScope,
override val position: Position) : IStatement { override val position: Position) : IStatement {
override lateinit var parent: Node override lateinit var parent: Node
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
condition.linkParents(this) condition.linkParents(this)
statements.forEach { it.linkParents(this) } body.linkParents(this)
} }
override fun process(processor: IAstProcessor): IStatement = processor.process(this) override fun process(processor: IAstProcessor): IStatement = processor.process(this)
} }
class RepeatLoop(var statements: List<IStatement>, class RepeatLoop(var body: AnonymousScope,
var untilCondition: IExpression, var untilCondition: IExpression,
override val position: Position) : IStatement { override val position: Position) : IStatement {
override lateinit var parent: Node override lateinit var parent: Node
@ -1411,7 +1415,7 @@ class RepeatLoop(var statements: List<IStatement>,
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
untilCondition.linkParents(this) untilCondition.linkParents(this)
statements.forEach { it.linkParents(this) } body.linkParents(this)
} }
override fun process(processor: IAstProcessor): IStatement = processor.process(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 { private fun prog8Parser.If_stmtContext.toAst(): IfStatement {
val condition = expression().toAst() val condition = expression().toAst()
val statements = statement_block()?.toAst() ?: listOf(statement().toAst()) val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
val elsepart = else_part()?.toAst() ?: emptyList() val elseStatements = else_part()?.toAst() ?: mutableListOf()
return IfStatement(condition, statements, elsepart, toPosition()) 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> { private fun prog8Parser.Else_partContext.toAst(): MutableList<IStatement> {
return statement_block()?.toAst() ?: listOf(statement().toAst()) return statement_block()?.toAst() ?: mutableListOf(statement().toAst())
} }
private fun prog8Parser.Branch_stmtContext.toAst(): BranchStatement { private fun prog8Parser.Branch_stmtContext.toAst(): BranchStatement {
val branchcondition = branchcondition().toAst() val branchcondition = branchcondition().toAst()
val statements = statement_block()?.toAst() ?: listOf(statement().toAst()) val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
val elsepart = else_part()?.toAst() ?: emptyList() val elseStatements = else_part()?.toAst() ?: mutableListOf()
return BranchStatement(branchcondition, statements, elsepart, toPosition()) 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()) 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 loopregister = register()?.toAst()
val loopvar = identifier()?.toAst() val loopvar = identifier()?.toAst()
val iterable = expression()!!.toAst() val iterable = expression()!!.toAst()
val body = statement_block().toAst() val scope = AnonymousScope(statement_block().toAst(), statement_block().toPosition())
return ForLoop(loopregister, loopvar, iterable, body, 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 { private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {
val condition = expression().toAst() val condition = expression().toAst()
val statements = statement_block()?.toAst() ?: listOf(statement().toAst()) val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
return WhileLoop(condition, statements, toPosition()) val scope = AnonymousScope(statements, statement_block()?.toPosition() ?: statement().toPosition())
return WhileLoop(condition, scope, toPosition())
} }
private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop { private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop {
val untilCondition = expression().toAst() val untilCondition = expression().toAst()
val statements = statement_block()?.toAst() ?: listOf(statement().toAst()) val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
return RepeatLoop(statements, untilCondition, toPosition()) val scope = AnonymousScope(statements, statement_block()?.toPosition() ?: statement().toPosition())
return RepeatLoop(scope, untilCondition, toPosition())
} }

View File

@ -275,19 +275,21 @@ class AstChecker(private val namespace: INameScope,
} }
val targetDatatype = assignment.target.determineDatatype(namespace, heap, assignment) val targetDatatype = assignment.target.determineDatatype(namespace, heap, assignment)
val constVal = assignment.value.constValue(namespace, heap) if(targetDatatype!=null) {
if(constVal!=null) { val constVal = assignment.value.constValue(namespace, heap)
checkValueTypeAndRange(targetDatatype, null, constVal, heap) if(constVal!=null) {
} else { checkValueTypeAndRange(targetDatatype, null, constVal, heap)
val sourceDatatype: DataType? = assignment.value.resultingDatatype(namespace, heap) } else {
if(sourceDatatype==null) { val sourceDatatype: DataType? = assignment.value.resultingDatatype(namespace, heap)
if(assignment.value is FunctionCall) if(sourceDatatype==null) {
checkResult.add(ExpressionError("function call doesn't return a value to use in assignment", assignment.value.position)) if(assignment.value is FunctionCall)
else checkResult.add(ExpressionError("function call doesn't return a value to use in assignment", assignment.value.position))
checkResult.add(ExpressionError("assignment value is invalid or has no proper datatype", 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) else {
checkAssignmentCompatible(targetDatatype, sourceDatatype, assignment.value, assignment.position)
}
} }
} }
@ -310,11 +312,6 @@ class AstChecker(private val namespace: INameScope,
err("recursive var declaration") 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) { when(decl.type) {
VarDeclType.VAR, VarDeclType.CONST -> { VarDeclType.VAR, VarDeclType.CONST -> {
if (decl.value == null) { if (decl.value == null) {

View File

@ -387,7 +387,6 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
for (stmt: IStatement in statements) { for (stmt: IStatement in statements) {
stmtUniqueSequenceNr++ stmtUniqueSequenceNr++
when (stmt) { when (stmt) {
is AnonymousStatementList -> translate(stmt.statements)
is Label -> translate(stmt) is Label -> translate(stmt)
is Return -> translate(stmt) is Return -> translate(stmt)
is Assignment -> translate(stmt) // normal and augmented assignments is Assignment -> translate(stmt) // normal and augmented assignments
@ -401,6 +400,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
is ForLoop -> translate(stmt) is ForLoop -> translate(stmt)
is WhileLoop -> translate(stmt) is WhileLoop -> translate(stmt)
is RepeatLoop -> translate(stmt) is RepeatLoop -> translate(stmt)
is AnonymousScope -> translate(stmt)
is Directive, is VarDecl, is Subroutine -> {} // skip this, already processed these. is Directive, is VarDecl, is Subroutine -> {} // skip this, already processed these.
is InlineAssembly -> throw CompilerException("inline assembly is not supported by the StackVM") is InlineAssembly -> throw CompilerException("inline assembly is not supported by the StackVM")
else -> TODO("translate statement $stmt to stackvm") else -> TODO("translate statement $stmt to stackvm")
@ -574,11 +574,11 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
} }
if(branch.elsepart.isEmpty()) { if(branch.elsepart.isEmpty()) {
stackvmProg.instr(opcode, callLabel = labelEnd) stackvmProg.instr(opcode, callLabel = labelEnd)
translate(branch.statements) translate(branch.truepart)
stackvmProg.label(labelEnd) stackvmProg.label(labelEnd)
} else { } else {
stackvmProg.instr(opcode, callLabel = labelElse) stackvmProg.instr(opcode, callLabel = labelElse)
translate(branch.statements) translate(branch.truepart)
stackvmProg.instr(Opcode.JUMP, callLabel = labelEnd) stackvmProg.instr(Opcode.JUMP, callLabel = labelEnd)
stackvmProg.label(labelElse) stackvmProg.label(labelElse)
translate(branch.elsepart) translate(branch.elsepart)
@ -616,12 +616,12 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
val labelEnd = makeLabel("end") val labelEnd = makeLabel("end")
if(stmt.elsepart.isEmpty()) { if(stmt.elsepart.isEmpty()) {
stackvmProg.instr(Opcode.BZ, callLabel = labelEnd) stackvmProg.instr(Opcode.BZ, callLabel = labelEnd)
translate(stmt.statements) translate(stmt.truepart)
stackvmProg.label(labelEnd) stackvmProg.label(labelEnd)
} else { } else {
val labelElse = makeLabel("else") val labelElse = makeLabel("else")
stackvmProg.instr(Opcode.BZ, callLabel = labelElse) stackvmProg.instr(Opcode.BZ, callLabel = labelElse)
translate(stmt.statements) translate(stmt.truepart)
stackvmProg.instr(Opcode.JUMP, callLabel = labelEnd) stackvmProg.instr(Opcode.JUMP, callLabel = labelEnd)
stackvmProg.label(labelElse) stackvmProg.label(labelElse)
translate(stmt.elsepart) translate(stmt.elsepart)
@ -1209,7 +1209,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
val target = stmt.target.identifier!!.targetStatement(namespace)!! val target = stmt.target.identifier!!.targetStatement(namespace)!!
when(target) { when(target) {
is VarDecl -> { 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) stackvmProg.instr(opcode, callLabel = target.scopedname)
} }
else -> throw CompilerException("invalid assignment target type ${target::class}") 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)!! val target = stmt.target.identifier!!.targetStatement(namespace)!!
when(target) { when(target) {
is VarDecl -> { 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) stackvmProg.instr(opcode, callLabel = target.scopedname)
} }
else -> throw CompilerException("invalid assignment target type ${target::class}") else -> throw CompilerException("invalid assignment target type ${target::class}")
@ -1383,10 +1383,10 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
} else { } else {
// loop over a range where one or more of the start, last or step values is not a constant // loop over a range where one or more of the start, last or step values is not a constant
if(loop.loopRegister!=null) { 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 { 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 { } else {
@ -1496,7 +1496,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
continueStmtLabelStack.pop() 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 } * for LV in start..last { body }
* (and we already know that the range is not empty, and first and last are exactly inclusive.) * (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() 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 } * for LV in start..last { body }
* (where at least one of the start, last, step values is not a constant) * (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) BinaryExpression(loopVar,"<", range.to, range.position)
} }
val ifstmt = IfStatement(condition, val ifstmt = IfStatement(condition,
listOf(Jump(null, null, breakLabel, range.position)), AnonymousScope(mutableListOf(Jump(null, null, breakLabel, range.position)), range.position),
emptyList(), AnonymousScope(mutableListOf(), range.position),
range.position) range.position)
ifstmt.linkParents(range.parent) ifstmt.linkParents(range.parent)
translate(ifstmt) translate(ifstmt)
@ -1665,8 +1666,9 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
} }
val branch = BranchStatement( val branch = BranchStatement(
BranchCondition.NZ, BranchCondition.NZ,
listOf(Jump(null, null, loopLabel, range.position)), AnonymousScope(mutableListOf(Jump(null, null, loopLabel, range.position)), range.position),
emptyList(), range.position) AnonymousScope(mutableListOf(), range.position),
range.position)
branch.linkParents(range.parent) branch.linkParents(range.parent)
translate(branch) translate(branch)
} }
@ -1690,6 +1692,8 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
continueStmtLabelStack.pop() continueStmtLabelStack.pop()
} }
private fun translate(scope: AnonymousScope) = translate(scope.statements)
private fun translate(stmt: WhileLoop) private fun translate(stmt: WhileLoop)
{ {
/* /*
@ -1714,7 +1718,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
continueStmtLabelStack.push(continueLabel) continueStmtLabelStack.push(continueLabel)
stackvmProg.instr(Opcode.JUMP, callLabel = continueLabel) stackvmProg.instr(Opcode.JUMP, callLabel = continueLabel)
stackvmProg.label(loopLabel) stackvmProg.label(loopLabel)
translate(stmt.statements) translate(stmt.body)
stackvmProg.label(continueLabel) stackvmProg.label(continueLabel)
translate(stmt.condition) translate(stmt.condition)
stackvmProg.instr(Opcode.BNZ, callLabel = loopLabel) stackvmProg.instr(Opcode.BNZ, callLabel = loopLabel)
@ -1747,7 +1751,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
breakStmtLabelStack.push(breakLabel) breakStmtLabelStack.push(breakLabel)
continueStmtLabelStack.push(continueLabel) continueStmtLabelStack.push(continueLabel)
stackvmProg.label(loopLabel) stackvmProg.label(loopLabel)
translate(stmt.statements) translate(stmt.body)
stackvmProg.label(continueLabel) stackvmProg.label(continueLabel)
translate(stmt.untilCondition) translate(stmt.untilCondition)
stackvmProg.instr(Opcode.BZ, callLabel = loopLabel) stackvmProg.instr(Opcode.BZ, callLabel = loopLabel)

View File

@ -50,11 +50,11 @@ class StatementOptimizer(private val globalNamespace: INameScope, private val he
return if(constvalue.asBooleanValue){ return if(constvalue.asBooleanValue){
// always true -> keep only if-part // always true -> keep only if-part
printWarning("condition is always true", ifStatement.position) printWarning("condition is always true", ifStatement.position)
AnonymousStatementList(ifStatement.parent, ifStatement.statements, ifStatement.position) ifStatement.truepart
} else { } else {
// always false -> keep only else-part // always false -> keep only else-part
printWarning("condition is always false", ifStatement.position) printWarning("condition is always false", ifStatement.position)
AnonymousStatementList(ifStatement.parent, ifStatement.elsepart, ifStatement.position) ifStatement.elsepart
} }
} }
return ifStatement 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 // for loop over a (constant) range of just a single value-- optimize the loop away
// loopvar/reg = range value , follow by block // loopvar/reg = range value , follow by block
val assignment = Assignment(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, forLoop.position), null, range.from, forLoop.position) val assignment = Assignment(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, forLoop.position), null, range.from, forLoop.position)
forLoop.body.add(0, assignment) forLoop.body.statements.add(0, assignment)
return AnonymousStatementList(forLoop.parent, forLoop.body, forLoop.position) return forLoop.body
} }
} }
return forLoop return forLoop
@ -86,7 +86,7 @@ class StatementOptimizer(private val globalNamespace: INameScope, private val he
} else { } else {
// always false -> ditch whole statement // always false -> ditch whole statement
printWarning("condition is always false", whileLoop.position) printWarning("condition is always false", whileLoop.position)
AnonymousStatementList(whileLoop.parent, emptyList(), whileLoop.position) AnonymousScope(mutableListOf(), whileLoop.position)
} }
} }
return whileLoop return whileLoop
@ -99,7 +99,7 @@ class StatementOptimizer(private val globalNamespace: INameScope, private val he
return if(constvalue.asBooleanValue){ return if(constvalue.asBooleanValue){
// always true -> keep only the statement block // always true -> keep only the statement block
printWarning("condition is always true", repeatLoop.position) printWarning("condition is always true", repeatLoop.position)
AnonymousStatementList(repeatLoop.parent, repeatLoop.statements, repeatLoop.position) repeatLoop.body
} else { } else {
// always false // always false
printWarning("condition is always false", repeatLoop.position) printWarning("condition is always false", repeatLoop.position)

View File

@ -90,7 +90,7 @@ Scope
Blocks, Scopes, and accessing Symbols 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:: into a single output program. No code or data can occur outside a block. Here's an example::
~ main $c000 { ~ main $c000 {
@ -111,6 +111,18 @@ Usually it is omitted, and the compiler will automatically choose the location (
the previous block in memory). the previous block in memory).
The address must be >= ``$0200`` (because ``$00``--``$ff`` is the ZP and ``$100``--``$200`` is the cpu stack). 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:: .. sidebar::
Scoped access to symbols / "dotted names" 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``, So, accessing a variable ``counter`` defined in subroutine ``worker`` in block ``main``,
can be done from anywhere by using ``main.worker.counter``. 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* are names defined in a certain *scope*. Inside the same scope, you can refer
symbols of the same name defined elsewhere in the same file or in another file. to them by their 'short' name directly. If the symbol is not found in the same scope,
You can refer to the symbols in a particular block by using a *dotted name*: ``blockname.symbolname``. the enclosing scope is searched for it, and so on, until the symbol is found.
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.
Scopes are created using several statements:
- blocks (top-level named scope)
**The special "ZP" ZeroPage block** - subroutines (nested named scopes)
- for, while, repeat loops (anonymous scope)
Blocks named "ZP" are treated a bit differently: they refer to the ZeroPage. - if statements and branching conditionals (anonymous scope)
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.
Program Start and Entry Point 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. 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. 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. 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:: Values will usually be part of an expression or assignment statement::

View File

@ -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]`` ``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]`` ``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]`` ``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`` ``byte[x,y]`` unsigned byte matrix x*y bytes ``byte[40,25] myvar = 255``
word-matrix not supported
``str`` string (petscii) varies ``str myvar = "hello."`` ``str`` string (petscii) varies ``str myvar = "hello."``
implicitly terminated by a 0-byte implicitly terminated by a 0-byte
``str_p`` pascal-string (petscii) varies ``str_p myvar = "hello."`` ``str_p`` pascal-string (petscii) varies ``str_p myvar = "hello."``