Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Open sidebar
badw-it
DHParser
Commits
cd2e8f1d
Commit
cd2e8f1d
authored
Apr 17, 2018
by
di68kap
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
- syntaxtree.py: added RootNode
parent
5d7bfd01
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
118 additions
and
74 deletions
+118
-74
DHParser/syntaxtree.py
DHParser/syntaxtree.py
+59
-58
test/test_parse.py
test/test_parse.py
+25
-0
test/test_syntaxtree.py
test/test_syntaxtree.py
+34
-16
No files found.
DHParser/syntaxtree.py
View file @
cd2e8f1d
...
...
@@ -42,6 +42,7 @@ __all__ = ('ParserBase',
'ZOMBIE_PARSER'
,
'ZOMBIE_NODE'
,
'Node'
,
'RootNode'
,
'parse_sxpr'
,
'flatten_sxpr'
)
...
...
@@ -207,13 +208,6 @@ class Node(collections.abc.Sized):
parsing (i.e. AST-transformation and compiling), for
example by calling ``isinstance(node.parer, ...)``.
errors (list): A list of parser- or compiler-errors:
tuple(position, string) attached to this node
error_flag (int): 0 if no error occurred in either the node
itself or any of its descendants. Otherwise contains the
highest warning or error level or all errors that occurred.
len (int): The full length of the node's string result if the
node is a leaf node or, otherwise, the concatenated string
result's of its descendants. The figure always represents
...
...
@@ -243,16 +237,13 @@ class Node(collections.abc.Sized):
_parent (Node): SLOT RESERVED FOR FUTURE USE!
"""
__slots__
=
[
'_result'
,
'children'
,
'_errors'
,
'_len'
,
'_pos'
,
'parser'
,
'error_flag'
,
'_xml_attr'
,
'_content'
,
'_parent'
]
__slots__
=
[
'_result'
,
'children'
,
'_len'
,
'_pos'
,
'parser'
,
'_xml_attr'
,
'_content'
]
def
__init__
(
self
,
parser
,
result
:
ResultType
,
leafhint
:
bool
=
False
)
->
None
:
"""
Initializes the ``Node``-object with the ``Parser``-Instance
that generated the node and the parser's result.
"""
self
.
error_flag
=
0
# type: int
self
.
_errors
=
[]
# type: List[Error]
self
.
_pos
=
-
1
# type: int
# Assignment to self.result initializes the attributes _result, children and _len
# The following if-clause is merely an optimization, i.e. a fast-path for leaf-Nodes
...
...
@@ -469,53 +460,13 @@ class Node(collections.abc.Sized):
return
self
@
property
def
errors
(
self
)
->
List
[
Error
]:
"""
Returns the errors that occurred at this Node,
not including any errors from child nodes.
"""
return
self
.
_errors
.
copy
()
def
add_error
(
self
,
message
:
str
,
code
:
int
=
Error
.
ERROR
)
->
'Node'
:
"""
Adds an error to this Node.
Parameters:
message(str): A string with the error message.abs
code(int): An error code to identify the kind of error
"""
self
.
_errors
.
append
(
Error
(
message
,
code
))
self
.
error_flag
=
max
(
self
.
error_flag
,
self
.
_errors
[
-
1
].
code
)
return
self
def
collect_errors
(
self
,
clear_errors
=
False
)
->
List
[
Error
]:
"""
Recursively adds line- and column-numbers to all error objects.
Returns all errors of this node or any child node in the form
of a set of tuples (position, error_message), where position
is always relative to this node.
"""
errors
=
self
.
errors
for
err
in
errors
:
err
.
pos
=
self
.
pos
if
self
.
children
:
for
child
in
self
.
children
:
errors
.
extend
(
child
.
collect_errors
(
clear_errors
))
if
clear_errors
:
self
.
_errors
=
[]
self
.
error_flag
=
0
else
:
if
self
.
_errors
:
self
.
error_flag
=
max
(
err
.
code
for
err
in
self
.
errors
)
if
self
.
children
:
max_child_error
=
max
(
child
.
error_flag
for
child
in
self
.
children
)
self
.
error_flag
=
max
(
self
.
error_flag
,
max_child_error
)
return
errors
# @property
# def errors(self) -> List[Error]:
# """
# Returns the errors that occurred at this Node,
# not including any errors from child nodes.
# """
# return self._errors.copy()
@
property
def
attributes
(
self
):
...
...
@@ -727,6 +678,56 @@ class Node(collections.abc.Sized):
return
sum
(
child
.
tree_size
()
for
child
in
self
.
children
)
+
1
class
RootNode
(
Node
):
"""
errors (list): A list of parser- or compiler-errors:
tuple(position, string) attached to this node
error_flag (int): 0 if no error occurred in either the node
itself or any of its descendants. Otherwise contains the
highest warning or error level or all errors that occurred.
"""
def
__init__
(
self
):
super
().
__init__
(
ZOMBIE_PARSER
,
''
)
self
.
errors
=
[]
self
.
error_flag
=
0
def
swallow
(
self
,
node
):
self
.
_result
=
node
.
_result
self
.
children
=
node
.
children
self
.
_len
=
node
.
_len
self
.
_pos
=
node
.
_pos
self
.
parser
=
node
.
parser
self
.
_xml_attr
=
node
.
_xml_attr
self
.
_content
=
node
.
_content
def
add_error
(
self
,
pos
:
int
,
message
:
str
,
code
:
int
=
Error
.
ERROR
)
->
'Node'
:
"""
Adds an error to this tree.
Parameters:
pos(int): The position of the error in the source text
message(str): A string with the error message.abs
code(int): An error code to identify the kind of error
"""
self
.
errors
.
append
(
Error
(
message
,
code
,
pos
))
self
.
error_flag
=
max
(
self
.
error_flag
,
code
)
return
self
def
collect_errors
(
self
,
clear_errors
=
False
)
->
List
[
Error
]:
"""Returns the list of errors, ordered bv their position.
"""
self
.
errors
.
sort
(
key
=
lambda
e
:
e
.
pos
)
errors
=
self
.
errors
if
clear_errors
:
self
.
errors
=
[]
self
.
error_flag
=
0
return
errors
ZOMBIE_NODE
=
Node
(
ZOMBIE_PARSER
,
''
)
...
...
test/test_parse.py
View file @
cd2e8f1d
...
...
@@ -535,6 +535,31 @@ class TestWhitespaceHandling:
assert
st
.
error_flag
class
TestErrorReporting
:
grammar
=
"""
root = series alpha | anything
series = subseries &alpha
subseries = alpha §beta
alpha = /[a-z]+/
beta = /[A-Z]+/
anything = /.*/
"""
def
setup
(
self
):
self
.
parser
=
grammar_provider
(
self
.
grammar
)()
def
test_error_propagation
(
self
):
testcode1
=
"halloB"
testcode2
=
"XYZ"
testcode3
=
"hallo "
cst
=
self
.
parser
(
testcode1
)
assert
not
cst
.
error_flag
,
str
(
cst
.
collect_errors
())
cst
=
self
.
parser
(
testcode2
)
assert
not
cst
.
error_flag
cst
=
self
.
parser
(
testcode3
)
assert
cst
.
error_flag
class
TestBorderlineCases
:
def
test_not_matching
(
self
):
minilang
=
"""parser = /X/"""
...
...
test/test_syntaxtree.py
View file @
cd2e8f1d
...
...
@@ -24,7 +24,7 @@ import sys
sys
.
path
.
extend
([
'../'
,
'./'
])
from
DHParser.error
import
Error
from
DHParser.syntaxtree
import
Node
,
parse_sxpr
,
flatten_sxpr
,
TOKEN_PTYPE
from
DHParser.syntaxtree
import
Node
,
RootNode
,
parse_sxpr
,
flatten_sxpr
,
TOKEN_PTYPE
from
DHParser.transform
import
traverse
,
reduce_single_child
,
\
replace_by_single_child
,
flatten
,
remove_expendables
from
DHParser.ebnf
import
get_ebnf_grammar
,
get_ebnf_transformer
,
get_ebnf_compiler
...
...
@@ -127,6 +127,34 @@ class TestNode:
assert
nd1
.
pos
==
0
,
"Expected Node.pos == 0, got %i"
%
nd1
.
pos
assert
nd2
.
pos
==
3
,
"Expected Node.pos == 3, got %i"
%
nd2
.
pos
class
TestRootNode
:
def
test_error_handling
(
self
):
root
=
RootNode
()
root
.
add_error
(
4
,
"error B"
)
root
.
add_error
(
2
,
"error A"
)
assert
root
.
error_flag
errors
=
root
.
collect_errors
(
False
)
assert
root
.
error_flag
assert
errors
==
root
.
collect_errors
(
True
)
assert
not
root
.
error_flag
and
not
root
.
collect_errors
()
error_str
=
"
\n
"
.
join
(
str
(
e
)
for
e
in
errors
)
assert
error_str
.
find
(
"A"
)
<
error_str
.
find
(
"B"
)
class
TestErrorHandling
:
def
test_error_flag_propagation
(
self
):
tree
=
parse_sxpr
(
'(a (b c) (d (e (f (g h)))))'
)
def
find_h
(
context
):
node
=
context
[
-
1
]
if
node
.
result
==
"h"
:
node
.
add_error
(
"an error deep inside the syntax tree"
)
assert
not
tree
.
error_flag
traverse
(
tree
,
{
"*"
:
find_h
})
assert
tree
.
error_flag
,
tree
.
as_sxpr
()
def
test_collect_errors
(
self
):
tree
=
parse_sxpr
(
'(A (B 1) (C (D (E 2) (F 3))))'
)
A
=
tree
...
...
@@ -145,20 +173,6 @@ class TestNode:
assert
not
D
.
error_flag
class
TestErrorHandling
:
def
test_error_flag_propagation
(
self
):
tree
=
parse_sxpr
(
'(a (b c) (d (e (f (g h)))))'
)
def
find_h
(
context
):
node
=
context
[
-
1
]
if
node
.
result
==
"h"
:
node
.
add_error
(
"an error deep inside the syntax tree"
)
assert
not
tree
.
error_flag
traverse
(
tree
,
{
"*"
:
find_h
})
assert
tree
.
error_flag
,
tree
.
as_sxpr
()
class
TestNodeFind
():
"""Test the select-functions of class Node.
"""
...
...
@@ -206,7 +220,10 @@ class TestNodeFind():
class
TestSerialization
:
def
test_attributes
(
self
):
def
test_sxpr_roundtrip
(
self
):
pass
def
test_sexpr_attributes
(
self
):
tree
=
parse_sxpr
(
'(A "B")'
)
tree
.
attributes
[
'attr'
]
=
"value"
tree2
=
parse_sxpr
(
'(A `(attr "value") "B")'
)
...
...
@@ -216,6 +233,7 @@ class TestSerialization:
assert
tree
.
as_sxpr
()
==
tree3
.
as_sxpr
()
if
__name__
==
"__main__"
:
from
DHParser.testing
import
runner
runner
(
""
,
globals
())
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