rley 0.5.02 → 0.5.03
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/.rubocop.yml +4 -0
- data/CHANGELOG.md +5 -0
- data/examples/data_formats/JSON/json_ast_builder.rb +18 -19
- data/examples/data_formats/JSON/json_ast_nodes.rb +9 -12
- data/examples/data_formats/JSON/json_demo.rb +1 -1
- data/examples/general/calc_iter1/calc_ast_builder.rb +27 -30
- data/examples/general/calc_iter1/calc_ast_nodes.rb +8 -17
- data/examples/general/calc_iter1/calc_lexer.rb +4 -5
- data/examples/general/calc_iter1/spec/calculator_spec.rb +0 -2
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/gfg/grm_flow_graph.rb +1 -1
- data/lib/rley/parser/cst_builder.rb +2 -3
- data/lib/rley/parser/parse_forest_factory.rb +1 -2
- data/lib/rley/parser/parse_tree_builder.rb +16 -19
- data/lib/rley/parser/parse_tree_factory.rb +0 -1
- data/lib/rley/parser/parse_walker_factory.rb +1 -1
- data/lib/rley/syntax/grammar.rb +17 -15
- data/spec/rley/gfg/grm_flow_graph_spec.rb +1 -1
- data/spec/rley/parser/ast_builder_spec.rb +29 -29
- data/spec/rley/parser/cst_builder_spec.rb +3 -3
- data/spec/rley/parser/error_reason_spec.rb +4 -4
- data/spec/rley/support/grammar_arr_int_helper.rb +3 -3
- data/spec/spec_helper.rb +6 -5
- metadata +2 -8
- data/examples/general/calc_iter2/calc_ast_builder.rb +0 -186
- data/examples/general/calc_iter2/calc_ast_nodes.rb +0 -151
- data/examples/general/calc_iter2/calc_demo.rb +0 -40
- data/examples/general/calc_iter2/calc_grammar.rb +0 -28
- data/examples/general/calc_iter2/calc_lexer.rb +0 -81
- data/examples/general/calc_iter2/calc_parser.rb +0 -24
data/lib/rley/constants.rb
CHANGED
@@ -284,7 +284,7 @@ module Rley # This module is used as a namespace
|
|
284
284
|
# from the start symbol to the start node .N
|
285
285
|
def mark_unreachable_symbols()
|
286
286
|
# Mark all non-terminals as unreachable
|
287
|
-
start_vertex_for.
|
287
|
+
start_vertex_for.each_value do |a_vertex|
|
288
288
|
a_vertex.non_terminal.unreachable = true
|
289
289
|
end
|
290
290
|
|
@@ -13,7 +13,6 @@ module Rley # This module is used as a namespace
|
|
13
13
|
# (say, a parse tree) from simpler objects (terminal and non-terminal
|
14
14
|
# nodes) and using a step by step approach.
|
15
15
|
class CSTBuilder < ParseTreeBuilder
|
16
|
-
|
17
16
|
protected
|
18
17
|
|
19
18
|
# Method to override
|
@@ -37,9 +36,9 @@ module Rley # This module is used as a namespace
|
|
37
36
|
# Factory method for creating a parent node object.
|
38
37
|
# @param aProduction [Production] Production rule
|
39
38
|
# @param aRange [Range] Range of tokens matched by the rule
|
40
|
-
# @param
|
39
|
+
# @param _tokens [Array] The input tokens
|
41
40
|
# @param theChildren [Array] Children nodes (one per rhs symbol)
|
42
|
-
def new_parent_node(aProduction, aRange,
|
41
|
+
def new_parent_node(aProduction, aRange, _tokens, theChildren)
|
43
42
|
node = Rley::PTree::NonTerminalNode.new(aProduction.lhs, aRange)
|
44
43
|
theChildren.reverse_each { |child| node.add_subnode(child) }
|
45
44
|
return node
|
@@ -6,12 +6,11 @@ module Rley # This module is used as a namespace
|
|
6
6
|
# Utility class that helps to create a ParseForest from
|
7
7
|
# a given Parsing object.
|
8
8
|
class ParseForestFactory < ParseRepCreator
|
9
|
-
|
10
9
|
protected
|
11
10
|
|
12
11
|
# Create a Builder, that is, an object
|
13
12
|
# that will create piece by piece the forest
|
14
|
-
def builder(aParseResult,
|
13
|
+
def builder(aParseResult, _builder = nil)
|
15
14
|
ParseForestBuilder.new(aParseResult.tokens)
|
16
15
|
end
|
17
16
|
end # class
|
@@ -96,10 +96,10 @@ module Rley # This module is used as a namespace
|
|
96
96
|
|
97
97
|
# Handler for visit events for ParseEntry matching .N pattern
|
98
98
|
# @param anEvent [Symbol] Kind of visit event. Should be: :visit
|
99
|
-
# @param
|
100
|
-
# @param
|
101
|
-
def process_start_entry(anEvent,
|
102
|
-
raise NotImplementedError unless [
|
99
|
+
# @param _entry [ParseEntry] The entry being visited
|
100
|
+
# @param _index [Integer] The token index at end of anEntry
|
101
|
+
def process_start_entry(anEvent, _entry, _index)
|
102
|
+
raise NotImplementedError unless %I[visit revisit].include?(anEvent)
|
103
103
|
end
|
104
104
|
|
105
105
|
# Handler for visit events for ParseEntry matching N => alpha* . beta*
|
@@ -130,7 +130,7 @@ module Rley # This module is used as a namespace
|
|
130
130
|
# @param anEntry [ParseEntry] Entry matching (pattern: N => alpha* .)
|
131
131
|
# @param anIndex [anIndex] The token index at end of anEntry
|
132
132
|
def process_exit_entry(anEntry, anIndex)
|
133
|
-
production =
|
133
|
+
production = anEntry.vertex.dotted_item.production
|
134
134
|
count_rhs = production.rhs.members.size
|
135
135
|
init_TOS_children(count_rhs) # Create placeholders for children
|
136
136
|
build_terminal(anEntry, anIndex) if terminal_before_dot?(anEntry)
|
@@ -142,20 +142,18 @@ module Rley # This module is used as a namespace
|
|
142
142
|
build_terminal(anEntry, anIndex) if terminal_before_dot?(anEntry)
|
143
143
|
end
|
144
144
|
|
145
|
-
|
146
|
-
|
147
145
|
# @param anEntry [ParseEntry] Entry matching (pattern: N => . alpha)
|
148
|
-
# @param anIndex [
|
149
|
-
def process_entry_entry(anEntry,
|
146
|
+
# @param anIndex [_index] The token index at end of anEntry
|
147
|
+
def process_entry_entry(anEntry, _index)
|
150
148
|
dotted_item = anEntry.vertex.dotted_item
|
151
149
|
rule = dotted_item.production
|
152
150
|
previous_tos = stack.pop
|
153
151
|
non_terminal = entry2nonterm(anEntry)
|
154
152
|
# For debugging purposes
|
155
153
|
raise StandardError if previous_tos.symbol != non_terminal
|
156
|
-
|
154
|
+
|
157
155
|
new_node = new_parent_node(rule, previous_tos.range,
|
158
|
-
|
156
|
+
tokens, previous_tos.children)
|
159
157
|
if stack.empty?
|
160
158
|
@result = create_tree(new_node)
|
161
159
|
else
|
@@ -196,13 +194,12 @@ module Rley # This module is used as a namespace
|
|
196
194
|
token = tokens[token_position]
|
197
195
|
prod = anEntry.vertex.dotted_item.production
|
198
196
|
term_node = new_leaf_node(prod, term_symbol, token_position, token)
|
199
|
-
|
197
|
+
|
200
198
|
# Second make it a child of TOS...
|
201
199
|
pos = anEntry.vertex.dotted_item.prev_position # pos. in rhs of rule
|
202
200
|
place_TOS_child(term_node, pos)
|
203
201
|
end
|
204
202
|
|
205
|
-
|
206
203
|
# Place the given node object as one of the children of the TOS
|
207
204
|
# (TOS = Top Of Stack).
|
208
205
|
# Each child has a position that is dictated by the position of the
|
@@ -217,7 +214,7 @@ module Rley # This module is used as a namespace
|
|
217
214
|
def place_TOS_child(aNode, aRHSPos)
|
218
215
|
if aRHSPos.nil?
|
219
216
|
# Retrieve index of most rightmost nil child...
|
220
|
-
pos = tos.children.rindex
|
217
|
+
pos = tos.children.rindex(&:nil?)
|
221
218
|
raise StandardError, 'Internal error' if pos.nil?
|
222
219
|
else
|
223
220
|
pos = aRHSPos
|
@@ -225,14 +222,14 @@ module Rley # This module is used as a namespace
|
|
225
222
|
|
226
223
|
tos.children[pos] = aNode
|
227
224
|
end
|
228
|
-
|
225
|
+
|
229
226
|
# Retrieve non-terminal symbol of given parse entry
|
230
227
|
def entry2nonterm(anEntry)
|
231
228
|
case anEntry.vertex
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
229
|
+
when GFG::StartVertex, GFG::EndVertex
|
230
|
+
non_terminal = anEntry.vertex.non_terminal
|
231
|
+
when GFG::ItemVertex
|
232
|
+
non_terminal = anEntry.vertex.lhs
|
236
233
|
end
|
237
234
|
|
238
235
|
return non_terminal
|
@@ -266,7 +266,7 @@ module Rley # This module is used as a namespace
|
|
266
266
|
(antecd.origin == tos.origin) && tos_dotted_item.successor_of?(item)
|
267
267
|
end
|
268
268
|
|
269
|
-
new_entry
|
269
|
+
new_entry ||= aContext.curr_entry
|
270
270
|
|
271
271
|
# puts "Pop from return stack matching entry #{new_entry}"
|
272
272
|
return new_entry
|
data/lib/rley/syntax/grammar.rb
CHANGED
@@ -31,7 +31,7 @@ module Rley # This module is used as a namespace
|
|
31
31
|
@symbols = []
|
32
32
|
@name2symbol = {}
|
33
33
|
valid_productions = validate_productions(theProductions)
|
34
|
-
valid_productions.each do |prod|
|
34
|
+
valid_productions.each do |prod|
|
35
35
|
add_production(prod)
|
36
36
|
name_production(prod)
|
37
37
|
end
|
@@ -68,21 +68,23 @@ module Rley # This module is used as a namespace
|
|
68
68
|
|
69
69
|
aProduction.rhs.each { |symb| add_symbol(symb) }
|
70
70
|
end
|
71
|
-
|
71
|
+
|
72
|
+
# If the production is anonymous, then assign it
|
73
|
+
# a default name
|
72
74
|
def name_production(aProduction)
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
aProduction.name = prefix + suffix
|
75
|
+
return unless aProduction.name.nil?
|
76
|
+
|
77
|
+
index = rules.find_index(aProduction)
|
78
|
+
prefix = aProduction.lhs.name.dup
|
79
|
+
previous = index.zero? ? nil : rules[index - 1]
|
80
|
+
if previous.nil? || previous.lhs != aProduction.lhs
|
81
|
+
suffix = '[0]'
|
82
|
+
else
|
83
|
+
prev_serial = previous.name.match(/\[(\d+)\]$/)
|
84
|
+
suffix = "[#{prev_serial[1].to_i + 1}]"
|
85
85
|
end
|
86
|
+
|
87
|
+
aProduction.name = prefix + suffix
|
86
88
|
end
|
87
89
|
|
88
90
|
# Perform some check of the grammar.
|
@@ -141,7 +143,7 @@ module Rley # This module is used as a namespace
|
|
141
143
|
could_mark_nterm_generative(a_rule)
|
142
144
|
end
|
143
145
|
break if prev_marked.size == curr_marked.size
|
144
|
-
end
|
146
|
+
end
|
145
147
|
|
146
148
|
# The nonterminals that are not marked yet are non-generative
|
147
149
|
non_terminals.each do |nterm|
|
@@ -80,7 +80,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
80
80
|
end
|
81
81
|
|
82
82
|
it 'should have one or more entry edges per start vertex' do
|
83
|
-
subject.start_vertex_for.
|
83
|
+
subject.start_vertex_for.each_value do |a_start|
|
84
84
|
expect(a_start.edges.size >= 1).to be_truthy
|
85
85
|
a_start.edges.each do |edge|
|
86
86
|
expect(edge.successor.dotted_item.at_start?).to be_truthy
|
@@ -12,7 +12,6 @@ require_relative '../support/grammar_arr_int_helper'
|
|
12
12
|
|
13
13
|
module Rley # This module is used as a namespace
|
14
14
|
module Parser # This module is used as a namespace
|
15
|
-
|
16
15
|
ArrayNode = Struct.new(:children) do
|
17
16
|
def initialize()
|
18
17
|
super
|
@@ -32,7 +31,7 @@ module Rley # This module is used as a namespace
|
|
32
31
|
end
|
33
32
|
|
34
33
|
def interpret()
|
35
|
-
|
34
|
+
value
|
36
35
|
end
|
37
36
|
end
|
38
37
|
|
@@ -44,7 +43,6 @@ module Rley # This module is used as a namespace
|
|
44
43
|
# (say, a parse tree) from simpler objects (terminal and non-terminal
|
45
44
|
# nodes) and using a step by step approach.
|
46
45
|
class ASTBuilder < ParseTreeBuilder
|
47
|
-
|
48
46
|
protected
|
49
47
|
|
50
48
|
# Method to override
|
@@ -76,53 +74,54 @@ module Rley # This module is used as a namespace
|
|
76
74
|
# @param theChildren [Array] Children nodes (one per rhs symbol)
|
77
75
|
def new_parent_node(aProduction, aRange, theTokens, theChildren)
|
78
76
|
node = case aProduction.name
|
79
|
-
|
80
|
-
|
77
|
+
when 'P[0]'
|
78
|
+
reduce_P_0(aRange, theTokens, theChildren)
|
81
79
|
|
82
|
-
|
83
|
-
|
80
|
+
when 'arr[0]'
|
81
|
+
reduce_arr_0(aRange, theTokens, theChildren)
|
84
82
|
|
85
|
-
|
86
|
-
|
83
|
+
when 'sequence[0]'
|
84
|
+
reduce_sequence_0(aRange, theTokens, theChildren)
|
87
85
|
|
88
|
-
|
89
|
-
|
86
|
+
when 'sequence[1]'
|
87
|
+
reduce_sequence_1(aRange, theTokens, theChildren)
|
90
88
|
|
91
|
-
|
92
|
-
|
89
|
+
when 'list[0]'
|
90
|
+
reduce_list_0(aRange, theTokens, theChildren)
|
93
91
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
92
|
+
when 'list[1]'
|
93
|
+
reduce_list_1(aRange, theTokens, theChildren)
|
94
|
+
else
|
95
|
+
err_msg = "Don't know production #{aProduction.name}"
|
96
|
+
raise StandardError, err_msg
|
97
|
+
end
|
99
98
|
|
100
99
|
return node
|
101
100
|
end
|
102
101
|
|
103
|
-
def reduce_P_0(
|
102
|
+
def reduce_P_0(_range, _tokens, theChildren)
|
104
103
|
return theChildren[0]
|
105
104
|
end
|
106
105
|
|
107
|
-
def reduce_arr_0(
|
106
|
+
def reduce_arr_0(_range, _tokens, theChildren)
|
108
107
|
return theChildren[1]
|
109
108
|
end
|
110
109
|
|
111
|
-
def reduce_sequence_0(
|
110
|
+
def reduce_sequence_0(_range, _tokens, theChildren)
|
112
111
|
return theChildren[0]
|
113
112
|
end
|
114
113
|
|
115
|
-
def reduce_sequence_1(
|
114
|
+
def reduce_sequence_1(_range, _tokens, _children)
|
116
115
|
return ArrayNode.new
|
117
116
|
end
|
118
117
|
|
119
|
-
def reduce_list_0(
|
118
|
+
def reduce_list_0(_range, _tokens, theChildren)
|
120
119
|
node = theChildren[0]
|
121
120
|
node.children << theChildren[2]
|
122
121
|
return node
|
123
122
|
end
|
124
123
|
|
125
|
-
def reduce_list_1(
|
124
|
+
def reduce_list_1(_range, _tokens, theChildren)
|
126
125
|
node = ArrayNode.new
|
127
126
|
node.children << theChildren[0]
|
128
127
|
return node
|
@@ -163,12 +162,13 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
163
162
|
subject.receive_event(*event)
|
164
163
|
end
|
165
164
|
end
|
165
|
+
|
166
166
|
def get_stack(aBuilder)
|
167
167
|
return aBuilder.send(:stack)
|
168
168
|
end
|
169
169
|
|
170
170
|
def create_range(low, high)
|
171
|
-
return Tokens::TokenRange.new(
|
171
|
+
return Tokens::TokenRange.new(low: low, high: high)
|
172
172
|
end
|
173
173
|
|
174
174
|
context 'Initialization:' do
|
@@ -190,7 +190,6 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
190
190
|
end # context
|
191
191
|
|
192
192
|
context 'Parse tree construction (no null symbol):' do
|
193
|
-
|
194
193
|
def next_event(expectation)
|
195
194
|
event = @walker.next
|
196
195
|
(ev_type, entry, index) = event
|
@@ -244,9 +243,9 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
244
243
|
expect(stack.size).to eq(1)
|
245
244
|
# stack: [P[0, 7]]
|
246
245
|
expect(stack.last.range).to eq(create_range(0, 7))
|
247
|
-
expect(stack.last.children).to be_nil
|
246
|
+
expect(stack.last.children).to be_nil
|
248
247
|
end
|
249
|
-
|
248
|
+
|
250
249
|
it 'should build a tree for an empty array' do
|
251
250
|
stack = get_stack(subject)
|
252
251
|
|
@@ -317,7 +316,8 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
317
316
|
|
318
317
|
next_event('visit list. | 1 2')
|
319
318
|
expect(stack.size).to eq(6)
|
320
|
-
# stack: [P[0, 7], arr[0, 7], sequence[1, 6], list[1, 6],
|
319
|
+
# stack: [P[0, 7], arr[0, 7], sequence[1, 6], list[1, 6],
|
320
|
+
# list[1, 4], list[1, 2]
|
321
321
|
expect(stack.last.range).to eq(create_range(1, 2))
|
322
322
|
expect(stack.last.children).to be_nil
|
323
323
|
|
@@ -48,7 +48,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def create_range(low, high)
|
51
|
-
return Tokens::TokenRange.new(
|
51
|
+
return Tokens::TokenRange.new(low: low, high: high)
|
52
52
|
end
|
53
53
|
|
54
54
|
context 'Initialization:' do
|
@@ -340,8 +340,8 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
340
340
|
|
341
341
|
# The visit events were generated with the following snippets:
|
342
342
|
# 13.times do
|
343
|
-
|
344
|
-
|
343
|
+
# event = @walker.next
|
344
|
+
# subject.receive_event(*event)
|
345
345
|
# end
|
346
346
|
# The events are:
|
347
347
|
# Event: visit P. | 0 2
|
@@ -80,10 +80,10 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
80
80
|
|
81
81
|
context 'Provided services:' do
|
82
82
|
it 'should emit a message' do
|
83
|
-
text = <<
|
83
|
+
text = <<MESSAGE_END
|
84
84
|
Syntax error at or near token 4 >>>-<<<
|
85
85
|
Expected one of: ['PLUS', 'LPAREN'], found a 'MINUS' instead.
|
86
|
-
|
86
|
+
MESSAGE_END
|
87
87
|
expect(subject.to_s).to eq(text.chomp)
|
88
88
|
expect(subject.message).to eq(text.chomp)
|
89
89
|
end
|
@@ -111,10 +111,10 @@ MSG_END
|
|
111
111
|
|
112
112
|
context 'Provided services:' do
|
113
113
|
it 'should emit a message' do
|
114
|
-
text = <<
|
114
|
+
text = <<MESSAGE_END
|
115
115
|
Premature end of input after '+' at position 4
|
116
116
|
Expected one of: ['INT', 'LPAREN'].
|
117
|
-
|
117
|
+
MESSAGE_END
|
118
118
|
expect(subject.to_s).to eq(text.chomp)
|
119
119
|
expect(subject.message).to eq(text.chomp)
|
120
120
|
end
|
@@ -13,10 +13,10 @@ module GrammarArrIntHelper
|
|
13
13
|
builder = Rley::Syntax::GrammarBuilder.new do
|
14
14
|
add_terminals('[', ']', ',', 'integer')
|
15
15
|
rule 'P' => 'arr'
|
16
|
-
rule 'arr' => %w(
|
16
|
+
rule 'arr' => %w([ sequence ])
|
17
17
|
rule 'sequence' => ['list']
|
18
18
|
rule 'sequence' => []
|
19
|
-
rule 'list' => %w[list , integer]
|
19
|
+
rule 'list' => %w[list , integer] # Right-recursive rule
|
20
20
|
rule 'list' => 'integer'
|
21
21
|
end
|
22
22
|
builder
|
@@ -27,7 +27,7 @@ module GrammarArrIntHelper
|
|
27
27
|
tokens = []
|
28
28
|
scanner = StringScanner.new(aText)
|
29
29
|
|
30
|
-
until scanner.eos?
|
30
|
+
until scanner.eos?
|
31
31
|
scanner.skip(/\s+/)
|
32
32
|
lexeme = scanner.scan(/[\[,\]]/)
|
33
33
|
if lexeme
|