mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +00:00
matrix decl parsing and fixes
This commit is contained in:
parent
e71fef709a
commit
efd3b1f5c6
@ -164,18 +164,20 @@ Values will usually be part of an expression or assignment statement::
|
||||
|
||||
byte counter = 42 ; variable of size 8 bits, with initial value 42
|
||||
|
||||
|
||||
Array and Matrix (2-dimensional array) types are also supported in a limited way::
|
||||
|
||||
byte[4] array = [1, 2, 3, 4] ; initialize the array
|
||||
byte[99] array = 255 ; initialize array with all 255's [255, 255, 255, 255, ...]
|
||||
byte[100] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199]
|
||||
byte[2,3] matrix = 1 ; a matrix of 2*3=6 bytes all with value 1
|
||||
byte[2,3] matrix = [1,2,3,4,5,6] ; a 2*3 matrix with value |(1,2) (3,4) (5,6)|
|
||||
|
||||
|
||||
Note that the various keywords for the data type and variable type (``byte``, ``word``, ``const``, etc.)
|
||||
cannot be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte``
|
||||
for instance.
|
||||
|
||||
.. todo::
|
||||
matrix datatype
|
||||
|
||||
.. todo::
|
||||
There must be a way to tell the compiler which variables you require to be in Zeropage:
|
||||
``zeropage`` modifier keyword on vardecl perhaps?
|
||||
|
@ -197,22 +197,23 @@ Variable declarations
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Variables should be declared with their exact type and size so the compiler can allocate storage
|
||||
for them. You can give them an initial value as well. That value can be a simple literal value,
|
||||
but you can put a (constant) expression there as well. The syntax is::
|
||||
for them. You must give them an initial value as well. That value can be a simple literal value,
|
||||
or a (constant) expression. The syntax is::
|
||||
|
||||
<datatype> <variable name> [ = <initial value> ]
|
||||
|
||||
Various examples::
|
||||
|
||||
word thing
|
||||
byte counter = 0
|
||||
word thing = 0
|
||||
byte counter = len([1, 2, 3]) * 20
|
||||
byte age = 2018 - 1974
|
||||
float wallet = 55.25
|
||||
str name = "my name is Irmen"
|
||||
word address = #counter
|
||||
byte[5] values = [11, 22, 33, 44, 55]
|
||||
byte[5] values = 255 ; initialize with five 255 bytes
|
||||
byte[5][6] empty_matrix
|
||||
byte[5][6] empty_matrix = 0 ; initialize with 30 zero bytes
|
||||
byte[2][3] other_matrix = [1,2,3,4,5,6] ; 2*3 matrix with value | (1,2) (3,4) (5,6) |
|
||||
|
||||
|
||||
|
||||
@ -348,7 +349,8 @@ range creation: ``to``
|
||||
}
|
||||
|
||||
|
||||
array indexing: ``[`` *index* ``]``
|
||||
.. todo::
|
||||
array indexing: ``[`` *index* ``]``
|
||||
When put after a sequence type (array, string or matrix) it means to point to the given element in that sequence::
|
||||
|
||||
array[2] ; the third byte in the array (index is 0-based)
|
||||
|
@ -20,10 +20,10 @@ input.result word 0
|
||||
nop
|
||||
syscall WRITE_MEMSTR w:1000
|
||||
loop:
|
||||
inc_var "main.textcolor"
|
||||
inc_var main.textcolor
|
||||
syscall RANDOM
|
||||
syscall RANDOM
|
||||
push_var "main.textcolor"
|
||||
push_var main.textcolor
|
||||
syscall GFX_PIXEL
|
||||
; syscall WRITE_VAR "input.prompt"
|
||||
; syscall INPUT_VAR "input.result"
|
||||
|
@ -4,10 +4,20 @@
|
||||
%import c64lib
|
||||
|
||||
~ not_main $d000 {
|
||||
const float len1 = 111 ;len([1,2,3])
|
||||
const float round1 = 111 ;len([1,2,3])
|
||||
const float sin1 = 111 ;len([1,2,3])
|
||||
float cos1 = 111 ;len([1,2,3])
|
||||
byte [100] array1 = 0
|
||||
word [100] array2 = 1
|
||||
byte [2,3] matrix1 = 2
|
||||
byte [2,3] matrix2 = [1,2,3,4,5,6]
|
||||
|
||||
const byte [100] carray1 = 0
|
||||
const word [100] carray2 = 1
|
||||
const byte [2,3] cmatrix1 = [1,2,3,4,5,255]
|
||||
|
||||
|
||||
const float len1 = len([1,2,3])
|
||||
const float round1 = len([1,2,3])
|
||||
const float sin1 = len([1,2,3])
|
||||
float cos1 = len([1,2,3])
|
||||
}
|
||||
|
||||
~ main $c003 {
|
||||
|
@ -62,6 +62,7 @@ fun main(args: Array<String>) {
|
||||
val compiler = Compiler(compilerOptions)
|
||||
val intermediate = compiler.compile(moduleAst)
|
||||
intermediate.optimize()
|
||||
intermediate.toTextLines().forEach { println(it) }
|
||||
|
||||
// val assembly = stackvmProg.compileToAssembly()
|
||||
//
|
||||
|
@ -68,15 +68,15 @@ class NameError(override var message: String, val position: Position?) : AstExce
|
||||
}
|
||||
}
|
||||
|
||||
open class ExpressionException(message: String, val position: Position?) : AstException(message) {
|
||||
open class ExpressionError(message: String, val position: Position?) : AstException(message) {
|
||||
override fun toString(): String {
|
||||
val location = position?.toString() ?: ""
|
||||
return "$location Error: $message"
|
||||
}
|
||||
}
|
||||
|
||||
class UndefinedSymbolException(symbol: IdentifierReference)
|
||||
: ExpressionException("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position)
|
||||
class UndefinedSymbolError(symbol: IdentifierReference)
|
||||
: ExpressionError("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position)
|
||||
|
||||
|
||||
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
||||
@ -514,26 +514,17 @@ enum class VarDeclType {
|
||||
}
|
||||
|
||||
class VarDecl(val type: VarDeclType,
|
||||
declaredDatatype: DataType,
|
||||
private val declaredDatatype: DataType,
|
||||
val arrayspec: ArraySpec?,
|
||||
val name: String,
|
||||
var value: IExpression?) : IStatement {
|
||||
override var position: Position? = null
|
||||
override lateinit var parent: Node
|
||||
val datatype: DataType
|
||||
|
||||
init {
|
||||
datatype = when {
|
||||
arrayspec!=null -> // it's not a scalar, adjust the datatype
|
||||
when(declaredDatatype) {
|
||||
DataType.BYTE -> DataType.ARRAY
|
||||
DataType.WORD -> DataType.ARRAY_W
|
||||
DataType.MATRIX -> TODO()
|
||||
else -> throw FatalAstException("invalid vardecl array datatype $declaredDatatype at $position")
|
||||
}
|
||||
else -> declaredDatatype
|
||||
}
|
||||
}
|
||||
// note: the actual datatype will be determined somewhat later (in the ast checker phase)
|
||||
// (we don't do it at init time, because we have to have a way to create a proper syntax error instead of a crash)
|
||||
lateinit var datatype: DataType
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
arrayspec?.linkParents(this)
|
||||
@ -543,10 +534,52 @@ class VarDecl(val type: VarDeclType,
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
|
||||
val scopedname: String by lazy { makeScopedName(name).joinToString(".") }
|
||||
val memorySize: Int
|
||||
get() = when(datatype) {
|
||||
DataType.BYTE -> 1
|
||||
DataType.WORD -> 2
|
||||
DataType.FLOAT -> 5 // MFLPT5
|
||||
DataType.STR,
|
||||
DataType.STR_P,
|
||||
DataType.STR_S,
|
||||
DataType.STR_PS -> {
|
||||
val lv = value as? LiteralValue ?: throw ExpressionError("need constant initializer value expression", position)
|
||||
lv.strvalue!!.length + 1
|
||||
}
|
||||
DataType.ARRAY -> {
|
||||
val aX = arrayspec?.x as? LiteralValue ?: throw ExpressionError("need constant value expression for arrayspec", position)
|
||||
aX.intvalue!!
|
||||
}
|
||||
DataType.ARRAY_W -> {
|
||||
val aX = arrayspec?.x as? LiteralValue ?: throw ExpressionError("need constant value expression for arrayspec", position)
|
||||
2*aX.intvalue!!
|
||||
}
|
||||
DataType.MATRIX -> {
|
||||
val aX = arrayspec?.x as? LiteralValue ?: throw ExpressionError("need constant value expression for arrayspec", position)
|
||||
val aY = arrayspec.y as? LiteralValue ?: throw ExpressionError("need constant value expression for arrayspec", position)
|
||||
aX.intvalue!! * aY.intvalue!!
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "VarDecl(name=$name, vartype=$type, datatype=$datatype, value=$value, pos=$position)"
|
||||
}
|
||||
|
||||
fun setDatatype() {
|
||||
datatype =
|
||||
when {
|
||||
arrayspec == null -> declaredDatatype
|
||||
arrayspec.y!=null -> when (declaredDatatype) {
|
||||
DataType.BYTE -> DataType.MATRIX
|
||||
else -> throw SyntaxError("matrix can only contain bytes", position)
|
||||
}
|
||||
else -> when (declaredDatatype) {
|
||||
DataType.BYTE -> DataType.ARRAY
|
||||
DataType.WORD -> DataType.ARRAY_W
|
||||
else -> throw SyntaxError("array can only contain bytes or words", position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -719,10 +752,10 @@ data class IdentifierReference(val nameInSource: List<String>) : IExpression {
|
||||
|
||||
override fun constValue(namespace: INameScope): LiteralValue? {
|
||||
val node = namespace.lookup(nameInSource, this)
|
||||
?: throw UndefinedSymbolException(this)
|
||||
?: throw UndefinedSymbolError(this)
|
||||
val vardecl = node as? VarDecl
|
||||
if(vardecl==null) {
|
||||
throw ExpressionException("name should be a constant, instead of: ${node::class.simpleName}", position)
|
||||
throw ExpressionError("name should be a constant, instead of: ${node::class.simpleName}", position)
|
||||
} else if(vardecl.type!=VarDeclType.CONST) {
|
||||
return null
|
||||
}
|
||||
@ -813,12 +846,12 @@ class FunctionCall(override var target: IdentifierReference, override var arglis
|
||||
"ceil" -> builtinCeil(arglist, position, namespace)
|
||||
"lsl" -> builtinLsl(arglist, position, namespace)
|
||||
"lsr" -> builtinLsr(arglist, position, namespace)
|
||||
"rol" -> throw ExpressionException("builtin function rol can't be used in expressions because it doesn't return a value", position)
|
||||
"rol2" -> throw ExpressionException("builtin function rol2 can't be used in expressions because it doesn't return a value", position)
|
||||
"ror" -> throw ExpressionException("builtin function ror can't be used in expressions because it doesn't return a value", position)
|
||||
"ror2" -> throw ExpressionException("builtin function ror2 can't be used in expressions because it doesn't return a value", position)
|
||||
"P_carry" -> throw ExpressionException("builtin function P_carry can't be used in expressions because it doesn't return a value", position)
|
||||
"P_irqd" -> throw ExpressionException("builtin function P_irqd can't be used in expressions because it doesn't return a value", position)
|
||||
"rol" -> throw ExpressionError("builtin function rol can't be used in expressions because it doesn't return a value", position)
|
||||
"rol2" -> throw ExpressionError("builtin function rol2 can't be used in expressions because it doesn't return a value", position)
|
||||
"ror" -> throw ExpressionError("builtin function ror can't be used in expressions because it doesn't return a value", position)
|
||||
"ror2" -> throw ExpressionError("builtin function ror2 can't be used in expressions because it doesn't return a value", position)
|
||||
"P_carry" -> throw ExpressionError("builtin function P_carry can't be used in expressions because it doesn't return a value", position)
|
||||
"P_irqd" -> throw ExpressionError("builtin function P_irqd can't be used in expressions because it doesn't return a value", position)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
@ -26,9 +26,9 @@ fun Module.checkValid(globalNamespace: INameScope, compilerOptions: CompilationO
|
||||
*/
|
||||
|
||||
class AstChecker(private val namespace: INameScope, private val compilerOptions: CompilationOptions) : IAstProcessor {
|
||||
private val checkResult: MutableList<SyntaxError> = mutableListOf()
|
||||
private val checkResult: MutableList<AstException> = mutableListOf()
|
||||
|
||||
fun result(): List<SyntaxError> {
|
||||
fun result(): List<AstException> {
|
||||
return checkResult
|
||||
}
|
||||
|
||||
@ -122,15 +122,23 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
||||
*/
|
||||
override fun process(assignment: Assignment): IStatement {
|
||||
if(assignment.target.identifier!=null) {
|
||||
val targetSymbol = namespace.lookup(assignment.target.identifier!!.nameInSource, assignment)
|
||||
if(targetSymbol !is VarDecl) {
|
||||
val targetName = assignment.target.identifier!!.nameInSource
|
||||
val targetSymbol = namespace.lookup(targetName, assignment)
|
||||
when {
|
||||
targetSymbol == null -> {
|
||||
checkResult.add(ExpressionError("undefined symbol: ${targetName.joinToString(".")}", assignment.position))
|
||||
return super.process(assignment)
|
||||
}
|
||||
targetSymbol !is VarDecl -> {
|
||||
checkResult.add(SyntaxError("assignment LHS must be register or variable", assignment.position))
|
||||
return super.process(assignment)
|
||||
} else if(targetSymbol.type==VarDeclType.CONST) {
|
||||
checkResult.add(SyntaxError("cannot assign new value to a constant", assignment.position))
|
||||
}
|
||||
targetSymbol.type == VarDeclType.CONST -> {
|
||||
checkResult.add(ExpressionError("cannot assign new value to a constant", assignment.position))
|
||||
return super.process(assignment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(assignment.value is LiteralValue) {
|
||||
val targetDatatype = assignment.target.determineDatatype(namespace, assignment)
|
||||
@ -361,7 +369,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
||||
|
||||
private fun checkValueTypeAndRange(datatype: DataType, arrayspec: ArraySpec?, value: LiteralValue, position: Position?) : Boolean {
|
||||
fun err(msg: String) : Boolean {
|
||||
checkResult.add(SyntaxError(msg, position))
|
||||
checkResult.add(ExpressionError(msg, position))
|
||||
return false
|
||||
}
|
||||
when (datatype) {
|
||||
@ -417,7 +425,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
||||
val number = (av as LiteralValue).intvalue
|
||||
?: return err("array must be all words")
|
||||
|
||||
val expectedSize = arrayspec?.x?.constValue(namespace)?.intvalue
|
||||
val expectedSize = arrayspec!!.x.constValue(namespace)?.intvalue!!
|
||||
if (value.arrayvalue.size != expectedSize)
|
||||
return err("initializer array size mismatch (expecting $expectedSize, got ${value.arrayvalue.size})")
|
||||
|
||||
@ -431,7 +439,29 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
||||
return err("value '$number' out of range for unsigned word")
|
||||
}
|
||||
}
|
||||
DataType.MATRIX -> TODO()
|
||||
DataType.MATRIX -> {
|
||||
// value can only be a single byte, or a byte array (which represents the matrix)
|
||||
if(value.isArray) {
|
||||
for (av in value.arrayvalue!!) {
|
||||
val number = (av as LiteralValue).intvalue
|
||||
?: return err("array must be all bytes")
|
||||
|
||||
val expectedSizeX = arrayspec!!.x.constValue(namespace)?.intvalue!!
|
||||
val expectedSizeY = arrayspec.y!!.constValue(namespace)?.intvalue!!
|
||||
val expectedSize = expectedSizeX * expectedSizeY
|
||||
if (value.arrayvalue.size != expectedSize)
|
||||
return err("initializer array size mismatch (expecting $expectedSize, got ${value.arrayvalue.size})")
|
||||
|
||||
if (number < 0 || number > 255)
|
||||
return err("value '$number' in byte array is out of range for unsigned byte")
|
||||
}
|
||||
} else {
|
||||
val number = value.intvalue
|
||||
?: return err("byte integer value expected")
|
||||
if (number < 0 || number > 255)
|
||||
return err("value '$number' out of range for unsigned byte")
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import il65.parser.ParsingFailedError
|
||||
/**
|
||||
* Checks the validity of all identifiers (no conflicts)
|
||||
* Also builds a list of all (scoped) symbol definitions
|
||||
* Finally, it also makes sure the datatype of all Var decls is set correctly.
|
||||
*/
|
||||
|
||||
fun Module.checkIdentifiers(): MutableMap<String, IStatement> {
|
||||
@ -47,6 +48,14 @@ class AstIdentifiersChecker : IAstProcessor {
|
||||
}
|
||||
|
||||
override fun process(decl: VarDecl): IStatement {
|
||||
// first, set the datatype
|
||||
try {
|
||||
decl.setDatatype()
|
||||
} catch(ax: AstException) {
|
||||
checkResult.add(ax)
|
||||
}
|
||||
|
||||
// now check the identifier
|
||||
if(BuiltinFunctionNames.contains(decl.name))
|
||||
// the builtin functions can't be redefined
|
||||
checkResult.add(NameError("builtin function cannot be redefined", decl.position))
|
||||
|
@ -85,39 +85,6 @@ data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, va
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
; source code for a stackvm program
|
||||
; init memory bytes/words/strings
|
||||
%memory
|
||||
0400 01 02 03 04 05 06 07 08 09 22 33 44 55 66
|
||||
0500 1111 2222 3333 4444
|
||||
1000 "Hello world!\n"
|
||||
%end_memory
|
||||
; init global var table with bytes/words/floats/strings
|
||||
%variables
|
||||
main.var1 str "This is main.var1"
|
||||
main.var2 byte aa
|
||||
main.var3 word ea44
|
||||
main.var4 float 3.1415927
|
||||
input.prompt str "Enter a number: "
|
||||
input.result word 0
|
||||
%end_variables
|
||||
; instructions and labels
|
||||
%instructions
|
||||
nop
|
||||
syscall WRITE_MEMSTR word:1000
|
||||
loop:
|
||||
syscall WRITE_VAR str:"input.prompt"
|
||||
syscall INPUT_VAR str:"input.result"
|
||||
syscall WRITE_VAR str:"input.result"
|
||||
push byte:8d
|
||||
syscall WRITE_CHAR
|
||||
jump loop
|
||||
%end_instructions
|
||||
|
||||
|
||||
*/
|
||||
|
||||
class Compiler(private val options: CompilationOptions) {
|
||||
fun compile(module: Module) : StackVmProgram {
|
||||
@ -125,19 +92,19 @@ class Compiler(private val options: CompilationOptions) {
|
||||
|
||||
val namespace = module.definingScope()
|
||||
|
||||
// todo
|
||||
val intermediate = StackVmProgram(module.name)
|
||||
namespace.debugPrint()
|
||||
|
||||
|
||||
// create the pool of all variables used in all blocks and scopes
|
||||
val varGather = VarGatherer(intermediate)
|
||||
varGather.process(module)
|
||||
println("Number of allocated variables and constants: ${intermediate.variables.size} (${intermediate.variablesMemSize} bytes)")
|
||||
|
||||
val stmtGatherer = StatementGatherer(intermediate, namespace)
|
||||
stmtGatherer.process(module)
|
||||
val translator = StatementTranslator(intermediate, namespace)
|
||||
translator.process(module)
|
||||
println("Number of source statements: ${translator.stmtUniqueSequenceNr}")
|
||||
println("Number of vm instructions: ${intermediate.instructions.size}")
|
||||
|
||||
intermediate.toTextLines().forEach { System.out.println(it) }
|
||||
return intermediate
|
||||
}
|
||||
|
||||
@ -151,7 +118,10 @@ class Compiler(private val options: CompilationOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
class StatementGatherer(val stackvmProg: StackVmProgram, val namespace: INameScope): IAstProcessor {
|
||||
class StatementTranslator(val stackvmProg: StackVmProgram, val namespace: INameScope): IAstProcessor {
|
||||
var stmtUniqueSequenceNr = 0
|
||||
private set
|
||||
|
||||
override fun process(subroutine: Subroutine): IStatement {
|
||||
translate(subroutine.statements)
|
||||
return super.process(subroutine)
|
||||
@ -164,6 +134,7 @@ class Compiler(private val options: CompilationOptions) {
|
||||
|
||||
private fun translate(statements: List<IStatement>) {
|
||||
for (stmt: IStatement in statements) {
|
||||
stmtUniqueSequenceNr++
|
||||
when (stmt) {
|
||||
is AnonymousStatementList -> translate(stmt.statements)
|
||||
is BuiltinFunctionStatementPlaceholder -> translate(stmt)
|
||||
@ -176,30 +147,90 @@ class Compiler(private val options: CompilationOptions) {
|
||||
is InlineAssembly -> translate(stmt)
|
||||
is IfStatement -> translate(stmt)
|
||||
is BranchStatement -> translate(stmt)
|
||||
is Directive, is VarDecl, is Subroutine -> {}
|
||||
is Directive, is VarDecl, is Subroutine -> {} // skip this, already processed these.
|
||||
else -> TODO("translate statement $stmt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(stmt: BranchStatement) {
|
||||
println("translate: $stmt")
|
||||
// todo
|
||||
private fun translate(branch: BranchStatement) {
|
||||
/*
|
||||
* A branch: IF_CC { stuff } else { other_stuff }
|
||||
* Which is desugared into:
|
||||
* BCS _stmt_999_else
|
||||
* stuff
|
||||
* JUMP _stmt_999_continue
|
||||
* _stmt_999_else:
|
||||
* other_stuff ;; optional
|
||||
* _stmt_999_continue:
|
||||
* ...
|
||||
*/
|
||||
val labelElse = makeLabel("else")
|
||||
val labelContinue = makeLabel("continue")
|
||||
val opcode = when(branch.condition) {
|
||||
BranchCondition.CS -> "bcc"
|
||||
BranchCondition.CC -> "bcs"
|
||||
BranchCondition.EQ -> "bne"
|
||||
BranchCondition.NE -> "beq"
|
||||
BranchCondition.VS -> "bvc"
|
||||
BranchCondition.VC -> "bvs"
|
||||
BranchCondition.MI -> "bpl"
|
||||
BranchCondition.PL -> "bmi"
|
||||
}
|
||||
if(branch.elsepart.isEmpty()) {
|
||||
stackvmProg.instruction("$opcode $labelContinue")
|
||||
translate(branch.statements)
|
||||
stackvmProg.label(labelContinue)
|
||||
} else {
|
||||
stackvmProg.instruction("$opcode $labelElse")
|
||||
translate(branch.statements)
|
||||
stackvmProg.instruction("jump $labelContinue")
|
||||
stackvmProg.label(labelElse)
|
||||
translate(branch.elsepart)
|
||||
stackvmProg.label(labelContinue)
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeLabel(postfix: String): String = "_il65stmt_${stmtUniqueSequenceNr}_$postfix"
|
||||
|
||||
private fun translate(stmt: IfStatement) {
|
||||
println("translate: $stmt")
|
||||
// todo
|
||||
println("@todo translate: #$stmtUniqueSequenceNr : $stmt")
|
||||
stackvmProg.instruction("nop") // todo translate if statement
|
||||
}
|
||||
|
||||
private fun translate(stmt: InlineAssembly) {
|
||||
println("translate: $stmt")
|
||||
println("@todo translate: #$stmtUniqueSequenceNr : $stmt")
|
||||
TODO("inline assembly not supported yet by stackvm")
|
||||
}
|
||||
|
||||
private fun translate(stmt: FunctionCallStatement) {
|
||||
println("translate: $stmt")
|
||||
// todo
|
||||
val targetStmt = stmt.target.targetStatement(namespace)!!
|
||||
if(targetStmt is BuiltinFunctionStatementPlaceholder) {
|
||||
// call to a builtin function
|
||||
TODO("BUILTIN_${stmt.target.nameInSource[0]}") // TODO
|
||||
return
|
||||
}
|
||||
|
||||
val targetname = when(targetStmt) {
|
||||
is Label -> targetStmt.scopedname
|
||||
is Subroutine -> targetStmt.scopedname
|
||||
else -> throw AstException("invalid call target node type: ${targetStmt::class}")
|
||||
}
|
||||
|
||||
for (arg in stmt.arglist) {
|
||||
val lv = arg.constValue(namespace)
|
||||
if(lv==null) TODO("argument must be constant for now") // TODO non-const args
|
||||
stackvmProg.instruction("push ${makeValue(lv)}")
|
||||
}
|
||||
stackvmProg.instruction("call $targetname")
|
||||
}
|
||||
|
||||
private fun makeValue(value: LiteralValue): String {
|
||||
return when {
|
||||
value.isString -> "\"${value.strvalue}\""
|
||||
value.isNumeric -> value.asNumericValue.toString()
|
||||
else -> TODO("stackvm value for $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(stmt: Jump) {
|
||||
@ -230,8 +261,8 @@ class Compiler(private val options: CompilationOptions) {
|
||||
}
|
||||
|
||||
private fun translate(stmt: Assignment) {
|
||||
println("translate: $stmt")
|
||||
// todo
|
||||
println("@todo translate: #$stmtUniqueSequenceNr : $stmt")
|
||||
stackvmProg.instruction("nop") // todo translate assignment
|
||||
}
|
||||
|
||||
private fun translate(stmt: Return) {
|
||||
@ -246,8 +277,8 @@ class Compiler(private val options: CompilationOptions) {
|
||||
}
|
||||
|
||||
private fun translate(stmt: BuiltinFunctionStatementPlaceholder) {
|
||||
println("translate: $stmt")
|
||||
// todo
|
||||
println("@todo translate: #$stmtUniqueSequenceNr : $stmt")
|
||||
stackvmProg.instruction("nop") // todo translate builtinfunction placeholder
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -258,20 +289,23 @@ class StackVmProgram(val name: String) {
|
||||
|
||||
val variables = mutableMapOf<String, VarDecl>()
|
||||
val instructions = mutableListOf<String>()
|
||||
val variablesMemSize: Int
|
||||
get() {
|
||||
return variables.values.fold(0) { acc, vardecl -> acc+vardecl.memorySize}
|
||||
}
|
||||
|
||||
fun optimize() {
|
||||
println("\nOptimizing stackvmProg code...")
|
||||
// todo
|
||||
// todo optimize stackvm code
|
||||
}
|
||||
|
||||
fun compileToAssembly(): AssemblyResult {
|
||||
println("\nGenerating assembly code from stackvmProg code... ")
|
||||
// todo
|
||||
// todo generate 6502 assembly
|
||||
return AssemblyResult(name)
|
||||
}
|
||||
|
||||
fun blockvar(scopedname: String, decl: VarDecl) {
|
||||
println("$scopedname $decl")
|
||||
variables[scopedname] = decl
|
||||
}
|
||||
|
||||
@ -295,7 +329,7 @@ class StackVmProgram(val name: String) {
|
||||
result.add("%end_variables")
|
||||
result.add("%instructions")
|
||||
result.addAll(instructions)
|
||||
result.add("%end_nstructions")
|
||||
result.add("%end_instructions")
|
||||
return result
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ class Zeropage(private val options: CompilationOptions) {
|
||||
// 2 dimensional matrix (only bytes for now)
|
||||
when(vardecl.datatype) {
|
||||
DataType.BYTE -> (vardecl.arrayspec.x as LiteralValue).intvalue!! * y
|
||||
else -> throw CompilerException("matrix can only be of byte")
|
||||
else -> throw CompilerException("matrix can only contain bytes")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -71,7 +71,7 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
||||
if(decl.value?.referencesIdentifier(decl.name) == true||
|
||||
decl.arrayspec?.x?.referencesIdentifier(decl.name) == true ||
|
||||
decl.arrayspec?.y?.referencesIdentifier(decl.name) == true) {
|
||||
errors.add(ExpressionException("recursive var declaration", decl.position))
|
||||
errors.add(ExpressionError("recursive var declaration", decl.position))
|
||||
return decl
|
||||
}
|
||||
|
||||
@ -148,14 +148,14 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
||||
optimizationsDone++
|
||||
LiteralValue(floatvalue = -subexpr.floatvalue)
|
||||
}
|
||||
else -> throw ExpressionException("can only take negative of int or float", subexpr.position)
|
||||
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
|
||||
}
|
||||
expr.operator == "~" -> when {
|
||||
subexpr.intvalue != null -> {
|
||||
optimizationsDone++
|
||||
LiteralValue(intvalue = subexpr.intvalue.inv())
|
||||
}
|
||||
else -> throw ExpressionException("can only take bitwise inversion of int", subexpr.position)
|
||||
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
|
||||
}
|
||||
expr.operator == "not" -> when {
|
||||
subexpr.intvalue != null -> {
|
||||
@ -166,9 +166,9 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
||||
optimizationsDone++
|
||||
LiteralValue(intvalue = if (subexpr.floatvalue == 0.0) 1 else 0)
|
||||
}
|
||||
else -> throw ExpressionException("can not take logical not of $subexpr", subexpr.position)
|
||||
else -> throw ExpressionError("can not take logical not of $subexpr", subexpr.position)
|
||||
}
|
||||
else -> throw ExpressionException(expr.operator, subexpr.position)
|
||||
else -> throw ExpressionError(expr.operator, subexpr.position)
|
||||
}
|
||||
result.position = subexpr.position
|
||||
return result
|
||||
@ -216,7 +216,7 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
||||
// int range
|
||||
val rangevalue = from.intvalue.rangeTo(to.intvalue)
|
||||
if (rangevalue.last - rangevalue.first > 65535) {
|
||||
throw ExpressionException("amount of values in range exceeds 65535", range.position)
|
||||
throw ExpressionError("amount of values in range exceeds 65535", range.position)
|
||||
}
|
||||
return LiteralValue(arrayvalue = rangevalue.map {
|
||||
val v = LiteralValue(intvalue = it)
|
||||
@ -229,7 +229,7 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
||||
// char range
|
||||
val rangevalue = from.strvalue[0].rangeTo(to.strvalue[0])
|
||||
if (rangevalue.last - rangevalue.first > 65535) {
|
||||
throw ExpressionException("amount of characters in range exceeds 65535", range.position)
|
||||
throw ExpressionError("amount of characters in range exceeds 65535", range.position)
|
||||
}
|
||||
val newval = LiteralValue(strvalue = rangevalue.toList().joinToString(""))
|
||||
newval.position = range.position
|
||||
@ -316,16 +316,16 @@ class ConstExprEvaluator {
|
||||
intvalue = if (left.intvalue >= right.intvalue) 1 else 0)
|
||||
right.floatvalue!=null -> LiteralValue(
|
||||
intvalue = if (left.intvalue >= right.floatvalue) 1 else 0)
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
left.floatvalue!=null -> when {
|
||||
right.intvalue!=null -> LiteralValue(
|
||||
intvalue = if (left.floatvalue >= right.intvalue) 1 else 0)
|
||||
right.floatvalue!=null -> LiteralValue(
|
||||
intvalue = if (left.floatvalue >= right.floatvalue) 1 else 0)
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
litval.position = left.position
|
||||
return litval
|
||||
@ -339,16 +339,16 @@ class ConstExprEvaluator {
|
||||
intvalue = if (left.intvalue <= right.intvalue) 1 else 0)
|
||||
right.floatvalue!=null -> LiteralValue(
|
||||
intvalue = if (left.intvalue <= right.floatvalue) 1 else 0)
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
left.floatvalue!=null -> when {
|
||||
right.intvalue!=null -> LiteralValue(
|
||||
intvalue = if (left.floatvalue <= right.intvalue) 1 else 0)
|
||||
right.floatvalue!=null -> LiteralValue(
|
||||
intvalue = if (left.floatvalue <= right.floatvalue) 1 else 0)
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
litval.position = left.position
|
||||
return litval
|
||||
@ -376,16 +376,16 @@ class ConstExprEvaluator {
|
||||
intvalue = if ((left.intvalue != 0).xor(right.intvalue != 0)) 1 else 0)
|
||||
right.floatvalue!=null -> LiteralValue(
|
||||
intvalue = if ((left.intvalue != 0).xor(right.floatvalue != 0.0)) 1 else 0)
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
left.floatvalue!=null -> when {
|
||||
right.intvalue!=null -> LiteralValue(
|
||||
intvalue = if ((left.floatvalue != 0.0).xor(right.intvalue != 0)) 1 else 0)
|
||||
right.floatvalue!=null -> LiteralValue(
|
||||
intvalue = if ((left.floatvalue != 0.0).xor(right.floatvalue != 0.0)) 1 else 0)
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
litval.position = left.position
|
||||
return litval
|
||||
@ -399,16 +399,16 @@ class ConstExprEvaluator {
|
||||
intvalue = if (left.intvalue != 0 || right.intvalue != 0) 1 else 0)
|
||||
right.floatvalue!=null -> LiteralValue(
|
||||
intvalue = if (left.intvalue != 0 || right.floatvalue != 0.0) 1 else 0)
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
left.floatvalue!=null -> when {
|
||||
right.intvalue!=null -> LiteralValue(
|
||||
intvalue = if (left.floatvalue != 0.0 || right.intvalue != 0) 1 else 0)
|
||||
right.floatvalue!=null -> LiteralValue(
|
||||
intvalue = if (left.floatvalue != 0.0 || right.floatvalue != 0.0) 1 else 0)
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
litval.position = left.position
|
||||
return litval
|
||||
@ -422,16 +422,16 @@ class ConstExprEvaluator {
|
||||
intvalue = if (left.intvalue != 0 && right.intvalue != 0) 1 else 0)
|
||||
right.floatvalue!=null -> LiteralValue(
|
||||
intvalue = if (left.intvalue != 0 && right.floatvalue != 0.0) 1 else 0)
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
left.floatvalue!=null -> when {
|
||||
right.intvalue!=null -> LiteralValue(
|
||||
intvalue = if (left.floatvalue != 0.0 && right.intvalue != 0) 1 else 0)
|
||||
right.floatvalue!=null -> LiteralValue(
|
||||
intvalue = if (left.floatvalue != 0.0 && right.floatvalue != 0.0) 1 else 0)
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
litval.position = left.position
|
||||
return litval
|
||||
@ -443,7 +443,7 @@ class ConstExprEvaluator {
|
||||
litval.position = left.position
|
||||
return litval
|
||||
}
|
||||
throw ExpressionException("cannot calculate $left ^ $right", left.position)
|
||||
throw ExpressionError("cannot calculate $left ^ $right", left.position)
|
||||
}
|
||||
|
||||
private fun bitwiseor(left: LiteralValue, right: LiteralValue): LiteralValue {
|
||||
@ -452,7 +452,7 @@ class ConstExprEvaluator {
|
||||
litval.position = left.position
|
||||
return litval
|
||||
}
|
||||
throw ExpressionException("cannot calculate $left | $right", left.position)
|
||||
throw ExpressionError("cannot calculate $left | $right", left.position)
|
||||
}
|
||||
|
||||
private fun bitwiseand(left: LiteralValue, right: LiteralValue): LiteralValue {
|
||||
@ -461,7 +461,7 @@ class ConstExprEvaluator {
|
||||
litval.position = left.position
|
||||
return litval
|
||||
}
|
||||
throw ExpressionException("cannot calculate $left & $right", left.position)
|
||||
throw ExpressionError("cannot calculate $left & $right", left.position)
|
||||
}
|
||||
|
||||
private fun power(left: LiteralValue, right: LiteralValue): LiteralValue {
|
||||
@ -470,14 +470,14 @@ class ConstExprEvaluator {
|
||||
left.intvalue!=null -> when {
|
||||
right.intvalue!=null -> LiteralValue(intvalue = left.intvalue.toDouble().pow(right.intvalue).toInt())
|
||||
right.floatvalue!=null -> LiteralValue(floatvalue = left.intvalue.toDouble().pow(right.floatvalue))
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
left.floatvalue!=null -> when {
|
||||
right.intvalue!=null -> LiteralValue(floatvalue = left.floatvalue.pow(right.intvalue))
|
||||
right.floatvalue!=null -> LiteralValue(floatvalue = left.floatvalue.pow(right.floatvalue))
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
litval.position = left.position
|
||||
return litval
|
||||
@ -489,14 +489,14 @@ class ConstExprEvaluator {
|
||||
left.intvalue!=null -> when {
|
||||
right.intvalue!=null -> LiteralValue(intvalue = left.intvalue + right.intvalue)
|
||||
right.floatvalue!=null -> LiteralValue(floatvalue = left.intvalue + right.floatvalue)
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
left.floatvalue!=null -> when {
|
||||
right.intvalue!=null -> LiteralValue(floatvalue = left.floatvalue + right.intvalue)
|
||||
right.floatvalue!=null -> LiteralValue(floatvalue = left.floatvalue + right.floatvalue)
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
litval.position = left.position
|
||||
return litval
|
||||
@ -508,14 +508,14 @@ class ConstExprEvaluator {
|
||||
left.intvalue!=null -> when {
|
||||
right.intvalue!=null -> LiteralValue(intvalue = left.intvalue - right.intvalue)
|
||||
right.floatvalue!=null -> LiteralValue(floatvalue = left.intvalue - right.floatvalue)
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
left.floatvalue!=null -> when {
|
||||
right.intvalue!=null -> LiteralValue(floatvalue = left.floatvalue - right.intvalue)
|
||||
right.floatvalue!=null -> LiteralValue(floatvalue = left.floatvalue - right.floatvalue)
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
litval.position = left.position
|
||||
return litval
|
||||
@ -528,24 +528,24 @@ class ConstExprEvaluator {
|
||||
right.intvalue!=null -> LiteralValue(intvalue = left.intvalue * right.intvalue)
|
||||
right.floatvalue!=null -> LiteralValue(floatvalue = left.intvalue * right.floatvalue)
|
||||
right.strvalue!=null -> {
|
||||
if(right.strvalue.length * left.intvalue > 65535) throw ExpressionException("string too large", left.position)
|
||||
if(right.strvalue.length * left.intvalue > 65535) throw ExpressionError("string too large", left.position)
|
||||
LiteralValue(strvalue = right.strvalue.repeat(left.intvalue))
|
||||
}
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
left.floatvalue!=null -> when {
|
||||
right.intvalue!=null -> LiteralValue(floatvalue = left.floatvalue * right.intvalue)
|
||||
right.floatvalue!=null -> LiteralValue(floatvalue = left.floatvalue * right.floatvalue)
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
left.strvalue!=null -> when {
|
||||
right.intvalue!=null -> {
|
||||
if(left.strvalue.length * right.intvalue > 65535) throw ExpressionException("string too large", left.position)
|
||||
if(left.strvalue.length * right.intvalue > 65535) throw ExpressionError("string too large", left.position)
|
||||
LiteralValue(strvalue = left.strvalue.repeat(right.intvalue))
|
||||
}
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
litval.position = left.position
|
||||
return litval
|
||||
@ -556,27 +556,27 @@ class ConstExprEvaluator {
|
||||
val litval = when {
|
||||
left.intvalue!=null -> when {
|
||||
right.intvalue!=null -> {
|
||||
if(right.intvalue==0) throw ExpressionException("attempt to divide by zero", left.position)
|
||||
if(right.intvalue==0) throw ExpressionError("attempt to divide by zero", left.position)
|
||||
LiteralValue(intvalue = left.intvalue / right.intvalue)
|
||||
}
|
||||
right.floatvalue!=null -> {
|
||||
if(right.floatvalue==0.0) throw ExpressionException("attempt to divide by zero", left.position)
|
||||
if(right.floatvalue==0.0) throw ExpressionError("attempt to divide by zero", left.position)
|
||||
LiteralValue(floatvalue = left.intvalue / right.floatvalue)
|
||||
}
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
left.floatvalue!=null -> when {
|
||||
right.intvalue!=null -> {
|
||||
if(right.intvalue==0) throw ExpressionException("attempt to divide by zero", left.position)
|
||||
if(right.intvalue==0) throw ExpressionError("attempt to divide by zero", left.position)
|
||||
LiteralValue(floatvalue = left.floatvalue / right.intvalue)
|
||||
}
|
||||
right.floatvalue!=null -> {
|
||||
if(right.floatvalue==0.0) throw ExpressionException("attempt to divide by zero", left.position)
|
||||
if(right.floatvalue==0.0) throw ExpressionError("attempt to divide by zero", left.position)
|
||||
LiteralValue(floatvalue = left.floatvalue / right.floatvalue)
|
||||
}
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionException(error, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
litval.position = left.position
|
||||
return litval
|
||||
|
@ -495,6 +495,14 @@ class Program (prog: MutableList<Instruction>,
|
||||
Opcode.JUMP -> {
|
||||
Instruction(opcode, callLabel = args)
|
||||
}
|
||||
Opcode.INC_VAR, Opcode.DEC_VAR,
|
||||
Opcode.SHR_VAR, Opcode.SHL_VAR, Opcode.ROL_VAR, Opcode.ROR_VAR,
|
||||
Opcode.ROL2_VAR, Opcode.ROR2_VAR, Opcode.POP_VAR, Opcode.PUSH_VAR -> {
|
||||
val withoutQuotes =
|
||||
if(args!!.startsWith('"') && args.endsWith('"'))
|
||||
args.substring(1, args.length-1) else args
|
||||
Instruction(opcode, Value(DataType.STR, null, withoutQuotes))
|
||||
}
|
||||
Opcode.SYSCALL -> {
|
||||
val syscallparts = args!!.split(' ')
|
||||
val call = Syscall.valueOf(syscallparts[0])
|
||||
|
7
il65/stackvm.sh
Executable file
7
il65/stackvm.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#/bin/env sh
|
||||
|
||||
IL65_LIBDIR=../lib65
|
||||
IL65CLASSPATH=out/production/il65
|
||||
LIBJARS=/opt/irmen/idea-2018/plugins/Kotlin/lib/kotlin-stdlib.jar:/opt/irmen/idea-2018/plugins/Kotlin/lib/kotlin-reflect.jar:antlr/lib/antlr-runtime-4.7.1.jar
|
||||
|
||||
java -cp ${IL65CLASSPATH}:${LIBJARS} il65.stackvm.StackVmKt $*
|
Loading…
x
Reference in New Issue
Block a user