Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
D
DHParser
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Iterations
Merge Requests
0
Merge Requests
0
Requirements
Requirements
List
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Operations
Operations
Incidents
Analytics
Analytics
Code Review
Insights
Issue
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
badw-it
DHParser
Commits
dcd152d7
Commit
dcd152d7
authored
Mar 08, 2019
by
di68kap
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
- test case for syntaxtree json serialization
parent
24538438
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
112 additions
and
99 deletions
+112
-99
DHParser/error.py
DHParser/error.py
+3
-3
DHParser/syntaxtree.py
DHParser/syntaxtree.py
+98
-96
test/test_syntaxtree.py
test/test_syntaxtree.py
+11
-0
No files found.
DHParser/error.py
View file @
dcd152d7
...
...
@@ -41,7 +41,7 @@ import bisect
from
DHParser.preprocess
import
SourceMapFunc
from
DHParser.stringview
import
StringView
from
DHParser.toolkit
import
typing
from
typing
import
Iterable
,
Iterator
,
Union
,
Tuple
,
List
from
typing
import
Iterable
,
Iterator
,
Union
,
Dict
,
Tuple
,
List
__all__
=
(
'ErrorCode'
,
'Error'
,
...
...
@@ -139,8 +139,8 @@ class Error:
'data'
:
[
self
.
message
,
self
.
_pos
,
self
.
code
,
self
.
orig_pos
,
self
.
line
,
self
.
column
]
}
@
static
def
from_json_obj
(
self
,
json_obj
:
Dict
)
->
Error
:
@
static
method
def
from_json_obj
(
json_obj
:
Dict
)
->
'Error'
:
"""Convert a json object representing an Error object back into an
Error object. Raises a ValueError, if json_obj does not represent
an Error object"""
...
...
DHParser/syntaxtree.py
View file @
dcd152d7
...
...
@@ -338,7 +338,6 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
or
isinstance
(
result
,
StringView
)),
"%s (%s)"
%
(
str
(
result
),
str
(
type
(
result
)))
# Possible optimization: Do not allow single nodes as argument:
# assert not isinstance(result, Node)
self
.
_len
=
-
1
# lazy evaluation
# self._content = None
if
isinstance
(
result
,
Node
):
self
.
children
=
(
result
,)
...
...
@@ -471,6 +470,98 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
return
True
# neither self nor other have any attributes
def
select
(
self
,
match_function
:
Callable
,
include_root
:
bool
=
False
,
reverse
:
bool
=
False
)
\
->
Iterator
[
'Node'
]:
"""
Finds nodes in the tree that fulfill a given criterion.
`select` is a generator that yields all nodes for which the
given `match_function` evaluates to True. The tree is
traversed pre-order.
See function `Node.select_by_tag` for some examples.
Args:
match_function (function): A function that takes as Node
object as argument and returns True or False
include_root (bool): If False, only descendant nodes will be
checked for a match.
reverse (bool): If True, the tree will be walked in reverse
order, i.e. last children first.
Yields:
Node: All nodes of the tree for which
``match_function(node)`` returns True
"""
if
include_root
and
match_function
(
self
):
yield
self
child_iterator
=
reversed
(
self
.
children
)
if
reverse
else
self
.
children
for
child
in
child_iterator
:
if
match_function
(
child
):
yield
child
yield
from
child
.
select
(
match_function
,
False
,
reverse
)
# The above variant is slightly faster
# for child in child_iterator:
# yield from child.select(match_function, True, reverse)
def
select_by_tag
(
self
,
tag_names
:
Union
[
str
,
AbstractSet
[
str
]],
include_root
:
bool
=
False
)
->
Iterator
[
'Node'
]:
"""
Returns an iterator that runs through all descendants that have one
of the given tag names.
Examples::
>>> tree = parse_sxpr('(a (b "X") (X (c "d")) (e (X "F")))')
>>> list(flatten_sxpr(item.as_sxpr()) for item in tree.select_by_tag("X", False))
['(X (c "d"))', '(X "F")']
>>> list(flatten_sxpr(item.as_sxpr()) for item in tree.select_by_tag({"X", "b"}, False))
['(b "X")', '(X (c "d"))', '(X "F")']
>>> any(tree.select_by_tag('a', False))
False
>>> list(flatten_sxpr(item.as_sxpr()) for item in tree.select_by_tag('a', True))
['(a (b "X") (X (c "d")) (e (X "F")))']
>>> flatten_sxpr(next(tree.select_by_tag("X", False)).as_sxpr())
'(X (c "d"))'
Args:
tag_name(set): A tag name or set of tag names that is being
searched for
include_root (bool): If False, only descendant nodes will be
checked for a match.
Yields:
Node: All nodes which have a given tag name.
"""
if
isinstance
(
tag_names
,
str
):
tag_names
=
frozenset
({
tag_names
})
return
self
.
select
(
lambda
node
:
node
.
tag_name
in
tag_names
,
include_root
)
def
pick
(
self
,
tag_names
:
Union
[
str
,
Set
[
str
]])
->
Optional
[
'Node'
]:
"""
Picks the first descendant with one of the given tag_names.
This function is mostly just syntactic sugar for
``next(node.select_by_tag(tag_names, False))``. However, rather than
raising a StopIterationError if no descendant with the given tag-name
exists, it returns None.
"""
try
:
return
next
(
self
.
select_by_tag
(
tag_names
,
False
))
except
StopIteration
:
return
None
def
tree_size
(
self
)
->
int
:
"""
Recursively counts the number of nodes in the tree including the root node.
"""
return
sum
(
child
.
tree_size
()
for
child
in
self
.
children
)
+
1
#
# serialization methods
#
def
_tree_repr
(
self
,
tab
,
open_fn
,
close_fn
,
data_fn
=
lambda
i
:
i
,
density
=
0
,
inline
=
False
,
inline_fn
=
lambda
node
:
False
)
->
str
:
"""
...
...
@@ -659,107 +750,18 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
density
=
1
,
inline_fn
=
inlining
)
def
select
(
self
,
match_function
:
Callable
,
include_root
:
bool
=
False
,
reverse
:
bool
=
False
)
\
->
Iterator
[
'Node'
]:
"""
Finds nodes in the tree that fulfill a given criterion.
`select` is a generator that yields all nodes for which the
given `match_function` evaluates to True. The tree is
traversed pre-order.
See function `Node.select_by_tag` for some examples.
Args:
match_function (function): A function that takes as Node
object as argument and returns True or False
include_root (bool): If False, only descendant nodes will be
checked for a match.
reverse (bool): If True, the tree will be walked in reverse
order, i.e. last children first.
Yields:
Node: All nodes of the tree for which
``match_function(node)`` returns True
"""
if
include_root
and
match_function
(
self
):
yield
self
child_iterator
=
reversed
(
self
.
children
)
if
reverse
else
self
.
children
for
child
in
child_iterator
:
if
match_function
(
child
):
yield
child
yield
from
child
.
select
(
match_function
,
False
,
reverse
)
# The above variant is slightly faster
# for child in child_iterator:
# yield from child.select(match_function, True, reverse)
def
select_by_tag
(
self
,
tag_names
:
Union
[
str
,
AbstractSet
[
str
]],
include_root
:
bool
=
False
)
->
Iterator
[
'Node'
]:
"""
Returns an iterator that runs through all descendants that have one
of the given tag names.
Examples::
>>> tree = parse_sxpr('(a (b "X") (X (c "d")) (e (X "F")))')
>>> list(flatten_sxpr(item.as_sxpr()) for item in tree.select_by_tag("X", False))
['(X (c "d"))', '(X "F")']
>>> list(flatten_sxpr(item.as_sxpr()) for item in tree.select_by_tag({"X", "b"}, False))
['(b "X")', '(X (c "d"))', '(X "F")']
>>> any(tree.select_by_tag('a', False))
False
>>> list(flatten_sxpr(item.as_sxpr()) for item in tree.select_by_tag('a', True))
['(a (b "X") (X (c "d")) (e (X "F")))']
>>> flatten_sxpr(next(tree.select_by_tag("X", False)).as_sxpr())
'(X (c "d"))'
Args:
tag_name(set): A tag name or set of tag names that is being
searched for
include_root (bool): If False, only descendant nodes will be
checked for a match.
Yields:
Node: All nodes which have a given tag name.
"""
if
isinstance
(
tag_names
,
str
):
tag_names
=
frozenset
({
tag_names
})
return
self
.
select
(
lambda
node
:
node
.
tag_name
in
tag_names
,
include_root
)
def
pick
(
self
,
tag_names
:
Union
[
str
,
Set
[
str
]])
->
Optional
[
'Node'
]:
"""
Picks the first descendant with one of the given tag_names.
This function is mostly just syntactic sugar for
``next(node.select_by_tag(tag_names, False))``. However, rather than
raising a StopIterationError if no descendant with the given tag-name
exists, it returns None.
"""
try
:
return
next
(
self
.
select_by_tag
(
tag_names
,
False
))
except
StopIteration
:
return
None
def
tree_size
(
self
)
->
int
:
"""
Recursively counts the number of nodes in the tree including the root node.
"""
return
sum
(
child
.
tree_size
()
for
child
in
self
.
children
)
+
1
def
to_json_obj
(
self
)
->
Dict
:
"""Seralize a node or tree as json-object"""
return
{
'__class__'
:
'DHParser.Node'
,
'data'
:
[
self
.
tag_name
,
[
child
.
to_json_obj
()
for
child
in
self
.
children
]
if
self
.
children
else
str
(
self
.
result_
),
else
str
(
self
.
_result
),
self
.
_pos
,
self
.
_xmlattr
if
self
.
attr_active
()
else
None
]
}
dict
(
self
.
_xml_attr
)
if
self
.
attr_active
()
else
None
]
}
@
static
def
from_json_obj
(
self
,
json_obj
:
Dict
)
->
Error
:
@
static
method
def
from_json_obj
(
json_obj
:
Dict
)
->
'Node'
:
"""Convert a json object representing a node (or tree) back into a
Node object. Raises a ValueError, if `json_obj` does not represent
a node."""
...
...
@@ -767,7 +769,7 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
raise
ValueError
(
'JSON object: '
+
str
(
json_obj
)
+
' does not represent a Node object.'
)
tag_name
,
result
,
pos
,
attr
=
json_obj
[
'data'
]
if
isinstance
(
str
):
if
isinstance
(
result
,
str
):
leafhint
=
True
else
:
leafhint
=
False
...
...
@@ -775,7 +777,7 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
node
=
Node
(
tag_name
,
result
,
leafhint
)
node
.
_pos
=
pos
if
attr
:
node
.
attr
=
attr
node
.
attr
.
update
(
attr
)
return
node
...
...
test/test_syntaxtree.py
View file @
dcd152d7
...
...
@@ -71,6 +71,17 @@ class TestParseXML:
assert
flat_xml
==
'<alpha><beta>gamma</beta></alpha>'
,
flat_xml
class
TestParseJSON
:
def
test_roundtrip
(
self
):
tree
=
parse_sxpr
(
'(a (b c) (d (e f) (h i)))'
)
d
=
tree
.
pick
(
'd'
)
d
.
attr
[
'name'
]
=
"James Bond"
d
.
attr
[
'id'
]
=
'007'
json_obj_tree
=
tree
.
to_json_obj
()
tree_copy
=
Node
.
from_json_obj
(
json_obj_tree
)
assert
tree_copy
.
equals
(
tree
)
class
TestNode
:
"""
Tests for class Node
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment