The expiration time for new job artifacts in CI/CD pipelines is now 30 days (GitLab default). Previously generated artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

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

- 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