rley 0.2.15 → 0.3.00
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/gfg/call_edge.rb +30 -0
- data/lib/rley/gfg/edge.rb +4 -0
- data/lib/rley/gfg/end_vertex.rb +1 -1
- data/lib/rley/gfg/epsilon_edge.rb +0 -4
- data/lib/rley/gfg/grm_flow_graph.rb +32 -7
- data/lib/rley/gfg/item_vertex.rb +71 -25
- data/lib/rley/gfg/non_terminal_vertex.rb +10 -1
- data/lib/rley/gfg/return_edge.rb +31 -0
- data/lib/rley/gfg/scan_edge.rb +2 -1
- data/lib/rley/gfg/shortcut_edge.rb +26 -0
- data/lib/rley/gfg/start_vertex.rb +2 -2
- data/lib/rley/gfg/vertex.rb +27 -1
- data/lib/rley/parse_forest_visitor.rb +115 -0
- data/lib/rley/parser/base_parser.rb +27 -0
- data/lib/rley/parser/dotted_item.rb +11 -0
- data/lib/rley/parser/earley_parser.rb +3 -15
- data/lib/rley/parser/gfg_chart.rb +106 -0
- data/lib/rley/parser/gfg_earley_parser.rb +139 -0
- data/lib/rley/parser/gfg_parsing.rb +384 -0
- data/lib/rley/parser/parse_entry.rb +148 -0
- data/lib/rley/parser/parse_entry_set.rb +104 -0
- data/lib/rley/parser/parse_entry_tracker.rb +56 -0
- data/lib/rley/parser/parse_forest_builder.rb +229 -0
- data/lib/rley/parser/parse_forest_factory.rb +54 -0
- data/lib/rley/parser/parse_walker_factory.rb +237 -0
- data/lib/rley/ptree/token_range.rb +14 -1
- data/lib/rley/sppf/alternative_node.rb +34 -0
- data/lib/rley/sppf/composite_node.rb +27 -0
- data/lib/rley/sppf/epsilon_node.rb +27 -0
- data/lib/rley/sppf/leaf_node.rb +12 -0
- data/lib/rley/sppf/non_terminal_node.rb +38 -0
- data/lib/rley/sppf/parse_forest.rb +48 -0
- data/lib/rley/sppf/sppf_node.rb +24 -0
- data/lib/rley/sppf/token_node.rb +29 -0
- data/lib/rley/syntax/grammar_builder.rb +16 -12
- data/lib/rley/syntax/grm_symbol.rb +6 -0
- data/lib/rley/syntax/terminal.rb +5 -0
- data/spec/rley/gfg/call_edge_spec.rb +51 -0
- data/spec/rley/gfg/end_vertex_spec.rb +1 -0
- data/spec/rley/gfg/grm_flow_graph_spec.rb +24 -2
- data/spec/rley/gfg/item_vertex_spec.rb +75 -6
- data/spec/rley/gfg/non_terminal_vertex_spec.rb +14 -0
- data/spec/rley/gfg/return_edge_spec.rb +51 -0
- data/spec/rley/gfg/shortcut_edge_spec.rb +43 -0
- data/spec/rley/gfg/vertex_spec.rb +52 -37
- data/spec/rley/parse_forest_visitor_spec.rb +238 -0
- data/spec/rley/parser/dotted_item_spec.rb +29 -8
- data/spec/rley/parser/gfg_chart_spec.rb +138 -0
- data/spec/rley/parser/gfg_earley_parser_spec.rb +918 -0
- data/spec/rley/parser/gfg_parsing_spec.rb +565 -0
- data/spec/rley/parser/parse_entry_set_spec.rb +179 -0
- data/spec/rley/parser/parse_entry_spec.rb +208 -0
- data/spec/rley/parser/parse_forest_builder_spec.rb +382 -0
- data/spec/rley/parser/parse_forest_factory_spec.rb +81 -0
- data/spec/rley/parser/parse_walker_factory_spec.rb +235 -0
- data/spec/rley/parser/state_set_spec.rb +4 -0
- data/spec/rley/sppf/alternative_node_spec.rb +72 -0
- data/spec/rley/sppf/antecedence_graph.rb +87 -0
- data/spec/rley/sppf/forest_representation.rb +136 -0
- data/spec/rley/sppf/gfg_representation.rb +111 -0
- data/spec/rley/sppf/non_terminal_node_spec.rb +64 -0
- data/spec/rley/support/ambiguous_grammar_helper.rb +36 -36
- data/spec/rley/support/expectation_helper.rb +36 -0
- data/spec/rley/support/grammar_helper.rb +28 -0
- data/spec/rley/support/grammar_sppf_helper.rb +25 -0
- data/spec/rley/syntax/grammar_builder_spec.rb +5 -0
- data/spec/rley/syntax/non_terminal_spec.rb +4 -0
- data/spec/rley/syntax/terminal_spec.rb +4 -0
- metadata +58 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5b7bad0d01ea1fbe0656bf1765843288c1521b26
|
|
4
|
+
data.tar.gz: d7b96f2d154b172c0847b3a96eaf0b6422439db1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 76fa3fcf95a3bb55ca2e13eb1b449032cb3030cf18c5ab114e2398aeec16401d8c2b738f9818831dd4360d143f24f1240ffd13b8f0c495dcb8f5c684675ec894
|
|
7
|
+
data.tar.gz: 52f7f640dfc96d9f78a9ee1a44da1fb81086d446b1196d9a422bb5f6e7312c0e6ea089ce595b78b3b4c1d51d4cf810b18cda26fefd2477f7b5190dc9dfb8bca3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
### 0.3.00 / 2016-10-23
|
|
2
|
+
* [CHANGE] Many new classes. The gem bundles a second parser that copes with ambiguous grammars.
|
|
3
|
+
|
|
4
|
+
|
|
1
5
|
### 0.2.15 / 2016-02-14
|
|
2
6
|
* [CHANGE] A lot of internal changes. This is the last version before a Grammar Flow Graph-based parsing implementation.
|
|
3
7
|
|
data/lib/rley/constants.rb
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require_relative 'edge'
|
|
2
|
+
|
|
3
|
+
module Rley # This module is used as a namespace
|
|
4
|
+
module GFG # This module is used as a namespace
|
|
5
|
+
# Specialization of an edge in a grammar flow graph
|
|
6
|
+
# that has a item vertex as its head (predecessor).
|
|
7
|
+
# and a start vertex (.X) as its tail (successor).
|
|
8
|
+
# Responsibilities:
|
|
9
|
+
# - To know the successor vertex (tail)
|
|
10
|
+
class CallEdge < Edge
|
|
11
|
+
attr_reader(:key)
|
|
12
|
+
|
|
13
|
+
# Pre-condition: thePredecessor is an ItemVertex
|
|
14
|
+
# Pre-condition: theSuccessor is an StartVertex
|
|
15
|
+
def initialize(thePredecessor, theSuccessor)
|
|
16
|
+
super(thePredecessor, theSuccessor)
|
|
17
|
+
do_set_key(thePredecessor, theSuccessor)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
def do_set_key(thePredecessor, theSuccessor)
|
|
22
|
+
tail_d_item = thePredecessor.dotted_item
|
|
23
|
+
@key = "CALL_#{tail_d_item.production.object_id}_#{tail_d_item.position}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end # class
|
|
27
|
+
end # module
|
|
28
|
+
end # module
|
|
29
|
+
|
|
30
|
+
# End of file
|
data/lib/rley/gfg/edge.rb
CHANGED
data/lib/rley/gfg/end_vertex.rb
CHANGED
|
@@ -2,7 +2,10 @@ require_relative 'start_vertex'
|
|
|
2
2
|
require_relative 'end_vertex'
|
|
3
3
|
require_relative 'item_vertex'
|
|
4
4
|
require_relative 'epsilon_edge'
|
|
5
|
+
require_relative 'call_edge'
|
|
6
|
+
require_relative 'return_edge'
|
|
5
7
|
require_relative 'scan_edge'
|
|
8
|
+
require_relative 'shortcut_edge'
|
|
6
9
|
|
|
7
10
|
module Rley # This module is used as a namespace
|
|
8
11
|
module GFG # This module is used as a namespace
|
|
@@ -27,11 +30,18 @@ module Rley # This module is used as a namespace
|
|
|
27
30
|
|
|
28
31
|
build_graph(theDottedItems)
|
|
29
32
|
end
|
|
33
|
+
|
|
34
|
+
# Return the vertex with given vertex label.
|
|
35
|
+
def find_vertex(aVertexLabel)
|
|
36
|
+
vertices.find { |a_vertex| a_vertex.label == aVertexLabel }
|
|
37
|
+
end
|
|
30
38
|
|
|
31
39
|
private
|
|
32
40
|
def add_vertex(aVertex)
|
|
41
|
+
fail StandardError, 'GFG vertex cannot be nil' if aVertex.nil?
|
|
42
|
+
|
|
33
43
|
# TODO: make setting of start vertex more robust
|
|
34
|
-
start_vertex = aVertex if vertices.empty?
|
|
44
|
+
@start_vertex = aVertex if vertices.empty?
|
|
35
45
|
vertices << aVertex
|
|
36
46
|
end
|
|
37
47
|
|
|
@@ -54,7 +64,9 @@ private
|
|
|
54
64
|
end
|
|
55
65
|
end
|
|
56
66
|
end
|
|
57
|
-
|
|
67
|
+
|
|
68
|
+
# For each non-terminal from the grammar, say N
|
|
69
|
+
# Add the .N and N. vertices to the graph
|
|
58
70
|
def build_all_starts_ends(theDottedItems)
|
|
59
71
|
productions_raw = theDottedItems.map(&:production)
|
|
60
72
|
productions = productions_raw.uniq
|
|
@@ -98,9 +110,12 @@ private
|
|
|
98
110
|
# Rule 5
|
|
99
111
|
# add a call edge: N => α[1] .A α[n] -> .A
|
|
100
112
|
# add a return edge: A. -> N => α[1] A. α[n]
|
|
113
|
+
# add a shortcut edge: ( N => α[1] .A α[n] ) -> ( N => α[1] A. α[n] )
|
|
101
114
|
def augment_graph(theDottedItems, firstItemPos)
|
|
102
115
|
production = theDottedItems[firstItemPos].production
|
|
103
116
|
max_index = production.rhs.size + 1
|
|
117
|
+
prev_vertex = nil
|
|
118
|
+
|
|
104
119
|
(0...max_index).each do |index|
|
|
105
120
|
current_item = theDottedItems[firstItemPos+index]
|
|
106
121
|
new_vertex = ItemVertex.new(current_item)
|
|
@@ -119,6 +134,12 @@ private
|
|
|
119
134
|
build_call_return_edges(vertices[-2], new_vertex)
|
|
120
135
|
end
|
|
121
136
|
end
|
|
137
|
+
|
|
138
|
+
prev_symbol = current_item.prev_symbol
|
|
139
|
+
if prev_symbol && prev_symbol.kind_of?(Syntax::NonTerminal)
|
|
140
|
+
build_shortcut_edge(prev_vertex, new_vertex)
|
|
141
|
+
end
|
|
142
|
+
prev_vertex = new_vertex
|
|
122
143
|
end
|
|
123
144
|
end
|
|
124
145
|
|
|
@@ -147,18 +168,22 @@ private
|
|
|
147
168
|
ScanEdge.new(fromVertex, toVertex, fromVertex.dotted_item.next_symbol)
|
|
148
169
|
end
|
|
149
170
|
|
|
150
|
-
def build_call_return_edges(
|
|
151
|
-
nt_symbol =
|
|
171
|
+
def build_call_return_edges(calling_vertex, return_vertex)
|
|
172
|
+
nt_symbol = calling_vertex.dotted_item.next_symbol
|
|
152
173
|
|
|
153
174
|
# Retrieve corresponding start vertex
|
|
154
175
|
start_vertex = start_vertex_for[nt_symbol]
|
|
155
|
-
# Create an edge
|
|
156
|
-
|
|
176
|
+
# Create an edge 'calling' vertex -> start vertex
|
|
177
|
+
CallEdge.new(calling_vertex, start_vertex)
|
|
157
178
|
|
|
158
179
|
# Retrieve corresponding end vertex
|
|
159
180
|
end_vertex = end_vertex_for[nt_symbol]
|
|
160
181
|
# Create an edge end vertex -> return vertex
|
|
161
|
-
|
|
182
|
+
ReturnEdge.new(end_vertex, return_vertex)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def build_shortcut_edge(fromVertex, toVertex)
|
|
186
|
+
ShortcutEdge.new(fromVertex, toVertex)
|
|
162
187
|
end
|
|
163
188
|
end # class
|
|
164
189
|
end # module
|
data/lib/rley/gfg/item_vertex.rb
CHANGED
|
@@ -1,26 +1,72 @@
|
|
|
1
|
-
require_relative '
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
1
|
+
require_relative '../syntax/non_terminal'
|
|
2
|
+
require_relative 'vertex'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
module Rley # This module is used as a namespace
|
|
6
|
+
module GFG # This module is used as a namespace
|
|
7
|
+
# TODO: modify definition
|
|
8
|
+
# Represents a specialized vertex in a grammar flow graph
|
|
9
|
+
# that is associated to a given dotted item.
|
|
10
|
+
# Responsibilities (in addition to inherited ones):
|
|
11
|
+
# - Know its related non-terminal symbol
|
|
12
|
+
class ItemVertex < Vertex
|
|
13
|
+
# Link to the dotted item object
|
|
14
|
+
attr_reader :dotted_item
|
|
15
|
+
|
|
16
|
+
# Optional link to a "shortcut" edge.
|
|
17
|
+
# Applicable only if the dotted expects a non-terminal symbol.
|
|
18
|
+
attr_reader :shortcut
|
|
19
|
+
|
|
20
|
+
def initialize(aDottedItem)
|
|
21
|
+
super()
|
|
22
|
+
@dotted_item = aDottedItem
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Set the "shortcut" edge.
|
|
26
|
+
def shortcut=(aShortcut)
|
|
27
|
+
unless aShortcut.kind_of?(ShortcutEdge)
|
|
28
|
+
fail StandardError, 'Invalid shortcut argument'
|
|
29
|
+
end
|
|
30
|
+
=begin
|
|
31
|
+
unless next_symbol && next_symbol.kind_of?(Syntax::NonTerminal)
|
|
32
|
+
fail StandardError, 'Invalid shortcut usage'
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
shortcut_d_item = aShortcut.successor.dotted_item
|
|
36
|
+
unless (dotted_item.production == shortcut_d_item.production) &&
|
|
37
|
+
(dotted_item.position == shortcut_d_item.prev_position)
|
|
38
|
+
fail StandardError, 'Shortcut refers to wrong vertex'
|
|
39
|
+
end
|
|
40
|
+
=end
|
|
41
|
+
@shortcut = aShortcut
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def label()
|
|
45
|
+
return "#{dotted_item}"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Returns true if the dotted item has a dot at the end of the production.
|
|
49
|
+
def complete?()
|
|
50
|
+
return dotted_item.reduce_item?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Return the symbol before the dot else nil.
|
|
54
|
+
def prev_symbol()
|
|
55
|
+
return dotted_item.prev_symbol
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Return the symbol after the dot else nil.
|
|
59
|
+
def next_symbol()
|
|
60
|
+
return dotted_item.next_symbol
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Return the non-terminal symbol at the left-hand side of the production
|
|
64
|
+
def lhs()
|
|
65
|
+
return dotted_item.lhs
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end # class
|
|
69
|
+
end # module
|
|
70
|
+
end # module
|
|
71
|
+
|
|
26
72
|
# End of file
|
|
@@ -3,7 +3,8 @@ require_relative 'vertex'
|
|
|
3
3
|
module Rley # This module is used as a namespace
|
|
4
4
|
module GFG # This module is used as a namespace
|
|
5
5
|
# Represents a specialized vertex in a grammar flow graph
|
|
6
|
-
# that is associated to a given non-terminal symbol
|
|
6
|
+
# that is associated to a given non-terminal symbol and
|
|
7
|
+
# that may have in-degree or out-degree > 1
|
|
7
8
|
# Responsibilities (in addition to inherited ones):
|
|
8
9
|
# - Know its related non-terminal symbol
|
|
9
10
|
class NonTerminalVertex < Vertex
|
|
@@ -13,6 +14,14 @@ module Rley # This module is used as a namespace
|
|
|
13
14
|
super()
|
|
14
15
|
@non_terminal = aNonTerminal
|
|
15
16
|
end
|
|
17
|
+
|
|
18
|
+
protected
|
|
19
|
+
|
|
20
|
+
# Validation method for adding an outgoing edge to the vertex.
|
|
21
|
+
# A start vertex may accept an indegree and outdegree greater than one
|
|
22
|
+
def check_add_edge(anEdge)
|
|
23
|
+
return anEdge
|
|
24
|
+
end
|
|
16
25
|
|
|
17
26
|
end # class
|
|
18
27
|
end # module
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require_relative 'edge'
|
|
2
|
+
|
|
3
|
+
module Rley # This module is used as a namespace
|
|
4
|
+
module GFG # This module is used as a namespace
|
|
5
|
+
# Specialization of an edge in a grammar flow graph
|
|
6
|
+
# that has a end vertex (X.) as its head
|
|
7
|
+
# and an item vertex as its tail
|
|
8
|
+
|
|
9
|
+
# Responsibilities:
|
|
10
|
+
# - To know the successor vertex (tail)
|
|
11
|
+
class ReturnEdge < Edge
|
|
12
|
+
attr_reader(:key)
|
|
13
|
+
|
|
14
|
+
# Pre-condition: thePredecessor is an EndVertex
|
|
15
|
+
# Pre-condition: theSuccessor is an ItemVertex
|
|
16
|
+
def initialize(thePredecessor, theSuccessor)
|
|
17
|
+
super(thePredecessor, theSuccessor)
|
|
18
|
+
do_set_key(thePredecessor, theSuccessor)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
def do_set_key(thePredecessor, theSuccessor)
|
|
23
|
+
tail_d_item = theSuccessor.dotted_item
|
|
24
|
+
@key = "RET_#{tail_d_item.production.object_id}_#{tail_d_item.prev_position}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end # class
|
|
28
|
+
end # module
|
|
29
|
+
end # module
|
|
30
|
+
|
|
31
|
+
# End of file
|
data/lib/rley/gfg/scan_edge.rb
CHANGED
|
@@ -2,7 +2,8 @@ require_relative 'edge'
|
|
|
2
2
|
|
|
3
3
|
module Rley # This module is used as a namespace
|
|
4
4
|
module GFG # This module is used as a namespace
|
|
5
|
-
#
|
|
5
|
+
# Specialization of an edge in a grammar flow graph
|
|
6
|
+
# that is taken as a consequence of a scan rule.
|
|
6
7
|
# Responsibilities:
|
|
7
8
|
# - To know the successor vertex
|
|
8
9
|
class ScanEdge < Edge
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require_relative 'edge'
|
|
2
|
+
|
|
3
|
+
module Rley # This module is used as a namespace
|
|
4
|
+
module GFG # This module is used as a namespace
|
|
5
|
+
# Abstract class. Represents an edge in a grammar flow graph
|
|
6
|
+
# Responsibilities:
|
|
7
|
+
# - To know the successor vertex
|
|
8
|
+
class ShortcutEdge < Edge
|
|
9
|
+
# The terminal symbol expected from the input stream
|
|
10
|
+
attr_reader :nonterminal
|
|
11
|
+
|
|
12
|
+
def initialize(thePredecessor, theSuccessor)
|
|
13
|
+
@successor = theSuccessor
|
|
14
|
+
@nonterminal = thePredecessor.next_symbol
|
|
15
|
+
thePredecessor.shortcut = self
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_s()
|
|
19
|
+
" -#{nonterminal}-> #{successor.label}"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end # class
|
|
23
|
+
end # module
|
|
24
|
+
end # module
|
|
25
|
+
|
|
26
|
+
# End of file
|
data/lib/rley/gfg/vertex.rb
CHANGED
|
@@ -14,7 +14,33 @@ module Rley # This module is used as a namespace
|
|
|
14
14
|
|
|
15
15
|
# Add an graph edge to this vertex
|
|
16
16
|
def add_edge(anEdge)
|
|
17
|
-
|
|
17
|
+
arrow = check_add_edge(anEdge)
|
|
18
|
+
edges << arrow
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Returns true iff the vertex corresponds to an dotted item that has its dot
|
|
22
|
+
# at the end of a production (i.e. is a reduced item).
|
|
23
|
+
def complete?()
|
|
24
|
+
return false # Default implementation
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Return the symbol before the dot else nil.
|
|
28
|
+
def prev_symbol()
|
|
29
|
+
return nil # Default implementation
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Return the symbol after the dot else nil.
|
|
33
|
+
def next_symbol()
|
|
34
|
+
return nil # Default implementation
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
protected
|
|
38
|
+
# Validation method for adding an outgoing edge to the vertex.
|
|
39
|
+
# Vertices will accept an indegree and outdegree of at most one
|
|
40
|
+
# unless this method is overridden in subclasses
|
|
41
|
+
def check_add_edge(anEdge)
|
|
42
|
+
fail StandardError, 'At most one edge accepted' unless edges.empty?
|
|
43
|
+
return anEdge
|
|
18
44
|
end
|
|
19
45
|
|
|
20
46
|
end # class
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
module Rley # This module is used as a namespace
|
|
2
|
+
# A visitor class dedicated in the visit of a parse forest.
|
|
3
|
+
# It combines the Visitor and Observer patterns.
|
|
4
|
+
class ParseForestVisitor
|
|
5
|
+
# Link to the parse forest to visit
|
|
6
|
+
attr_reader(:pforest)
|
|
7
|
+
|
|
8
|
+
# List of objects that subscribed to the visit event notification.
|
|
9
|
+
attr_reader(:subscribers)
|
|
10
|
+
|
|
11
|
+
# A Hash with pairs of the form: Node => node visit data
|
|
12
|
+
attr_reader(:agenda)
|
|
13
|
+
|
|
14
|
+
# Indicates the kind of forest traversal to perform: :post_order, :pre-order
|
|
15
|
+
attr_reader(:traversal)
|
|
16
|
+
|
|
17
|
+
# Build a visitor for the given pforest.
|
|
18
|
+
# @param aParseForest [ParseForest] the parse tree to visit.
|
|
19
|
+
def initialize(aParseForest, aTraversalStrategy = :post_order)
|
|
20
|
+
@pforest = aParseForest
|
|
21
|
+
@subscribers = []
|
|
22
|
+
@traversal = aTraversalStrategy
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
public
|
|
26
|
+
|
|
27
|
+
# Add a subscriber for the visit event notifications.
|
|
28
|
+
# @param aSubscriber [Object]
|
|
29
|
+
def subscribe(aSubscriber)
|
|
30
|
+
subscribers << aSubscriber
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Remove the given object from the subscription list.
|
|
34
|
+
# The object won't be notified of visit events.
|
|
35
|
+
# @param aSubscriber [Object]
|
|
36
|
+
def unsubscribe(aSubscriber)
|
|
37
|
+
subscribers.delete_if { |entry| entry == aSubscriber }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The signal to begin the visit of the parse forest.
|
|
41
|
+
def start()
|
|
42
|
+
pforest.accept(self)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# Visit event. The visitor is about to visit the pforest.
|
|
47
|
+
# @param aParseForest [ParseForest] the pforest to visit.
|
|
48
|
+
def start_visit_pforest(aParseForest)
|
|
49
|
+
broadcast(:before_pforest, aParseForest)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# Visit event. The visitor is about to visit the given non terminal node.
|
|
54
|
+
# @param aNonTerminalNode [NonTerminalNode] the node to visit.
|
|
55
|
+
def visit_nonterminal(aNonTerminalNode)
|
|
56
|
+
if @traversal == :post_order
|
|
57
|
+
broadcast(:before_non_terminal, aNonTerminalNode)
|
|
58
|
+
traverse_children(aNonTerminalNode)
|
|
59
|
+
else
|
|
60
|
+
traverse_children(aNonTerminalNode)
|
|
61
|
+
broadcast(:before_non_terminal, aNonTerminalNode)
|
|
62
|
+
end
|
|
63
|
+
broadcast(:after_non_terminal, aNonTerminalNode)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Visit event. The visitor is visiting the
|
|
67
|
+
# given terminal node.
|
|
68
|
+
# @param aTerminalNode [TerminalNode] the terminal to visit.
|
|
69
|
+
def visit_terminal(aTerminalNode)
|
|
70
|
+
broadcast(:before_terminal, aTerminalNode)
|
|
71
|
+
broadcast(:after_terminal, aTerminalNode)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# Visit event. The visitor has completed its visit of the given
|
|
76
|
+
# non-terminal node.
|
|
77
|
+
# @param aNonTerminalNode [NonTerminalNode] the node to visit.
|
|
78
|
+
def end_visit_nonterminal(aNonTerminalNode)
|
|
79
|
+
broadcast(:after_non_terminal, aNonTerminalNode)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Visit event. The visitor has completed the visit of the pforest.
|
|
83
|
+
# @param aParseForest [ParseForest] the pforest to visit.
|
|
84
|
+
def end_visit_pforest(aParseForest)
|
|
85
|
+
broadcast(:after_pforest, aParseForest)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
# Visit event. The visitor is about to visit the children of a non
|
|
91
|
+
# terminal node.
|
|
92
|
+
# @param aParentNode [NonTeminalNode] the (non-terminal) parent node.
|
|
93
|
+
def traverse_children(aParentNode)
|
|
94
|
+
children = aParentNode.children
|
|
95
|
+
broadcast(:before_children, aParentNode, children)
|
|
96
|
+
|
|
97
|
+
# Let's proceed with the visit of children
|
|
98
|
+
children.each { |a_node| a_node.accept(self) }
|
|
99
|
+
|
|
100
|
+
broadcast(:after_children, aParentNode, children)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Send a notification to all subscribers.
|
|
104
|
+
# @param msg [Symbol] event to notify
|
|
105
|
+
# @param args [Array] arguments of the notification.
|
|
106
|
+
def broadcast(msg, *args)
|
|
107
|
+
subscribers.each do |a_subscriber|
|
|
108
|
+
next unless a_subscriber.respond_to?(msg)
|
|
109
|
+
a_subscriber.send(msg, *args)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end # class
|
|
113
|
+
end # module
|
|
114
|
+
|
|
115
|
+
# End of file
|