rley 0.4.08 → 0.5.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 +5 -0
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/parse_tree_visitor.rb +1 -0
- data/lib/rley/parser/cst_builder.rb +271 -0
- data/lib/rley/parser/parse_tree_factory.rb +3 -2
- data/lib/rley/parser/parse_walker_factory.rb +4 -0
- data/lib/rley/tokens/token_range.rb +2 -0
- data/spec/rley/parser/cst_builder_spec.rb +439 -0
- data/spec/rley/support/grammar_arr_int_helper.rb +41 -0
- data/spec/rley/tokens/token_range_spec.rb +1 -0
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a0a5683c3a64ffa38c57d916ecbca45fa2f8f0b2
|
|
4
|
+
data.tar.gz: 77c4d6c99c3836c37e129cce40a9457fe74fbb8e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7f4d20ab683694d83fb8f16933bd3cf7dccfa39e1166d0ae2571503f576074c53d13ad26006016ea4518627e3d94596ec91af527394e33d71d825f12c64611dc
|
|
7
|
+
data.tar.gz: 620870b9f7ddb191fb1899a13ae08dd97feb01ef33e96d6b40e752ac4e01d47b5bfd0f09107a017babe00fec08cb44579d45fae0d8583a4d8d801dd1f76de216
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
### 0.5.00 / 2017-08-20
|
|
2
|
+
* [CHANGE] Concrete Syntax tree generation re-designed in prevision of customized tree generation.
|
|
3
|
+
* [NEW] Class `Parser::CSTBuilder` Builder class that creates Concrete Syntax (parse) Tree.
|
|
4
|
+
|
|
5
|
+
|
|
1
6
|
### 0.4.08 / 2017-08-06
|
|
2
7
|
* [FIX] File `/spec/spec_helper.rb` replaced deprecated syntax for `SimpleCov::Formatter::MultiFormatter` construction
|
|
3
8
|
* [NEW] File `examples/data_formats/JSON/json_minifier.rb` Added a working JSON minifier to the demo app.
|
data/lib/rley/constants.rb
CHANGED
|
@@ -14,6 +14,7 @@ module Rley # This module is used as a namespace
|
|
|
14
14
|
# Build a visitor for the given ptree.
|
|
15
15
|
# @param aParseTree [ParseTree] the parse tree to visit.
|
|
16
16
|
def initialize(aParseTree, aTraversalStrategy = :post_order)
|
|
17
|
+
raise StandardError if aParseTree.nil?
|
|
17
18
|
@ptree = aParseTree
|
|
18
19
|
@subscribers = []
|
|
19
20
|
@traversal = aTraversalStrategy
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
require_relative '../tokens/token_range'
|
|
2
|
+
require_relative '../syntax/terminal'
|
|
3
|
+
require_relative '../syntax/non_terminal'
|
|
4
|
+
require_relative '../gfg/end_vertex'
|
|
5
|
+
require_relative '../gfg/item_vertex'
|
|
6
|
+
require_relative '../gfg/start_vertex'
|
|
7
|
+
require_relative '../ptree/non_terminal_node'
|
|
8
|
+
require_relative '../ptree/terminal_node'
|
|
9
|
+
require_relative '../ptree/parse_tree'
|
|
10
|
+
|
|
11
|
+
module Rley # This module is used as a namespace
|
|
12
|
+
module Parser # This module is used as a namespace
|
|
13
|
+
# Structure used internally by CSTBuilder class.
|
|
14
|
+
CSTRawNode = Struct.new(:range, :symbol, :children) do
|
|
15
|
+
def initialize(aRange, aSymbol)
|
|
16
|
+
super
|
|
17
|
+
self.range = aRange
|
|
18
|
+
self.symbol = aSymbol
|
|
19
|
+
self.children = nil
|
|
20
|
+
end
|
|
21
|
+
end # Struct
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# The purpose of a CSTBuilder is to build piece by piece a CST
|
|
25
|
+
# (Concrete Syntax Tree) from a sequence of input tokens and
|
|
26
|
+
# visit events produced by walking over a GFGParsing object.
|
|
27
|
+
# Uses the Builder GoF pattern.
|
|
28
|
+
# The Builder pattern creates a complex object
|
|
29
|
+
# (say, a parse tree) from simpler objects (terminal and non-terminal
|
|
30
|
+
# nodes) and using a step by step approach.
|
|
31
|
+
class CSTBuilder
|
|
32
|
+
# @return [Array<Token>] The sequence of input tokens
|
|
33
|
+
attr_reader(:tokens)
|
|
34
|
+
|
|
35
|
+
# Link to CST object (being) built.
|
|
36
|
+
attr_reader(:result)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Create a new builder instance.
|
|
40
|
+
# @param theTokens [Array<Token>] The sequence of input tokens.
|
|
41
|
+
def initialize(theTokens)
|
|
42
|
+
@tokens = theTokens
|
|
43
|
+
@stack = []
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Receive events resulting from a visit of GFGParsing object.
|
|
47
|
+
# These events are produced by a specialized Enumerator created
|
|
48
|
+
# with a ParseWalkerFactory instance.
|
|
49
|
+
# @param anEvent [Symbol] Kind of visit event. Should be: :visit
|
|
50
|
+
# @param anEntry [ParseEntry] The entry being visited
|
|
51
|
+
# @param anIndex [anIndex] The token index associated with anEntry
|
|
52
|
+
def receive_event(anEvent, anEntry, anIndex)
|
|
53
|
+
# puts "Event: #{anEvent} #{anEntry} #{anIndex}"
|
|
54
|
+
if anEntry.dotted_entry? # A N => alpha . beta pattern?
|
|
55
|
+
process_item_entry(anEvent, anEntry, anIndex)
|
|
56
|
+
elsif anEntry.start_entry? # A .N pattern?
|
|
57
|
+
process_start_entry(anEvent, anEntry, anIndex)
|
|
58
|
+
elsif anEntry.end_entry? # A N. pattern?
|
|
59
|
+
process_end_entry(anEvent, anEntry, anIndex)
|
|
60
|
+
else
|
|
61
|
+
raise NotImplementedError
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
@last_visitee = anEntry
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
protected
|
|
68
|
+
|
|
69
|
+
# Return the stack
|
|
70
|
+
def stack()
|
|
71
|
+
return @stack
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
# Return the top of stack element.
|
|
77
|
+
def tos()
|
|
78
|
+
return @stack.last
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Handler for visit events for ParseEntry matching N. pattern
|
|
82
|
+
# @param anEvent [Symbol] Kind of visit event. Should be: :visit
|
|
83
|
+
# @param anEntry [ParseEntry] The entry being visited
|
|
84
|
+
# @param anIndex [anIndex] The token index at end of anEntry
|
|
85
|
+
def process_end_entry(anEvent, anEntry, anIndex)
|
|
86
|
+
case anEvent
|
|
87
|
+
when :visit
|
|
88
|
+
range = { low: anEntry.origin, high: anIndex }
|
|
89
|
+
non_terminal = entry2nonterm(anEntry)
|
|
90
|
+
# Create raw node and push onto stack
|
|
91
|
+
push_raw_node(range, non_terminal)
|
|
92
|
+
else
|
|
93
|
+
raise NotImplementedError
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Handler for visit events for ParseEntry matching .N pattern
|
|
98
|
+
# @param anEvent [Symbol] Kind of visit event. Should be: :visit
|
|
99
|
+
# @param anEntry [ParseEntry] The entry being visited
|
|
100
|
+
# @param anIndex [anIndex] The token index at end of anEntry
|
|
101
|
+
def process_start_entry(anEvent, anEntry, anIndex)
|
|
102
|
+
raise NotImplementedError unless [:visit, :revisit].include?(anEvent)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Handler for visit events for ParseEntry matching N => alpha* . beta*
|
|
106
|
+
# @param anEvent [Symbol] Kind of visit event. Should be: :visit
|
|
107
|
+
# @param anEntry [ParseEntry] The entry being visited
|
|
108
|
+
# @param anIndex [anIndex] The token index at end of anEntry
|
|
109
|
+
def process_item_entry(anEvent, anEntry, anIndex)
|
|
110
|
+
# TODO: what if rhs is empty?
|
|
111
|
+
case anEvent
|
|
112
|
+
when :visit, :revisit
|
|
113
|
+
dot_pos = anEntry.vertex.dotted_item.position
|
|
114
|
+
if dot_pos.zero? || dot_pos < 0
|
|
115
|
+
# Check for pattern: N => alpha* .
|
|
116
|
+
process_exit_entry(anEntry, anIndex) if anEntry.exit_entry?
|
|
117
|
+
|
|
118
|
+
# Check for pattern: N => . alpha*
|
|
119
|
+
process_entry_entry(anEntry, anIndex) if anEntry.entry_entry?
|
|
120
|
+
else
|
|
121
|
+
# (pattern: N => alpha+ . beta+)
|
|
122
|
+
process_middle_entry(anEntry, anIndex)
|
|
123
|
+
end
|
|
124
|
+
else
|
|
125
|
+
$stderr.puts "waiko '#{anEvent}'"
|
|
126
|
+
raise NotImplementedError
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# @param anEntry [ParseEntry] Entry matching (pattern: N => alpha* .)
|
|
131
|
+
# @param anIndex [anIndex] The token index at end of anEntry
|
|
132
|
+
def process_exit_entry(anEntry, anIndex)
|
|
133
|
+
production = anEntry.vertex.dotted_item.production
|
|
134
|
+
count_rhs = production.rhs.members.size
|
|
135
|
+
init_TOS_children(count_rhs) # Create placeholders for children
|
|
136
|
+
build_terminal(anEntry, anIndex) if terminal_before_dot?(anEntry)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# @param anEntry [ParseEntry] Entry matching pattern: N => alpha+ . beta+
|
|
140
|
+
# @param anIndex [anIndex] The token index at end of anEntry
|
|
141
|
+
def process_middle_entry(anEntry, anIndex)
|
|
142
|
+
build_terminal(anEntry, anIndex) if terminal_before_dot?(anEntry)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# @param anEntry [ParseEntry] Entry matching (pattern: N => . alpha)
|
|
148
|
+
# @param anIndex [anIndex] The token index at end of anEntry
|
|
149
|
+
def process_entry_entry(anEntry, anIndex)
|
|
150
|
+
dotted_item = anEntry.vertex.dotted_item
|
|
151
|
+
rule = dotted_item.production
|
|
152
|
+
previous_tos = stack.pop
|
|
153
|
+
non_terminal = entry2nonterm(anEntry)
|
|
154
|
+
# For debugging purposes
|
|
155
|
+
raise StandardError if previous_tos.symbol != non_terminal
|
|
156
|
+
|
|
157
|
+
new_node = new_parent_node(rule, previous_tos.range,
|
|
158
|
+
tokens, previous_tos.children)
|
|
159
|
+
if stack.empty?
|
|
160
|
+
@result = create_tree(new_node)
|
|
161
|
+
else
|
|
162
|
+
place_TOS_child(new_node, nil)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Create a raw node with given range
|
|
167
|
+
# and push it on top of stack.
|
|
168
|
+
def push_raw_node(aRange, aSymbol)
|
|
169
|
+
raw_node = CSTRawNode.new(Tokens::TokenRange.new(aRange), aSymbol)
|
|
170
|
+
stack.push(raw_node)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Initialize children array of TOS with nil placeholders.
|
|
174
|
+
# The number of elements equals the number of symbols at rhs.
|
|
175
|
+
def init_TOS_children(aCount)
|
|
176
|
+
tos.children = Array.new(aCount)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Does the position on the left side of the dot correspond
|
|
180
|
+
# a terminal symbol?
|
|
181
|
+
# @param anEntry [ParseEntry] The entry being visited
|
|
182
|
+
def terminal_before_dot?(anEntry)
|
|
183
|
+
prev_symbol = anEntry.prev_symbol
|
|
184
|
+
return prev_symbol && prev_symbol.terminal?
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# A terminal symbol was detected at left of dot.
|
|
188
|
+
# Build a raw node for that terminal and make it
|
|
189
|
+
# a child of TOS.
|
|
190
|
+
# @param anEntry [ParseEntry] The entry being visited
|
|
191
|
+
# @param anIndex [anIndex] The token index at end of anEntry
|
|
192
|
+
def build_terminal(anEntry, anIndex)
|
|
193
|
+
# First, build node for terminal...
|
|
194
|
+
term_symbol = anEntry.prev_symbol
|
|
195
|
+
token_position = anIndex - 1
|
|
196
|
+
token = tokens[token_position]
|
|
197
|
+
term_node = new_leaf_node(term_symbol, token_position, token)
|
|
198
|
+
|
|
199
|
+
# Second make it a child of TOS...
|
|
200
|
+
pos = anEntry.vertex.dotted_item.prev_position # position in rhs of rule
|
|
201
|
+
place_TOS_child(term_node, pos)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Method to override
|
|
205
|
+
# Create a parse tree object with given
|
|
206
|
+
# node as root node.
|
|
207
|
+
def create_tree(aRootNode)
|
|
208
|
+
return Rley::PTree::ParseTree.new(aRootNode)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Method to override
|
|
212
|
+
# Factory method for creating a node object for the given
|
|
213
|
+
# input token.
|
|
214
|
+
# @param _terminal [Terminal] Terminal symbol associated with the token
|
|
215
|
+
# @param aTokenPosition [Integer] Position of token in the input stream
|
|
216
|
+
# @param aToken [Token] The input token
|
|
217
|
+
def new_leaf_node(_terminal, aTokenPosition, aToken)
|
|
218
|
+
PTree::TerminalNode.new(aToken, aTokenPosition)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Method to override.
|
|
222
|
+
# Factory method for creating a parent node object.
|
|
223
|
+
# @param aProduction [Production] Production rule
|
|
224
|
+
# @param aRange [Range] Range of tokens matched by the rule
|
|
225
|
+
# @param theTokens [Array] The input tokens
|
|
226
|
+
# @param theChildren [Array] Children nodes (one per rhs symbol)
|
|
227
|
+
def new_parent_node(aProduction, aRange, theTokens, theChildren)
|
|
228
|
+
node = Rley::PTree::NonTerminalNode.new(aProduction.lhs, aRange)
|
|
229
|
+
theChildren.reverse_each { |child| node.add_subnode(child) }
|
|
230
|
+
return node
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Place the given node object as one of the children of the TOS
|
|
234
|
+
# (TOS = Top Of Stack).
|
|
235
|
+
# Each child has a position that is dictated by the position of the
|
|
236
|
+
# related grammar symbol in the right-handed side (RHS) of the grammar
|
|
237
|
+
# rule.
|
|
238
|
+
# @param aNode [TerminalNode, NonTerminalNode] Node object to be placed
|
|
239
|
+
# @param aRHSPos [Integer, NilClass] Position in RHS of rule.
|
|
240
|
+
# If the position is provided, then the node will placed in the children
|
|
241
|
+
# array at that position.
|
|
242
|
+
# If the position is nil, then the node will be placed at the position of
|
|
243
|
+
# the rightmost nil element in children array.
|
|
244
|
+
def place_TOS_child(aNode, aRHSPos)
|
|
245
|
+
if aRHSPos.nil?
|
|
246
|
+
# Retrieve index of most rightmost nil child...
|
|
247
|
+
pos = tos.children.rindex { |child| child.nil? }
|
|
248
|
+
raise StandardError, 'Internal error' if pos.nil?
|
|
249
|
+
else
|
|
250
|
+
pos = aRHSPos
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
tos.children[pos] = aNode
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Retrieve non-terminal symbol of given parse entry
|
|
257
|
+
def entry2nonterm(anEntry)
|
|
258
|
+
case anEntry.vertex
|
|
259
|
+
when GFG::StartVertex, GFG::EndVertex
|
|
260
|
+
non_terminal = anEntry.vertex.non_terminal
|
|
261
|
+
when GFG::ItemVertex
|
|
262
|
+
non_terminal = anEntry.vertex.lhs
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
return non_terminal
|
|
266
|
+
end
|
|
267
|
+
end # class
|
|
268
|
+
end # module
|
|
269
|
+
end # module
|
|
270
|
+
|
|
271
|
+
# End of file
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require_relative 'parse_rep_creator'
|
|
2
|
-
require_relative 'parse_tree_builder'
|
|
2
|
+
# require_relative 'parse_tree_builder' # TODO remove this line
|
|
3
|
+
require_relative 'cst_builder'
|
|
3
4
|
|
|
4
5
|
module Rley # This module is used as a namespace
|
|
5
6
|
module Parser # This module is used as a namespace
|
|
@@ -12,7 +13,7 @@ module Rley # This module is used as a namespace
|
|
|
12
13
|
# Create a Builder, that is, an object
|
|
13
14
|
# that will create piece by piece the forest
|
|
14
15
|
def builder(aParseResult)
|
|
15
|
-
|
|
16
|
+
CSTBuilder.new(aParseResult.tokens)
|
|
16
17
|
end
|
|
17
18
|
end # class
|
|
18
19
|
end # module
|
|
@@ -124,9 +124,13 @@ module Rley # This module is used as a namespace
|
|
|
124
124
|
event = [:revisit, anEntry, index]
|
|
125
125
|
|
|
126
126
|
when GFG::StartVertex
|
|
127
|
+
# Even for non-ambiguous parse, can be caused by
|
|
128
|
+
# left recursive rule e.g. (S => S A)
|
|
127
129
|
event = [:revisit, anEntry, index]
|
|
128
130
|
|
|
129
131
|
when GFG::ItemVertex
|
|
132
|
+
# Even for non-ambiguous parse, can be caused by
|
|
133
|
+
# left recursive rule e.g. (S => S A)
|
|
130
134
|
# Skip item entries while revisiting
|
|
131
135
|
event = [:revisit, anEntry, index]
|
|
132
136
|
else
|
|
@@ -29,6 +29,8 @@ module Rley # This module is used as a namespace
|
|
|
29
29
|
result = low == other[:low] && high == other[:high]
|
|
30
30
|
when TokenRange
|
|
31
31
|
result = low == other.low && high == other.high
|
|
32
|
+
when Range
|
|
33
|
+
result = low == other.first && high == other.last
|
|
32
34
|
when Array
|
|
33
35
|
result = low == other[0] && high == other[1]
|
|
34
36
|
end
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
require_relative '../../spec_helper'
|
|
2
|
+
|
|
3
|
+
require_relative '../../../lib/rley/parser/gfg_earley_parser'
|
|
4
|
+
require_relative '../../../lib/rley/parser/parse_walker_factory'
|
|
5
|
+
|
|
6
|
+
require_relative '../support/expectation_helper'
|
|
7
|
+
require_relative '../support/grammar_b_expr_helper'
|
|
8
|
+
require_relative '../support/grammar_arr_int_helper'
|
|
9
|
+
|
|
10
|
+
# Load the class under test
|
|
11
|
+
require_relative '../../../lib/rley/parser/cst_builder'
|
|
12
|
+
|
|
13
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
|
14
|
+
module Parser
|
|
15
|
+
describe CSTBuilder do
|
|
16
|
+
include ExpectationHelper # Mix-in with expectation on parse entry sets
|
|
17
|
+
include GrammarBExprHelper # Mix-in for basic arithmetic language
|
|
18
|
+
include GrammarArrIntHelper # Mix-in for array of integers language
|
|
19
|
+
|
|
20
|
+
let(:sample_grammar) do
|
|
21
|
+
builder = grammar_expr_builder
|
|
22
|
+
builder.grammar
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
let(:sample_tokens) do
|
|
26
|
+
expr_tokenizer('2 + 3 * 4', sample_grammar)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
subject { CSTBuilder.new(sample_tokens) }
|
|
30
|
+
|
|
31
|
+
def init_walker(theParser, theTokens)
|
|
32
|
+
result = theParser.parse(theTokens)
|
|
33
|
+
factory = ParseWalkerFactory.new
|
|
34
|
+
accept_entry = result.accepting_entry
|
|
35
|
+
accept_index = result.chart.last_index
|
|
36
|
+
@walker = factory.build_walker(accept_entry, accept_index)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def skip_events(count)
|
|
40
|
+
count.times do
|
|
41
|
+
event = @walker.next
|
|
42
|
+
subject.receive_event(*event)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def get_stack(aBuilder)
|
|
47
|
+
return aBuilder.send(:stack)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def create_range(low, high)
|
|
51
|
+
return Tokens::TokenRange.new({low: low, high: high })
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context 'Initialization:' do
|
|
55
|
+
it 'should be created with a sequence of tokens' do
|
|
56
|
+
expect { CSTBuilder.new(sample_tokens) }.not_to raise_error
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'should know the input tokens' do
|
|
60
|
+
expect(subject.tokens).to eq(sample_tokens)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "shouldn't know the result yet" do
|
|
64
|
+
expect(subject.result).to be_nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'should have an empty stack' do
|
|
68
|
+
expect(subject.send(:stack)).to be_empty
|
|
69
|
+
end
|
|
70
|
+
end # context
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
context 'Parse tree construction (no null symbol):' do
|
|
75
|
+
before(:each) do
|
|
76
|
+
parser = Parser::GFGEarleyParser.new(sample_grammar)
|
|
77
|
+
init_walker(parser, sample_tokens)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Event: visit P. | 0 5
|
|
81
|
+
# Event: visit P => S . | 0 5
|
|
82
|
+
# Event: visit S. | 0 5
|
|
83
|
+
# Event: visit S => S + M . | 0 5
|
|
84
|
+
# Event: visit M. | 2 5
|
|
85
|
+
# Event: visit M => M * T . | 2 5
|
|
86
|
+
# Event: visit T. | 4 5
|
|
87
|
+
# Event: visit T => integer . | 4 5
|
|
88
|
+
# Event: visit T => . integer | 4 4
|
|
89
|
+
# Event: visit .T | 4 4
|
|
90
|
+
# Event: visit M => M * . T | 2 4
|
|
91
|
+
# Event: visit M => M . * T | 2 3
|
|
92
|
+
# Event: visit M. | 2 3
|
|
93
|
+
# Event: visit M => T . | 2 3
|
|
94
|
+
# Event: visit T. | 2 3
|
|
95
|
+
# Event: visit T => integer . | 2 3
|
|
96
|
+
# Event: visit T => . integer | 2 2
|
|
97
|
+
# Event: visit .T | 2 2
|
|
98
|
+
# Event: visit M => . T | 2 2
|
|
99
|
+
# Event: visit .M | 2 2
|
|
100
|
+
# Event: visit M => . M * T | 2 2
|
|
101
|
+
# Event: revisit .M | 2 2 <= revisit because of left recursive rule
|
|
102
|
+
# Event: visit S => S + . M | 0 2
|
|
103
|
+
# Event: visit S => S . + M | 0 1
|
|
104
|
+
# Event: visit S. | 0 1
|
|
105
|
+
# Event: visit S => M . | 0 1
|
|
106
|
+
# Event: visit M. | 0 1
|
|
107
|
+
# Event: visit M => T . | 0 1
|
|
108
|
+
# Event: visit T. | 0 1
|
|
109
|
+
# Event: visit T => integer . | 0 1
|
|
110
|
+
# Event: visit T => . integer | 0 0
|
|
111
|
+
# Event: visit .T | 0 0
|
|
112
|
+
# Event: visit M => . T | 0 0
|
|
113
|
+
# Event: visit .M | 0 0
|
|
114
|
+
# Event: visit S => . M | 0 0
|
|
115
|
+
# Event: visit .S | 0 0
|
|
116
|
+
# Event: visit S => . S + M | 0 0
|
|
117
|
+
# Event: revisit .S | 0 0
|
|
118
|
+
# Event: visit P => . S | 0 0
|
|
119
|
+
# Event: visit .P | 0 0
|
|
120
|
+
|
|
121
|
+
it 'should react to a first end event' do
|
|
122
|
+
event = @walker.next
|
|
123
|
+
expect { subject.receive_event(*event) }.not_to raise_error
|
|
124
|
+
stack = get_stack(subject)
|
|
125
|
+
expect(stack.size).to eq(1)
|
|
126
|
+
expect(stack.last.range).to eq(create_range(0, 5))
|
|
127
|
+
expect(stack.last.children).to be_nil
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'should react to a first exit event' do
|
|
131
|
+
skip_events(1)
|
|
132
|
+
event = @walker.next
|
|
133
|
+
expect { subject.receive_event(*event) }.not_to raise_error
|
|
134
|
+
stack = get_stack(subject)
|
|
135
|
+
expect(stack.size).to eq(1)
|
|
136
|
+
expect(stack.last.children).to eq([nil])
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it 'should react to a second end event' do
|
|
140
|
+
skip_events(2)
|
|
141
|
+
event = @walker.next
|
|
142
|
+
expect { subject.receive_event(*event) }.not_to raise_error
|
|
143
|
+
stack = get_stack(subject)
|
|
144
|
+
expect(stack.size).to eq(2)
|
|
145
|
+
expect(stack.last.range).to eq(create_range(0, 5))
|
|
146
|
+
expect(stack.last.children).to be_nil
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it 'should react to a second exit event' do
|
|
150
|
+
skip_events(3)
|
|
151
|
+
event = @walker.next
|
|
152
|
+
expect { subject.receive_event(*event) }.not_to raise_error
|
|
153
|
+
stack = get_stack(subject)
|
|
154
|
+
expect(stack.size).to eq(2)
|
|
155
|
+
expect(stack.last.children).to eq([nil, nil, nil])
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it 'should react to an exit event that creates a terminal node' do
|
|
159
|
+
skip_events(7)
|
|
160
|
+
event = @walker.next
|
|
161
|
+
expect { subject.receive_event(*event) }.not_to raise_error
|
|
162
|
+
stack = get_stack(subject)
|
|
163
|
+
expect(stack.size).to eq(4)
|
|
164
|
+
expect(stack.last.children.size).to eq(1)
|
|
165
|
+
child = stack.last.children[-1]
|
|
166
|
+
expect(child).to be_kind_of(PTree::TerminalNode)
|
|
167
|
+
expect(child.to_s).to eq("integer[4, 5]: '4'")
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
it 'should react to a first entry event' do
|
|
172
|
+
skip_events(8)
|
|
173
|
+
event = @walker.next
|
|
174
|
+
expect { subject.receive_event(*event) }.not_to raise_error
|
|
175
|
+
stack = get_stack(subject)
|
|
176
|
+
expect(stack.size).to eq(3) # Element popped
|
|
177
|
+
expect(stack.last.children.size).to eq(3)
|
|
178
|
+
child = stack.last.children[-1]
|
|
179
|
+
expect(child).to be_kind_of(PTree::NonTerminalNode)
|
|
180
|
+
expect(child.to_s).to eq('T[4, 5]')
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it 'should react to a first start event' do
|
|
184
|
+
skip_events(9)
|
|
185
|
+
event = @walker.next
|
|
186
|
+
expect { subject.receive_event(*event) }.not_to raise_error
|
|
187
|
+
stack = get_stack(subject)
|
|
188
|
+
expect(stack.size).to eq(3)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
it 'should react to an middle event that creates a terminal node' do
|
|
192
|
+
skip_events(10)
|
|
193
|
+
event = @walker.next
|
|
194
|
+
expect { subject.receive_event(*event) }.not_to raise_error
|
|
195
|
+
stack = get_stack(subject)
|
|
196
|
+
expect(stack.size).to eq(3)
|
|
197
|
+
expect(stack.last.children.size).to eq(3)
|
|
198
|
+
child = stack.last.children[1]
|
|
199
|
+
expect(child).to be_kind_of(PTree::TerminalNode)
|
|
200
|
+
expect(child.to_s).to eq("*[3, 4]: '*'")
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
it 'should react to an exit event that creates a terminal node' do
|
|
204
|
+
skip_events(15)
|
|
205
|
+
event = @walker.next
|
|
206
|
+
expect { subject.receive_event(*event) }.not_to raise_error
|
|
207
|
+
stack = get_stack(subject)
|
|
208
|
+
expect(stack.size).to eq(5)
|
|
209
|
+
expect(stack.last.children.size).to eq(1)
|
|
210
|
+
child = stack.last.children[-1]
|
|
211
|
+
expect(child).to be_kind_of(PTree::TerminalNode)
|
|
212
|
+
expect(child.to_s).to eq("integer[2, 3]: '3'")
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it 'should ignore to a revisit event' do
|
|
216
|
+
skip_events(21)
|
|
217
|
+
event = @walker.next
|
|
218
|
+
expect { subject.receive_event(*event) }.not_to raise_error
|
|
219
|
+
stack = get_stack(subject)
|
|
220
|
+
expect(stack.size).to eq(2)
|
|
221
|
+
expect(stack.last.children.size).to eq(3)
|
|
222
|
+
child = stack.last.children[-1]
|
|
223
|
+
expect(child).to be_kind_of(PTree::NonTerminalNode)
|
|
224
|
+
expect(child.to_s).to eq('M[2, 5]')
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
it 'should react to a 2nd middle event that creates a terminal node' do
|
|
228
|
+
skip_events(22)
|
|
229
|
+
event = @walker.next
|
|
230
|
+
expect { subject.receive_event(*event) }.not_to raise_error
|
|
231
|
+
stack = get_stack(subject)
|
|
232
|
+
expect(stack.size).to eq(2)
|
|
233
|
+
expect(stack.last.children.size).to eq(3)
|
|
234
|
+
child = stack.last.children[1]
|
|
235
|
+
expect(child).to be_kind_of(PTree::TerminalNode)
|
|
236
|
+
expect(child.to_s).to eq("+[1, 2]: '+'")
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
it 'should react to a exit event that creates a terminal node' do
|
|
240
|
+
skip_events(29)
|
|
241
|
+
event = @walker.next
|
|
242
|
+
expect { subject.receive_event(*event) }.not_to raise_error
|
|
243
|
+
stack = get_stack(subject)
|
|
244
|
+
expect(stack.size).to eq(5)
|
|
245
|
+
expect(stack.last.children.size).to eq(1)
|
|
246
|
+
child = stack.last.children[-1]
|
|
247
|
+
expect(child).to be_kind_of(PTree::TerminalNode)
|
|
248
|
+
expect(child.to_s).to eq("integer[0, 1]: '2'")
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
it 'should react to entry event 31' do
|
|
253
|
+
skip_events(30)
|
|
254
|
+
event = @walker.next
|
|
255
|
+
expect { subject.receive_event(*event) }.not_to raise_error
|
|
256
|
+
stack = get_stack(subject)
|
|
257
|
+
expect(stack.size).to eq(4)
|
|
258
|
+
expect(stack.last.children.size).to eq(1)
|
|
259
|
+
child = stack.last.children[-1]
|
|
260
|
+
expect(child).to be_kind_of(PTree::NonTerminalNode)
|
|
261
|
+
expect(child.to_s).to eq('T[0, 1]')
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
it 'should react to entry event 33' do
|
|
265
|
+
skip_events(32)
|
|
266
|
+
event = @walker.next
|
|
267
|
+
expect { subject.receive_event(*event) }.not_to raise_error
|
|
268
|
+
stack = get_stack(subject)
|
|
269
|
+
expect(stack.size).to eq(3)
|
|
270
|
+
expect(stack.last.children.size).to eq(1)
|
|
271
|
+
child = stack.last.children[-1]
|
|
272
|
+
expect(child).to be_kind_of(PTree::NonTerminalNode)
|
|
273
|
+
expect(child.to_s).to eq('M[0, 1]')
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
it 'should react to entry event 35' do
|
|
277
|
+
skip_events(34)
|
|
278
|
+
event = @walker.next
|
|
279
|
+
expect { subject.receive_event(*event) }.not_to raise_error
|
|
280
|
+
stack = get_stack(subject)
|
|
281
|
+
expect(stack.size).to eq(2)
|
|
282
|
+
expect(stack.last.children.size).to eq(3)
|
|
283
|
+
child = stack.last.children[0]
|
|
284
|
+
expect(child).to be_kind_of(PTree::NonTerminalNode)
|
|
285
|
+
expect(child.to_s).to eq('S[0, 1]')
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
it 'should react to entry event 37' do
|
|
289
|
+
skip_events(36)
|
|
290
|
+
event = @walker.next
|
|
291
|
+
expect { subject.receive_event(*event) }.not_to raise_error
|
|
292
|
+
stack = get_stack(subject)
|
|
293
|
+
expect(stack.size).to eq(1)
|
|
294
|
+
expect(stack.last.children.size).to eq(1)
|
|
295
|
+
child = stack.last.children[0]
|
|
296
|
+
expect(child).to be_kind_of(PTree::NonTerminalNode)
|
|
297
|
+
expect(child.to_s).to eq('S[0, 5]')
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
it 'should react to entry event that creates the tree' do
|
|
301
|
+
skip_events(38)
|
|
302
|
+
event = @walker.next
|
|
303
|
+
expect { subject.receive_event(*event) }.not_to raise_error
|
|
304
|
+
stack = get_stack(subject)
|
|
305
|
+
expect(stack).to be_empty
|
|
306
|
+
expect(subject.result).to be_kind_of(PTree::ParseTree)
|
|
307
|
+
|
|
308
|
+
# Lightweight sanity check
|
|
309
|
+
expect(subject.result.root.to_s).to eq('P[0, 5]')
|
|
310
|
+
expect(subject.result.root.subnodes.size).to eq(1)
|
|
311
|
+
child_node = subject.result.root.subnodes[0]
|
|
312
|
+
expect(child_node.to_s).to eq('S[0, 5]')
|
|
313
|
+
expect(child_node.subnodes.size).to eq(3)
|
|
314
|
+
first_grandchild = child_node.subnodes[0]
|
|
315
|
+
expect(first_grandchild.to_s).to eq('S[0, 1]')
|
|
316
|
+
second_grandchild = child_node.subnodes[1]
|
|
317
|
+
expect(second_grandchild.to_s).to eq("+[1, 2]: '+'")
|
|
318
|
+
third_grandchild = child_node.subnodes[2]
|
|
319
|
+
expect(third_grandchild.to_s).to eq('M[2, 5]')
|
|
320
|
+
end
|
|
321
|
+
end # context
|
|
322
|
+
|
|
323
|
+
context 'Parse tree construction with null symbol:' do
|
|
324
|
+
def next_event(expectation)
|
|
325
|
+
event = @walker.next
|
|
326
|
+
(ev_type, entry, index) = event
|
|
327
|
+
actual = "#{ev_type} #{entry} #{index}"
|
|
328
|
+
expect(actual).to eq(expectation)
|
|
329
|
+
@instance.receive_event(*event)
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
let(:array_grammar) do
|
|
333
|
+
builder = grammar_arr_int_builder
|
|
334
|
+
builder.grammar
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
before(:each) do
|
|
338
|
+
@parser = Parser::GFGEarleyParser.new(array_grammar)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# The visit events were generated with the following snippets:
|
|
342
|
+
# 13.times do
|
|
343
|
+
# event = @walker.next
|
|
344
|
+
# subject.receive_event(*event)
|
|
345
|
+
# end
|
|
346
|
+
# The events are:
|
|
347
|
+
# Event: visit P. | 0 2
|
|
348
|
+
# Event: visit P => arr . | 0 2
|
|
349
|
+
# Event: visit arr. | 0 2
|
|
350
|
+
# Event: visit arr => [ sequence ] . | 0 2
|
|
351
|
+
# Event: visit arr => [ sequence . ] | 0 1
|
|
352
|
+
# Event: visit sequence. | 1 1
|
|
353
|
+
# Event: visit sequence => . | 1 1
|
|
354
|
+
# Event: visit .sequence | 1 1
|
|
355
|
+
# Event: visit arr => [ . sequence ] | 0 1
|
|
356
|
+
# Event: visit arr => . [ sequence ] | 0 0
|
|
357
|
+
# Event: visit .arr | 0 0
|
|
358
|
+
# Event: visit P => . arr | 0 0
|
|
359
|
+
# Event: visit .P | 0 0
|
|
360
|
+
it 'should build a tree for an empty array' do
|
|
361
|
+
empty_arr_tokens = arr_int_tokenizer('[ ]', array_grammar)
|
|
362
|
+
@instance = CSTBuilder.new(empty_arr_tokens)
|
|
363
|
+
init_walker(@parser, empty_arr_tokens)
|
|
364
|
+
stack = get_stack(@instance)
|
|
365
|
+
|
|
366
|
+
next_event('visit P. | 0 2')
|
|
367
|
+
expect(stack.size).to eq(1)
|
|
368
|
+
# stack: [P[0, 2]]
|
|
369
|
+
expect(stack.last.range).to eq(create_range(0, 2))
|
|
370
|
+
expect(stack.last.children).to be_nil
|
|
371
|
+
|
|
372
|
+
next_event('visit P => arr . | 0 2')
|
|
373
|
+
expect(stack.last.children).to eq([nil])
|
|
374
|
+
|
|
375
|
+
next_event('visit arr. | 0 2')
|
|
376
|
+
expect(stack.size).to eq(2)
|
|
377
|
+
# stack: [arr[0, 2], P[0, 2]]
|
|
378
|
+
expect(stack.last.range).to eq(create_range(0, 2))
|
|
379
|
+
expect(stack.last.children).to be_nil
|
|
380
|
+
|
|
381
|
+
next_event('visit arr => [ sequence ] . | 0 2')
|
|
382
|
+
expect(stack.size).to eq(2)
|
|
383
|
+
expect(stack.last.range).to eq(create_range(0, 2))
|
|
384
|
+
expect(stack.last.children.size).to eq(3)
|
|
385
|
+
child = stack.last.children.last
|
|
386
|
+
expect(child.to_s).to eq("][1, 2]: ']'")
|
|
387
|
+
|
|
388
|
+
next_event('visit arr => [ sequence . ] | 0 1')
|
|
389
|
+
expect(stack.size).to eq(2)
|
|
390
|
+
|
|
391
|
+
next_event('visit sequence. | 1 1')
|
|
392
|
+
expect(stack.size).to eq(3)
|
|
393
|
+
# stack: [sequence[1, 1], arr[0, 2], P[0, 2]]
|
|
394
|
+
expect(stack.last.range).to eq(create_range(1, 1))
|
|
395
|
+
expect(stack.last.children).to be_nil
|
|
396
|
+
|
|
397
|
+
next_event('visit sequence => . | 1 1')
|
|
398
|
+
expect(stack.size).to eq(2)
|
|
399
|
+
# stack: [arr[0, 2], P[0, 2]]
|
|
400
|
+
expect(stack.last.range).to eq(create_range(0, 2))
|
|
401
|
+
sequence = stack.last.children[1]
|
|
402
|
+
expect(sequence).to be_kind_of(PTree::NonTerminalNode)
|
|
403
|
+
expect(sequence.subnodes).to be_empty
|
|
404
|
+
expect(sequence.to_s).to eq('sequence[1, 1]')
|
|
405
|
+
|
|
406
|
+
next_event('visit .sequence | 1 1')
|
|
407
|
+
expect(stack.size).to eq(2)
|
|
408
|
+
|
|
409
|
+
next_event('visit arr => [ . sequence ] | 0 1')
|
|
410
|
+
expect(stack.size).to eq(2)
|
|
411
|
+
expect(stack.last.range).to eq(create_range(0, 2))
|
|
412
|
+
sequence = stack.last.children[0]
|
|
413
|
+
expect(sequence).to be_kind_of(PTree::TerminalNode)
|
|
414
|
+
expect(sequence.to_s).to eq("[[0, 1]: '['")
|
|
415
|
+
|
|
416
|
+
next_event('visit arr => . [ sequence ] | 0 0')
|
|
417
|
+
expect(stack.size).to eq(1)
|
|
418
|
+
# stack: [P[0, 2]]
|
|
419
|
+
expect(stack.last.range).to eq(create_range(0, 2))
|
|
420
|
+
expect(stack.last.children.size).to eq(1)
|
|
421
|
+
expect(stack.last.children[0]).to be_kind_of(PTree::NonTerminalNode)
|
|
422
|
+
expect(stack.last.children[0].to_s).to eq('arr[0, 2]')
|
|
423
|
+
|
|
424
|
+
next_event('visit .arr | 0 0')
|
|
425
|
+
expect(stack.size).to eq(1)
|
|
426
|
+
|
|
427
|
+
next_event('visit P => . arr | 0 0')
|
|
428
|
+
expect(stack).to be_empty
|
|
429
|
+
expect(@instance.result).not_to be_nil
|
|
430
|
+
|
|
431
|
+
next_event('visit .P | 0 0')
|
|
432
|
+
expect(stack).to be_empty
|
|
433
|
+
expect(@instance.result).not_to be_nil
|
|
434
|
+
end
|
|
435
|
+
end # context
|
|
436
|
+
end # describe
|
|
437
|
+
end # module
|
|
438
|
+
end # module
|
|
439
|
+
# End of file
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Load the builder class
|
|
2
|
+
require_relative '../../../lib/rley/syntax/grammar_builder'
|
|
3
|
+
require_relative '../../../lib/rley/tokens/token'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
module GrammarArrIntHelper
|
|
7
|
+
# Factory method. Creates a grammar builder for a grammar of
|
|
8
|
+
# array of integers.
|
|
9
|
+
# (based on the article about Earley's algorithm in Wikipedia)
|
|
10
|
+
def grammar_arr_int_builder()
|
|
11
|
+
builder = Rley::Syntax::GrammarBuilder.new do
|
|
12
|
+
add_terminals('[', ']', ',', 'integer')
|
|
13
|
+
rule 'P' => 'arr'
|
|
14
|
+
rule 'arr' => %w( [ sequence ] )
|
|
15
|
+
rule 'sequence' => ['list']
|
|
16
|
+
rule 'sequence' => []
|
|
17
|
+
rule 'list' => %w[list , integer]
|
|
18
|
+
rule 'list' => 'integer'
|
|
19
|
+
end
|
|
20
|
+
builder
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Basic tokenizer for array of integers
|
|
24
|
+
def arr_int_tokenizer(aText, aGrammar)
|
|
25
|
+
tokens = aText.scan(/\S+/).map do |lexeme|
|
|
26
|
+
case lexeme
|
|
27
|
+
when '[', ']', ','
|
|
28
|
+
terminal = aGrammar.name2symbol[lexeme]
|
|
29
|
+
when /^[-+]?\d+$/
|
|
30
|
+
terminal = aGrammar.name2symbol['integer']
|
|
31
|
+
else
|
|
32
|
+
msg = "Unknown input text '#{lexeme}'"
|
|
33
|
+
raise StandardError, msg
|
|
34
|
+
end
|
|
35
|
+
Rley::Tokens::Token.new(lexeme, terminal)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
return tokens
|
|
39
|
+
end
|
|
40
|
+
end # module
|
|
41
|
+
# End of file
|
|
@@ -55,6 +55,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
|
55
55
|
expect(subject == me).to eq(true)
|
|
56
56
|
equal = TokenRange.new(low: 0, high: 5)
|
|
57
57
|
expect(subject == equal).to eq(true)
|
|
58
|
+
# expect(subject == [0..5]).to eq(true)
|
|
58
59
|
expect(subject == [0, 5]).to eq(true)
|
|
59
60
|
end
|
|
60
61
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rley
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.00
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dimitri Geshef
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2017-08-
|
|
11
|
+
date: 2017-08-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: coveralls
|
|
@@ -163,6 +163,7 @@ files:
|
|
|
163
163
|
- lib/rley/parse_forest_visitor.rb
|
|
164
164
|
- lib/rley/parse_tree_visitor.rb
|
|
165
165
|
- lib/rley/parser/base_parser.rb
|
|
166
|
+
- lib/rley/parser/cst_builder.rb
|
|
166
167
|
- lib/rley/parser/dotted_item.rb
|
|
167
168
|
- lib/rley/parser/error_reason.rb
|
|
168
169
|
- lib/rley/parser/gfg_chart.rb
|
|
@@ -225,6 +226,7 @@ files:
|
|
|
225
226
|
- spec/rley/parse_forest_visitor_spec.rb
|
|
226
227
|
- spec/rley/parse_tree_visitor_spec.rb
|
|
227
228
|
- spec/rley/parser/ambiguous_parse_spec.rb
|
|
229
|
+
- spec/rley/parser/cst_builder_spec.rb
|
|
228
230
|
- spec/rley/parser/dotted_item_spec.rb
|
|
229
231
|
- spec/rley/parser/error_reason_spec.rb
|
|
230
232
|
- spec/rley/parser/gfg_chart_spec.rb
|
|
@@ -252,6 +254,7 @@ files:
|
|
|
252
254
|
- spec/rley/support/expectation_helper.rb
|
|
253
255
|
- spec/rley/support/grammar_abc_helper.rb
|
|
254
256
|
- spec/rley/support/grammar_ambig01_helper.rb
|
|
257
|
+
- spec/rley/support/grammar_arr_int_helper.rb
|
|
255
258
|
- spec/rley/support/grammar_b_expr_helper.rb
|
|
256
259
|
- spec/rley/support/grammar_helper.rb
|
|
257
260
|
- spec/rley/support/grammar_l0_helper.rb
|
|
@@ -315,6 +318,7 @@ test_files:
|
|
|
315
318
|
- spec/rley/gfg/start_vertex_spec.rb
|
|
316
319
|
- spec/rley/gfg/vertex_spec.rb
|
|
317
320
|
- spec/rley/parser/ambiguous_parse_spec.rb
|
|
321
|
+
- spec/rley/parser/cst_builder_spec.rb
|
|
318
322
|
- spec/rley/parser/dotted_item_spec.rb
|
|
319
323
|
- spec/rley/parser/error_reason_spec.rb
|
|
320
324
|
- spec/rley/parser/gfg_chart_spec.rb
|