diff --git a/il65/plyparser.py b/il65/plyparser.py index 3aa99a529..a8edd2a7a 100644 --- a/il65/plyparser.py +++ b/il65/plyparser.py @@ -6,170 +6,187 @@ Written by Irmen de Jong (irmen@razorvine.net) License: GNU GPL 3.0, see LICENSE """ -from typing import List, Set +import attr from ply.yacc import yacc -from .symbols import SourceRef, AstNode +from typing import Union +from .symbols import SourceRef from .lexer import tokens, lexer, find_tok_column # get the lexer tokens. required. + start = "start" +@attr.s(cmp=False, slots=True, frozen=False) +class AstNode: + sourceref = attr.ib(type=SourceRef) + + @property + def lineref(self) -> str: + return "src l. " + str(self.sourceref.line) + + def print_tree(self) -> None: + def tostr(node: AstNode, level: int) -> None: + if not isinstance(node, AstNode): + return + indent = " " * level + name = getattr(node, "name", "") + print(indent, node.__class__.__name__, repr(name)) + try: + variables = vars(node).items() + except TypeError: + return + for name, value in variables: + if isinstance(value, AstNode): + tostr(value, level + 1) + if isinstance(value, (list, tuple, set)): + if len(value) > 0: + elt = list(value)[0] + if isinstance(elt, AstNode) or name == "nodes": + print(indent, " >", name, "=") + for elt in value: + tostr(elt, level + 2) + tostr(self, 0) + + +@attr.s(cmp=False) class Module(AstNode): - def __init__(self, nodes: List[AstNode], sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.nodes = nodes or [] + nodes = attr.ib(type=list) +@attr.s(cmp=False) class Directive(AstNode): - def __init__(self, name: str, args, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.name = name - self.args = args or [] - - -class Block(AstNode): - def __init__(self, name: str, address, scope, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.name = name - self.address = address - self.scope = scope + name = attr.ib(type=str) + args = attr.ib(type=list, default=attr.Factory(list)) +@attr.s(cmp=False) class Scope(AstNode): - def __init__(self, nodes: List[AstNode], sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.nodes = nodes + nodes = attr.ib(type=list) +@attr.s(cmp=False) +class Block(AstNode): + scope = attr.ib(type=Scope) + name = attr.ib(type=str, default=None) + address = attr.ib(type=int, default=None) + + +@attr.s(cmp=False) class Label(AstNode): - def __init__(self, name: str, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.name = name + name = attr.ib(type=str) +@attr.s(cmp=False) class Register(AstNode): - def __init__(self, name: str, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.name = name + name = attr.ib(type=str) +@attr.s(cmp=False) class PreserveRegs(AstNode): - def __init__(self, registers, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.registers = registers + registers = attr.ib(type=str) +@attr.s(cmp=False) class Assignment(AstNode): - def __init__(self, lhs, rhs, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.lhs = lhs - self.rhs = rhs + left = attr.ib() # type: Union[str, TargetRegisters, Dereference] + right = attr.ib() +@attr.s(cmp=False) class AugAssignment(Assignment): - def __init__(self, lhs, operator: str, rhs, sourceref: SourceRef) -> None: - super().__init__(lhs, rhs, sourceref) - self.operator = operator + operator = attr.ib(type=str) +@attr.s(cmp=False) class SubCall(AstNode): - def __init__(self, target, preserveregs, arguments, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.target = target - self.preserveregs = preserveregs - self.arguments = arguments + target = attr.ib() + preserve_regs = attr.ib() + arguments = attr.ib() +@attr.s(cmp=False) class Return(AstNode): - def __init__(self, valueA, valueX, valueY, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.valueA = valueA - self.valueX = valueX - self.valueY = valueY + value_A = attr.ib(default=None) + value_X = attr.ib(default=None) + value_Y = attr.ib(default=None) +@attr.s(cmp=False) class TargetRegisters(AstNode): - def __init__(self, registers: List[str], sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.registers = registers + registers = attr.ib(type=list) - def add_register(self, register) -> None: + def add(self, register: str) -> None: self.registers.append(register) +@attr.s(cmp=False) class InlineAssembly(AstNode): - def __init__(self, assembly: str, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.assembly = assembly + assembly = attr.ib(type=str) +@attr.s(cmp=False) class VarDef(AstNode): - def __init__(self, name: str, vartype, datatype, value, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.name = name - self.vartype = vartype - self.datatype = datatype - self.value = value + name = attr.ib(type=str) + vartype = attr.ib() + datatype = attr.ib() + value = attr.ib(default=None) +@attr.s(cmp=False, slots=True) class Datatype(AstNode): - def __init__(self, name: str, dimension, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.name = name - self.dimension = dimension + name = attr.ib(type=str) + dimension = attr.ib(type=list, default=None) +@attr.s(cmp=False) class Subroutine(AstNode): - def __init__(self, name: str, paramspec, resultspec, code, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.name = name - self.paramspec = paramspec - self.resultspec = resultspec - self.code = code + name = attr.ib(type=str) + param_spec = attr.ib() + result_spec = attr.ib() + scope = attr.ib(type=Scope, default=None) + address = attr.ib(type=int, default=None) + + def __attrs_post_init__(self): + if self.scope is not None and self.address is not None: + raise ValueError("subroutine must have either a scope or an address, not both") +@attr.s(cmp=False) class Goto(AstNode): - def __init__(self, target, ifstmt, condition, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.target = target - self.ifstmt = ifstmt - self.condition = condition + target = attr.ib() + if_stmt = attr.ib(default=None) + condition = attr.ib(default=None) +@attr.s(cmp=False) class Dereference(AstNode): - def __init__(self, location, datatype, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.location = location - self.datatype = datatype + location = attr.ib() + datatype = attr.ib() +@attr.s(cmp=False, slots=True) class CallTarget(AstNode): - def __init__(self, target, address_of: bool, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.target = target - self.address_of = address_of + target = attr.ib() + address_of = attr.ib(type=bool) +@attr.s(cmp=False, slots=True) class CallArgument(AstNode): - def __init__(self, name: str, value, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.name = name - self.value = value + value = attr.ib() + name = attr.ib(type=str, default=None) +@attr.s(cmp=False) class UnaryOp(AstNode): - def __init__(self, operator, operand, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.operator = operator - self.operand = operand + operator = attr.ib(type=str) + operand = attr.ib() +@attr.s(cmp=False, slots=True) class Expression(AstNode): - def __init__(self, lhs, operator, rhs, sourceref: SourceRef) -> None: - super().__init__(sourceref) - self.lhs = lhs - self.operator = operator - self.rhs = rhs + left = attr.ib() + operator = attr.ib(type=str) + right = attr.ib() def p_start(p): @@ -178,7 +195,7 @@ def p_start(p): | module_elements """ if p[1]: - p[0] = Module(p[1], _token_sref(p, 1)) + p[0] = Module(nodes=p[1], sourceref=_token_sref(p, 1)) def p_module(p): @@ -207,9 +224,9 @@ def p_directive(p): | DIRECTIVE directive_args ENDL """ if len(p) == 2: - p[0] = Directive(p[1], None, _token_sref(p, 1)) + p[0] = Directive(name=p[1], sourceref=_token_sref(p, 1)) else: - p[0] = Directive(p[1], p[2], _token_sref(p, 1)) + p[0] = Directive(name=p[1], args=p[2], sourceref=_token_sref(p, 1)) def p_directive_args(p): @@ -236,21 +253,21 @@ def p_block_name_addr(p): """ block : BITINVERT NAME INTEGER endl_opt scope """ - p[0] = Block(p[2], p[3], p[5], _token_sref(p, 1)) + p[0] = Block(name=p[2], address=p[3], scope=p[5], sourceref=_token_sref(p, 1)) def p_block_name(p): """ block : BITINVERT NAME endl_opt scope """ - p[0] = Block(p[2], None, p[4], _token_sref(p, 1)) + p[0] = Block(name=p[2], scope=p[4], sourceref=_token_sref(p, 1)) def p_block(p): """ block : BITINVERT endl_opt scope """ - p[0] = Block(None, None, p[3], _token_sref(p, 1)) + p[0] = Block(scope=p[3], sourceref=_token_sref(p, 1)) def p_endl_opt(p): @@ -265,7 +282,7 @@ def p_scope(p): """ scope : '{' scope_elements_opt '}' """ - p[0] = Scope(p[2], _token_sref(p, 1)) + p[0] = Scope(nodes=p[2], sourceref=_token_sref(p, 1)) def p_scope_elements_opt(p): @@ -304,28 +321,28 @@ def p_label(p): """ label : LABEL """ - p[0] = Label(p[1], _token_sref(p, 1)) + p[0] = Label(name=p[1], sourceref=_token_sref(p, 1)) def p_inlineasm(p): """ inlineasm : INLINEASM ENDL """ - p[0] = InlineAssembly(p[1], _token_sref(p, 1)) + p[0] = InlineAssembly(assembly=p[1], sourceref=_token_sref(p, 1)) def p_vardef(p): """ vardef : VARTYPE type_opt NAME ENDL """ - p[0] = VarDef(p[3], p[1], p[2], None, _token_sref(p, 1)) + p[0] = VarDef(name=p[3], vartype=p[1], datatype=p[2], sourceref=_token_sref(p, 1)) def p_vardef_value(p): """ vardef : VARTYPE type_opt NAME IS expression """ - p[0] = VarDef(p[3], p[1], p[2], p[5], _token_sref(p, 1)) + p[0] = VarDef(name=p[3], vartype=p[1], datatype=p[2], value=p[5], sourceref=_token_sref(p, 1)) def p_type_opt(p): @@ -335,9 +352,9 @@ def p_type_opt(p): | empty """ if len(p) == 5: - p[0] = Datatype(p[1], p[3], _token_sref(p, 1)) + p[0] = Datatype(name=p[1], dimension=p[3], sourceref=_token_sref(p, 1)) elif len(p) == 2: - p[0] = Datatype(p[1], None, _token_sref(p, 1)) + p[0] = Datatype(name=p[1], sourceref=_token_sref(p, 1)) def p_dimensions(p): @@ -362,9 +379,15 @@ def p_literal_value(p): def p_subroutine(p): """ - subroutine : SUB NAME '(' sub_param_spec ')' RARROW '(' sub_result_spec ')' subroutine_body ENDL + subroutine : SUB NAME '(' sub_param_spec ')' RARROW '(' sub_result_spec ')' subroutine_body ENDL """ - p[0] = Subroutine(p[1], p[3], p[7], p[9], _token_sref(p, 1)) + body = p[10] + if isinstance(body, Scope): + p[0] = Subroutine(name=p[2], param_spec=p[4], result_spec=p[8], scope=body, sourceref=_token_sref(p, 1)) + elif isinstance(body, int): + p[0] = Subroutine(name=p[2], param_spec=p[4], result_spec=p[8], address=body, sourceref=_token_sref(p, 1)) + else: + raise TypeError("subroutine_body", p.slice) def p_sub_param_spec(p): @@ -457,14 +480,14 @@ def p_incrdecr(p): incrdecr : assignment_target INCR | assignment_target DECR """ - p[0] = UnaryOp(p[2], p[1], _token_sref(p, 1)) + p[0] = UnaryOp(operator=p[2], operand=p[1], sourceref=_token_sref(p, 1)) def p_call_subroutine(p): """ subroutine_call : calltarget preserveregs_opt '(' call_arguments_opt ')' """ - p[0] = SubCall(p[1], p[2], p[4], _token_sref(p, 1)) + p[0] = SubCall(target=p[1], preserve_regs=p[2], arguments=p[4], sourceref=_token_sref(p, 1)) def p_preserveregs_opt(p): @@ -479,7 +502,7 @@ def p_preserveregs(p): """ preserveregs : PRESERVEREGS """ - p[0] = PreserveRegs(p[1], _token_sref(p, 1)) + p[0] = PreserveRegs(registers=p[1], sourceref=_token_sref(p, 1)) def p_call_arguments_opt(p): @@ -508,9 +531,9 @@ def p_call_argument(p): | NAME IS expression """ if len(p) == 2: - p[0] = CallArgument(None, p[1], _token_sref(p, 1)) + p[0] = CallArgument(value=p[1], sourceref=_token_sref(p, 1)) elif len(p) == 4: - p[0] = CallArgument(p[1], p[3], _token_sref(p, 1)) + p[0] = CallArgument(name=p[1], value=p[3], sourceref=_token_sref(p, 1)) def p_return(p): @@ -521,41 +544,41 @@ def p_return(p): | RETURN expression ',' expression ',' expression """ if len(p) == 2: - p[0] = Return(None, None, None, _token_sref(p, 1)) + p[0] = Return(sourceref=_token_sref(p, 1)) elif len(p) == 3: - p[0] = Return(p[2], None, None, _token_sref(p, 1)) + p[0] = Return(value_A=p[2], sourceref=_token_sref(p, 1)) elif len(p) == 5: - p[0] = Return(p[2], p[4], None, _token_sref(p, 1)) + p[0] = Return(value_A=p[2], value_X=p[4], sourceref=_token_sref(p, 1)) elif len(p) == 7: - p[0] = Return(p[2], p[4], p[6], _token_sref(p, 1)) + p[0] = Return(value_A=p[2], value_X=p[4], value_Y=p[6], sourceref=_token_sref(p, 1)) def p_register(p): """ register : REGISTER """ - p[0] = Register(p[1], _token_sref(p, 1)) + p[0] = Register(name=p[1], sourceref=_token_sref(p, 1)) def p_goto(p): """ goto : GOTO calltarget """ - p[0] = Goto(p[2], None, None, _token_sref(p, 1)) + p[0] = Goto(target=p[2], sourceref=_token_sref(p, 1)) def p_conditional_goto_plain(p): """ conditional_goto : IF GOTO calltarget """ - p[0] = Goto(p[3], p[1], None, _token_sref(p, 1)) + p[0] = Goto(target=p[3], if_stmt=p[1], sourceref=_token_sref(p, 1)) def p_conditional_goto_expr(p): """ conditional_goto : IF expression GOTO calltarget """ - p[0] = Goto(p[4], p[1], p[2], _token_sref(p, 1)) + p[0] = Goto(target=p[4], if_stmt=p[1], condition=p[2], sourceref=_token_sref(p, 1)) def p_calltarget(p): @@ -566,16 +589,16 @@ def p_calltarget(p): | dereference """ if len(p) == 2: - p[0] = CallTarget(p[1], False, _token_sref(p, 1)) + p[0] = CallTarget(target=p[1], address_of=False, sourceref=_token_sref(p, 1)) elif len(p) == 3: - p[0] = CallTarget(p[2], True, _token_sref(p, 1)) + p[0] = CallTarget(target=p[2], address_of=True, sourceref=_token_sref(p, 1)) def p_dereference(p): """ dereference : '[' dereference_operand ']' """ - p[0] = Dereference(p[2][0], p[2][1], _token_sref(p, 1)) + p[0] = Dereference(location=p[2][0], datatype=p[2][1], sourceref=_token_sref(p, 1)) def p_dereference_operand(p): @@ -600,14 +623,14 @@ def p_assignment(p): assignment : assignment_target IS expression | assignment_target IS assignment """ - p[0] = Assignment(p[1], p[3], _token_sref(p, 1)) + p[0] = Assignment(left=p[1], right=p[3], sourceref=_token_sref(p, 1)) def p_aug_assignment(p): """ aug_assignment : assignment_target AUGASSIGN expression """ - p[0] = AugAssignment(p[1], p[2], p[3], _token_sref(p, 1)) + p[0] = AugAssignment(left=p[1], operator=p[2], right=p[3], sourceref=_token_sref(p, 1)) precedence = ( @@ -632,28 +655,28 @@ def p_expression(p): | expression EQUALS expression | expression NOTEQUALS expression """ - p[0] = Expression(p[1], p[2], p[3], _token_sref(p, 1)) + p[0] = Expression(left=p[1], operator=p[2], right=p[3], sourceref=_token_sref(p, 1)) def p_expression_uminus(p): """ expression : '-' expression %prec UNARY_MINUS """ - p[0] = UnaryOp(p[1], p[2], _token_sref(p, 1)) + p[0] = UnaryOp(operator=p[1], operand=p[2], sourceref=_token_sref(p, 1)) def p_expression_addressof(p): """ expression : BITAND symbolname %prec UNARY_ADDRESSOF """ - p[0] = UnaryOp(p[1], p[2], _token_sref(p, 1)) + p[0] = UnaryOp(operator=p[1], operand=p[2], sourceref=_token_sref(p, 1)) def p_unary_expression_bitinvert(p): """ expression : BITINVERT expression """ - p[0] = UnaryOp(p[1], p[2], _token_sref(p, 1)) + p[0] = UnaryOp(operator=p[1], operand=p[2], sourceref=_token_sref(p, 1)) def p_expression_group(p): @@ -663,7 +686,7 @@ def p_expression_group(p): p[0] = p[2] -def p_expression_ass_rhs(p): +def p_expression_expr_value(p): """expression : expression_value""" p[0] = p[1] @@ -694,13 +717,12 @@ def p_target_registers(p): | target_registers ',' register """ if len(p) == 2: - p[0] = TargetRegisters([p[1]], _token_sref(p, 1)) + p[0] = TargetRegisters(registers=[p[1]], sourceref=_token_sref(p, 1)) else: - p[1].add_register(p[3]) + p[1].add(p[3]) p[0] = p[1] - def p_empty(p): """empty :""" pass diff --git a/mypy.ini b/mypy.ini index f31a065a1..15b82b2c3 100644 --- a/mypy.ini +++ b/mypy.ini @@ -5,3 +5,6 @@ incremental = True [mypy-il65/parsetab.*] ignore_errors = True + +[mypy-il65/plyparser.*] +ignore_errors = True