Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
A
arpeggio-gm
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
backend
arpeggio-gm
Commits
ecf34778
Commit
ecf34778
authored
Aug 07, 2014
by
Igor Dejanovic
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Changed PTNode.rule->rule_name. rule is now a reference to ParsingExpression.
parent
1c76b784
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
80 additions
and
74 deletions
+80
-74
__init__.py
arpeggio/__init__.py
+61
-55
peg.py
arpeggio/peg.py
+7
-7
test_peg_parser.py
tests/unit/test_peg_parser.py
+1
-1
test_ptnode_navigation_expressions.py
tests/unit/test_ptnode_navigation_expressions.py
+3
-3
test_python_parser.py
tests/unit/test_python_parser.py
+1
-1
test_reduce_tree.py
tests/unit/test_reduce_tree.py
+7
-7
No files found.
arpeggio/__init__.py
View file @
ecf34778
...
...
@@ -51,13 +51,13 @@ class NoMatch(Exception):
match is not successful.
Args:
rule (str): A name of the rule.
rule
_name
(str): A name of the rule.
position (int): A position in the input stream where exception
occurred.
parser (Parser): An instance of a parser.
"""
def
__init__
(
self
,
rule
,
position
,
parser
):
self
.
rule
=
rul
e
def
__init__
(
self
,
rule
_name
,
position
,
parser
):
self
.
rule
_name
=
rule_nam
e
self
.
position
=
position
self
.
parser
=
parser
...
...
@@ -65,7 +65,7 @@ class NoMatch(Exception):
self
.
_up
=
True
def
__str__
(
self
):
return
"Expected '{}' at position {} => '{}'."
.
format
(
self
.
rule
,
return
"Expected '{}' at position {} => '{}'."
.
format
(
self
.
rule
_name
,
str
(
self
.
parser
.
pos_to_linecol
(
self
.
position
)),
self
.
parser
.
context
(
position
=
self
.
position
))
...
...
@@ -94,7 +94,7 @@ class ParsingExpression(object):
elements: A list (or other python object) used as a staging structure
for python based grammar definition. Used in _from_python for
building nodes list of child parser expressions.
rule (str): The name of the parser rule if this is the root rule.
rule
_name
(str): The name of the parser rule if this is the root rule.
root (bool): Does this parser expression represents the
root of the parser rule? The root parser rule will create
non-terminal node of the parse tree during parsing.
...
...
@@ -106,7 +106,7 @@ class ParsingExpression(object):
elements
=
elements
[
0
]
self
.
elements
=
elements
self
.
rule
=
kwargs
.
get
(
'rule
'
)
self
.
rule
_name
=
kwargs
.
get
(
'rule_name'
,
'
'
)
self
.
root
=
kwargs
.
get
(
'root'
,
False
)
nodes
=
kwargs
.
get
(
'nodes'
,
[])
...
...
@@ -125,14 +125,14 @@ class ParsingExpression(object):
@property
def
name
(
self
):
if
self
.
root
:
return
"
%
s=
%
s"
%
(
self
.
rule
,
self
.
__class__
.
__name__
)
return
"
%
s=
%
s"
%
(
self
.
rule
_name
,
self
.
__class__
.
__name__
)
else
:
return
self
.
__class__
.
__name__
@property
def
id
(
self
):
if
self
.
root
:
return
self
.
rule
return
self
.
rule
_name
else
:
return
id
(
self
)
...
...
@@ -241,9 +241,9 @@ class ParsingExpression(object):
if
len
(
result
)
==
1
:
result
=
result
[
0
]
else
:
result
=
NonTerminal
(
self
.
rule
,
c_pos
,
result
)
result
=
NonTerminal
(
self
,
c_pos
,
result
)
else
:
result
=
NonTerminal
(
self
.
rule
,
c_pos
,
result
)
result
=
NonTerminal
(
self
,
c_pos
,
result
)
# Result caching for use by memoization.
self
.
result_cache
[
c_pos
]
=
(
result
,
parser
.
position
)
...
...
@@ -260,7 +260,7 @@ class ParsingExpression(object):
place of the NoMatch exception.
"""
if
self
.
root
and
parser
.
position
==
nm
.
position
and
nm
.
_up
:
nm
.
rule
=
self
.
rul
e
nm
.
rule
_name
=
self
.
rule_nam
e
class
Sequence
(
ParsingExpression
):
...
...
@@ -446,7 +446,7 @@ class Combine(Decorator):
results
=
flatten
(
results
)
# Create terminal from result
return
Terminal
(
self
.
rule
if
self
.
root
else
''
,
c_pos
,
\
return
Terminal
(
self
,
c_pos
,
\
""
.
join
([
str
(
result
)
for
result
in
results
]))
except
NoMatch
:
parser
.
position
=
c_pos
# Backtracking
...
...
@@ -461,13 +461,13 @@ class Match(ParsingExpression):
"""
Base class for all classes that will try to match something from the input.
"""
def
__init__
(
self
,
rule
,
root
=
False
):
super
(
Match
,
self
)
.
__init__
(
rule
=
rul
e
,
root
=
root
)
def
__init__
(
self
,
rule
_name
,
root
=
False
):
super
(
Match
,
self
)
.
__init__
(
rule
_name
=
rule_nam
e
,
root
=
root
)
@property
def
name
(
self
):
if
self
.
root
:
return
"
%
s=
%
s(
%
s)"
%
(
self
.
rule
,
self
.
__class__
.
__name__
,
self
.
to_match
)
return
"
%
s=
%
s(
%
s)"
%
(
self
.
rule
_name
,
self
.
__class__
.
__name__
,
self
.
to_match
)
else
:
return
"
%
s(
%
s)"
%
(
self
.
__class__
.
__name__
,
self
.
to_match
)
...
...
@@ -495,8 +495,8 @@ class RegExMatch(Match):
Default is None to support propagation from global parser setting.
'''
def
__init__
(
self
,
to_match
,
rule
=
None
,
root
=
False
,
ignore_case
=
None
):
super
(
RegExMatch
,
self
)
.
__init__
(
rule
,
root
)
def
__init__
(
self
,
to_match
,
rule
_name
=
''
,
root
=
False
,
ignore_case
=
None
):
super
(
RegExMatch
,
self
)
.
__init__
(
rule
_name
,
root
)
self
.
to_match
=
to_match
self
.
ignore_case
=
ignore_case
...
...
@@ -517,8 +517,7 @@ class RegExMatch(Match):
print
(
"++ Match '
%
s' at
%
d => '
%
s'"
%
(
m
.
group
(),
\
c_pos
,
parser
.
context
(
len
(
m
.
group
()))))
parser
.
position
+=
len
(
m
.
group
())
return
Terminal
(
self
.
rule
if
self
.
root
else
''
,
c_pos
,
m
.
group
())
return
Terminal
(
self
,
c_pos
,
m
.
group
())
else
:
if
parser
.
debug
:
print
(
"-- NoMatch at {}"
.
format
(
c_pos
))
...
...
@@ -534,8 +533,8 @@ class StrMatch(Match):
ignore_case(bool): If case insensitive match is needed.
Default is None to support propagation from global parser setting.
"""
def
__init__
(
self
,
to_match
,
rule
=
None
,
root
=
False
,
ignore_case
=
None
):
super
(
StrMatch
,
self
)
.
__init__
(
rule
,
root
)
def
__init__
(
self
,
to_match
,
rule
_name
=
''
,
root
=
False
,
ignore_case
=
None
):
super
(
StrMatch
,
self
)
.
__init__
(
rule
_name
,
root
)
self
.
to_match
=
to_match
self
.
ignore_case
=
ignore_case
...
...
@@ -557,8 +556,7 @@ class StrMatch(Match):
# If this match is inside sequence than mark for suppression
suppress
=
type
(
parser
.
_last_pexpression
)
is
Sequence
return
Terminal
(
self
.
rule
if
self
.
root
else
''
,
c_pos
,
self
.
to_match
,
suppress
=
suppress
)
return
Terminal
(
self
,
c_pos
,
self
.
to_match
,
suppress
=
suppress
)
else
:
if
parser
.
debug
:
print
(
"-- NoMatch at {}"
.
format
(
c_pos
))
...
...
@@ -584,15 +582,15 @@ class Kwd(StrMatch):
super
(
Kwd
,
self
)
.
__init__
(
to_match
,
rule
=
None
)
self
.
to_match
=
to_match
self
.
root
=
True
self
.
rule
=
'keyword'
self
.
rule
_name
=
'keyword'
class
EndOfFile
(
Match
):
"""
The Match class that will succeed in case end of input is reached.
"""
def
__init__
(
self
,
rule
=
None
):
super
(
EndOfFile
,
self
)
.
__init__
(
rule
)
def
__init__
(
self
):
super
(
EndOfFile
,
self
)
.
__init__
(
"EOF"
)
@property
def
name
(
self
):
...
...
@@ -601,7 +599,7 @@ class EndOfFile(Match):
def
_parse
(
self
,
parser
):
c_pos
=
parser
.
position
if
len
(
parser
.
input
)
==
c_pos
:
return
Terminal
(
'EOF'
,
c_pos
,
''
,
suppress
=
True
)
return
Terminal
(
EOF
()
,
c_pos
,
''
,
suppress
=
True
)
else
:
if
parser
.
debug
:
print
(
"!! EOF not matched."
)
...
...
@@ -623,8 +621,9 @@ class ParseTreeNode(object):
The node can be terminal(the leaf of the parse tree) or non-terminal.
Attributes:
rule (str): The name of the rule that created this node or empty
string in case this node is created by a non-root pexpression.
rule (ParsingExpression): The rule that created this node.
rule_name (str): The name of the rule that created this node if root rule
or empty string otherwise.
position (int): A position in the input stream where the match
occurred.
error (bool): Is this a false parse tree node created during error
...
...
@@ -632,14 +631,17 @@ class ParseTreeNode(object):
comments : A parse tree of comment(s) attached to this node.
"""
def
__init__
(
self
,
rule
,
position
,
error
):
assert
rule
assert
rule
.
rule_name
is
not
None
self
.
rule
=
rule
self
.
rule_name
=
rule
.
rule_name
self
.
position
=
position
self
.
error
=
error
self
.
comments
=
None
@property
def
name
(
self
):
return
"
%
s [
%
s]"
%
(
self
.
rule
,
self
.
position
)
return
"
%
s [
%
s]"
%
(
self
.
rule
_name
,
self
.
position
)
class
Terminal
(
ParseTreeNode
):
...
...
@@ -647,7 +649,7 @@ class Terminal(ParseTreeNode):
Leaf node of the Parse Tree. Represents matched string.
Attributes:
rule (
str): The name of t
he rule that created this terminal.
rule (
ParsingExpression): T
he rule that created this terminal.
position (int): A position in the input stream where match occurred.
value (str): Matched string at the given position or missing token
name in the case of an error node.
...
...
@@ -662,9 +664,9 @@ class Terminal(ParseTreeNode):
@property
def
desc
(
self
):
if
self
.
value
:
return
"
%
s '
%
s' [
%
s]"
%
(
self
.
rule
,
self
.
value
,
self
.
position
)
return
"
%
s '
%
s' [
%
s]"
%
(
self
.
rule
_name
,
self
.
value
,
self
.
position
)
else
:
return
"
%
s [
%
s]"
%
(
self
.
rule
,
self
.
position
)
return
"
%
s [
%
s]"
%
(
self
.
rule
_name
,
self
.
position
)
def
__str__
(
self
):
return
self
.
value
...
...
@@ -720,7 +722,8 @@ class NonTerminal(ParseTreeNode, list):
this node rule.
"""
# Prevent infinite recursion
if
rule_name
==
'_expr_cache'
:
if
rule_name
in
[
'_expr_cache'
,
'_filtered'
,
'rule'
,
'rule_name'
,
'position'
,
'append'
,
'extend'
]:
raise
AttributeError
# First check the cache
...
...
@@ -731,19 +734,22 @@ class NonTerminal(ParseTreeNode, list):
# with the given rule name and create new NonTerminal
# and cache it for later access.
nodes
=
[]
rule
=
None
for
n
in
self
:
if
self
.
_filtered
:
# For filtered NT rule_name is a rule on
# each of its children
for
m
in
n
:
if
m
.
rule
==
rule_name
:
if
m
.
rule
_name
==
rule_name
:
nodes
.
append
(
m
)
rule
=
m
.
rule
else
:
if
n
.
rule
==
rule_name
:
if
n
.
rule
_name
==
rule_name
:
nodes
.
append
(
n
)
rule
=
n
.
rule
# For expression NonTerminals instances position does not have any sense.
result
=
NonTerminal
(
rule
=
rule
_name
,
position
=
None
,
nodes
=
nodes
,
_filtered
=
True
)
result
=
NonTerminal
(
rule
=
rule
,
position
=
None
,
nodes
=
nodes
,
_filtered
=
True
)
self
.
_expr_cache
[
rule_name
]
=
result
return
result
...
...
@@ -894,9 +900,9 @@ class Parser(object):
# visualization
if
self
.
debug
:
from
arpeggio.export
import
PTDOTExporter
root_rule
=
self
.
parse_tree
.
rul
e
root_rule
_name
=
self
.
parse_tree
.
rule_nam
e
PTDOTExporter
()
.
exportFile
(
self
.
parse_tree
,
"{}_parse_tree.dot"
.
format
(
root_rule
))
"{}_parse_tree.dot"
.
format
(
root_rule
_name
))
return
self
.
parse_tree
def
getASG
(
self
,
sem_actions
=
None
,
defaults
=
True
):
...
...
@@ -940,7 +946,7 @@ class Parser(object):
for
n
in
node
:
child
=
tree_walk
(
n
)
if
child
is
not
None
:
children
.
append_result
(
n
.
rule
,
child
)
children
.
append_result
(
n
.
rule
_name
,
child
)
if
self
.
debug
:
print
(
"Processing "
,
node
.
name
,
"= '"
,
str
(
node
),
...
...
@@ -949,15 +955,15 @@ class Parser(object):
for
i
,
a
in
enumerate
(
children
):
print
(
"
\t
%
d:"
%
(
i
+
1
),
unicode
(
a
),
"type:"
,
type
(
a
)
.
__name__
)
if
node
.
rule
in
sem_actions
:
sem_action
=
sem_actions
[
node
.
rule
]
if
node
.
rule
_name
in
sem_actions
:
sem_action
=
sem_actions
[
node
.
rule
_name
]
if
type
(
sem_action
)
is
types
.
FunctionType
:
retval
=
sem_action
(
self
,
node
,
children
)
else
:
retval
=
sem_action
.
first_pass
(
self
,
node
,
children
)
if
hasattr
(
sem_action
,
"second_pass"
):
for_second_pass
.
append
((
node
.
rule
,
retval
))
for_second_pass
.
append
((
node
.
rule
_name
,
retval
))
if
self
.
debug
:
print
(
"
\t
Applying semantic action "
,
type
(
sem_action
))
...
...
@@ -1071,9 +1077,9 @@ class Parser(object):
if
self
.
nm
is
None
or
args
[
0
]
.
position
>
self
.
nm
.
position
:
self
.
nm
=
args
[
0
]
else
:
rule
,
position
,
parser
=
args
rule
_name
,
position
,
parser
=
args
if
self
.
nm
is
None
or
position
>
self
.
nm
.
position
:
self
.
nm
=
NoMatch
(
rule
,
position
,
parser
)
self
.
nm
=
NoMatch
(
rule
_name
,
position
,
parser
)
raise
self
.
nm
...
...
@@ -1106,7 +1112,7 @@ class ParserPython(Parser):
# Comments should be optional and there can be more of them
if
self
.
comments_model
:
# and not isinstance(self.comments_model, ZeroOrMore):
self
.
comments_model
.
root
=
True
self
.
comments_model
.
rule
=
comment_def
.
__name__
self
.
comments_model
.
rule
_name
=
comment_def
.
__name__
def
_parse
(
self
):
return
self
.
parser_model
.
parse
(
self
)
...
...
@@ -1127,11 +1133,11 @@ class ParserPython(Parser):
def
inner_from_python
(
expression
):
retval
=
None
if
type
(
expression
)
==
types
.
FunctionType
:
# Is this expression a parser rule?
rule
=
expression
.
__name__
if
rule
in
__rule_cache
:
c_rule
=
__rule_cache
.
get
(
rule
)
rule
_name
=
expression
.
__name__
if
rule
_name
in
__rule_cache
:
c_rule
=
__rule_cache
.
get
(
rule
_name
)
if
self
.
debug
:
print
(
"Rule {} founded in cache."
.
format
(
rule
))
print
(
"Rule {} founded in cache."
.
format
(
rule
_name
))
if
isinstance
(
c_rule
,
CrossRef
):
self
.
__cross_refs
+=
1
if
self
.
debug
:
...
...
@@ -1141,10 +1147,10 @@ class ParserPython(Parser):
# Semantic action for the rule
if
hasattr
(
expression
,
"sem"
):
self
.
sem_actions
[
rule
]
=
expression
.
sem
self
.
sem_actions
[
rule
_name
]
=
expression
.
sem
# Register rule cross-ref to support recursion
__rule_cache
[
rule
]
=
CrossRef
(
rul
e
)
__rule_cache
[
rule
_name
]
=
CrossRef
(
rule_nam
e
)
curr_expr
=
expression
while
type
(
curr_expr
)
is
types
.
FunctionType
:
...
...
@@ -1152,14 +1158,14 @@ class ParserPython(Parser):
# go into until non-function is returned.
curr_expr
=
curr_expr
()
retval
=
inner_from_python
(
curr_expr
)
retval
.
rule
=
rul
e
retval
.
rule
_name
=
rule_nam
e
retval
.
root
=
True
# Update cache
__rule_cache
[
rule
]
=
retval
__rule_cache
[
rule
_name
]
=
retval
if
self
.
debug
:
print
(
"New rule: {} -> {}"
.
format
(
rule
,
retval
.
__class__
.
__name__
))
.
format
(
rule
_name
,
retval
.
__class__
.
__name__
))
elif
isinstance
(
expression
,
StrMatch
):
if
expression
.
ignore_case
is
None
:
...
...
arpeggio/peg.py
View file @
ecf34778
...
...
@@ -81,14 +81,14 @@ class PEGSemanticAction(SemanticAction):
# If resolved rule hasn't got the same name it
# should be cloned and preserved in the peg_rules cache
if
resolved_rule
.
rule
!=
n
.
rule_name
:
if
resolved_rule
.
rule
_name
!=
n
.
rule_name
:
resolved_rule
=
copy
.
copy
(
resolved_rule
)
resolved_rule
.
rule
=
n
.
rule_name
parser
.
peg_rules
[
resolved_rule
.
rule
]
=
resolved_rule
resolved_rule
.
rule
_name
=
n
.
rule_name
parser
.
peg_rules
[
resolved_rule
.
rule
_name
]
=
resolved_rule
if
parser
.
debug
:
print
(
"Resolving: cloned to {} = > {}"
\
.
format
(
resolved_rule
.
rule
,
resolved_rule
.
name
))
.
format
(
resolved_rule
.
rule
_name
,
resolved_rule
.
name
))
node
.
nodes
[
i
]
=
resolved_rule
...
...
@@ -111,7 +111,7 @@ class SemRule(PEGSemanticAction):
retval
=
Sequence
(
nodes
=
children
[
1
:])
else
:
retval
=
children
[
1
]
retval
.
rule
=
rule_name
retval
.
rule
_name
=
rule_name
retval
.
root
=
True
if
not
hasattr
(
parser
,
"peg_rules"
):
...
...
@@ -230,14 +230,14 @@ class ParserPEG(Parser):
# visualization
if
self
.
debug
:
from
arpeggio.export
import
PMDOTExporter
root_rule
=
self
.
parser_model
.
rule
root_rule
=
self
.
parser_model
.
rule
_name
PMDOTExporter
()
.
exportFile
(
self
.
parser_model
,
"{}_peg_parser_model.dot"
.
format
(
root_rule
))
# Comments should be optional and there can be more of them
if
self
.
comments_model
:
# and not isinstance(self.comments_model, ZeroOrMore):
self
.
comments_model
.
root
=
True
self
.
comments_model
.
rule
=
comment_rule_name
self
.
comments_model
.
rule
_name
=
comment_rule_name
def
_parse
(
self
):
return
self
.
parser_model
.
parse
(
self
)
...
...
tests/unit/test_peg_parser.py
View file @
ecf34778
...
...
@@ -24,7 +24,7 @@ def test_construct_parser():
parser
=
ParserPEG
(
grammar
,
'calc'
)
assert
parser
.
parser_model
.
rule
==
'calc'
assert
parser
.
parser_model
.
rule
_name
==
'calc'
assert
isinstance
(
parser
.
parser_model
,
Sequence
)
assert
parser
.
parser_model
.
nodes
[
0
]
.
name
==
'OneOrMore'
...
...
tests/unit/test_ptnode_navigation_expressions.py
View file @
ecf34778
...
...
@@ -34,9 +34,9 @@ def test_lookup_single():
assert
isinstance
(
result
,
ParseTreeNode
)
assert
isinstance
(
result
.
bar
,
NonTerminal
)
# dot access
assert
result
.
bar
.
rule
==
'bar'
assert
result
.
bar
.
rule
_name
==
'bar'
# Index access
assert
result
[
1
]
.
rule
==
'bar'
assert
result
[
1
]
.
rule
_name
==
'bar'
# There are six children from result
assert
len
(
result
)
==
6
...
...
@@ -52,7 +52,7 @@ def test_lookup_single():
# For example this returns all bum from all bar in result
assert
len
(
result
.
bar
.
bum
)
==
2
# Verify that proper bum are returned
assert
result
.
bar
.
bum
[
0
]
.
rule
==
'bum'
assert
result
.
bar
.
bum
[
0
]
.
rule
_name
==
'bum'
assert
result
.
bar
.
bum
[
1
]
.
position
==
18
# Access to terminal
...
...
tests/unit/test_python_parser.py
View file @
ecf34778
...
...
@@ -29,7 +29,7 @@ def test_pp_construction():
'''
parser
=
ParserPython
(
calc
)
assert
parser
.
parser_model
.
rule
==
'calc'
assert
parser
.
parser_model
.
rule
_name
==
'calc'
assert
isinstance
(
parser
.
parser_model
,
Sequence
)
assert
parser
.
parser_model
.
nodes
[
0
]
.
desc
==
'OneOrMore'
...
...
tests/unit/test_reduce_tree.py
View file @
ecf34778
...
...
@@ -31,21 +31,21 @@ def test_reduce_tree():
# PTDOTExporter().exportFile(result, 'test_reduce_tree_pt.dot')
assert
result
[
0
]
.
rule
==
'first'
assert
result
[
0
]
.
rule
_name
==
'first'
assert
isinstance
(
result
[
0
],
NonTerminal
)
assert
result
[
3
]
.
rule
==
'first'
assert
result
[
0
][
0
]
.
rule
==
'fourth'
assert
result
[
3
]
.
rule
_name
==
'first'
assert
result
[
0
][
0
]
.
rule
_name
==
'fourth'
# Check reduction for direct OrderedChoice
assert
result
[
2
][
0
]
.
rule
==
'third'
assert
result
[
2
][
0
]
.
rule
_name
==
'third'
parser
=
ParserPython
(
grammar
,
reduce_tree
=
True
)
result
=
parser
.
parse
(
input
)
# PTDOTExporter().exportFile(result, 'test_reduce_tree_pt.dot')
assert
result
[
0
]
.
rule
==
'fourth'
assert
result
[
0
]
.
rule
_name
==
'fourth'
assert
isinstance
(
result
[
0
],
Terminal
)
assert
result
[
3
]
.
rule
==
'fourth'
assert
result
[
3
]
.
rule
_name
==
'fourth'
# Check reduction for direct OrderedChoice
assert
result
[
2
][
0
]
.
rule
==
'third_str'
assert
result
[
2
][
0
]
.
rule
_name
==
'third_str'
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