From 44c0d243efab66e6603a30345de5a44c3dc71d97 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 6 Mar 2018 22:02:50 +0100 Subject: [PATCH] astnode properties fix --- il65/emit/__init__.py | 4 ---- il65/emit/incrdecr.py | 4 ++-- il65/optimize.py | 35 +---------------------------------- il65/plyparse.py | 36 ++++++++++++++++++++---------------- todo.ill | 5 ++--- 5 files changed, 25 insertions(+), 59 deletions(-) diff --git a/il65/emit/__init__.py b/il65/emit/__init__.py index eaec41a47..cd7fd9912 100644 --- a/il65/emit/__init__.py +++ b/il65/emit/__init__.py @@ -129,7 +129,3 @@ def preserving_registers(registers: Set[str], scope: Scope, out: Callable, loads yield -@no_type_check -def scoped_name(node_with_name: AstNode, current_scope: Scope) -> str: - node_scope = node_with_name.my_scope() - return node_with_name.name if node_scope is current_scope else node_scope.name + "." + node_with_name.name diff --git a/il65/emit/incrdecr.py b/il65/emit/incrdecr.py index 040a2c9e2..0689623bf 100644 --- a/il65/emit/incrdecr.py +++ b/il65/emit/incrdecr.py @@ -7,9 +7,9 @@ is quite frequent and this generates assembly code tweaked for this case. Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 """ -from ..plyparse import VarDef, Register, IncrDecr, SymbolName, Dereference, LiteralValue +from ..plyparse import VarDef, Register, IncrDecr, SymbolName, Dereference, LiteralValue, scoped_name from ..datatypes import VarType, DataType, REGISTER_BYTES -from . import CodeError, preserving_registers, to_hex, Context, scoped_name +from . import CodeError, preserving_registers, to_hex, Context def generate_incrdecr(ctx: Context) -> None: diff --git a/il65/optimize.py b/il65/optimize.py index 8e8b5f7b8..5ce0f0e0e 100644 --- a/il65/optimize.py +++ b/il65/optimize.py @@ -36,7 +36,7 @@ class Optimizer: # @todo expression optimization: reduce expression nesting / flattening of parenthesis # @todo expression optimization: simplify logical expression when a term makes it always true or false # @todo expression optimization: optimize some simple multiplications into shifts (A*=8 -> A<<3) - self.create_aug_assignments() + # @todo expression optimization: create augmented assignment from assignment that only refers to its lvalue (A=A+10, A=4*A, ...) self.optimize_assignments() self.remove_superfluous_assignments() # @todo optimize addition with self into shift 1 (A+=A -> A<<=1) @@ -132,39 +132,6 @@ class Optimizer: raise TypeError("same_target called with invalid type(s)", node1, node2) return False - @no_type_check - def create_aug_assignments(self) -> None: - # create augmented assignments from regular assignment that only refers to the lvalue - # A=A+10, A=10+A -> A+=10, A=A*4, A=4*A -> A*=4, etc - for assignment in self.module.all_nodes(Assignment): - if len(assignment.left.nodes) > 1: - continue - if not isinstance(assignment.right, ExpressionWithOperator) or assignment.right.unary: - continue - expr = assignment.right - if expr.operator in ('-', '/', '//', '**', '<<', '>>', '&'): # non-associative operators - if isinstance(expr.right, (LiteralValue, SymbolName)) and self._same_target(assignment.left, expr.left): - num_val = expr.right.const_value() - operator = expr.operator + '=' - aug_assign = self._make_aug_assign(assignment, assignment.left.nodes[0], num_val, operator) - assignment.my_scope().replace_node(assignment, aug_assign) - self.optimizations_performed = True - continue - if expr.operator not in ('+', '*', '|', '^'): # associative operators - continue - if isinstance(expr.right, (LiteralValue, SymbolName)) and self._same_target(assignment.left, expr.left): - num_val = expr.right.const_value() - operator = expr.operator + '=' - aug_assign = self._make_aug_assign(assignment, assignment.left.nodes[0], num_val, operator) - assignment.my_scope().replace_node(assignment, aug_assign) - self.optimizations_performed = True - elif isinstance(expr.left, (LiteralValue, SymbolName)) and self._same_target(assignment.left, expr.right): - num_val = expr.left.const_value() - operator = expr.operator + '=' - aug_assign = self._make_aug_assign(assignment, assignment.left.nodes[0], num_val, operator) - assignment.my_scope().replace_node(assignment, aug_assign) - self.optimizations_performed = True - def remove_superfluous_assignments(self) -> None: # remove consecutive assignment statements to the same target, only keep the last value (only if its a constant!) # this is NOT done for memory mapped variables because these often represent a volatile register of some sort! diff --git a/il65/plyparse.py b/il65/plyparse.py index 38a5a73a1..237acdfd8 100644 --- a/il65/plyparse.py +++ b/il65/plyparse.py @@ -70,14 +70,12 @@ class UndefinedSymbolError(LookupError): start = "start" +@attr.s(repr=False, cmp=False, slots=True) class AstNode: # all ast nodes have: sourceref, parent, and nodes (=list of zero or more sub-nodes) - __slots__ = ["sourceref", "parent", "nodes"] - - def __init__(self, sourceref: SourceRef) -> None: - self.sourceref = sourceref - self.parent = None # type: AstNode # will be hooked up later - self.nodes = [] # type: List[AstNode] + sourceref = attr.ib(type=SourceRef, init=True) + parent = attr.ib(type='AstNode', init=False, default=None) # will be hooked up later + nodes = attr.ib(type=List['AstNode'], init=False, default=attr.Factory(list)) @property def lineref(self) -> str: @@ -139,7 +137,7 @@ class Directive(AstNode): @attr.s(cmp=False, slots=True, repr=False) class Scope(AstNode): # has zero or more subnodes - level = attr.ib(type=str, init=True) + level = attr.ib(type=str, init=True) # type: ignore nodes = attr.ib(type=list, init=True) # requires nodes in __init__ symbols = attr.ib(init=False) name = attr.ib(init=False) # will be set by enclosing block, or subroutine etc. @@ -400,20 +398,20 @@ class Register(Expression): @attr.s(cmp=False) class PreserveRegs(AstNode): - registers = attr.ib(type=str) + registers = attr.ib(type=str) # type: ignore # no subnodes. @attr.s(cmp=False, repr=False) class InlineAssembly(AstNode): # no subnodes. - assembly = attr.ib(type=str) + assembly = attr.ib(type=str) # type: ignore @attr.s(cmp=False, slots=True) class DatatypeNode(AstNode): # no subnodes. - name = attr.ib(type=str) + name = attr.ib(type=str) # type: ignore dimensions = attr.ib(type=list, default=None, validator=dimensions_validator) # if set, 1 or more dimensions (ints) def to_enum(self): @@ -436,14 +434,14 @@ class BuiltinFunction(AstNode): # This is a pseudo-node that will be artificially injected in the top-most scope, # to represent all supported built-in functions or math-functions. # No child nodes. - name = attr.ib(type=str) + name = attr.ib(type=str) # type: ignore func = attr.ib(type=Callable) @attr.s(cmp=False, repr=False) class Subroutine(AstNode): # one subnode: the Scope. - name = attr.ib(type=str) + name = attr.ib(type=str) # type: ignore param_spec = attr.ib(type=list) result_spec = attr.ib(type=list) address = attr.ib(type=int, default=None, validator=validate_address) @@ -552,7 +550,7 @@ class IncrDecr(AstNode): # increment or decrement something by a small CONSTANT value (1..255) # larger values will be treated/converted as an augmented assignment += or -=. # one subnode: target (Register, SymbolName, or Dereference). - operator = attr.ib(type=str, validator=attr.validators.in_(["++", "--"])) + operator = attr.ib(type=str, validator=attr.validators.in_(["++", "--"])) # type: ignore howmuch = attr.ib(default=1) @property @@ -730,7 +728,7 @@ class CallArgument(AstNode): @attr.s(cmp=False) class CallArguments(AstNode): # subnodes are zero or more subroutine call arguments (CallArgument) - nodes = attr.ib(type=list, init=True) # requires nodes in __init__ + nodes = attr.ib(type=list, init=True) # type: ignore # requires nodes in __init__ @attr.s(cmp=False, repr=False) @@ -772,7 +770,7 @@ class SubCall(Expression): @attr.s(cmp=False, slots=True, repr=False) class VarDef(AstNode): # zero or one subnode: value (Expression). - name = attr.ib(type=str) + name = attr.ib(type=str) # type: ignore vartype = attr.ib() datatype = attr.ib() size = attr.ib(type=list, default=None) @@ -872,7 +870,7 @@ class Assignment(AstNode): @attr.s(cmp=False, slots=True, repr=False) class AugAssignment(AstNode): # has two subnodes: left (=Register, SymbolName, or Dereference) and right (=Expression) - operator = attr.ib(type=str) + operator = attr.ib(type=str) # type: ignore @property def left(self) -> Union[Register, SymbolName, Dereference]: @@ -973,6 +971,12 @@ def check_symbol_definition(name: str, scope: Scope, sref: SourceRef) -> Any: raise ParseError(str(x), sref) +@no_type_check +def scoped_name(node_with_name: AstNode, current_scope: Scope) -> str: + node_scope = node_with_name.my_scope() + return node_with_name.name if node_scope is current_scope else node_scope.name + "." + node_with_name.name + + # ----------------- PLY parser definition follows ---------------------- def p_start(p): diff --git a/todo.ill b/todo.ill index 3d9b689c2..6445a8d00 100644 --- a/todo.ill +++ b/todo.ill @@ -7,15 +7,14 @@ start: counter ++ main.counter ++ - ; @ - - todo float augassign + ; @todo float augassign flt += 1000.1 flt *= 2.34 flt *= flt ;[border] &= 2 ; @todo augassign on dereference + flt = flt+ 1 ; @todo optimize into augassign XY*=3 ; @todo operator XY=XY/0 ; @todo zerodiv (during expression to code generation) @todo operator XY=XY//0 ; @todo zerodiv (during expression to code generation) @todo operator