Commit 6ef3b9fe authored by Eckhart Arnold's avatar Eckhart Arnold

- transform.py: left_associative() and swing_left() added for rearranging trees (see docstrings.)

parent d34bec8e
......@@ -511,6 +511,10 @@ def has_parent(context: List[Node], tag_name_set: AbstractSet[str]) -> bool:
def update_attr(node: Node, child: Node):
"""
Adds all attributes from `child` to `node`.This is needed, in order
to keep the attributes if the child node is going to be eliminated.
"""
if hasattr(child, '_xml_attr'):
for k, v in child.attr:
if k in node.attr and v != node.attr[k]:
......@@ -520,7 +524,29 @@ def update_attr(node: Node, child: Node):
node.attr[k] = v
def swap_attributes(node: Node, other: Node):
"""
Exchanges the attributes between node and other. This might be
needed when rearanging trees.
"""
NA = node.attr_active()
OA = other.attr_active()
if NA or OA:
save = node._xml_attr if NA else None
if OA:
node._xml_attr = other._xml_attr
elif NA:
node._xml_attr = None
if NA:
other._xml_attr = node._xml_attr
elif OA:
other._xml_attr = None
def _replace_by(node: Node, child: Node):
"""
Replaces node's contents by child's content including the tag name.
"""
if node.is_anonymous() or not child.is_anonymous():
node.tag_name = child.tag_name
# name, ptype = (node.tag_name.split(':') + [''])[:2]
......@@ -531,6 +557,9 @@ def _replace_by(node: Node, child: Node):
def _reduce_child(node: Node, child: Node):
"""
Sets node's results to the child's result, keeping node's tag_name.
"""
node.result = child.result
update_attr(node, child)
......@@ -846,9 +875,68 @@ def move_adjacent(context: List[Node], condition: Callable = is_insignificant_wh
def left_associative(context: List[Node]):
"""
Rearranges a flat node into a left associative tree.
Rearranges a flat node with infix operators into a left associative tree.
"""
node = context[-1]
assert len(node.children) >= 3
assert (len(node.children) + 1) % 2 == 0
rest = list(node.result)
left, rest = rest[0], rest[1:]
while rest:
infix, right, rest = rest[:2], rest[2:]
assert not infix.children
assert infix.tag_name[0:1] != ":"
left = Node(infix.tag_name, (left, right))
node.result = left
@transformation_factory(collections.abc.Set)
def left_swing(context: List[Node], operators: AbstractSet[str]):
"""
Rearranges a node that contains a sub-node on the right
with a left-associative operator so that the tree structure
reflects its left-associative character.
"""
node = context[-1]
assert node.children and len(node.children) == 2
assert node.tag_name in operators
right = node.children[1]
if right.tag_name in operators:
assert right.children and len(right.children) == 2
a, b, c = node.children[0], right.children[0], right.children[1]
op1 = node.tag_name
op2 = right.tag_name
right.result = (a, b)
right.tag_name = op1
node.result = (right, c)
node.tag_name = op2
swap_attributes(node, right)
# @transformation_factory(collections.abc.Set)
# def left_associative_tree(context: List[Node], operators: AbstracSet[str]):
# """
# Rearranges a right associative tree into a left associative tree.
# ``operators`` is a list of tag names of nodes that shall be rearranged.
# Other nodes will be lelft untouched.
# """
# node = context[-1]
# assert node.tag_name in operators
# right = node.children[1]
# while right.tag_name in operators:
# node.result = (node.children[0], right.children[0])
# right.result = (node, right.children[1])
# node = right
# right = node.children[1]
# parent = context[-2]
# result = list(parent.result)
# for i in range(len(result)):
# if result[i] == contexnt[-1]:
# result[i] = node
# parent.result = tuple(result)
# break
# else:
# assert False, "???"
#######################################################################
......
......@@ -19,10 +19,9 @@
#
#######################################################################
expression = ((addition | subtraction | term) !/[+-]/)
| ((subtraction | addition | term) !/[-+]/)
addition = (subtraction | addition | term) "+" term
subtraction = (addition | subtraction | term) "-" term
expression = add | sub | term
add = term "+" expression
sub = term "-" expression
#######################################################################
......@@ -31,9 +30,9 @@ subtraction = (addition | subtraction | term) "-" term
#
#######################################################################
term = multiplication | division | factor
multiplication = factor ["*"] term
division = term "/" (multiplication | factor)
term = mul | div | factor
mul = factor ["*"] term
div = factor "/" term
#######################################################################
......@@ -42,9 +41,9 @@ division = term "/" (multiplication | factor)
#
#######################################################################
factor = [sign] ( NUMBER | VARIABLE | group )
sign = PLUS | MINUS
group = "(" §expression ")"
factor = [sign] ( NUMBER | VARIABLE | group )
sign = PLUS | MINUS
group = "(" §expression ")"
#######################################################################
......@@ -53,7 +52,7 @@ group = "(" §expression ")"
#
#######################################################################
PLUS = /\+/
MINUS = /-/
NUMBER = /(?:0|(?:[1-9]\d*))(?:\.\d+)?/~
VARIABLE = /[A-Za-z]/~
PLUS = /\+/
MINUS = /-/
NUMBER = /(?:0|(?:[1-9]\d*))(?:\.\d+)?/~
VARIABLE = /[A-Za-z]/~
......@@ -58,12 +58,9 @@ def get_preprocessor() -> PreprocessorFunc:
class ArithmeticExperimentalGrammar(Grammar):
r"""Parser for an ArithmeticExperimental source file.
"""
addition = Forward()
expression = Forward()
multiplication = Forward()
subtraction = Forward()
term = Forward()
source_hash__ = "1811447b927aae855ee58328bce76d20"
source_hash__ = "59c96ad0b1c0703bea6ba8fc50253bea"
static_analysis_pending__ = [True]
parser_initialization__ = ["upon instantiation"]
resume_rules__ = {}
......@@ -79,12 +76,12 @@ class ArithmeticExperimentalGrammar(Grammar):
group = Series(Series(DropToken("("), dwsp__), expression, Series(DropToken(")"), dwsp__), mandatory=1)
sign = Alternative(PLUS, MINUS)
factor = Series(Option(sign), Alternative(NUMBER, VARIABLE, group))
division = Series(term, Series(DropToken("/"), dwsp__), Alternative(multiplication, factor))
multiplication.set(Series(factor, Option(Series(DropToken("*"), dwsp__)), term))
term.set(Alternative(multiplication, division, factor))
subtraction.set(Series(Alternative(addition, subtraction, term), Series(DropToken("-"), dwsp__), term))
addition.set(Series(Alternative(subtraction, addition, term), Series(DropToken("+"), dwsp__), term))
expression.set(Alternative(Series(Alternative(addition, subtraction, term), NegativeLookahead(RegExp('[+-]'))), Series(Alternative(subtraction, addition, term), NegativeLookahead(RegExp('[-+]')))))
div = Series(factor, Series(DropToken("/"), dwsp__), term)
mul = Series(factor, Option(Series(DropToken("*"), dwsp__)), term)
term.set(Alternative(mul, div, factor))
sub = Series(term, Series(DropToken("-"), dwsp__), expression)
add = Series(term, Series(DropToken("+"), dwsp__), expression)
expression.set(Alternative(add, sub, term))
root__ = expression
def get_grammar() -> ArithmeticExperimentalGrammar:
......
......@@ -13,19 +13,19 @@ M7: "-2x"
[fail:term]
[match:multiplication]
[match:mul]
M1: "2 * 4"
[ast:multiplication]
[ast:mul]
[fail:multiplication]
[fail:mul]
[match:division]
[match:div]
M1: "2 / 4"
M2: "-x / 7"
[ast:division]
[ast:div]
[fail:division]
[fail:div]
......@@ -13,23 +13,25 @@ M10: "1 + 2 + 3 - 4"
M11: "1 - 2 + 3 - 4"
M12: "1 + 2 - 3 - 4"
M13: "1 - 2 - 3 - 4"
M14: "1 - 2 * 3 - 4 + 5"
M15: "1 - 3 + 3 * 4"
[ast:expression]
[fail:expression]
[match:addition]
[match:add]
M1: "2 + x"
[ast:addition]
[ast:add]
[fail:addition]
[fail:add]
[match:subtraction]
[match:sub]
[ast:subtraction]
[ast:sub]
[fail:subtraction]
[fail:sub]
......@@ -15,8 +15,8 @@ setup(
name='DHParser',
version=__version__,
packages=['DHParser'],
ext_modules=cythonize(['DHParser/toolkit.py',
'DHParser/stringview.py',
ext_modules=cythonize(['DHParser/stringview.py',
'DHParser/toolkit.py',
'DHParser/preprocess.py',
'DHParser/error.py',
'DHParser/syntaxtree.py',
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment