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
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require_relative 'parse_walker_factory'
|
|
2
|
+
require_relative 'parse_forest_builder'
|
|
3
|
+
|
|
4
|
+
module Rley # This module is used as a namespace
|
|
5
|
+
module Parser # This module is used as a namespace
|
|
6
|
+
# Utility class that helps to create a ParseForest from
|
|
7
|
+
# a given Parsing object.
|
|
8
|
+
class ParseForestFactory
|
|
9
|
+
# Link to Parsing object (= results of recognizer)
|
|
10
|
+
attr_reader(:parsing)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def initialize(aParsingResult)
|
|
14
|
+
@parsing = aParsingResult
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Factory that produces the parse forest
|
|
18
|
+
def build_parse_forest()
|
|
19
|
+
a_walker = walker(parsing)
|
|
20
|
+
a_builder = builder(parsing)
|
|
21
|
+
|
|
22
|
+
begin
|
|
23
|
+
loop do
|
|
24
|
+
event = a_walker.next
|
|
25
|
+
# puts "EVENT #{event[0]} #{event[1]}"
|
|
26
|
+
a_builder.receive_event(*event)
|
|
27
|
+
end
|
|
28
|
+
rescue StopIteration
|
|
29
|
+
# Do nothing
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
return a_builder.forest
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
# Create a Parsing walker, that is, an object
|
|
38
|
+
# that will iterate over the relevant nodes (= parsing entries)
|
|
39
|
+
# of a GFGParsing
|
|
40
|
+
def walker(aParseResult)
|
|
41
|
+
walker_factory = ParseWalkerFactory.new
|
|
42
|
+
walker = walker_factory.build_walker(aParseResult)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Create a Builder, that is, an object
|
|
46
|
+
# that will create piece by piece the forest
|
|
47
|
+
def builder(aParseResult)
|
|
48
|
+
ParseForestBuilder.new(aParseResult)
|
|
49
|
+
end
|
|
50
|
+
end # class
|
|
51
|
+
end # module
|
|
52
|
+
end # module
|
|
53
|
+
|
|
54
|
+
# End of file
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
|
|
3
|
+
module Rley # This module is used as a namespace
|
|
4
|
+
module Parser # This module is used as a namespace
|
|
5
|
+
ParseWalkerContext = Struct.new(
|
|
6
|
+
:curr_entry, # Parse entry currently being visited
|
|
7
|
+
:entry_set_index, # Sigma set index of current parse entry
|
|
8
|
+
:visitees, # The set of already visited parse entries
|
|
9
|
+
:nterm2start, # A Hash non-terminal symbol => start entry
|
|
10
|
+
:return_stack, # A stack of parse entries
|
|
11
|
+
:backtrack_points
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
WalkerBacktrackpoint = Struct.new(
|
|
15
|
+
:entry_set_index, # Sigma set index of current parse entry
|
|
16
|
+
:return_stack, # A stack of parse entries
|
|
17
|
+
:visitee, # The parse entry being visited
|
|
18
|
+
:antecedent_index
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# A factory that creates an enumerator
|
|
23
|
+
# that itself walks through a given parsing graph.
|
|
24
|
+
# The walker yields visit events.
|
|
25
|
+
# Terminology warning: this class implements an external iterator
|
|
26
|
+
# for a given GFGParsing object. In other words, its instances are objects
|
|
27
|
+
# distinct for the GFGParsing.
|
|
28
|
+
# This is different from the internal iterators, usually implemented in Ruby
|
|
29
|
+
# with an each method.
|
|
30
|
+
# Allows to perform a backwards traversal over the relevant parse entries.
|
|
31
|
+
# backwards traversal means that the traversal starts from the accepting (final)
|
|
32
|
+
# parse entries and goes to the initial parse entry.
|
|
33
|
+
# Relevant parse entries are parse entries that "count" in the parse
|
|
34
|
+
# (i.e. they belong to a path that leads to the accepting parse entry)
|
|
35
|
+
class ParseWalkerFactory
|
|
36
|
+
# Build an Enumerator that will yield the parse entries as it
|
|
37
|
+
# walks backwards on the parse graph
|
|
38
|
+
def build_walker(aParseResult)
|
|
39
|
+
# Local context for the enumerator
|
|
40
|
+
parsing = aParseResult
|
|
41
|
+
ctx = init_context(aParseResult)
|
|
42
|
+
|
|
43
|
+
walker = Enumerator.new do |receiver| # 'receiver' is a Yielder
|
|
44
|
+
# At this point: current entry == accepting entry
|
|
45
|
+
|
|
46
|
+
loop do
|
|
47
|
+
event = visit_entry(ctx.curr_entry, ctx)
|
|
48
|
+
receiver << event unless event.nil?
|
|
49
|
+
|
|
50
|
+
if ctx.curr_entry.orphan? # No antecedent?...
|
|
51
|
+
if ctx.backtrack_points.empty?
|
|
52
|
+
break
|
|
53
|
+
else
|
|
54
|
+
receiver << use_backtrack_point(ctx)
|
|
55
|
+
receiver << visit_entry(ctx.curr_entry, ctx)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
result = jump_to_antecedent(ctx, parsing)
|
|
60
|
+
# Emit detection of scan edge if any...
|
|
61
|
+
receiver << result[0] if result.size > 1
|
|
62
|
+
ctx.curr_entry = result.last
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
return walker
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
# Context factory method
|
|
71
|
+
def init_context(aParseResult)
|
|
72
|
+
context = ParseWalkerContext.new
|
|
73
|
+
context.entry_set_index = aParseResult.chart.last_index
|
|
74
|
+
context.curr_entry = aParseResult.accepting_entry
|
|
75
|
+
context.visitees = Set.new
|
|
76
|
+
context.nterm2start = {}
|
|
77
|
+
context.return_stack = []
|
|
78
|
+
context.backtrack_points = []
|
|
79
|
+
|
|
80
|
+
return context
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# [event, entry, index, vertex]
|
|
84
|
+
def visit_entry(anEntry, aContext)
|
|
85
|
+
index = aContext.entry_set_index
|
|
86
|
+
|
|
87
|
+
if anEntry.start_entry?
|
|
88
|
+
aContext.nterm2start[anEntry.vertex.non_terminal] = anEntry
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
if aContext.visitees.include?(anEntry)
|
|
92
|
+
# multiple time visit
|
|
93
|
+
case anEntry.vertex
|
|
94
|
+
when GFG::EndVertex
|
|
95
|
+
# Jump to related start entry...
|
|
96
|
+
new_entry = aContext.nterm2start[anEntry.vertex.non_terminal]
|
|
97
|
+
aContext.curr_entry = new_entry
|
|
98
|
+
aContext.entry_set_index = new_entry.origin
|
|
99
|
+
event = [:revisit, anEntry, index]
|
|
100
|
+
|
|
101
|
+
when GFG::StartVertex
|
|
102
|
+
# Skip start entries while revisiting
|
|
103
|
+
event = nil
|
|
104
|
+
|
|
105
|
+
when GFG::ItemVertex
|
|
106
|
+
# Skip item entries while revisiting
|
|
107
|
+
event = nil
|
|
108
|
+
else
|
|
109
|
+
fail NotImplementedError
|
|
110
|
+
end
|
|
111
|
+
else
|
|
112
|
+
# first time visit
|
|
113
|
+
aContext.visitees << anEntry
|
|
114
|
+
event = [:visit, anEntry, index]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
return event
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def detect_scan_edge(ctx)
|
|
121
|
+
return nil unless aContext.curr_entry.dotted_entry?
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# Given the current entry from context object
|
|
126
|
+
# Go to the parse entry that is one of its antecedent
|
|
127
|
+
# The context object is updated
|
|
128
|
+
def jump_to_antecedent(aContext, aParseResult)
|
|
129
|
+
entries = []
|
|
130
|
+
return entries if aContext.curr_entry.orphan?
|
|
131
|
+
|
|
132
|
+
if aContext.curr_entry.antecedents.size == 1
|
|
133
|
+
entries = antecedent_of(aContext, aParseResult)
|
|
134
|
+
else
|
|
135
|
+
entries = select_antecedent(aContext)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
return entries
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Handle the case of an entry having one antecedent only
|
|
142
|
+
def antecedent_of(aContext, aParseResult)
|
|
143
|
+
new_entry = aContext.curr_entry.antecedents.first
|
|
144
|
+
events = [new_entry]
|
|
145
|
+
traversed_edge = new_entry.vertex.edges.first
|
|
146
|
+
case new_entry.vertex
|
|
147
|
+
when GFG::EndVertex
|
|
148
|
+
# Return edge encountered
|
|
149
|
+
# Push current entry onto stack
|
|
150
|
+
# puts "Push on return stack #{aContext.curr_entry}"
|
|
151
|
+
aContext.return_stack << aContext.curr_entry
|
|
152
|
+
else
|
|
153
|
+
if traversed_edge.is_a?(GFG::ScanEdge)
|
|
154
|
+
# Scan edge encountered, decrease sigma set index
|
|
155
|
+
aContext.entry_set_index -= 1
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
return events
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Handle the case of an entry having multiple antecedents
|
|
163
|
+
def select_antecedent(aContext)
|
|
164
|
+
case aContext.curr_entry.vertex
|
|
165
|
+
when GFG::EndVertex
|
|
166
|
+
# puts "Add backtrack point stack #{aContext.curr_entry}"
|
|
167
|
+
# An end vertex with multiple antecedents requires
|
|
168
|
+
# a backtrack point for a correct graph traversal
|
|
169
|
+
bp = add_backtrack_point(aContext)
|
|
170
|
+
new_entry = bp.visitee.antecedents[bp.antecedent_index]
|
|
171
|
+
|
|
172
|
+
when GFG::StartVertex
|
|
173
|
+
# An start vertex with multiple requires a backtrack point
|
|
174
|
+
new_entry = select_calling_entry(aContext)
|
|
175
|
+
else
|
|
176
|
+
fail StandardError, "Internal error"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
return [ new_entry ]
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def add_backtrack_point(aContext)
|
|
183
|
+
bp = WalkerBacktrackpoint.new
|
|
184
|
+
|
|
185
|
+
bp.entry_set_index = aContext.entry_set_index
|
|
186
|
+
bp.return_stack = aContext.return_stack.dup
|
|
187
|
+
bp.visitee = aContext.curr_entry
|
|
188
|
+
bp.antecedent_index = 0
|
|
189
|
+
aContext.backtrack_points << bp
|
|
190
|
+
|
|
191
|
+
return bp
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def use_backtrack_point(aContext)
|
|
196
|
+
bp = aContext.backtrack_points.last
|
|
197
|
+
bp.antecedent_index += 1
|
|
198
|
+
|
|
199
|
+
# Restore state
|
|
200
|
+
aContext.entry_set_index = bp.entry_set_index
|
|
201
|
+
aContext.return_stack = bp.return_stack.dup
|
|
202
|
+
aContext.curr_entry = bp.visitee.antecedents[bp.antecedent_index]
|
|
203
|
+
|
|
204
|
+
# Drop backtrack point if useless in future
|
|
205
|
+
if bp.antecedent_index == bp.visitee.antecedents.size - 1
|
|
206
|
+
aContext.backtrack_points.pop
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Emit a backtrack event
|
|
210
|
+
return [:backtrack, bp.visitee, aContext.entry_set_index]
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# From the antecedent of the current parse entry
|
|
214
|
+
# Retrieve the one that corresponds to the parse entry on
|
|
215
|
+
# top of return stack
|
|
216
|
+
# Observation: calling parse entry is an parse entry linked
|
|
217
|
+
# to a item vertex
|
|
218
|
+
def select_calling_entry(aContext)
|
|
219
|
+
# Retrieve top of stack
|
|
220
|
+
tos = aContext.return_stack.pop
|
|
221
|
+
tos_dotted_item = tos.vertex.dotted_item
|
|
222
|
+
|
|
223
|
+
antecedents = aContext.curr_entry.antecedents
|
|
224
|
+
new_entry = antecedents.find do |antecd|
|
|
225
|
+
item = antecd.vertex.dotted_item
|
|
226
|
+
(antecd.origin == tos.origin) && tos_dotted_item.successor_of?(item)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# TODO: double-check validity of next line
|
|
230
|
+
new_entry = aContext.curr_entry unless new_entry
|
|
231
|
+
|
|
232
|
+
# puts "Pop from return stack matching entry #{new_entry}"
|
|
233
|
+
return new_entry
|
|
234
|
+
end
|
|
235
|
+
end # class
|
|
236
|
+
end # module
|
|
237
|
+
end # module
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
module Rley # This module is used as a namespace
|
|
2
2
|
module PTree # This module is used as a namespace
|
|
3
|
+
# A token range (also called an extent) represents an interval
|
|
4
|
+
# of token positions that is matched by a given grammar symbol.
|
|
5
|
+
# For instance consider the expression E: 3 + 11,
|
|
6
|
+
# let's assume that the integer literal '3' is the fifth input token and
|
|
7
|
+
# that the '+' and '11' tokens are respectively at position 6 and 7;
|
|
8
|
+
# then the token range associated with E is [5, 7]
|
|
9
|
+
# While the parse tree/forest is being constructed the boundaries of the token range
|
|
10
|
+
# can be temporarily undefined (= set to nil)
|
|
3
11
|
class TokenRange
|
|
4
12
|
# The index of the lower bound of token range
|
|
5
13
|
attr_reader(:low)
|
|
@@ -58,7 +66,12 @@ module Rley # This module is used as a namespace
|
|
|
58
66
|
high_text = high.nil? ? '?' : high.to_s
|
|
59
67
|
|
|
60
68
|
return "[#{low_text}, #{high_text}]"
|
|
61
|
-
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Generate a String that represents a value-based identifier
|
|
72
|
+
def keystr()
|
|
73
|
+
return "#{low.object_id}-#{high.object_id}"
|
|
74
|
+
end
|
|
62
75
|
|
|
63
76
|
private
|
|
64
77
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require_relative 'composite_node'
|
|
2
|
+
|
|
3
|
+
module Rley # This module is used as a namespace
|
|
4
|
+
module SPPF # This module is used as a namespace
|
|
5
|
+
# A node in a parse forest that is a child
|
|
6
|
+
# of a parent node with :or refinement
|
|
7
|
+
class AlternativeNode < CompositeNode
|
|
8
|
+
|
|
9
|
+
# GFG vertex label
|
|
10
|
+
attr_reader(:label)
|
|
11
|
+
|
|
12
|
+
# Link to lhs symbol
|
|
13
|
+
attr_reader(:symbol)
|
|
14
|
+
|
|
15
|
+
# @param aVertex [ItemVertex] An GFG vertex that corresponds
|
|
16
|
+
# a dotted item (with the dot at the end)for the alternative under
|
|
17
|
+
# consideration.
|
|
18
|
+
# @param aRange [TokenRange]
|
|
19
|
+
def initialize(aVertex, aRange)
|
|
20
|
+
super(aRange)
|
|
21
|
+
@label = aVertex.label
|
|
22
|
+
@symbol = aVertex.dotted_item.lhs
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Emit a (formatted) string representation of the node.
|
|
26
|
+
# Mainly used for diagnosis/debugging purposes.
|
|
27
|
+
def to_string(indentation)
|
|
28
|
+
return "Alt(#{label})#{range.to_string(indentation)}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end # class
|
|
32
|
+
end # module
|
|
33
|
+
end # module
|
|
34
|
+
# End of file
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require_relative 'sppf_node'
|
|
2
|
+
|
|
3
|
+
module Rley # This module is used as a namespace
|
|
4
|
+
module SPPF # This module is used as a namespace
|
|
5
|
+
# Abstract class. The generalization for nodes that have
|
|
6
|
+
# children node(s).
|
|
7
|
+
class CompositeNode < SPPFNode
|
|
8
|
+
# Array of sub-nodes.
|
|
9
|
+
attr_reader(:subnodes)
|
|
10
|
+
|
|
11
|
+
def initialize(aRange)
|
|
12
|
+
super(aRange)
|
|
13
|
+
@subnodes = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def add_subnode(aSubnode)
|
|
18
|
+
subnodes.unshift(aSubnode)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def key()
|
|
22
|
+
@key ||= to_string(0)
|
|
23
|
+
end
|
|
24
|
+
end # class
|
|
25
|
+
end # module
|
|
26
|
+
end # module
|
|
27
|
+
# End of file
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require_relative 'leaf_node'
|
|
2
|
+
|
|
3
|
+
module Rley # This module is used as a namespace
|
|
4
|
+
module SPPF # This module is used as a namespace
|
|
5
|
+
# A leaf node in a parse forest that matches an empty
|
|
6
|
+
# string from the input
|
|
7
|
+
class EpsilonNode < LeafNode
|
|
8
|
+
|
|
9
|
+
# aPosition is the position of the token in the input stream.
|
|
10
|
+
def initialize(aPosition)
|
|
11
|
+
range = {low: aPosition, high: aPosition}
|
|
12
|
+
super(range)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Emit a (formatted) string representation of the node.
|
|
16
|
+
# Mainly used for diagnosis/debugging purposes.
|
|
17
|
+
def to_string(indentation)
|
|
18
|
+
return "_#{range.to_string(indentation)}"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def key()
|
|
22
|
+
@key ||= to_string(0)
|
|
23
|
+
end
|
|
24
|
+
end # class
|
|
25
|
+
end # module
|
|
26
|
+
end # module
|
|
27
|
+
# End of file
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require_relative 'sppf_node'
|
|
2
|
+
|
|
3
|
+
module Rley # This module is used as a namespace
|
|
4
|
+
module SPPF # This module is used as a namespace
|
|
5
|
+
# Abstract class. The generalization for nodes that don't have
|
|
6
|
+
# child node.
|
|
7
|
+
class LeafNode < SPPFNode
|
|
8
|
+
|
|
9
|
+
end # class
|
|
10
|
+
end # module
|
|
11
|
+
end # module
|
|
12
|
+
# End of file
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require_relative 'composite_node'
|
|
2
|
+
|
|
3
|
+
module Rley # This module is used as a namespace
|
|
4
|
+
module SPPF # This module is used as a namespace
|
|
5
|
+
# A node in a parse forest that matches exactly one
|
|
6
|
+
# non-terminal symbol
|
|
7
|
+
class NonTerminalNode < CompositeNode
|
|
8
|
+
# Link to the non-terminal symbol
|
|
9
|
+
attr_reader(:symbol)
|
|
10
|
+
|
|
11
|
+
# Indication on how the sub-nodes contribute to the 'success'
|
|
12
|
+
# of parent node. Possible values: :and, :or
|
|
13
|
+
attr_accessor :refinement
|
|
14
|
+
|
|
15
|
+
def initialize(aNonTerminal, aRange)
|
|
16
|
+
super(aRange)
|
|
17
|
+
@symbol = aNonTerminal
|
|
18
|
+
@refinement = :and
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def add_subnode(aSubnode)
|
|
22
|
+
if refinement == :or
|
|
23
|
+
subnodes << aSubnode
|
|
24
|
+
else
|
|
25
|
+
super(aSubnode)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Emit a (formatted) string representation of the node.
|
|
30
|
+
# Mainly used for diagnosis/debugging purposes.
|
|
31
|
+
def to_string(indentation)
|
|
32
|
+
return "#{symbol.name}#{range.to_string(indentation)}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end # class
|
|
36
|
+
end # module
|
|
37
|
+
end # module
|
|
38
|
+
# End of file
|