diff --git a/il65/compile.py b/il65/compile.py index 5a5e23396..42467b9b8 100644 --- a/il65/compile.py +++ b/il65/compile.py @@ -101,8 +101,11 @@ class PlyParser: raise ParseError("invalid number of arguments ({:d}, required: {:d})" .format(len(call.arguments.nodes), len(subdef.param_spec)), call.sourceref) for arg, param in zip(call.arguments.nodes, subdef.param_spec): - if arg.name and arg.name != param[0]: - raise ParseError("parameter name mismatch", arg.sourceref) + if arg.name: + if not param[0]: + raise ParseError("parameter is unnamed but name was used", arg.sourceref) + if arg.name != param[0]: + raise ParseError("parameter name mismatch", arg.sourceref) def check_and_merge_zeropages(self, module: Module) -> None: # merge all ZP blocks into one @@ -111,7 +114,7 @@ class PlyParser: if block.name == "ZP": # type: ignore if zeropage: # merge other ZP block into first ZP block - for node in block.nodes: + for node in block.scope.nodes: if isinstance(node, Directive): zeropage.scope.add_node(node, 0) elif isinstance(node, VarDef): @@ -172,7 +175,7 @@ class PlyParser: lvalue_types = set(datatype_of(lv, node.my_scope()) for lv in node.left.nodes) if len(lvalue_types) == 1: _, newright = coerce_constant_value(lvalue_types.pop(), node.right, node.sourceref) - if isinstance(newright, (Register, LiteralValue, Expression)): + if isinstance(newright, (Register, LiteralValue, Expression, Dereference, SymbolName, SubCall)): node.right = newright else: raise TypeError("invalid coerced constant type", newright) diff --git a/il65/emit/variables.py b/il65/emit/variables.py index 7a3c82ef7..89fe76253 100644 --- a/il65/emit/variables.py +++ b/il65/emit/variables.py @@ -247,6 +247,8 @@ def _format_string(value: str, screencodes: bool = False) -> str: def _numeric_value_str(value: Any, as_hex: bool=False) -> str: + if isinstance(value, LiteralValue): + value = value.value if isinstance(value, bool): return "1" if value else "0" if type(value) is int: diff --git a/il65/plyparse.py b/il65/plyparse.py index 0332c9682..d08d00ace 100644 --- a/il65/plyparse.py +++ b/il65/plyparse.py @@ -73,11 +73,11 @@ class AstNode: if isinstance(scope, Scope): return scope scope = scope.parent - raise LookupError("no scope found in node ancestry") + raise LookupError("no scope found in node ancestry", self) def all_nodes(self, *nodetypes: type) -> Generator['AstNode', None, None]: nodetypes = nodetypes or (AstNode, ) - for node in self.nodes: + for node in list(self.nodes): if isinstance(node, nodetypes): # type: ignore yield node for node in self.nodes: @@ -121,7 +121,10 @@ class Scope(AstNode): def save_registers(self) -> bool: if self._save_registers is not None: return self._save_registers - return self.my_scope().save_registers + try: + return self.my_scope().save_registers + except LookupError: + return False @save_registers.setter def save_registers(self, save: bool) -> None: @@ -507,7 +510,7 @@ class Goto(AstNode): return self.nodes[1] if len(self.nodes) == 2 else None # type: ignore -@attr.s(cmp=False, slots=True) +@attr.s(cmp=False, slots=True, repr=False) class CallArgument(AstNode): # one subnode: the value (Expression) name = attr.ib(type=str, default=None) @@ -553,12 +556,12 @@ class VarDef(AstNode): zp_address = attr.ib(type=int, default=None, init=False) # the address in the zero page if this var is there, will be set later @property - def value(self) -> Union[LiteralValue, Expression]: + def value(self) -> Union[LiteralValue, Expression, AddressOf, SymbolName]: return self.nodes[0] if self.nodes else None # type: ignore @value.setter - def value(self, value: Union[LiteralValue, Expression]) -> None: - assert isinstance(value, (LiteralValue, Expression)) + def value(self, value: Union[LiteralValue, Expression, AddressOf, SymbolName]) -> None: + assert isinstance(value, (LiteralValue, Expression, AddressOf, SymbolName)) if self.nodes: self.nodes[0] = value else: @@ -634,8 +637,8 @@ class Assignment(AstNode): return self.nodes[1] # type: ignore @right.setter - def right(self, rvalue: Union[Register, LiteralValue, Expression]) -> None: - assert isinstance(rvalue, (Register, LiteralValue, Expression)) + def right(self, rvalue: Union[Register, LiteralValue, Expression, Dereference, SymbolName, SubCall]) -> None: + assert isinstance(rvalue, (Register, LiteralValue, Expression, Dereference, SymbolName, SubCall)) self.nodes[1] = rvalue @@ -704,10 +707,16 @@ def coerce_constant_value(datatype: DataType, value: AstNode, raise TypeError("cannot assign '{:s}' to {:s}".format(type(value.value).__name__, datatype.name.lower()), sourceref) elif isinstance(value, (Expression, SubCall)): return False, value + elif isinstance(value, SymbolName): + symboldef = value.my_scope().lookup(value.name) + if isinstance(symboldef, VarDef) and symboldef.vartype == VarType.CONST: + return True, symboldef.value + elif isinstance(value, AddressOf): + raise NotImplementedError("addressof const coerce", value) # XXX implement this if datatype == DataType.WORD and not isinstance(value, (LiteralValue, Dereference, Register, SymbolName, AddressOf)): raise TypeError("cannot assign '{:s}' to {:s}".format(type(value).__name__, datatype.name.lower()), sourceref) elif datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT) \ - and not isinstance(value, (LiteralValue, Dereference, Register, SymbolName)): + and not isinstance(value, (LiteralValue, Dereference, Register, SymbolName, AddressOf)): raise TypeError("cannot assign '{:s}' to {:s}".format(type(value).__name__, datatype.name.lower()), sourceref) return False, value @@ -734,6 +743,8 @@ def process_constant_expression(expr: Any, sourceref: SourceRef, symbolscope: Sc value = value.value if isinstance(value, Expression): raise ExpressionEvaluationError("circular reference?", expr.sourceref) + elif isinstance(value, LiteralValue): + return value elif isinstance(value, (int, float, str, bool)): raise TypeError("symbol value node should not be a python primitive value", expr) else: @@ -1199,10 +1210,11 @@ def p_call_subroutine(p): """ subroutine_call : calltarget preserveregs_opt '(' call_arguments_opt ')' """ - p[0] = SubCall(sourceref=_token_sref(p, 3)) + sref = _token_sref(p, 3) + p[0] = SubCall(sourceref=sref) p[0].nodes.append(p[1]) p[0].nodes.append(p[2]) - p[0].nodes.append(CallArguments(nodes=p[4] or [], sourceref=p[0].sourceref)) + p[0].nodes.append(CallArguments(nodes=p[4] or [], sourceref=sref)) def p_preserveregs_opt(p): @@ -1249,7 +1261,11 @@ def p_call_argument(p): p[0] = CallArgument(sourceref=_token_sref(p, 1)) p[0].nodes.append(p[1]) elif len(p) == 4: - p[0] = CallArgument(name=p[1], sourceref=_token_sref(p, 1)) + if isinstance(p[1], AstNode): + sref = p[1].sourceref + else: + sref = _token_sref(p, 2) + p[0] = CallArgument(name=p[1], sourceref=sref) p[0].nodes.append(p[3]) diff --git a/testsource/calls.ill b/testsource/calls.ill index 6e59b7b81..f986280a9 100644 --- a/testsource/calls.ill +++ b/testsource/calls.ill @@ -32,11 +32,9 @@ bar: goto $c000 ; ---- goto sub1 - return sub2 ( ) - return sub2 () return sub2 (1 ) return sub3 (3) - return sub3 (XY="hello") + return sub3 ("hello") return sub3 ("hello, there") return sub4 (string="hello, there", other = 42) return sub4 ("hello", 42) @@ -77,7 +75,7 @@ bar: goto $c000 sub1!() sub2!(11) sub3 !(3) - sub3! (XY="hello") + sub3! ("hello") sub3! ("hello, there") sub4! ("hello", 42) sub4! ("hello", other=42) @@ -113,7 +111,7 @@ bar: goto $c000 sub1() sub2(11) sub3 (3) - sub3 (XY="hello") + sub3 ("hello") sub3 ("hello, there") sub4 ("hello", 42) sub4 ("hello", other= 42) diff --git a/testsource/dtypes.ill b/testsource/dtypes.ill index 5d40bf6cc..356781b47 100644 --- a/testsource/dtypes.ill +++ b/testsource/dtypes.ill @@ -29,6 +29,8 @@ ~ ZP { ; will be merged with other ZP blocks! var zpvar1b = $88 + + ; @todo should not need %noreturn because is zeropage } @@ -190,7 +192,7 @@ start: XY = membyte2 XY = memword1 XY = sin - XY = &sin + ; XY = &sin ; @todo not yet implemented [$c000] = A @@ -214,7 +216,7 @@ start: [$c000.word] = "" [$c000.word] = uninitbyte1 [$c000.word] = membyte2 - [$c000.word] = &membyte2 + ; [$c000.word] = &membyte2 ; @todo not yet implemented [$c000.word] = [cword2] [$c000.word] = memword1 [$c000.float] = 65535 @@ -228,7 +230,7 @@ start: [$c112.word] = [$c223.byte] [$c222.word] = [$c333.word] [$c333.word] = sin - [$c333.word] = &sin + ; [$c333.word] = &sin ; @todo not yet implemented SC = 0 @@ -264,7 +266,8 @@ start: uninitfloat = 9.8765 uninitfloat = '@' initword1 = sin - initword1 = &sin + ; initword1 = &sin ; @todo not yet implemented + membyte1 = A membyte1 = cbyte3 @@ -279,7 +282,7 @@ start: memword1 = 2233 memfloat = 3.4567 memword1 = sin - memword1 = &sin + ; memword1 = &sin ; @todo not yet implemented membyte1 = A memword1 = A diff --git a/testsource/numbergame.ill b/testsource/numbergame.ill index 9631f57a5..c650b98c5 100644 --- a/testsource/numbergame.ill +++ b/testsource/numbergame.ill @@ -106,7 +106,6 @@ sub goodbye ()->() { xxxxxx++ y++ xxxxxx = q *4 - xxxxxx = qqqqq *44 ;@todo symbol error c64scr.print_string("\nThanks for playing. Bye!\n") return