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(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)
}
}
}

View File

@ -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

View File

@ -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")

View File

@ -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++

View File

@ -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
}

View File

@ -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())
}

View File

@ -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) {

View File

@ -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)

View File

@ -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)

View File

@ -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::

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]``
``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."``