Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
badw-it
DHParser
Commits
b4d5d7ba
Commit
b4d5d7ba
authored
Jan 24, 2019
by
eckhart
Browse files
- more cython optimizations
parent
3ee27952
Changes
18
Hide whitespace changes
Inline
Side-by-side
DHParser/compile.pxd
View file @
b4d5d7ba
...
@@ -2,3 +2,5 @@
...
@@ -2,3 +2,5 @@
#cython: language_level=3
#cython: language_level=3
#cython: c_string_type=unicode
#cython: c_string_type=unicode
#cython: c_string_encoding=utf-8
#cython: c_string_encoding=utf-8
# cpdef visitor_name(node_name)
DHParser/compile.py
View file @
b4d5d7ba
...
@@ -47,7 +47,7 @@ from DHParser.toolkit import typing, sane_parser_name, load_if_file
...
@@ -47,7 +47,7 @@ from DHParser.toolkit import typing, sane_parser_name, load_if_file
from
typing
import
Any
,
Optional
,
Tuple
,
List
,
Callable
from
typing
import
Any
,
Optional
,
Tuple
,
List
,
Callable
__all__
=
(
'CompilerError'
,
'Compiler'
,
'compile_source'
)
__all__
=
(
'CompilerError'
,
'Compiler'
,
'compile_source'
,
'visitor_name'
)
class
CompilerError
(
Exception
):
class
CompilerError
(
Exception
):
...
@@ -60,6 +60,17 @@ class CompilerError(Exception):
...
@@ -60,6 +60,17 @@ class CompilerError(Exception):
pass
pass
def
visitor_name
(
node_name
:
str
)
->
str
:
"""
Returns the method name for `node_name`, e.g.::
>>> visitor_name('expression')
'on_expression'
"""
# assert re.match(r'\w+$', node_name)
return
'on_'
+
node_name
class
Compiler
:
class
Compiler
:
"""
"""
Class Compiler is the abstract base class for compilers. Compiler
Class Compiler is the abstract base class for compilers. Compiler
...
@@ -112,30 +123,6 @@ class Compiler:
...
@@ -112,30 +123,6 @@ class Compiler:
result
=
self
.
compile
(
root
)
result
=
self
.
compile
(
root
)
return
result
return
result
# @staticmethod
# def propagate_error_flags(node: Node, lazy: bool = True) -> None:
# # See test_parser.TestCompilerClass.test_propagate_error()..
# """Propagates error flags from children to parent nodes to make sure
# that the parent's error flag is always greater or equal the maximum
# of the children's error flags."""
# if not lazy or node.error_flag < Error.HIGHEST:
# for child in node.children:
# Compiler.propagate_error_flags(child)
# node.error_flag = max(node.error_flag, child.error_flag)
# if lazy and node.error_flag >= Error.HIGHEST:
# return
@
staticmethod
def
method_name
(
node_name
:
str
)
->
str
:
"""
Returns the method name for `node_name`, e.g.::
>>> Compiler.method_name('expression')
'on_expression'
"""
assert
re
.
match
(
r
'\w+$'
,
node_name
)
return
'on_'
+
node_name
def
compile_children
(
self
,
node
:
Node
)
->
StrictResultType
:
def
compile_children
(
self
,
node
:
Node
)
->
StrictResultType
:
"""Compiles all children of the given node and returns the tuple
"""Compiles all children of the given node and returns the tuple
of the compiled children or the node's (potentially empty) result
of the compiled children or the node's (potentially empty) result
...
@@ -171,29 +158,17 @@ class Compiler:
...
@@ -171,29 +158,17 @@ class Compiler:
elem
=
node
.
tag_name
elem
=
node
.
tag_name
if
elem
.
startswith
(
':'
):
if
elem
.
startswith
(
':'
):
elem
=
elem
[
1
:]
elem
=
elem
[
1
:]
if
not
sane_parser_name
(
elem
):
try
:
self
.
tree
.
new_error
(
node
,
"Reserved name '%s' not allowed as parser "
compiler
=
self
.
__getattribute__
(
visitor_name
(
elem
))
"name! "
%
elem
+
"(Any name starting with "
except
AttributeError
:
"'_' or '__' or ending with '__' is reserved.)"
)
compiler
=
self
.
fallback_compiler
return
None
self
.
context
.
append
(
node
)
else
:
result
=
compiler
(
node
)
try
:
self
.
context
.
pop
()
compiler
=
self
.
__getattribute__
(
self
.
method_name
(
elem
))
if
result
is
None
:
except
AttributeError
:
raise
CompilerError
(
'Method on_%s returned `None` instead of a '
compiler
=
self
.
fallback_compiler
'valid compilation result!'
%
elem
)
self
.
context
.
append
(
node
)
return
result
result
=
compiler
(
node
)
self
.
context
.
pop
()
if
result
is
None
:
raise
CompilerError
(
'Method on_%s returned `None` instead of a '
'valid compilation result!'
%
elem
)
# # the following statement makes sure that the error_flag
# # is propagated early on. Otherwise it is redundant, because
# # the __call__ method globally propagates the node's error_flag
# # later anyway. So, maybe it could be removed here.
# for child in node.children:
# node.error_flag = node.error_flag or child.error_flag
return
result
def
compile_source
(
source
:
str
,
def
compile_source
(
source
:
str
,
...
...
DHParser/ebnf.py
View file @
b4d5d7ba
...
@@ -29,7 +29,7 @@ from functools import partial
...
@@ -29,7 +29,7 @@ from functools import partial
import
keyword
import
keyword
import
os
import
os
from
DHParser.compile
import
CompilerError
,
Compiler
,
compile_source
from
DHParser.compile
import
CompilerError
,
Compiler
,
compile_source
,
visitor_name
from
DHParser.error
import
Error
from
DHParser.error
import
Error
from
DHParser.parse
import
Grammar
,
mixin_comment
,
Forward
,
RegExp
,
Whitespace
,
\
from
DHParser.parse
import
Grammar
,
mixin_comment
,
Forward
,
RegExp
,
Whitespace
,
\
NegativeLookahead
,
Alternative
,
Series
,
Option
,
OneOrMore
,
ZeroOrMore
,
Token
NegativeLookahead
,
Alternative
,
Series
,
Option
,
OneOrMore
,
ZeroOrMore
,
Token
...
@@ -124,7 +124,7 @@ class EBNFGrammar(Grammar):
...
@@ -124,7 +124,7 @@ class EBNFGrammar(Grammar):
"""
"""
expression
=
Forward
()
expression
=
Forward
()
source_hash__
=
"82a7c668f86b83f86515078e6c9093ed"
source_hash__
=
"82a7c668f86b83f86515078e6c9093ed"
parser_initialization__
=
"upon instantiation"
parser_initialization__
=
[
"upon instantiation"
]
COMMENT__
=
r
'#.*(?:\n|$)'
COMMENT__
=
r
'#.*(?:\n|$)'
WHITESPACE__
=
r
'\s*'
WHITESPACE__
=
r
'\s*'
WSP_RE__
=
mixin_comment
(
whitespace
=
WHITESPACE__
,
comment
=
COMMENT__
)
WSP_RE__
=
mixin_comment
(
whitespace
=
WHITESPACE__
,
comment
=
COMMENT__
)
...
@@ -591,7 +591,7 @@ class EBNFCompiler(Compiler):
...
@@ -591,7 +591,7 @@ class EBNFCompiler(Compiler):
' super()._reset()'
,
' super()._reset()'
,
' # initialize your variables here, not in the constructor!'
]
' # initialize your variables here, not in the constructor!'
]
for
name
in
self
.
rules
:
for
name
in
self
.
rules
:
method_name
=
Compiler
.
method
_name
(
name
)
method_name
=
visitor
_name
(
name
)
if
name
==
self
.
root_symbol
:
if
name
==
self
.
root_symbol
:
compiler
+=
[
' def '
+
method_name
+
'(self, node):'
,
compiler
+=
[
' def '
+
method_name
+
'(self, node):'
,
' return self.fallback_compiler(node)'
,
''
]
' return self.fallback_compiler(node)'
,
''
]
...
@@ -781,7 +781,7 @@ class EBNFCompiler(Compiler):
...
@@ -781,7 +781,7 @@ class EBNFCompiler(Compiler):
'r"""Parser for '
+
article
+
self
.
grammar_name
'r"""Parser for '
+
article
+
self
.
grammar_name
+
' source file'
+
' source file'
+
(
'. Grammar:'
if
self
.
grammar_source
and
show_source
else
'.'
)]
+
(
'. Grammar:'
if
self
.
grammar_source
and
show_source
else
'.'
)]
definitions
.
append
((
'parser_initialization__'
,
'"upon instantiation"'
))
definitions
.
append
((
'parser_initialization__'
,
'
[
"upon instantiation"
]
'
))
if
self
.
grammar_source
:
if
self
.
grammar_source
:
definitions
.
append
((
'source_hash__'
,
definitions
.
append
((
'source_hash__'
,
'"%s"'
%
md5
(
self
.
grammar_source
,
__version__
)))
'"%s"'
%
md5
(
self
.
grammar_source
,
__version__
)))
...
...
DHParser/parse.pxd
View file @
b4d5d7ba
...
@@ -13,26 +13,27 @@ cdef class Parser:
...
@@ -13,26 +13,27 @@ cdef class Parser:
# cpdef _parse(self, text)
# cpdef _parse(self, text)
# cdef class Grammar:
cdef
class
Grammar
:
# cdef public set all_parsers__
cdef
dict
__dict__
# cdef public object start_parser__
cdef
public
set
all_parsers__
# cdef bint _dirty_flag__
cdef
public
object
start_parser__
# cdef public bint history_tracking__
cdef
bint
_dirty_flag__
# cdef public bint memoization__
cdef
public
bint
history_tracking__
# cdef public bint left_recursion_handling__
cdef
public
bint
memoization__
# cdef public object root__
cdef
public
bint
left_recursion_handling__
# cdef public object tree__
# cdef public object root_parser__ # do not uncomment this!!!
# cdef public object document__
cdef
public
object
tree__
# cdef public object _reversed__
cdef
public
object
document__
# cdef public int document_length__
cdef
public
object
_reversed__
# cdef public list document_lbreaks__
cdef
public
int
document_length__
# cdef public object variables__
cdef
public
list
document_lbreaks__
# cdef public list rollback__
cdef
public
object
variables__
# cdef public int last_rb__loc__
cdef
public
list
rollback__
# cdef public list call_stack__
cdef
public
int
last_rb__loc__
# cdef public list history__
cdef
public
list
call_stack__
# cdef public bint moving_forward__
cdef
public
list
history__
# cdef public set recursion_locations__
cdef
public
bint
moving_forward__
cdef
public
set
recursion_locations__
cdef
class
PreprocessorToken
(
Parser
):
cdef
class
PreprocessorToken
(
Parser
):
pass
pass
...
...
DHParser/parse.py
View file @
b4d5d7ba
...
@@ -39,7 +39,7 @@ from DHParser.preprocess import BEGIN_TOKEN, END_TOKEN, RX_TOKEN_NAME
...
@@ -39,7 +39,7 @@ from DHParser.preprocess import BEGIN_TOKEN, END_TOKEN, RX_TOKEN_NAME
from
DHParser.stringview
import
StringView
,
EMPTY_STRING_VIEW
from
DHParser.stringview
import
StringView
,
EMPTY_STRING_VIEW
from
DHParser.syntaxtree
import
Node
,
RootNode
,
WHITESPACE_PTYPE
,
\
from
DHParser.syntaxtree
import
Node
,
RootNode
,
WHITESPACE_PTYPE
,
\
TOKEN_PTYPE
,
ZOMBIE
,
ResultType
TOKEN_PTYPE
,
ZOMBIE
,
ResultType
from
DHParser.toolkit
import
sane_parser_name
,
escape_control_characters
,
re
,
typing
from
DHParser.toolkit
import
sane_parser_name
,
escape_control_characters
,
re
,
typing
,
cython
from
typing
import
Callable
,
cast
,
List
,
Tuple
,
Set
,
Dict
,
DefaultDict
,
Union
,
Optional
,
Any
from
typing
import
Callable
,
cast
,
List
,
Tuple
,
Set
,
Dict
,
DefaultDict
,
Union
,
Optional
,
Any
...
@@ -247,9 +247,10 @@ class Parser:
...
@@ -247,9 +247,10 @@ class Parser:
the `reset()`-method of the parent class must be called from the
the `reset()`-method of the parent class must be called from the
`reset()`-method of the derived class."""
`reset()`-method of the derived class."""
self
.
visited
=
dict
()
# type: Dict[int, Tuple[Optional[Node], StringView]]
self
.
visited
=
dict
()
# type: Dict[int, Tuple[Optional[Node], StringView]]
self
.
recursion_counter
=
defaultdict
(
lambda
:
0
)
# type: DefaultDict[int, int]
self
.
recursion_counter
=
defaultdict
(
int
)
# type: DefaultDict[int, int]
self
.
cycle_detection
=
set
()
# type: Set[ApplyFunc]
self
.
cycle_detection
=
set
()
# type: Set[ApplyFunc]
@
cython
.
locals
(
location
=
cython
.
int
,
gap
=
cython
.
int
,
i
=
cython
.
int
)
def
__call__
(
self
:
'Parser'
,
text
:
StringView
)
->
Tuple
[
Optional
[
Node
],
StringView
]:
def
__call__
(
self
:
'Parser'
,
text
:
StringView
)
->
Tuple
[
Optional
[
Node
],
StringView
]:
"""Applies the parser to the given text. This is a wrapper method that adds
"""Applies the parser to the given text. This is a wrapper method that adds
the business intelligence that is common to all parsers. The actual parsing is
the business intelligence that is common to all parsers. The actual parsing is
...
@@ -702,7 +703,7 @@ class Grammar:
...
@@ -702,7 +703,7 @@ class Grammar:
python_src__
=
''
# type: str
python_src__
=
''
# type: str
root__
=
ZOMBIE_PARSER
# type: Parser
root__
=
ZOMBIE_PARSER
# type: Parser
# root__ must be overwritten with the root-parser by grammar subclass
# root__ must be overwritten with the root-parser by grammar subclass
parser_initialization__
=
"pending"
# type: list[str]
parser_initialization__
=
[
"pending"
]
# type: list[str]
resume_rules__
=
dict
()
# type: Dict[str, ResumeList]
resume_rules__
=
dict
()
# type: Dict[str, ResumeList]
# some default values
# some default values
# COMMENT__ = r'' # type: str # r'#.*(?:\n|$)'
# COMMENT__ = r'' # type: str # r'#.*(?:\n|$)'
...
@@ -733,7 +734,7 @@ class Grammar:
...
@@ -733,7 +734,7 @@ class Grammar:
selected reference will be chosen. See PEP 520
selected reference will be chosen. See PEP 520
(www.python.org/dev/peps/pep-0520/) for an explanation of why.
(www.python.org/dev/peps/pep-0520/) for an explanation of why.
"""
"""
if
cls
.
parser_initialization__
!=
"done"
:
if
cls
.
parser_initialization__
[
0
]
!=
"done"
:
cdict
=
cls
.
__dict__
cdict
=
cls
.
__dict__
for
entry
,
parser
in
cdict
.
items
():
for
entry
,
parser
in
cdict
.
items
():
if
isinstance
(
parser
,
Parser
)
and
sane_parser_name
(
entry
):
if
isinstance
(
parser
,
Parser
)
and
sane_parser_name
(
entry
):
...
@@ -742,7 +743,7 @@ class Grammar:
...
@@ -742,7 +743,7 @@ class Grammar:
cast
(
Forward
,
parser
).
parser
.
pname
=
entry
cast
(
Forward
,
parser
).
parser
.
pname
=
entry
else
:
# if not parser.pname:
else
:
# if not parser.pname:
parser
.
pname
=
entry
parser
.
pname
=
entry
cls
.
parser_initialization__
=
"done"
cls
.
parser_initialization__
[
0
]
=
"done"
def
__init__
(
self
,
root
:
Parser
=
None
)
->
None
:
def
__init__
(
self
,
root
:
Parser
=
None
)
->
None
:
...
@@ -761,20 +762,22 @@ class Grammar:
...
@@ -761,20 +762,22 @@ class Grammar:
# parsers not connected to the root object will be copied later
# parsers not connected to the root object will be copied later
# on demand (see Grammar.__getitem__()). Usually, the need to
# on demand (see Grammar.__getitem__()). Usually, the need to
# do so only arises during testing.
# do so only arises during testing.
self
.
root__
=
copy
.
deepcopy
(
root
)
if
root
else
copy
.
deepcopy
(
self
.
__class__
.
root__
)
self
.
root_parser__
=
copy
.
deepcopy
(
root
)
if
root
else
copy
.
deepcopy
(
self
.
__class__
.
root__
)
self
.
root__
.
apply
(
self
.
_add_parser__
)
self
.
root_parser__
.
apply
(
self
.
_add_parser__
)
assert
'root_parser__'
in
self
.
__dict__
assert
self
.
root_parser__
==
self
.
__dict__
[
'root_parser__'
]
def
__getitem__
(
self
,
key
):
def
__getitem__
(
self
,
key
):
try
:
try
:
return
self
.
__dict__
[
key
]
return
self
.
__dict__
[
key
]
except
KeyError
:
except
KeyError
:
parser_template
=
getattr
(
self
,
key
,
None
)
parser_template
=
getattr
(
self
.
__class__
,
key
,
None
)
if
parser_template
:
if
parser_template
:
# add parser to grammar object on the fly...
# add parser to grammar object on the fly...
parser
=
copy
.
deepcopy
(
parser_template
)
parser
=
copy
.
deepcopy
(
parser_template
)
parser
.
apply
(
self
.
_add_parser__
)
parser
.
apply
(
self
.
_add_parser__
)
#
assert self[key] == parser
assert
self
[
key
]
==
parser
return
self
[
key
]
return
self
[
key
]
raise
UnknownParserError
(
'Unknown parser "%s" !'
%
key
)
raise
UnknownParserError
(
'Unknown parser "%s" !'
%
key
)
...
@@ -832,7 +835,7 @@ class Grammar:
...
@@ -832,7 +835,7 @@ class Grammar:
def
__call__
(
self
,
def
__call__
(
self
,
document
:
str
,
document
:
str
,
start_parser
:
Union
[
str
,
Parser
]
=
"root__"
,
start_parser
:
Union
[
str
,
Parser
]
=
"root_
parser_
_"
,
track_history
:
bool
=
False
)
->
RootNode
:
track_history
:
bool
=
False
)
->
RootNode
:
"""
"""
Parses a document with with parser-combinators.
Parses a document with with parser-combinators.
...
@@ -857,8 +860,6 @@ class Grammar:
...
@@ -857,8 +860,6 @@ class Grammar:
return
predecessors
[
-
1
].
pos
+
len
(
predecessors
[
-
1
])
if
predecessors
else
0
return
predecessors
[
-
1
].
pos
+
len
(
predecessors
[
-
1
])
if
predecessors
else
0
# assert isinstance(document, str), type(document)
# assert isinstance(document, str), type(document)
if
self
.
root__
is
None
:
raise
NotImplementedError
()
if
self
.
_dirty_flag__
:
if
self
.
_dirty_flag__
:
self
.
_reset__
()
self
.
_reset__
()
for
parser
in
self
.
all_parsers__
:
for
parser
in
self
.
all_parsers__
:
...
@@ -901,7 +902,7 @@ class Grammar:
...
@@ -901,7 +902,7 @@ class Grammar:
# in a test case this is not necessarily an error.
# in a test case this is not necessarily an error.
last_record
=
self
.
history__
[
-
2
]
if
len
(
self
.
history__
)
>
1
else
None
# type: Optional[HistoryRecord]
last_record
=
self
.
history__
[
-
2
]
if
len
(
self
.
history__
)
>
1
else
None
# type: Optional[HistoryRecord]
if
last_record
and
parser
!=
self
.
root__
\
if
last_record
and
parser
!=
self
.
root_
parser_
_
\
and
last_record
.
status
==
HistoryRecord
.
MATCH
\
and
last_record
.
status
==
HistoryRecord
.
MATCH
\
and
last_record
.
node
.
pos
\
and
last_record
.
node
.
pos
\
+
len
(
last_record
.
node
)
>=
len
(
self
.
document__
)
\
+
len
(
last_record
.
node
)
>=
len
(
self
.
document__
)
\
...
@@ -1353,6 +1354,7 @@ class ZeroOrMore(Option):
...
@@ -1353,6 +1354,7 @@ class ZeroOrMore(Option):
EBNF-Example: ``sentence = { /\w+,?/ } "."``
EBNF-Example: ``sentence = { /\w+,?/ } "."``
"""
"""
@
cython
.
locals
(
n
=
cython
.
int
)
def
_parse
(
self
,
text
:
StringView
)
->
Tuple
[
Optional
[
Node
],
StringView
]:
def
_parse
(
self
,
text
:
StringView
)
->
Tuple
[
Optional
[
Node
],
StringView
]:
results
=
()
# type: Tuple[Node, ...]
results
=
()
# type: Tuple[Node, ...]
n
=
len
(
text
)
+
1
# type: int
n
=
len
(
text
)
+
1
# type: int
...
@@ -1523,6 +1525,7 @@ class Series(NaryOperator):
...
@@ -1523,6 +1525,7 @@ class Series(NaryOperator):
duplicate
.
tag_name
=
self
.
tag_name
duplicate
.
tag_name
=
self
.
tag_name
return
duplicate
return
duplicate
@
cython
.
locals
(
pos
=
cython
.
int
,
reloc
=
cython
.
int
)
def
_parse
(
self
,
text
:
StringView
)
->
Tuple
[
Optional
[
Node
],
StringView
]:
def
_parse
(
self
,
text
:
StringView
)
->
Tuple
[
Optional
[
Node
],
StringView
]:
results
=
()
# type: Tuple[Node, ...]
results
=
()
# type: Tuple[Node, ...]
text_
=
text
# type: StringView
text_
=
text
# type: StringView
...
@@ -1546,8 +1549,8 @@ class Series(NaryOperator):
...
@@ -1546,8 +1549,8 @@ class Series(NaryOperator):
results
+=
(
node
,)
results
+=
(
node
,)
break
break
results
+=
(
node
,)
results
+=
(
node
,)
assert
len
(
results
)
<=
len
(
self
.
parsers
)
\
#
assert len(results) <= len(self.parsers) \
or
len
(
self
.
parsers
)
>=
len
([
p
for
p
in
results
if
p
.
tag_name
!=
ZOMBIE
])
#
or len(self.parsers) >= len([p for p in results if p.tag_name != ZOMBIE])
node
=
Node
(
self
.
tag_name
,
results
)
node
=
Node
(
self
.
tag_name
,
results
)
if
error
:
if
error
:
raise
ParserError
(
node
,
text
,
first_throw
=
True
)
raise
ParserError
(
node
,
text
,
first_throw
=
True
)
...
...
DHParser/stringview.py
View file @
b4d5d7ba
...
@@ -154,7 +154,7 @@ class StringView: # collections.abc.Sized
...
@@ -154,7 +154,7 @@ class StringView: # collections.abc.Sized
else
:
else
:
return
StringView
(
str
(
other
)
+
str
(
self
))
return
StringView
(
str
(
other
)
+
str
(
self
))
@
cython
.
locals
(
start
=
cython
.
int
,
end
=
cython
.
int
)
@
cython
.
locals
(
start
=
cython
.
int
,
stop
=
cython
.
int
)
def
__getitem__
(
self
,
index
:
Union
[
slice
,
int
])
->
'StringView'
:
def
__getitem__
(
self
,
index
:
Union
[
slice
,
int
])
->
'StringView'
:
# assert isinstance(index, slice), "As of now, StringView only allows slicing."
# assert isinstance(index, slice), "As of now, StringView only allows slicing."
# assert index.step is None or index.step == 1, \
# assert index.step is None or index.step == 1, \
...
...
DHParser/syntaxtree.py
View file @
b4d5d7ba
...
@@ -193,7 +193,7 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
...
@@ -193,7 +193,7 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
duplicate
.
errors
=
copy
.
deepcopy
(
self
.
errors
)
if
self
.
errors
else
[]
duplicate
.
errors
=
copy
.
deepcopy
(
self
.
errors
)
if
self
.
errors
else
[]
duplicate
.
_pos
=
self
.
_pos
duplicate
.
_pos
=
self
.
_pos
duplicate
.
_len
=
self
.
_len
duplicate
.
_len
=
self
.
_len
if
hasattr
(
self
,
'_xml_attr'
):
if
self
.
attr_active
(
):
duplicate
.
_xml_attr
=
copy
.
deepcopy
(
self
.
_xml_attr
)
duplicate
.
_xml_attr
=
copy
.
deepcopy
(
self
.
_xml_attr
)
return
duplicate
return
duplicate
...
@@ -398,12 +398,28 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
...
@@ -398,12 +398,28 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
return
self
return
self
def
attr_active
(
self
)
->
bool
:
"""
Returns True, if XML-Attributes of this node have ever been set
or queried, even if unsuccessfully.
"""
try
:
if
self
.
_xml_attr
is
not
None
:
return
True
except
AttributeError
:
pass
return
False
@
property
@
property
def
attr
(
self
):
def
attr
(
self
):
"""
"""
Returns a dictionary of XML-attr attached to the node.
Returns a dictionary of XML-attr attached to the node.
"""
"""
if
not
hasattr
(
self
,
'_xml_attr'
):
try
:
if
self
.
_xml_attr
is
None
:
# cython compatibility
self
.
_xml_attr
=
OrderedDict
()
except
AttributeError
:
self
.
_xml_attr
=
OrderedDict
()
self
.
_xml_attr
=
OrderedDict
()
return
self
.
_xml_attr
return
self
.
_xml_attr
...
@@ -495,7 +511,7 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
...
@@ -495,7 +511,7 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
"""Returns the opening string for the representation of `node`."""
"""Returns the opening string for the representation of `node`."""
txt
=
[
left_bracket
,
node
.
tag_name
]
txt
=
[
left_bracket
,
node
.
tag_name
]
# s += " '(pos %i)" % node.add_pos
# s += " '(pos %i)" % node.add_pos
if
hasattr
(
node
,
'_xml_attr'
):
if
node
.
attr_active
(
):
txt
.
extend
(
' `(%s "%s")'
%
(
k
,
v
)
for
k
,
v
in
node
.
attr
.
items
())
txt
.
extend
(
' `(%s "%s")'
%
(
k
,
v
)
for
k
,
v
in
node
.
attr
.
items
())
if
src
:
if
src
:
line
,
col
=
line_col
(
lbreaks
,
node
.
pos
)
line
,
col
=
line_col
(
lbreaks
,
node
.
pos
)
...
@@ -548,9 +564,9 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
...
@@ -548,9 +564,9 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
if
node
.
tag_name
in
omit_tags
:
if
node
.
tag_name
in
omit_tags
:
return
''
return
''
txt
=
[
'<'
,
node
.
tag_name
]
txt
=
[
'<'
,
node
.
tag_name
]
has_reserved_attrs
=
hasattr
(
node
,
'_xml_attr'
)
\
has_reserved_attrs
=
node
.
attr_active
(
)
\
and
any
(
r
in
node
.
attr
for
r
in
{
'err'
,
'line'
,
'col'
})
and
any
(
r
in
node
.
attr
for
r
in
{
'err'
,
'line'
,
'col'
})
if
hasattr
(
node
,
'_xml_attr'
):
if
node
.
attr_active
(
):
txt
.
extend
(
' %s="%s"'
%
(
k
,
v
)
for
k
,
v
in
node
.
attr
.
items
())
txt
.
extend
(
' %s="%s"'
%
(
k
,
v
)
for
k
,
v
in
node
.
attr
.
items
())
if
src
and
not
has_reserved_attrs
:
if
src
and
not
has_reserved_attrs
:
txt
.
append
(
' line="%i" col="%i"'
%
line_col
(
line_breaks
,
node
.
pos
))
txt
.
append
(
' line="%i" col="%i"'
%
line_col
(
line_breaks
,
node
.
pos
))
...
@@ -584,7 +600,7 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
...
@@ -584,7 +600,7 @@ class Node: # (collections.abc.Sized): Base class omitted for cython-compatibil
printed on several lines to avoid unwanted gaps in the output.
printed on several lines to avoid unwanted gaps in the output.
"""
"""
return
node
.
tag_name
in
inline_tags
\
return
node
.
tag_name
in
inline_tags
\
or
(
hasattr
(
node
,
'_xml_attr'
)
or
(
node
.
attr_active
(
)
and
node
.
attr
.
get
(
'xml:space'
,
'default'
)
==
'preserve'
)
and
node
.
attr
.
get
(
'xml:space'
,
'default'
)
==
'preserve'
)
line_breaks
=
linebreaks
(
src
)
if
src
else
[]
line_breaks
=
linebreaks
(
src
)
if
src
else
[]
...
@@ -713,7 +729,7 @@ class RootNode(Node):
...
@@ -713,7 +729,7 @@ class RootNode(Node):
duplicate
.
errors
=
copy
.
deepcopy
(
self
.
errors
)
if
self
.
errors
else
[]
duplicate
.
errors
=
copy
.
deepcopy
(
self
.
errors
)
if
self
.
errors
else
[]
duplicate
.
_pos
=
self
.
_pos
duplicate
.
_pos
=
self
.
_pos
duplicate
.
_len
=
self
.
_len
duplicate
.
_len
=
self
.
_len
if
hasattr
(
self
,
'_xml_attr'
):
if
self
.
attr_active
(
):
duplicate
.
_xml_attr
=
copy
.
deepcopy
(
self
.
_xml_attr
)
duplicate
.
_xml_attr
=
copy
.
deepcopy
(
self
.
_xml_attr
)
duplicate
.
all_errors
=
copy
.
deepcopy
(
self
.
all_errors
)
duplicate
.
all_errors
=
copy
.
deepcopy
(
self
.
all_errors
)
duplicate
.
error_flag
=
self
.
error_flag
duplicate
.
error_flag
=
self
.
error_flag
...
@@ -741,7 +757,7 @@ class RootNode(Node):
...
@@ -741,7 +757,7 @@ class RootNode(Node):
self
.
_len
=
node
.
_len
self
.
_len
=
node
.
_len
self
.
_pos
=
node
.
_pos
self
.
_pos
=
node
.
_pos
self
.
tag_name
=
node
.
tag_name
self
.
tag_name
=
node
.
tag_name
if
hasattr
(
node
,
'_xml_attr'
):
if
node
.
attr_active
(
):
self
.
_xml_attr
=
node
.
_xml_attr
self
.
_xml_attr
=
node
.
_xml_attr
self
.
_content
=
node
.
_content
self
.
_content
=
node
.
_content
return
self
return
self
...
...
DHParser/toolkit.py
View file @
b4d5d7ba
...
@@ -43,8 +43,19 @@ except ImportError:
...
@@ -43,8 +43,19 @@ except ImportError:
from
typing
import
Any
,
Iterable
,
Sequence
,
Set
,
Union
,
Dict
,
Hashable
# , cast
from
typing
import
Any
,
Iterable
,
Sequence
,
Set
,
Union
,
Dict
,
Hashable
# , cast
try
:
import
cython
cython_optimized
=
cython
.
compiled
# type: bool
except
ImportError
:
# import DHParser.Shadow as cython
cython_optimized
=
False
# type: bool
import
DHParser.shadow_cython
as
cython
__all__
=
(
'escape_re'
,
__all__
=
(
'typing'
,
'cython'
,
'cython_optimized'
,
'escape_re'
,
'escape_control_characters'
,
'escape_control_characters'
,
'is_filename'
,
'is_filename'
,
'concurrent_ident'
,
'concurrent_ident'
,
...
...
build_cython-modules.sh
View file @
b4d5d7ba
...
@@ -3,10 +3,12 @@
...
@@ -3,10 +3,12 @@
# rm DHParser/*.c
# rm DHParser/*.c
# rm DHParser/*.so
# rm DHParser/*.so
# rm DHParser/parse.c
rm
DHParser/parse.c
# rm DHParser/parse.cpython*.so
rm
DHParser/parse.cpython
*
.so
rm
DHParser/syntaxtree.c
# rm DHParser/syntaxtree.c
rm
DHParser/syntaxtree.cpython
*
.so
# rm DHParser/syntaxtree.cpython*.so
# rm DHParser/transform.c
# rm DHParser/transform.cpython*.so
# CFLAGS="-O3 -march=native -mtune=native"
# CFLAGS="-O3 -march=native -mtune=native"
python3 setup.py build_ext
--inplace
python3 setup.py build_ext
--inplace
...
...
examples/Arithmetic/ArithmeticCompiler.py
View file @
b4d5d7ba
...
@@ -60,7 +60,7 @@ class ArithmeticGrammar(Grammar):
...
@@ -60,7 +60,7 @@ class ArithmeticGrammar(Grammar):
expression
=
Forward
()
expression
=
Forward
()
variable
=
Forward
()
variable
=
Forward
()
source_hash__
=
"120070baa84f5a2bd1bbb900627078fc"