mirror of
https://github.com/irmen/prog8.git
synced 2024-08-16 10:29:01 +00:00
astnode properties fix
This commit is contained in:
parent
6696887f0e
commit
44c0d243ef
@ -129,7 +129,3 @@ def preserving_registers(registers: Set[str], scope: Scope, out: Callable, loads
|
|||||||
yield
|
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
|
|
||||||
|
@ -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
|
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 ..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:
|
def generate_incrdecr(ctx: Context) -> None:
|
||||||
|
@ -36,7 +36,7 @@ class Optimizer:
|
|||||||
# @todo expression optimization: reduce expression nesting / flattening of parenthesis
|
# @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: 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)
|
# @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.optimize_assignments()
|
||||||
self.remove_superfluous_assignments()
|
self.remove_superfluous_assignments()
|
||||||
# @todo optimize addition with self into shift 1 (A+=A -> A<<=1)
|
# @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)
|
raise TypeError("same_target called with invalid type(s)", node1, node2)
|
||||||
return False
|
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:
|
def remove_superfluous_assignments(self) -> None:
|
||||||
# remove consecutive assignment statements to the same target, only keep the last value (only if its a constant!)
|
# 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!
|
# this is NOT done for memory mapped variables because these often represent a volatile register of some sort!
|
||||||
|
@ -70,14 +70,12 @@ class UndefinedSymbolError(LookupError):
|
|||||||
start = "start"
|
start = "start"
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(repr=False, cmp=False, slots=True)
|
||||||
class AstNode:
|
class AstNode:
|
||||||
# all ast nodes have: sourceref, parent, and nodes (=list of zero or more sub-nodes)
|
# all ast nodes have: sourceref, parent, and nodes (=list of zero or more sub-nodes)
|
||||||
__slots__ = ["sourceref", "parent", "nodes"]
|
sourceref = attr.ib(type=SourceRef, init=True)
|
||||||
|
parent = attr.ib(type='AstNode', init=False, default=None) # will be hooked up later
|
||||||
def __init__(self, sourceref: SourceRef) -> None:
|
nodes = attr.ib(type=List['AstNode'], init=False, default=attr.Factory(list))
|
||||||
self.sourceref = sourceref
|
|
||||||
self.parent = None # type: AstNode # will be hooked up later
|
|
||||||
self.nodes = [] # type: List[AstNode]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lineref(self) -> str:
|
def lineref(self) -> str:
|
||||||
@ -139,7 +137,7 @@ class Directive(AstNode):
|
|||||||
@attr.s(cmp=False, slots=True, repr=False)
|
@attr.s(cmp=False, slots=True, repr=False)
|
||||||
class Scope(AstNode):
|
class Scope(AstNode):
|
||||||
# has zero or more subnodes
|
# 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__
|
nodes = attr.ib(type=list, init=True) # requires nodes in __init__
|
||||||
symbols = attr.ib(init=False)
|
symbols = attr.ib(init=False)
|
||||||
name = attr.ib(init=False) # will be set by enclosing block, or subroutine etc.
|
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)
|
@attr.s(cmp=False)
|
||||||
class PreserveRegs(AstNode):
|
class PreserveRegs(AstNode):
|
||||||
registers = attr.ib(type=str)
|
registers = attr.ib(type=str) # type: ignore
|
||||||
# no subnodes.
|
# no subnodes.
|
||||||
|
|
||||||
|
|
||||||
@attr.s(cmp=False, repr=False)
|
@attr.s(cmp=False, repr=False)
|
||||||
class InlineAssembly(AstNode):
|
class InlineAssembly(AstNode):
|
||||||
# no subnodes.
|
# no subnodes.
|
||||||
assembly = attr.ib(type=str)
|
assembly = attr.ib(type=str) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
@attr.s(cmp=False, slots=True)
|
@attr.s(cmp=False, slots=True)
|
||||||
class DatatypeNode(AstNode):
|
class DatatypeNode(AstNode):
|
||||||
# no subnodes.
|
# 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)
|
dimensions = attr.ib(type=list, default=None, validator=dimensions_validator) # if set, 1 or more dimensions (ints)
|
||||||
|
|
||||||
def to_enum(self):
|
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,
|
# 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.
|
# to represent all supported built-in functions or math-functions.
|
||||||
# No child nodes.
|
# No child nodes.
|
||||||
name = attr.ib(type=str)
|
name = attr.ib(type=str) # type: ignore
|
||||||
func = attr.ib(type=Callable)
|
func = attr.ib(type=Callable)
|
||||||
|
|
||||||
|
|
||||||
@attr.s(cmp=False, repr=False)
|
@attr.s(cmp=False, repr=False)
|
||||||
class Subroutine(AstNode):
|
class Subroutine(AstNode):
|
||||||
# one subnode: the Scope.
|
# one subnode: the Scope.
|
||||||
name = attr.ib(type=str)
|
name = attr.ib(type=str) # type: ignore
|
||||||
param_spec = attr.ib(type=list)
|
param_spec = attr.ib(type=list)
|
||||||
result_spec = attr.ib(type=list)
|
result_spec = attr.ib(type=list)
|
||||||
address = attr.ib(type=int, default=None, validator=validate_address)
|
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)
|
# increment or decrement something by a small CONSTANT value (1..255)
|
||||||
# larger values will be treated/converted as an augmented assignment += or -=.
|
# larger values will be treated/converted as an augmented assignment += or -=.
|
||||||
# one subnode: target (Register, SymbolName, or Dereference).
|
# 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)
|
howmuch = attr.ib(default=1)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -730,7 +728,7 @@ class CallArgument(AstNode):
|
|||||||
@attr.s(cmp=False)
|
@attr.s(cmp=False)
|
||||||
class CallArguments(AstNode):
|
class CallArguments(AstNode):
|
||||||
# subnodes are zero or more subroutine call arguments (CallArgument)
|
# 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)
|
@attr.s(cmp=False, repr=False)
|
||||||
@ -772,7 +770,7 @@ class SubCall(Expression):
|
|||||||
@attr.s(cmp=False, slots=True, repr=False)
|
@attr.s(cmp=False, slots=True, repr=False)
|
||||||
class VarDef(AstNode):
|
class VarDef(AstNode):
|
||||||
# zero or one subnode: value (Expression).
|
# zero or one subnode: value (Expression).
|
||||||
name = attr.ib(type=str)
|
name = attr.ib(type=str) # type: ignore
|
||||||
vartype = attr.ib()
|
vartype = attr.ib()
|
||||||
datatype = attr.ib()
|
datatype = attr.ib()
|
||||||
size = attr.ib(type=list, default=None)
|
size = attr.ib(type=list, default=None)
|
||||||
@ -872,7 +870,7 @@ class Assignment(AstNode):
|
|||||||
@attr.s(cmp=False, slots=True, repr=False)
|
@attr.s(cmp=False, slots=True, repr=False)
|
||||||
class AugAssignment(AstNode):
|
class AugAssignment(AstNode):
|
||||||
# has two subnodes: left (=Register, SymbolName, or Dereference) and right (=Expression)
|
# has two subnodes: left (=Register, SymbolName, or Dereference) and right (=Expression)
|
||||||
operator = attr.ib(type=str)
|
operator = attr.ib(type=str) # type: ignore
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def left(self) -> Union[Register, SymbolName, Dereference]:
|
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)
|
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 ----------------------
|
# ----------------- PLY parser definition follows ----------------------
|
||||||
|
|
||||||
def p_start(p):
|
def p_start(p):
|
||||||
|
5
todo.ill
5
todo.ill
@ -7,15 +7,14 @@
|
|||||||
start:
|
start:
|
||||||
counter ++
|
counter ++
|
||||||
main.counter ++
|
main.counter ++
|
||||||
; @
|
; @todo float augassign
|
||||||
|
|
||||||
todo float augassign
|
|
||||||
flt += 1000.1
|
flt += 1000.1
|
||||||
flt *= 2.34
|
flt *= 2.34
|
||||||
flt *= flt
|
flt *= flt
|
||||||
|
|
||||||
;[border] &= 2 ; @todo augassign on dereference
|
;[border] &= 2 ; @todo augassign on dereference
|
||||||
|
|
||||||
|
flt = flt+ 1 ; @todo optimize into augassign
|
||||||
XY*=3 ; @todo operator
|
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
|
||||||
XY=XY//0 ; @todo zerodiv (during expression to code generation) @todo operator
|
XY=XY//0 ; @todo zerodiv (during expression to code generation) @todo operator
|
||||||
|
Loading…
Reference in New Issue
Block a user