From efd3b1f5c6f62acced3859309bafe6e66953917d Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 12 Sep 2018 00:51:48 +0200 Subject: [PATCH] matrix decl parsing and fixes --- docs/source/programming.rst | 14 +- docs/source/syntaxreference.rst | 32 ++-- il65/examples/stackvmtest.txt | 4 +- il65/examples/test.ill | 18 ++- il65/src/il65/Main.kt | 1 + il65/src/il65/ast/AST.kt | 83 +++++++--- il65/src/il65/ast/AstChecker.kt | 54 +++++-- il65/src/il65/ast/AstIdentifiersChecker.kt | 9 ++ il65/src/il65/compiler/Compiler.kt | 146 +++++++++++------- il65/src/il65/compiler/Zeropage.kt | 2 +- .../il65/optimizing/ExpressionOptimizer.kt | 94 +++++------ il65/src/il65/stackvm/StackVm.kt | 8 + il65/stackvm.sh | 7 + 13 files changed, 304 insertions(+), 168 deletions(-) create mode 100755 il65/stackvm.sh diff --git a/docs/source/programming.rst b/docs/source/programming.rst index e2df11c69..de7727ab4 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -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 - 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] + +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? diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 2bb97484f..081849527 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -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:: [ = ] Various examples:: - word thing - byte counter = 0 - 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 + 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 = 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,11 +349,12 @@ range creation: ``to`` } -array indexing: ``[`` *index* ``]`` - When put after a sequence type (array, string or matrix) it means to point to the given element in that sequence:: +.. 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) - matrix[4,2] ; the byte at the 5th column and 3rd row in the matrix + array[2] ; the third byte in the array (index is 0-based) + matrix[4,2] ; the byte at the 5th column and 3rd row in the matrix precedence grouping in expressions, or subroutine parameter list: ``(`` *expression* ``)`` diff --git a/il65/examples/stackvmtest.txt b/il65/examples/stackvmtest.txt index 1ed2e86ee..1746ef5d0 100644 --- a/il65/examples/stackvmtest.txt +++ b/il65/examples/stackvmtest.txt @@ -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" diff --git a/il65/examples/test.ill b/il65/examples/test.ill index a34bd3950..6c9f2936f 100644 --- a/il65/examples/test.ill +++ b/il65/examples/test.ill @@ -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 { diff --git a/il65/src/il65/Main.kt b/il65/src/il65/Main.kt index 39effdc90..55bd20efa 100644 --- a/il65/src/il65/Main.kt +++ b/il65/src/il65/Main.kt @@ -62,6 +62,7 @@ fun main(args: Array) { val compiler = Compiler(compilerOptions) val intermediate = compiler.compile(moduleAst) intermediate.optimize() + intermediate.toTextLines().forEach { println(it) } // val assembly = stackvmProg.compileToAssembly() // diff --git a/il65/src/il65/ast/AST.kt b/il65/src/il65/ast/AST.kt index c447247fa..6bd9a7aa4 100644 --- a/il65/src/il65/ast/AST.kt +++ b/il65/src/il65/ast/AST.kt @@ -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) : 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 } } diff --git a/il65/src/il65/ast/AstChecker.kt b/il65/src/il65/ast/AstChecker.kt index 02294e418..d928a2723 100644 --- a/il65/src/il65/ast/AstChecker.kt +++ b/il65/src/il65/ast/AstChecker.kt @@ -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 = mutableListOf() + private val checkResult: MutableList = mutableListOf() - fun result(): List { + fun result(): List { return checkResult } @@ -122,13 +122,21 @@ 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) { - 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)) - return super.process(assignment) + 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) + } + targetSymbol.type == VarDeclType.CONST -> { + checkResult.add(ExpressionError("cannot assign new value to a constant", assignment.position)) + return super.process(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 } diff --git a/il65/src/il65/ast/AstIdentifiersChecker.kt b/il65/src/il65/ast/AstIdentifiersChecker.kt index c59f8a1ad..e85bcb4cb 100644 --- a/il65/src/il65/ast/AstIdentifiersChecker.kt +++ b/il65/src/il65/ast/AstIdentifiersChecker.kt @@ -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 { @@ -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)) diff --git a/il65/src/il65/compiler/Compiler.kt b/il65/src/il65/compiler/Compiler.kt index b34266f9a..0a218bca3 100644 --- a/il65/src/il65/compiler/Compiler.kt +++ b/il65/src/il65/compiler/Compiler.kt @@ -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) { 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() val instructions = mutableListOf() + 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 } diff --git a/il65/src/il65/compiler/Zeropage.kt b/il65/src/il65/compiler/Zeropage.kt index 44fa990e6..80c73fb30 100644 --- a/il65/src/il65/compiler/Zeropage.kt +++ b/il65/src/il65/compiler/Zeropage.kt @@ -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 { diff --git a/il65/src/il65/optimizing/ExpressionOptimizer.kt b/il65/src/il65/optimizing/ExpressionOptimizer.kt index 1efd77fb2..fcff59285 100644 --- a/il65/src/il65/optimizing/ExpressionOptimizer.kt +++ b/il65/src/il65/optimizing/ExpressionOptimizer.kt @@ -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 diff --git a/il65/src/il65/stackvm/StackVm.kt b/il65/src/il65/stackvm/StackVm.kt index 4236eecd4..30f918d7f 100644 --- a/il65/src/il65/stackvm/StackVm.kt +++ b/il65/src/il65/stackvm/StackVm.kt @@ -495,6 +495,14 @@ class Program (prog: MutableList, 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]) diff --git a/il65/stackvm.sh b/il65/stackvm.sh new file mode 100755 index 000000000..4c6d488a5 --- /dev/null +++ b/il65/stackvm.sh @@ -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 $*