mirror of
https://github.com/irmen/prog8.git
synced 2024-12-25 08:29:25 +00:00
building more of the simulator
This commit is contained in:
parent
abcdd331db
commit
e6e84859b7
@ -1,6 +1,7 @@
|
||||
package prog8.sim
|
||||
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.StringLiteral
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.compilerinterface.Encoding
|
||||
import prog8.compilerinterface.IMemSizer
|
||||
@ -8,11 +9,26 @@ import prog8.compilerinterface.IStringEncoding
|
||||
|
||||
internal object MemSizer: IMemSizer {
|
||||
override fun memorySize(dt: DataType): Int {
|
||||
TODO("Not yet implemented")
|
||||
return when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||
DataType.FLOAT -> Double.SIZE_BYTES
|
||||
else -> Int.MIN_VALUE
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(decl: VarDecl): Int {
|
||||
TODO("Not yet implemented")
|
||||
return when(decl.type) {
|
||||
VarDeclType.CONST -> 0
|
||||
VarDeclType.VAR, VarDeclType.MEMORY -> {
|
||||
when(val dt = decl.datatype) {
|
||||
in NumericDatatypes -> return memorySize(dt)
|
||||
in ArrayDatatypes -> decl.arraysize!!.constIndex()!! * memorySize(ArrayToElementTypes.getValue(dt))
|
||||
DataType.STR -> (decl.value as StringLiteral).value.length + 1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package prog8.sim
|
||||
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.compilerinterface.Encoding
|
||||
import prog8.compilerinterface.StNodeType
|
||||
import prog8.compilerinterface.StStaticVariable
|
||||
import prog8.compilerinterface.SymbolTable
|
||||
@ -16,15 +17,15 @@ class Evaluator(
|
||||
fun evaluateExpression(expr: PtNode): Pair<Double, DataType> {
|
||||
return when(expr) {
|
||||
is PtAddressOf -> evaluate(expr)
|
||||
is PtArrayIndexer -> TODO()
|
||||
is PtArrayIndexer -> evaluate(expr)
|
||||
is PtArrayLiteral -> throw IllegalArgumentException("arrayliteral $expr")
|
||||
is PtBinaryExpression -> TODO()
|
||||
is PtBuiltinFunctionCall -> TODO()
|
||||
is PtConstant -> TODO()
|
||||
is PtBinaryExpression -> evaluate(expr)
|
||||
is PtBuiltinFunctionCall -> evaluate(expr)
|
||||
is PtConstant -> Pair(expr.value, expr.type)
|
||||
is PtContainmentCheck -> TODO()
|
||||
is PtFunctionCall -> evaluate(expr)
|
||||
is PtIdentifier -> evaluate(expr)
|
||||
is PtMemoryByte -> TODO()
|
||||
is PtMemoryByte -> evaluate(expr)
|
||||
is PtNumber -> Pair(expr.number, expr.type)
|
||||
is PtPipe -> TODO()
|
||||
is PtPrefix -> TODO()
|
||||
@ -35,6 +36,59 @@ class Evaluator(
|
||||
}
|
||||
}
|
||||
|
||||
private fun evaluate(memoryByte: PtMemoryByte): Pair<Double, DataType> {
|
||||
val address = evaluateExpression(memoryByte.address).first.toUInt()
|
||||
val value = memory[address]
|
||||
return Pair(value.toDouble(), DataType.UBYTE)
|
||||
}
|
||||
|
||||
private fun evaluate(arrayIdx: PtArrayIndexer): Pair<Double, DataType> {
|
||||
val index = evaluateExpression(arrayIdx.index)
|
||||
println("TODO: get array value ${arrayIdx.variable.ref}[${index.first.toInt()}]")
|
||||
return Pair(0.0, DataType.UBYTE)
|
||||
}
|
||||
|
||||
internal fun evaluate(expr: PtBinaryExpression): Pair<Double, DataType> {
|
||||
val left = evaluateExpression(expr.left)
|
||||
val right = evaluateExpression(expr.right)
|
||||
require(left.second==right.second)
|
||||
|
||||
// TODO implement 8/16 bit maths
|
||||
|
||||
return when(expr.operator) {
|
||||
"+" -> Pair(left.first+right.first, left.second)
|
||||
"-" -> Pair(left.first-right.first, left.second)
|
||||
"*" -> Pair(left.first*right.first, left.second)
|
||||
"/" -> Pair(left.first/right.first, left.second)
|
||||
"==" -> {
|
||||
val bool = if(left.first==right.first) 1 else 0
|
||||
Pair(bool.toDouble(), DataType.UBYTE)
|
||||
}
|
||||
"!=" -> {
|
||||
val bool = if(left.first!=right.first) 1 else 0
|
||||
Pair(bool.toDouble(), DataType.UBYTE)
|
||||
}
|
||||
"<" -> {
|
||||
val bool = if(left.first<right.first) 1 else 0
|
||||
Pair(bool.toDouble(), DataType.UBYTE)
|
||||
}
|
||||
">" -> {
|
||||
val bool = if(left.first>right.first) 1 else 0
|
||||
Pair(bool.toDouble(), DataType.UBYTE)
|
||||
}
|
||||
"<=" -> {
|
||||
val bool = if(left.first<=right.first) 1 else 0
|
||||
Pair(bool.toDouble(), DataType.UBYTE)
|
||||
}
|
||||
">=" -> {
|
||||
val bool = if(left.first>=right.first) 1 else 0
|
||||
Pair(bool.toDouble(), DataType.UBYTE)
|
||||
}
|
||||
|
||||
else -> TODO("binexpr operator ${expr.operator}")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun evaluate(fcall: PtFunctionCall): Pair<Double, DataType> {
|
||||
val ref = fcall.target.ref
|
||||
val args = fcall.args.children.map { evaluateExpression(it) }
|
||||
@ -47,14 +101,26 @@ class Evaluator(
|
||||
print(memory.getString(args.single().first.toUInt())) // strings are passed as a memory address
|
||||
Pair(0.0, DataType.UBYTE)
|
||||
}
|
||||
listOf("txt", "print_uw") -> {
|
||||
print(args.single().first.toUInt())
|
||||
Pair(0.0, DataType.UBYTE)
|
||||
}
|
||||
else -> {
|
||||
val sub = findPtNode(fcall.target.targetName, fcall)
|
||||
passCallArgs(sub, args)
|
||||
return simulator.executeSubroutine(sub)
|
||||
val node = findPtNode(fcall.target.targetName, fcall)
|
||||
if(node is PtAsmSub)
|
||||
throw NotImplementedError("simulator can't run asmsub ${node.name}")
|
||||
node as PtSub
|
||||
passCallArgs(node, args)
|
||||
return simulator.executeSubroutine(node)!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun evaluate(fcall: PtBuiltinFunctionCall): Pair<Double, DataType> {
|
||||
println("TODO: builtin function call ${fcall.name}")
|
||||
return Pair(0.0, DataType.UBYTE)
|
||||
}
|
||||
|
||||
private fun passCallArgs(sub: PtSub, args: List<Pair<Double, DataType>>) {
|
||||
require(sub.parameters.size==args.size)
|
||||
for ((param, arg) in sub.parameters.zip(args)) {
|
||||
@ -63,28 +129,15 @@ class Evaluator(
|
||||
}
|
||||
}
|
||||
|
||||
private fun findPtNode(scopedName: List<String>, scope: PtNode): PtSub {
|
||||
var root = scope
|
||||
while(root !is PtProgram) {
|
||||
root=root.parent
|
||||
}
|
||||
val block = root.allBlocks().first {
|
||||
it.name == scopedName.first()
|
||||
}
|
||||
var sub: PtNode = block
|
||||
scopedName.drop(1).forEach { namepart->
|
||||
val nodes = sub.children.filterIsInstance<PtNamedNode>()
|
||||
sub = nodes.first { it.name==namepart }
|
||||
}
|
||||
return sub as PtSub
|
||||
}
|
||||
|
||||
private fun evaluate(ident: PtIdentifier): Pair<Double, DataType> {
|
||||
val target = symboltable.flat.getValue(ident.targetName)
|
||||
when(target.type) {
|
||||
StNodeType.STATICVAR -> {
|
||||
val variable = target as StStaticVariable
|
||||
return Pair(variables.getValue(variable), target.dt)
|
||||
val value = variables.getValue(variable)
|
||||
if(value.number==null)
|
||||
TODO("${ident.ref} -> $value")
|
||||
return Pair(value.number!!, target.dt)
|
||||
}
|
||||
StNodeType.CONSTANT -> throw IllegalArgumentException("constants should have been const folded")
|
||||
else -> throw IllegalArgumentException("weird ref target")
|
||||
@ -92,13 +145,7 @@ class Evaluator(
|
||||
}
|
||||
|
||||
private fun evaluate(addressOf: PtAddressOf): Pair<Double, DataType> {
|
||||
val target = symboltable.flat.getValue(addressOf.identifier.targetName)
|
||||
val alloc = variables[target]
|
||||
return if(alloc==null) {
|
||||
// TODO throw IllegalArgumentException("can't get address of ${target.scopedName}")
|
||||
println("warning: returning dummy address for ${target.scopedName}")
|
||||
Pair(4096.0, DataType.UWORD)
|
||||
} else
|
||||
Pair(alloc.toDouble(), DataType.UWORD)
|
||||
val target = symboltable.flat.getValue(addressOf.identifier.targetName) as StStaticVariable
|
||||
return Pair(variables.getAddress(target).toDouble(), DataType.UWORD)
|
||||
}
|
||||
}
|
@ -16,8 +16,7 @@ class InstructionPointer(var instructions: List<PtNode>, start: Int=0) {
|
||||
require(instructions.isNotEmpty())
|
||||
}
|
||||
|
||||
operator fun inc(): InstructionPointer {
|
||||
fun next() {
|
||||
currentIdx++
|
||||
return this
|
||||
}
|
||||
}
|
@ -9,43 +9,63 @@ import prog8.compilerinterface.intermediate.*
|
||||
import java.util.Stack
|
||||
|
||||
|
||||
class ExitProgram(val status: Int): Exception()
|
||||
sealed class FlowcontrolException : Exception()
|
||||
|
||||
class ReturnValue(val value: Pair<Double, DataType>?): FlowcontrolException()
|
||||
|
||||
class ExitProgram(val status: Int): FlowcontrolException()
|
||||
|
||||
class JumpTo(val targetName: List<String>): FlowcontrolException()
|
||||
|
||||
|
||||
class Simulator(val program: PtProgram, val symboltable: SymbolTable) {
|
||||
val memory = Memory()
|
||||
private val variables = Variables(symboltable, program.memsizer)
|
||||
private val variables = Variables(symboltable)
|
||||
private val eval = Evaluator(symboltable, memory, variables, this)
|
||||
private val callStack = Stack<InstructionPointer>()
|
||||
private var instructionPtr = InstructionPointer(listOf(PtReturn(Position.DUMMY)))
|
||||
|
||||
fun run() {
|
||||
memory.clear()
|
||||
val start = program.entrypoint() ?: throw NoSuchElementException("no main.start() found")
|
||||
try {
|
||||
executeSubroutine(start)
|
||||
} catch(r: ReturnValue) {
|
||||
println("Program Exit.")
|
||||
} catch (exit: ExitProgram) {
|
||||
println("Program Exit! Status code: ${exit.status}")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun executeSubroutine(sub: PtSub): Pair<Double, DataType> {
|
||||
instructionPtr = InstructionPointer(sub.children)
|
||||
callStack.push(instructionPtr)
|
||||
internal fun executeSubroutine(sub: PtSub): Pair<Double, DataType>? {
|
||||
return try {
|
||||
executeStatementList(sub.children)
|
||||
null
|
||||
} catch(r: ReturnValue) {
|
||||
r.value
|
||||
}
|
||||
}
|
||||
|
||||
internal fun executeStatementList(nodes: List<PtNode>) {
|
||||
var instructionPtr = InstructionPointer(nodes)
|
||||
while(true) {
|
||||
val incr = executeStatement(instructionPtr.current)
|
||||
if(callStack.empty())
|
||||
throw ExitProgram(0)
|
||||
if(incr)
|
||||
instructionPtr++
|
||||
try {
|
||||
if (executeStatement(instructionPtr.current))
|
||||
instructionPtr.next()
|
||||
} catch(jump: JumpTo) {
|
||||
val target = findPtNode(jump.targetName, instructionPtr.current)
|
||||
val nodes = target.parent.children
|
||||
instructionPtr = InstructionPointer(nodes, nodes.indexOf(target))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun executeStatement(node: PtNode): Boolean {
|
||||
return when(node) {
|
||||
is PtAsmSub -> true
|
||||
is PtAsmSub -> throw NotImplementedError("can't run assembly subroutine in simulator at this time")
|
||||
is PtAssignment -> execute(node)
|
||||
is PtBuiltinFunctionCall -> execute(node)
|
||||
is PtBuiltinFunctionCall -> {
|
||||
eval.evaluate(node) // throw away any result
|
||||
true
|
||||
}
|
||||
is PtConditionalBranch -> TODO()
|
||||
is PtDirective -> execute(node)
|
||||
is PtForLoop -> TODO()
|
||||
@ -54,12 +74,12 @@ class Simulator(val program: PtProgram, val symboltable: SymbolTable) {
|
||||
true
|
||||
}
|
||||
is PtGosub -> TODO()
|
||||
is PtIfElse -> TODO()
|
||||
is PtIfElse -> execute(node)
|
||||
is PtInlineAssembly -> throw NotImplementedError("can't run inline assembly in simulator at this time")
|
||||
is PtJump -> TODO()
|
||||
is PtJump -> execute(node)
|
||||
is PtLabel -> true
|
||||
is PtPipe -> execute(node)
|
||||
is PtPostIncrDecr -> TODO()
|
||||
is PtPostIncrDecr -> execute(node)
|
||||
is PtRepeatLoop -> execute(node)
|
||||
is PtReturn -> execute(node)
|
||||
is PtSub -> true // this simulator doesn't "fall through" into nested subroutines
|
||||
@ -69,6 +89,47 @@ class Simulator(val program: PtProgram, val symboltable: SymbolTable) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun execute(jump: PtJump): Boolean {
|
||||
if(jump.address!=null)
|
||||
throw NotImplementedError("simulator can't jump into memory machine code")
|
||||
else if(jump.generatedLabel!=null)
|
||||
throw NotImplementedError("simulator can't jump into generated label")
|
||||
else {
|
||||
throw JumpTo(jump.identifier!!.targetName)
|
||||
}
|
||||
}
|
||||
|
||||
private fun execute(post: PtPostIncrDecr): Boolean {
|
||||
val identifier = post.target.identifier
|
||||
val memoryAddr = post.target.memory
|
||||
val array = post.target.array
|
||||
if(identifier!=null) {
|
||||
val variable = symboltable.lookup(identifier.targetName) as StStaticVariable
|
||||
var value = variables.getValue(variable).number!!
|
||||
if(post.operator=="++") value++ else value--
|
||||
variables.setValue(variable, Variables.Value(value, null, null))
|
||||
} else if(memoryAddr!=null) {
|
||||
val addr = eval.evaluateExpression(memoryAddr.address).first.toUInt()
|
||||
if(post.operator=="++")
|
||||
memory[addr] = (memory[addr]+1u).toUByte()
|
||||
else
|
||||
memory[addr] = (memory[addr]-1u).toUByte()
|
||||
} else if(array!=null) {
|
||||
println("TODO: ${array.variable.ref}[] ${post.operator}")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun execute(ifelse: PtIfElse): Boolean {
|
||||
val condition = eval.evaluateExpression(ifelse.condition)
|
||||
if(condition.first!=0.0) {
|
||||
println("TODO: if part")
|
||||
} else {
|
||||
println("TODO: else part")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun execute(repeat: PtRepeatLoop): Boolean {
|
||||
TODO("repeat $repeat. ${repeat.position}")
|
||||
return true
|
||||
@ -79,8 +140,10 @@ class Simulator(val program: PtProgram, val symboltable: SymbolTable) {
|
||||
}
|
||||
|
||||
private fun execute(ret: PtReturn): Boolean {
|
||||
instructionPtr = callStack.pop()
|
||||
return true
|
||||
if(ret.hasValue)
|
||||
throw ReturnValue(eval.evaluateExpression(ret.value!!))
|
||||
else
|
||||
throw ReturnValue(null)
|
||||
}
|
||||
|
||||
private fun execute(directive: PtDirective): Boolean {
|
||||
@ -95,7 +158,7 @@ class Simulator(val program: PtProgram, val symboltable: SymbolTable) {
|
||||
val array = assign.target.array
|
||||
if(identifier!=null) {
|
||||
val targetvar = symboltable.flat.getValue(identifier.targetName) as StStaticVariable
|
||||
variables.setValue(targetvar, value.first)
|
||||
variables.setValue(targetvar, Variables.Value(value.first, null, null))
|
||||
}
|
||||
else if(memoryAddr!=null) {
|
||||
val address = eval.evaluateExpression(memoryAddr.address)
|
||||
@ -109,16 +172,22 @@ class Simulator(val program: PtProgram, val symboltable: SymbolTable) {
|
||||
throw IllegalArgumentException("missing assign target")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private fun execute(fcall: PtBuiltinFunctionCall): Boolean {
|
||||
when(fcall.name) {
|
||||
"print" -> {
|
||||
val string = fcall.children.single() as PtString
|
||||
require(string.encoding==Encoding.DEFAULT)
|
||||
print(string.value)
|
||||
}
|
||||
else -> TODO("missing builtin function ${fcall.name}")
|
||||
}
|
||||
return true
|
||||
|
||||
|
||||
internal fun findPtNode(scopedName: List<String>, scope: PtNode): PtNode {
|
||||
var root = scope
|
||||
while(root !is PtProgram) {
|
||||
root=root.parent
|
||||
}
|
||||
}
|
||||
val block = root.allBlocks().first {
|
||||
it.name == scopedName.first()
|
||||
}
|
||||
var node: PtNode = block
|
||||
scopedName.drop(1).forEach { namepart->
|
||||
val nodes = node.children.filterIsInstance<PtNamedNode>()
|
||||
node = nodes.first { it.name==namepart }
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
@ -1,25 +1,59 @@
|
||||
package prog8.sim
|
||||
|
||||
import prog8.compilerinterface.IMemSizer
|
||||
import prog8.compilerinterface.StNode
|
||||
import prog8.ast.base.ArrayDatatypes
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.NumericDatatypes
|
||||
import prog8.compilerinterface.Encoding
|
||||
import prog8.compilerinterface.StStaticVariable
|
||||
import prog8.compilerinterface.SymbolTable
|
||||
|
||||
class Variables(symboltable: SymbolTable, memsizer: IMemSizer) {
|
||||
class Variables(symboltable: SymbolTable) {
|
||||
|
||||
class Value(
|
||||
val number: Double?,
|
||||
val string: Pair<String, Encoding>?,
|
||||
val array: DoubleArray?
|
||||
)
|
||||
|
||||
val allVars = symboltable.allVariables
|
||||
val flatSymbolTable = symboltable.flat
|
||||
val allocations = mutableMapOf<StStaticVariable, UInt>()
|
||||
val values = mutableMapOf<StStaticVariable, Value>()
|
||||
|
||||
operator fun get(target: StNode): UInt? {
|
||||
return null
|
||||
init {
|
||||
var address = 0x1000u
|
||||
for (variable in allVars) {
|
||||
allocations[variable] = address
|
||||
address += MemSizer.memorySize(variable.dt).toUInt()
|
||||
when(variable.dt) {
|
||||
in NumericDatatypes -> {
|
||||
val number = if(variable.initialNumericValue==null) 0.0 else variable.initialNumericValue!!
|
||||
values[variable] = Value(number, null, null)
|
||||
}
|
||||
DataType.STR -> {
|
||||
values[variable] = Value(null, variable.initialStringValue!!, null)
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
val array = if(variable.initialArrayValue==null) DoubleArray(variable.arraysize!!) else variable.initialArrayValue!!
|
||||
values[variable] = Value(null, null, array)
|
||||
}
|
||||
else -> throw FatalAstException("weird dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getValue(variable: StStaticVariable): Double {
|
||||
println("warning: returning dummy value for staticvar ${variable.scopedName}")
|
||||
return 0.0
|
||||
}
|
||||
fun getAddress(variable: StStaticVariable): UInt = allocations.getValue(variable)
|
||||
|
||||
fun setValue(variable: StStaticVariable, value: Double) {
|
||||
println("warning: discarding value for staticvar ${variable.scopedName}")
|
||||
fun getValue(variable: StStaticVariable): Value = values.getValue(variable)
|
||||
|
||||
fun setValue(variable: StStaticVariable, value: Value) {
|
||||
when(variable.dt) {
|
||||
in NumericDatatypes-> require(value.number!=null)
|
||||
DataType.STR -> require(value.string!=null)
|
||||
in ArrayDatatypes -> require(value.array!=null)
|
||||
else -> throw FatalAstException("weird dt")
|
||||
}
|
||||
values[variable] = value
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user