diff --git a/il65/compile.py b/il65/compile.py index aa952d0b4..d1e9da28a 100644 --- a/il65/compile.py +++ b/il65/compile.py @@ -13,7 +13,7 @@ from typing import Optional, Tuple, Set, Dict, Any, no_type_check import attr from .plyparse import parse_file, ParseError, Module, Directive, Block, Subroutine, Scope, VarDef, LiteralValue, \ SubCall, Goto, Return, Assignment, InlineAssembly, Register, Expression, ProgramFormat, ZpOptions,\ - SymbolName, process_constant_expression, process_dynamic_expression + SymbolName, Dereference, AddressOf from .plylex import SourceRef, print_bold from .optimize import optimize @@ -74,7 +74,12 @@ class PlyParser: # process/simplify all expressions (constant folding etc) for block, parent in module.all_scopes(): for node in block.nodes: - node.process_expressions(block.scope) + try: + node.process_expressions(block.scope) + except ParseError: + raise + except Exception as x: + self.handle_internal_error(x, "process_expressions of node {} in block {}".format(node, block.name)) @no_type_check def create_multiassigns(self, module: Module) -> None: @@ -212,6 +217,10 @@ class PlyParser: self._get_subroutine_usages_from_expression(usages, expr.right, parent_scope) elif isinstance(expr, LiteralValue): return + elif isinstance(expr, Dereference): + return self._get_subroutine_usages_from_expression(usages, expr.location, parent_scope) + elif isinstance(expr, AddressOf): + return self._get_subroutine_usages_from_expression(usages, expr.name, parent_scope) elif isinstance(expr, SymbolName): try: symbol = parent_scope[expr.name] @@ -357,6 +366,16 @@ class PlyParser: if sys.stderr.isatty(): print("\x1b[0m", file=sys.stderr, end="", flush=True) + def handle_internal_error(self, exc: Exception, msg: str="") -> None: + if sys.stderr.isatty(): + print("\x1b[1m", file=sys.stderr) + print("\nERROR: internal parser error: ", exc, file=sys.stderr) + if msg: + print(" Message:", msg, end="\n\n") + if sys.stderr.isatty(): + print("\x1b[0m", file=sys.stderr, end="", flush=True) + raise exc + if __name__ == "__main__": description = "Compiler for IL65 language, code name 'Sick'" diff --git a/il65/generateasm.py b/il65/generateasm.py index e71e0ea9f..cc5238f29 100644 --- a/il65/generateasm.py +++ b/il65/generateasm.py @@ -213,7 +213,7 @@ class AssemblyGenerator: def generate_block_init(self, block: Block) -> None: # generate the block initializer - # @todo add a block initializer subroutine that can contain custom reset/init code? (static initializers) + # @todo add a block initializer subroutine that can contain custom reset/init code? (static initializer) self.p("_il65_init_block\v; (re)set vars to initial values") # @todo optimize init order (sort on value first to avoid needless register loads, etc) self.p("\vlda #0\n\vldx #0") @@ -240,6 +240,12 @@ class AssemblyGenerator: float_inits[variable.name] = (vname, fpbytes, vvalue) elif variable.datatype in STRING_DATATYPES: string_inits.append(variable) + elif variable.datatype == DataType.BYTEARRAY: + pass # @todo init bytearray + elif variable.datatype == DataType.WORDARRAY: + pass # @todo init wordarray + elif variable.datatype == DataType.MATRIX: + pass # @todo init matrix else: raise CodeError("weird var datatype", variable.datatype) if float_inits: @@ -330,8 +336,8 @@ class AssemblyGenerator: raise CodeError("unknown variable type " + str(vardef.datatype)) if string_vars: self.p("il65_string_vars_start") - for sv in sorted(string_vars): # must be the same order as in the init routine!!! - self.p("{:s}\v.fill {:d}+1\t\t; {}".format(sv.name, len(sv.value), sv.datatype.name.lower())) + for svar in sorted(string_vars, key=lambda v: v.name): # must be the same order as in the init routine!!! + self.p("{:s}\v.fill {:d}+1\t\t; {}".format(svar.name, len(svar.value), svar.datatype.name.lower())) self.p("") def _generate_string_var(self, vardef: VarDef, init: bool=False) -> None: @@ -375,8 +381,7 @@ class AssemblyGenerator: assert rvalue is not None if isinstance(rvalue, LiteralValue): rvalue = rvalue.value - print("ASSIGN", lvalue, lvalue.datatype, operator, rvalue) - # @todo + print("ASSIGN", lvalue, lvalue.datatype, operator, rvalue) # @todo class Assembler64Tass: diff --git a/il65/optimize.py b/il65/optimize.py index f1f9bf980..cac4518a8 100644 --- a/il65/optimize.py +++ b/il65/optimize.py @@ -107,7 +107,7 @@ class Optimizer: if block.scope: for stmt in block.scope.filter_nodes(Goto): if isinstance(stmt.condition, Expression): - raise NotImplementedError("optimize goto conditionals", stmt.condition) # @todo + print("NOT IMPLEMENTED YET: optimize goto conditionals", stmt.condition) # @todo # if cond and isinstance(cond.rvalue, (int, float)) and cond.rvalue.value == 0: # simplified = False # if cond.ifstatus in ("true", "ne"): diff --git a/il65/plylex.py b/il65/plylex.py index 0debb0a4e..d76c006c4 100644 --- a/il65/plylex.py +++ b/il65/plylex.py @@ -5,6 +5,7 @@ This is the lexer of the IL65 code, that generates a stream of tokens for the pa Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 """ +import ast import sys import ply.lex import attr @@ -258,7 +259,7 @@ def t_STRING(t): (?" @@ -135,3 +151,32 @@ def test_block_nodes(): assert sub2.scope is not None assert len(sub2.scope.nodes) > 0 assert sub2.nodes is sub2.scope.nodes + + +test_source_2 = """ +~ { + 999(1,2) + &zz() +} +""" + + +def test_parser_2(): + lexer.lineno = 1 + lexer.source_filename = "sourcefile" + filter = TokenFilter(lexer) + result = parser.parse(input=test_source_2, tokenfunc=filter.token) + block = result.nodes[0] + call = block.nodes[0] + assert isinstance(call, SubCall) + assert len(call.arguments) == 2 + assert isinstance(call.target, CallTarget) + assert call.target.target == 999 + assert call.target.address_of is False + call = block.nodes[1] + assert isinstance(call, SubCall) + assert len(call.arguments) == 0 + assert isinstance(call.target, CallTarget) + assert isinstance(call.target.target, SymbolName) + assert call.target.target.name == "zz" + assert call.target.address_of is True diff --git a/testsource/calls.ill b/testsource/calls.ill index 89ed6bf8a..ff5002aa6 100644 --- a/testsource/calls.ill +++ b/testsource/calls.ill @@ -50,6 +50,10 @@ bar: goto $c000 ; goto [AX()] % ; @todo error, must be return() goto [var1] goto [mem1] ; comment + goto $c000 + goto 64738 + 64738(1,2) ; @todo jsr $64738 ?? + return 9999() ; @todo jsr 9999 ?? return [AX]() return [var1] () ; comment return [mem1] () diff --git a/testsource/conditionals.ill b/testsource/conditionals.ill index 6aa1b3991..dfc772f15 100644 --- a/testsource/conditionals.ill +++ b/testsource/conditionals.ill @@ -1,4 +1,4 @@ -%output prg,basic +%output basic %import c64lib diff --git a/testsource/dtypes.ill b/testsource/dtypes.ill index 3dc236a72..154694777 100644 --- a/testsource/dtypes.ill +++ b/testsource/dtypes.ill @@ -125,7 +125,7 @@ ; taking the address of things from the ZP will work even when it is a var ; because zp-vars get assigned a specific address (from a pool). Also, it's a byte. - var .word initword0a = &ZP.zpmem1 + var .word initword0a = &ZP.zpmem1 ; @todo should work, reference this symbols' generated address (@todo generate address for ZP) var .word initword0 = &ZP.zpvar1 var initbytea0 = &ZP.zpmem1 var .word initworda1 = &ZP.zpvar1 diff --git a/testsource/input.ill b/testsource/input.ill index 99a7dc5ae..ca3051e61 100644 --- a/testsource/input.ill +++ b/testsource/input.ill @@ -1,4 +1,4 @@ -%output prg,basic +%output basic %import c64lib diff --git a/testsource/large.ill b/testsource/large.ill index 0fa6a3a1b..d5a44058b 100644 --- a/testsource/large.ill +++ b/testsource/large.ill @@ -1,6 +1,6 @@ ; var definitions and immediate primitive data type tests -%output prg, basic +%output basic %zp clobber %import c64lib diff --git a/testsource/numbergame.ill b/testsource/numbergame.ill index 588051111..686c46753 100644 --- a/testsource/numbergame.ill +++ b/testsource/numbergame.ill @@ -1,4 +1,4 @@ -%output prg,basic +%output basic %import c64lib %import mathlib @@ -95,4 +95,4 @@ sub goodbye ()->() { return } -} \ No newline at end of file +} diff --git a/testsource/source1.ill b/testsource/source1.ill index c24b4bf5c..3bb17b541 100644 --- a/testsource/source1.ill +++ b/testsource/source1.ill @@ -4,7 +4,7 @@ ; line 3 comment -%output basic , prg ; create a c-64 program with basic SYS call to launch it +%output basic ; create a c-64 program with basic SYS call to launch it %zp restore , clobber ; clobber over the zp memory normally used by basic/kernel rom, frees up more zp diff --git a/testsource/source3.ill b/testsource/source3.ill index 3bb552ef3..669690f8f 100644 --- a/testsource/source3.ill +++ b/testsource/source3.ill @@ -1,5 +1,5 @@ -%output prg,basic ; create a c-64 program with basic SYS call to launch it +%output basic ; create a c-64 program with basic SYS call to launch it %import c64lib ; searched in several locations and with .ill file extension added diff --git a/testsource/source4.ill b/testsource/source4.ill index 571ad6ae1..7f691aa9d 100644 --- a/testsource/source4.ill +++ b/testsource/source4.ill @@ -1,4 +1,4 @@ -%output prg,basic ; create a c-64 program with basic SYS to() launch it +%output basic ; create a c-64 program with basic SYS to() launch it %import "c64lib.ill" diff --git a/todo.ill b/todo.ill index ece0f0877..b3af776f0 100644 --- a/todo.ill +++ b/todo.ill @@ -1,29 +1,36 @@ %output basic ~ ZP { - var zp1_1 - var zp1_2 - var zp1_3 - var zp1_4 - const zpc1_1 - const zpc1_2 + var zp1_1 = 200 + var zp1_2 = 200 + var .word zp1_3 = $ff99 + var .word zp1_4 = $ee88 + const zpc1_1 = 55 + const .word zpc1_2 = 2333.54566 + const .float zpc1_3 = 6.54566 } ~ ZP { - var zp2_1 - var zp2_2 - var zp2_3 - var zp2_4 - const zpc2_1 - const zpc2_2 + var zp2_1 = 100 + var zp2_2 = 100 + var .word zp2_3 = $55aa + var .word zp2_4 = $66bb + const .text zpc2_1 = "hello" + const zpc2_2 = 0 } -~ main { +~ main { + var .text hello_str = "hello there" + var .float float1 = 3.14 + var .float float2 = 3.14 + var .float float3 = 3.14 + var .float float4 = 3.14 + var .float float5 = 3.14 start: - return + return 1.22, 46 }