rley 0.5.02 → 0.5.03
Sign up to get free protection for your applications and to get access to all the features.
- 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
|