From e6804b2bf925816d30873886c5831f0bdd1d4e99 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 12 Jan 2018 23:25:00 +0100 Subject: [PATCH] symbol table fixes --- il65/compile.py | 14 ++++++++++---- il65/generateasm.py | 10 ++++++---- il65/plyparse.py | 14 +++++++------- testsource/dtypes.ill | 6 +++--- testsource/floats.ill | 2 +- testsource/source1.ill | 4 ++-- 6 files changed, 29 insertions(+), 21 deletions(-) diff --git a/il65/compile.py b/il65/compile.py index d1e9da28a..98d35b249 100644 --- a/il65/compile.py +++ b/il65/compile.py @@ -31,12 +31,12 @@ class PlyParser: self.check_directives(module) self.process_imports(module) self.create_multiassigns(module) + self.check_and_merge_zeropages(module) self.process_all_expressions(module) if not self.parsing_import: # these shall only be done on the main module after all imports have been done: self.apply_directive_options(module) self.determine_subroutine_usage(module) - self.check_and_merge_zeropages(module) except ParseError as x: self.handle_parse_error(x) if self.parse_errors: @@ -60,6 +60,7 @@ class PlyParser: zeropage.scope.add_node(node, 0) elif isinstance(node, VarDef): zeropage.scope.add_node(node) + print("ADDED ZP VAR", node) # XXX else: raise ParseError("only variables and directives allowed in zeropage block", node.sourceref) else: @@ -72,7 +73,11 @@ class PlyParser: @no_type_check def process_all_expressions(self, module: Module) -> None: # process/simplify all expressions (constant folding etc) + encountered_blocks = set() for block, parent in module.all_scopes(): + if block.name in encountered_blocks: + raise ValueError("block names not unique:", block.name) + encountered_blocks.add(block.name) for node in block.nodes: try: node.process_expressions(block.scope) @@ -266,7 +271,7 @@ class PlyParser: if len(splits) == 2: for match in re.finditer(r"(?P[a-zA-Z_$][a-zA-Z0-9_\.]+)", splits[1]): name = match.group("symbol") - if name[0] == '$': + if name[0] == '$' or "." not in name: continue try: symbol = parent_scope[name] @@ -274,7 +279,8 @@ class PlyParser: pass else: if isinstance(symbol, Subroutine): - usages[(parent_scope.name, symbol.name)].add(str(asmnode.sourceref)) + namespace, name = name.rsplit(".", maxsplit=2) + usages[(namespace, name)].add(str(asmnode.sourceref)) def check_directives(self, module: Module) -> None: for node, parent in module.all_scopes(): @@ -329,7 +335,7 @@ class PlyParser: # append the imported module's contents (blocks) at the end of the current module for imported_module in imported: for block in imported_module.scope.filter_nodes(Block): - module.scope.nodes.append(block) + module.scope.add_node(block) def import_file(self, filename: str) -> Tuple[Module, int]: sub_parser = PlyParser(parsing_import=True) diff --git a/il65/generateasm.py b/il65/generateasm.py index cc5238f29..3c454fdd7 100644 --- a/il65/generateasm.py +++ b/il65/generateasm.py @@ -127,6 +127,7 @@ class AssemblyGenerator: self.p("{:s}\t.proc\n".format(zpblock.label)) self.generate_block_init(zpblock) self.generate_block_vars(zpblock) + # there's no code in the zero page block. self.p("\v.pend\n") for block in sorted(self.module.scope.filter_nodes(Block), key=lambda b: b.address or 0): if block.name == "ZP": @@ -149,8 +150,8 @@ class AssemblyGenerator: self.p("\v{:s} = {:s}".format(subdef.name, to_hex(subdef.address))) self.p("; end external subroutines\n") for stmt in block.scope.nodes: - if isinstance(stmt, (VarDef, Directive)): - continue # should have been handled already + if isinstance(stmt, (VarDef, Directive, Subroutine)): + continue # should have been handled already or will be later self.generate_statement(stmt) if block.name == "main" and isinstance(stmt, Label) and stmt.name == "start": # make sure the main.start routine clears the decimal and carry flags as first steps @@ -215,12 +216,13 @@ class AssemblyGenerator: # generate the block initializer # @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") float_inits = {} string_inits = [] prev_value = 0 - for variable in [vd for vd in block.scope.filter_nodes(VarDef) if vd.vartype == VarType.VAR]: + vardefs = [vd for vd in block.scope.filter_nodes(VarDef) if vd.vartype == VarType.VAR] + # @todo optimize init order (sort on value first to avoid needless register loads, etc) + for variable in vardefs: vname = variable.name vvalue = variable.value if variable.datatype == DataType.BYTE: diff --git a/il65/plyparse.py b/il65/plyparse.py index 413d687ec..596b4fef0 100644 --- a/il65/plyparse.py +++ b/il65/plyparse.py @@ -53,6 +53,8 @@ start = "start" @attr.s(cmp=False, slots=True, frozen=False) class AstNode: sourceref = attr.ib(type=SourceRef) + # when evaluating an expression, does it have to be a constant value?: + processed_expr_must_be_constant = attr.ib(type=bool, init=False, default=False) @property def lineref(self) -> str: @@ -410,8 +412,8 @@ class VarDef(AstNode): raise ParseError("constant value assignment is missing", attr.evolve(self.sourceref, column=self.sourceref.column+len(self.name))) # if the value is an expression, mark it as a *constant* expression here - if isinstance(self.value, Expression): - self.value.processed_must_be_constant = True + if isinstance(self.value, AstNode): + self.value.processed_expr_must_be_constant = True elif self.value is None and self.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT): self.value = 0 # note: value coercion is done later, when all expressions are evaluated @@ -423,7 +425,7 @@ class VarDef(AstNode): try: _, self.value = coerce_value(self.datatype, self.value, self.sourceref) except (TypeError, OverflowError) as x: - raise ParseError(str(x), self.sourceref) from None + raise ParseError("processed expression vor vardef is not a constant value: " + str(x), self.sourceref) from None @attr.s(cmp=False, slots=True, repr=False) @@ -548,7 +550,6 @@ class Expression(AstNode): operator = attr.ib(type=str) right = attr.ib() unary = attr.ib(type=bool, default=False) - processed_must_be_constant = attr.ib(type=bool, init=False, default=False) # does the expression have to be a constant value? def __attrs_post_init__(self): assert self.operator not in ("++", "--"), "incr/decr should not be an expression" @@ -586,8 +587,8 @@ class Expression(AstNode): def process_expression(value: Any, scope: Scope, sourceref: SourceRef) -> Any: # process/simplify all expressions (constant folding etc) - if isinstance(value, Expression): - must_be_constant = value.processed_must_be_constant + if isinstance(value, AstNode): + must_be_constant = value.processed_expr_must_be_constant else: must_be_constant = False if must_be_constant: @@ -631,7 +632,6 @@ def process_constant_expression(expr: Any, sourceref: SourceRef, symbolscope: Sc raise ParseError(str(x), expr.sourceref) from None elif isinstance(expr, SubCall): if isinstance(expr.target, CallTarget): - print("CALLTARGET", expr.target.address_of, expr.target.target) # XXX target = expr.target.target if isinstance(target, SymbolName): # 'function(1,2,3)' funcname = target.name diff --git a/testsource/dtypes.ill b/testsource/dtypes.ill index 154694777..3c79681b3 100644 --- a/testsource/dtypes.ill +++ b/testsource/dtypes.ill @@ -125,10 +125,10 @@ ; 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 ; @todo should work, reference this symbols' generated address (@todo generate address for ZP) - var .word initword0 = &ZP.zpvar1 + var .word initword0a = &ZP.zpmem1 + var .word initword0 = &ZP.zpvar1 ; @todo should work, reference this symbols' generated address (@todo generate address for ZP) var initbytea0 = &ZP.zpmem1 - var .word initworda1 = &ZP.zpvar1 + var .word initworda1 = &ZP.zpvar1 ; @todo should work, reference this symbols' generated address (@todo generate address for ZP) ; (constant) expressions diff --git a/testsource/floats.ill b/testsource/floats.ill index 23e4596c5..849b43b5e 100644 --- a/testsource/floats.ill +++ b/testsource/floats.ill @@ -1,6 +1,6 @@ ; floating point tests -%output prg, basic +%output basic %import c64lib diff --git a/testsource/source1.ill b/testsource/source1.ill index 3bb17b541..829e2fe89 100644 --- a/testsource/source1.ill +++ b/testsource/source1.ill @@ -8,7 +8,7 @@ %zp restore , clobber ; clobber over the zp memory normally used by basic/kernel rom, frees up more zp -~ main $0a00 +~ main { ; this is the main block with the start routine. @@ -178,7 +178,7 @@ somelabel1: } -~ fidget $1000 +~ fidget $0c00 { subroutine: