rley 0.2.00 → 0.2.01
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 +8 -8
- data/.rubocop.yml +5 -1
- data/CHANGELOG.md +4 -0
- data/examples/parsers/parsing_L0.rb +0 -1
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/formatter/base_formatter.rb +0 -1
- data/lib/rley/parser/chart.rb +1 -1
- data/lib/rley/parser/parse_state_tracker.rb +3 -3
- data/lib/rley/parser/parse_tree_builder.rb +178 -181
- data/lib/rley/parser/parsing.rb +14 -16
- data/lib/rley/ptree/parse_tree.rb +0 -1
- data/lib/rley/syntax/grammar.rb +15 -15
- data/spec/rley/parser/parse_tree_builder_spec.rb +179 -179
- data/spec/rley/parser/parsing_spec.rb +3 -4
- data/spec/rley/support/grammar_b_expr_helper.rb +13 -13
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NzlkNmE1NzA2OWEzMzVlNTNjOTBkNDI3ZGQ1MDljYTI4M2I3ZmNhNQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NGNlMGZhM2UyNjNiYmMwZTBjMGFiNmEwM2U3NWI2MjUxMzFlYmY5YQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MjA1YjJmYjRiNjFjZTViNmFhMDk4ZjA3MDVkOWJjNDBhMjExYjhkMDg5MTUy
|
10
|
+
ZTJkNzhjN2I1NjY5M2I2MWY5NzEyZmJkMjJiZTIwYjEyNTk2MzFmN2ViZWU2
|
11
|
+
YTYzYzkzNTUxYWE1MjU3NGI5NTk3YzAzNmI0MGNkNGExYmEyMDE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
N2U4Y2I5MjA0YWQyMWZmMmU4MDM0N2ZlNGU1NzA5MzViMDAxZGI1YTc5Zjcy
|
14
|
+
M2MzMDFmOWU5ZGI1NjllODliZTkwNjE2MmMyOTQzZjRkODBjNzUwNDg2MDJl
|
15
|
+
NmExN2FiMjA4ZjBhNTdkNDE4YWUxMzgxZTQ1Y2M4ZDE0OTE4NGY=
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
### 0.2.01 / 2015-01-03
|
2
|
+
* [CHANGE] File `.rubocop.yml`: AbcMetric setting relaxed.
|
3
|
+
* [CHANGE] Fixed most style offenses reported by Rubocop.
|
4
|
+
|
1
5
|
### 0.2.00 / 2015-01-03
|
2
6
|
Version number bump: major re-design of the parse tree generation.
|
3
7
|
* [NEW] Class `ParseTreeBuilder`: builder for creating parse tree.
|
@@ -109,7 +109,6 @@ puts "Parsing success? #{result.success?}"
|
|
109
109
|
########################################
|
110
110
|
# Step 6. Generate a parse tree from the parse result
|
111
111
|
ptree = result.parse_tree
|
112
|
-
pp ptree
|
113
112
|
|
114
113
|
########################################
|
115
114
|
# Step 7. Render the parse tree (in JSON)
|
data/lib/rley/constants.rb
CHANGED
data/lib/rley/parser/chart.rb
CHANGED
@@ -27,7 +27,7 @@ module Rley # This module is used as a namespace
|
|
27
27
|
|
28
28
|
# Return the index value of the last non-empty state set.
|
29
29
|
def last_index()
|
30
|
-
first_empty = state_sets.find_index
|
30
|
+
first_empty = state_sets.find_index(&:empty?)
|
31
31
|
if first_empty.nil?
|
32
32
|
index = state_sets.size - 1
|
33
33
|
else
|
@@ -26,12 +26,12 @@ module Rley # This module is used as a namespace
|
|
26
26
|
# Write accessor. Set the given parse state as the current one.
|
27
27
|
def parse_state=(aParseState)
|
28
28
|
@parse_state = aParseState
|
29
|
-
|
29
|
+
processed_states[parse_state] = true
|
30
30
|
end
|
31
31
|
|
32
32
|
# Take the first provided state that wasn't processed yet.
|
33
33
|
def select_state(theStates)
|
34
|
-
a_state = theStates.find { |st| !
|
34
|
+
a_state = theStates.find { |st| !processed_states.include?(st) }
|
35
35
|
self.parse_state = a_state
|
36
36
|
end
|
37
37
|
|
@@ -46,7 +46,7 @@ module Rley # This module is used as a namespace
|
|
46
46
|
|
47
47
|
# Notification that one begins with the previous state set
|
48
48
|
def to_prev_state_set()
|
49
|
-
self.state_set_index =
|
49
|
+
self.state_set_index = state_set_index - 1
|
50
50
|
end
|
51
51
|
end # class
|
52
52
|
end # module
|
@@ -1,181 +1,178 @@
|
|
1
|
-
|
2
|
-
require_relative '../ptree/
|
3
|
-
require_relative '../ptree/
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
module
|
8
|
-
|
9
|
-
#
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
attr_reader(:
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
start_symbol
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
|
42
|
-
|
43
|
-
prod
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
#
|
48
|
-
|
49
|
-
|
50
|
-
curr_node
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
path_increment
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
#
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
(
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
current_path <<
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
when Syntax::
|
112
|
-
new_node = PTree::
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
curr_node
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
end # module
|
180
|
-
|
181
|
-
# End of file
|
1
|
+
require_relative '../ptree/terminal_node'
|
2
|
+
require_relative '../ptree/non_terminal_node'
|
3
|
+
require_relative '../ptree/parse_tree'
|
4
|
+
|
5
|
+
|
6
|
+
module Rley # This module is used as a namespace
|
7
|
+
module Parser # This module is used as a namespace
|
8
|
+
# Builder GoF pattern. Builder pattern builds a complex object
|
9
|
+
# (say, a parse tree) from simpler objects (terminal and non-terminal
|
10
|
+
# nodes) and using a step by step approach.
|
11
|
+
class ParseTreeBuilder
|
12
|
+
attr_reader(:root)
|
13
|
+
attr_reader(:current_path)
|
14
|
+
|
15
|
+
def initialize(aStartProduction, aRange)
|
16
|
+
@current_path = []
|
17
|
+
start_symbol = aStartProduction.lhs
|
18
|
+
add_node(start_symbol, aRange)
|
19
|
+
use_production(aStartProduction, aRange)
|
20
|
+
move_down
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return the active node.
|
24
|
+
def current_node()
|
25
|
+
return current_path.last
|
26
|
+
end
|
27
|
+
|
28
|
+
# Factory method.
|
29
|
+
def parse_tree()
|
30
|
+
return PTree::ParseTree.new(root)
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
# Given that the current node is also lhs of the production
|
35
|
+
# associated with the complete parse state,
|
36
|
+
# Then add the rhs constituents as child nodes of the current node.
|
37
|
+
# Assumption: current node is lhs of the production association
|
38
|
+
# with the parse state.
|
39
|
+
# @param aCompleteState [ParseState] A complete parse state
|
40
|
+
# (dot is at end of rhs)
|
41
|
+
def use_complete_state(aCompleteState)
|
42
|
+
prod = aCompleteState.dotted_rule.production
|
43
|
+
use_production(prod, { low: aCompleteState.origin })
|
44
|
+
end
|
45
|
+
|
46
|
+
# Given that the current node is a non-terminal
|
47
|
+
# Make its last child node the current node.
|
48
|
+
def move_down()
|
49
|
+
curr_node = current_node
|
50
|
+
unless curr_node.is_a?(PTree::NonTerminalNode)
|
51
|
+
msg = "Current node isn't a non-terminal node #{curr_node.class}"
|
52
|
+
fail StandardError, msg
|
53
|
+
end
|
54
|
+
children = curr_node.children
|
55
|
+
path_increment = [children.size - 1, children.last]
|
56
|
+
@current_path.concat(path_increment)
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
# Make the predecessor of current node the
|
61
|
+
# new current node.
|
62
|
+
def move_back()
|
63
|
+
begin
|
64
|
+
if current_path.length == 1
|
65
|
+
msg = 'Cannot move further back'
|
66
|
+
fail StandardError, msg
|
67
|
+
end
|
68
|
+
(parent, pos) = current_path[-3, 2]
|
69
|
+
current_path.pop(2)
|
70
|
+
if pos > 0
|
71
|
+
new_pos = pos - 1
|
72
|
+
new_curr_node = parent.children[new_pos]
|
73
|
+
current_path << new_pos
|
74
|
+
current_path << new_curr_node
|
75
|
+
end
|
76
|
+
end while pos == 0 && new_curr_node.is_a?(PTree::NonTerminalNode)
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
# Add a child node to the current node.
|
81
|
+
def add_node(aSymbol, aRange)
|
82
|
+
# Create the node
|
83
|
+
a_node = new_node(aSymbol, aRange)
|
84
|
+
|
85
|
+
# Add it to the current node
|
86
|
+
add_child(a_node)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Set unbound endpoints of current node range
|
90
|
+
# to the given range.
|
91
|
+
def range=(aRange)
|
92
|
+
curr_node = current_node
|
93
|
+
return if curr_node.nil?
|
94
|
+
lower = low_bound(aRange)
|
95
|
+
unless lower.nil?
|
96
|
+
current_node.range = lower
|
97
|
+
if curr_node.is_a?(PTree::TerminalNode)
|
98
|
+
current_node.range = high_bound(lower[:low] + 1)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
upper = high_bound(aRange)
|
102
|
+
current_node.range = upper unless upper.nil?
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def new_node(aSymbol, aRange)
|
108
|
+
case aSymbol
|
109
|
+
when Syntax::Terminal
|
110
|
+
new_node = PTree::TerminalNode.new(aSymbol, aRange)
|
111
|
+
when Syntax::NonTerminal
|
112
|
+
new_node = PTree::NonTerminalNode.new(aSymbol, aRange)
|
113
|
+
end
|
114
|
+
|
115
|
+
return new_node
|
116
|
+
end
|
117
|
+
|
118
|
+
# Add children nodes to current one.
|
119
|
+
# The children correspond to the members of the rhs of the production.
|
120
|
+
def use_production(aProduction, aRange)
|
121
|
+
prod = aProduction
|
122
|
+
curr_node = current_node
|
123
|
+
|
124
|
+
if curr_node.symbol != prod.lhs
|
125
|
+
msg = "Current node is a #{curr_node.symbol} instead of #{prod.lhs}"
|
126
|
+
fail StandardError, msg
|
127
|
+
end
|
128
|
+
self.range = aRange
|
129
|
+
prod.rhs.each { |symb| add_node(symb, {}) }
|
130
|
+
|
131
|
+
return if curr_node.children.empty?
|
132
|
+
curr_node.children.first.range.assign(low: curr_node.range.low)
|
133
|
+
curr_node.children.last.range.assign(high: curr_node.range.high)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Add the given node as child node of current node
|
137
|
+
def add_child(aNode)
|
138
|
+
curr_node = current_node
|
139
|
+
|
140
|
+
if curr_node.nil?
|
141
|
+
self.root = aNode
|
142
|
+
else
|
143
|
+
curr_node.children << aNode
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Set the root node of the tree.
|
148
|
+
def root=(aNode)
|
149
|
+
@root = aNode
|
150
|
+
@current_path = [ @root ]
|
151
|
+
root.range = low_bound(0)
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
def low_bound(aRange)
|
156
|
+
result = case aRange
|
157
|
+
when Fixnum then aRange
|
158
|
+
when Hash then aRange[:low]
|
159
|
+
when PTree::TokenRange then aRange.low
|
160
|
+
end
|
161
|
+
|
162
|
+
return { low: result }
|
163
|
+
end
|
164
|
+
|
165
|
+
def high_bound(aRange)
|
166
|
+
result = case aRange
|
167
|
+
when Fixnum then aRange
|
168
|
+
when Hash then aRange[:high]
|
169
|
+
when PTree::TokenRange then aRange.high
|
170
|
+
end
|
171
|
+
|
172
|
+
return { high: result }
|
173
|
+
end
|
174
|
+
end # class
|
175
|
+
end # module
|
176
|
+
end # module
|
177
|
+
|
178
|
+
# End of file
|
data/lib/rley/parser/parsing.rb
CHANGED
@@ -123,11 +123,11 @@ module Rley # This module is used as a namespace
|
|
123
123
|
return expecting if !toSort || expecting.size < 2
|
124
124
|
|
125
125
|
# Put predicted states ahead
|
126
|
-
(predicted, others) = expecting.partition
|
126
|
+
(predicted, others) = expecting.partition(&:predicted?)
|
127
127
|
|
128
128
|
# Sort state in reverse order of their origin value
|
129
129
|
[predicted, others].each do |set|
|
130
|
-
set.sort! { |a,b| b.origin <=> a.origin }
|
130
|
+
set.sort! { |a, b| b.origin <=> a.origin }
|
131
131
|
end
|
132
132
|
|
133
133
|
return predicted + others
|
@@ -158,13 +158,13 @@ module Rley # This module is used as a namespace
|
|
158
158
|
|
159
159
|
# A terminal symbol is on the left of dot.
|
160
160
|
# Go to the predecessor state for the given terminal
|
161
|
-
def predecessor_state_terminal(
|
161
|
+
def predecessor_state_terminal(_a_symb, aStateTracker, aTreeBuilder)
|
162
162
|
aTreeBuilder.current_node.range = { low: aStateTracker.state_set_index }
|
163
163
|
link_node_to_token(aTreeBuilder, aStateTracker.state_set_index)
|
164
164
|
unless aTreeBuilder.current_node.is_a?(PTree::TerminalNode)
|
165
165
|
pp aTreeBuilder.root
|
166
166
|
pp aTreeBuilder.current_node
|
167
|
-
fail StandardError,
|
167
|
+
fail StandardError, 'Expected terminal node'
|
168
168
|
end
|
169
169
|
aTreeBuilder.move_back
|
170
170
|
state_set = chart[aStateTracker.state_set_index]
|
@@ -174,12 +174,12 @@ module Rley # This module is used as a namespace
|
|
174
174
|
|
175
175
|
|
176
176
|
# Retrieve a complete state with given symbol as lhs.
|
177
|
-
def completed_state_for(a_symb,
|
178
|
-
new_states = chart[
|
179
|
-
|
180
|
-
aTreeBuilder.range = { high:
|
181
|
-
aTreeBuilder.use_complete_state(
|
182
|
-
link_node_to_token(aTreeBuilder,
|
177
|
+
def completed_state_for(a_symb, aTracker, aTreeBuilder)
|
178
|
+
new_states = chart[aTracker.state_set_index].states_rewriting(a_symb)
|
179
|
+
aTracker.select_state(new_states)
|
180
|
+
aTreeBuilder.range = { high: aTracker.state_set_index }
|
181
|
+
aTreeBuilder.use_complete_state(aTracker.parse_state)
|
182
|
+
link_node_to_token(aTreeBuilder, aTracker.state_set_index - 1)
|
183
183
|
aTreeBuilder.move_down
|
184
184
|
end
|
185
185
|
|
@@ -204,10 +204,10 @@ module Rley # This module is used as a namespace
|
|
204
204
|
# If the current node is a terminal node
|
205
205
|
# then link the token to that node
|
206
206
|
def link_node_to_token(aTreeBuilder, aStateSetIndex)
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
207
|
+
return unless aTreeBuilder.current_node.is_a?(PTree::TerminalNode)
|
208
|
+
|
209
|
+
a_node = aTreeBuilder.current_node
|
210
|
+
a_node.token = tokens[aStateSetIndex] unless a_node.token
|
211
211
|
end
|
212
212
|
|
213
213
|
# Factory method. Initializes a ParseTreeBuilder object
|
@@ -216,8 +216,6 @@ module Rley # This module is used as a namespace
|
|
216
216
|
start_production = chart.start_dotted_rule.production
|
217
217
|
return ParseTreeBuilder.new(start_production, full_range)
|
218
218
|
end
|
219
|
-
|
220
|
-
|
221
219
|
end # class
|
222
220
|
end # module
|
223
221
|
end # module
|
data/lib/rley/syntax/grammar.rb
CHANGED
@@ -20,7 +20,7 @@ module Rley # This module is used as a namespace
|
|
20
20
|
|
21
21
|
# The list of grammar symbols in the language.
|
22
22
|
attr_reader(:symbols)
|
23
|
-
|
23
|
+
|
24
24
|
# A Hash with pairs of the kind: symbol name => grammar symbol
|
25
25
|
attr_reader(:name2symbol)
|
26
26
|
|
@@ -41,7 +41,7 @@ module Rley # This module is used as a namespace
|
|
41
41
|
def non_terminals()
|
42
42
|
return symbols.select { |s| s.kind_of?(NonTerminal) }
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
# @return [Production] The start production of the grammar (i.e.
|
46
46
|
# the rule that specifies the syntax for the start symbol.
|
47
47
|
def start_production()
|
@@ -66,17 +66,17 @@ module Rley # This module is used as a namespace
|
|
66
66
|
aProduction.rhs.each { |symb| add_symbol(symb) }
|
67
67
|
end
|
68
68
|
|
69
|
-
|
69
|
+
|
70
70
|
# For each non-terminal determine whether it is nullable or not.
|
71
71
|
# A nullable nonterminal is a nonterminal that can match an empty string.
|
72
72
|
def compute_nullable()
|
73
73
|
non_terminals.each { |nterm| nterm.nullable = false }
|
74
74
|
nullable_sets = [ direct_nullable ]
|
75
|
-
|
75
|
+
|
76
76
|
# Drop productions with one terminal in rhs or with a nullable lhs
|
77
77
|
filtered_rules = rules.reject do |prod|
|
78
|
-
prod.lhs.nullable? || prod.rhs.find do |symb|
|
79
|
-
symb.kind_of?(Terminal)
|
78
|
+
prod.lhs.nullable? || prod.rhs.find do |symb|
|
79
|
+
symb.kind_of?(Terminal)
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
@@ -96,8 +96,8 @@ module Rley # This module is used as a namespace
|
|
96
96
|
nullable_sets[i] = nullable_sets[i - 1].merge(new_nullables)
|
97
97
|
end
|
98
98
|
end
|
99
|
-
|
100
|
-
|
99
|
+
|
100
|
+
|
101
101
|
# Return the set of nonterminals which have one of their
|
102
102
|
# production rules empty
|
103
103
|
def direct_nullable()
|
@@ -108,18 +108,18 @@ module Rley # This module is used as a namespace
|
|
108
108
|
prod.lhs.nullable = true
|
109
109
|
nullable << prod.lhs
|
110
110
|
end
|
111
|
-
|
111
|
+
|
112
112
|
return nullable
|
113
113
|
end
|
114
|
-
|
114
|
+
|
115
115
|
private
|
116
|
-
|
116
|
+
|
117
117
|
def add_symbol(aSymbol)
|
118
118
|
its_name = aSymbol.name
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
119
|
+
return if name2symbol.include? its_name
|
120
|
+
|
121
|
+
@symbols << aSymbol
|
122
|
+
@name2symbol[its_name] = aSymbol
|
123
123
|
end
|
124
124
|
end # class
|
125
125
|
end # module
|
@@ -1,179 +1,179 @@
|
|
1
|
-
require_relative '../../spec_helper'
|
2
|
-
require_relative '../../../lib/rley/parser/token'
|
3
|
-
require_relative '../../../lib/rley/parser/earley_parser'
|
4
|
-
require_relative '../../../lib/rley/parser/parsing'
|
5
|
-
# Load the class under test
|
6
|
-
require_relative '../../../lib/rley/parser/parse_tree_builder'
|
7
|
-
require_relative '../support/grammar_abc_helper'
|
8
|
-
|
9
|
-
module Rley # Open this namespace to avoid module qualifier prefixes
|
10
|
-
module Parser # Open this namespace to avoid module qualifier prefixes
|
11
|
-
describe ParseTreeBuilder do
|
12
|
-
include GrammarABCHelper # Mix-in module with builder for grammar abc
|
13
|
-
|
14
|
-
let(:grammar_abc) do
|
15
|
-
builder = grammar_abc_builder
|
16
|
-
builder.grammar
|
17
|
-
end
|
18
|
-
|
19
|
-
let(:capital_a) { grammar_abc.name2symbol['A'] }
|
20
|
-
let(:capital_s) { grammar_abc.name2symbol['S'] }
|
21
|
-
let(:small_a) { grammar_abc.name2symbol['a'] }
|
22
|
-
let(:small_b) { grammar_abc.name2symbol['b'] }
|
23
|
-
let(:small_c) { grammar_abc.name2symbol['c'] }
|
24
|
-
|
25
|
-
let(:start_prod) { grammar_abc.start_production }
|
26
|
-
|
27
|
-
let(:tokens_abc) do
|
28
|
-
%w(a a b c c).map do |letter|
|
29
|
-
Token.new(letter, grammar_abc.name2symbol[letter])
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
let(:sample_parsing) do
|
34
|
-
parser = EarleyParser.new(grammar_abc)
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
subject { ParseTreeBuilder.new(start_prod, {low: 0, high: 5}) }
|
39
|
-
|
40
|
-
context 'Initialization:' do
|
41
|
-
it 'should be created with a proposition and a range' do
|
42
|
-
expect { ParseTreeBuilder.new(start_prod, {}) }.not_to raise_error
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'should have a root node at start' do
|
46
|
-
expect(subject.root.symbol).to eq(capital_s)
|
47
|
-
end
|
48
|
-
|
49
|
-
it
|
50
|
-
expect(subject.current_path).not_to be_empty
|
51
|
-
end
|
52
|
-
|
53
|
-
it
|
54
|
-
expect(subject.current_node.symbol).to eq(capital_a)
|
55
|
-
end
|
56
|
-
end # context
|
57
|
-
|
58
|
-
context 'Adding nodes to parse tree:' do
|
59
|
-
it 'should process parse state for a non-terminal node' do
|
60
|
-
# Expectation:
|
61
|
-
# S[0, 5]
|
62
|
-
# +- A[0,5]
|
63
|
-
expect(subject.root.symbol).to eq(capital_s)
|
64
|
-
expect(subject.root.children.size).to eq(1)
|
65
|
-
child1 = subject.root.children[0]
|
66
|
-
expect(child1.symbol).to eq(capital_a)
|
67
|
-
expect(child1.range.low).to eq(0)
|
68
|
-
expect(child1.range.high).to eq(5)
|
69
|
-
expect(subject.current_node).to eq(child1)
|
70
|
-
|
71
|
-
# Add children to A
|
72
|
-
other_state = sample_parsing.chart.state_sets.last.states.first
|
73
|
-
subject.use_complete_state(other_state)
|
74
|
-
|
75
|
-
# Tree is:
|
76
|
-
# S[0,5]
|
77
|
-
# +- A[0,5]
|
78
|
-
# +- a[0, ?]
|
79
|
-
# +- A[?, ?]
|
80
|
-
# +- c[?, 5]
|
81
|
-
expect(child1.children.size).to eq(3) # a A c
|
82
|
-
%w(a A c).each_with_index do |letter, i|
|
83
|
-
grm_symbol = grammar_abc.name2symbol[letter]
|
84
|
-
expect(child1.children[i].symbol).to eq(grm_symbol)
|
85
|
-
end
|
86
|
-
expect(child1.children[0].range.low).to eq(0)
|
87
|
-
expect(child1.children[-1].range.high).to eq(5)
|
88
|
-
|
89
|
-
subject.move_down # ... to c
|
90
|
-
subject.range = {low: 4}
|
91
|
-
expect(child1.children[-1].range.low).to eq(4)
|
92
|
-
expect(child1.children.last).to eq(subject.current_node)
|
93
|
-
subject.move_back # ... to A
|
94
|
-
expect(subject.current_node).to eq(child1.children[1])
|
95
|
-
grand_child_A = subject.current_node
|
96
|
-
|
97
|
-
other_state = sample_parsing.chart.state_sets[4].first
|
98
|
-
subject.use_complete_state(other_state)
|
99
|
-
expect(grand_child_A.children.size).to eq(3) # a A c
|
100
|
-
%w(a A c).each_with_index do |letter, i|
|
101
|
-
grm_symbol = grammar_abc.name2symbol[letter]
|
102
|
-
expect(grand_child_A.children[i].symbol).to eq(grm_symbol)
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end # context
|
106
|
-
|
107
|
-
context 'Moving the current node:' do
|
108
|
-
it 'should move down to last child' do
|
109
|
-
# Tree is:
|
110
|
-
# S[0,?]
|
111
|
-
# +- A[0,?]
|
112
|
-
|
113
|
-
# Add children to A
|
114
|
-
parse_state = sample_parsing.chart.state_sets.last.states.first
|
115
|
-
subject.use_complete_state(parse_state)
|
116
|
-
|
117
|
-
# Tree is:
|
118
|
-
# S[0,?]
|
119
|
-
# +- A[0,?]
|
120
|
-
# +- a[0, ?]
|
121
|
-
# +- A[?, ?]
|
122
|
-
# +- c[?, ?]
|
123
|
-
subject.move_down # ...to grand-child c
|
124
|
-
expect(subject.current_node.symbol).to eq(small_c)
|
125
|
-
|
126
|
-
|
127
|
-
subject.move_back # ...to grand-child A
|
128
|
-
expect(subject.current_node.symbol).to eq(capital_a)
|
129
|
-
|
130
|
-
# Add more children
|
131
|
-
other_state = sample_parsing.chart.state_sets[4].states.first
|
132
|
-
subject.use_complete_state(other_state)
|
133
|
-
|
134
|
-
# Tree is:
|
135
|
-
# S[0,?]
|
136
|
-
# +- A[0,?]
|
137
|
-
# +- a[0, ?]
|
138
|
-
# +- A[?, ?]
|
139
|
-
# +- a[?, ?]
|
140
|
-
# +- A[?, ?]
|
141
|
-
# +- c [?, ?]
|
142
|
-
# +- c[?, ?]
|
143
|
-
|
144
|
-
subject.move_down # ...to grand-grand-child c
|
145
|
-
expect(subject.current_node.symbol).to eq(small_c)
|
146
|
-
|
147
|
-
subject.move_back # ...to grand-grand-child A
|
148
|
-
expect(subject.current_node.symbol).to eq(capital_a)
|
149
|
-
|
150
|
-
subject.move_back # ...to grand-grand-child a
|
151
|
-
expect(subject.current_node.symbol).to eq(small_a)
|
152
|
-
|
153
|
-
subject.move_back # ...to grand-child A
|
154
|
-
expect(subject.current_node.symbol).to eq(capital_a)
|
155
|
-
|
156
|
-
subject.move_back # ...to grand-child a
|
157
|
-
expect(subject.current_node.symbol).to eq(small_a)
|
158
|
-
|
159
|
-
subject.move_back # ...to child A
|
160
|
-
expect(subject.current_node.symbol).to eq(capital_a)
|
161
|
-
|
162
|
-
subject.move_back # ...to S
|
163
|
-
expect(subject.current_node.symbol).to eq(capital_s)
|
164
|
-
end
|
165
|
-
end # context
|
166
|
-
|
167
|
-
context 'Parse tree building:' do
|
168
|
-
it 'should build a parse tree' do
|
169
|
-
expect(subject.parse_tree).to be_kind_of(PTree::ParseTree)
|
170
|
-
actual = subject.parse_tree
|
171
|
-
expect(actual.root).to eq(subject.root)
|
172
|
-
end
|
173
|
-
end # context
|
174
|
-
|
175
|
-
end # describe
|
176
|
-
end # module
|
177
|
-
end # module
|
178
|
-
|
179
|
-
# End of file
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
require_relative '../../../lib/rley/parser/token'
|
3
|
+
require_relative '../../../lib/rley/parser/earley_parser'
|
4
|
+
require_relative '../../../lib/rley/parser/parsing'
|
5
|
+
# Load the class under test
|
6
|
+
require_relative '../../../lib/rley/parser/parse_tree_builder'
|
7
|
+
require_relative '../support/grammar_abc_helper'
|
8
|
+
|
9
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
10
|
+
module Parser # Open this namespace to avoid module qualifier prefixes
|
11
|
+
describe ParseTreeBuilder do
|
12
|
+
include GrammarABCHelper # Mix-in module with builder for grammar abc
|
13
|
+
|
14
|
+
let(:grammar_abc) do
|
15
|
+
builder = grammar_abc_builder
|
16
|
+
builder.grammar
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:capital_a) { grammar_abc.name2symbol['A'] }
|
20
|
+
let(:capital_s) { grammar_abc.name2symbol['S'] }
|
21
|
+
let(:small_a) { grammar_abc.name2symbol['a'] }
|
22
|
+
let(:small_b) { grammar_abc.name2symbol['b'] }
|
23
|
+
let(:small_c) { grammar_abc.name2symbol['c'] }
|
24
|
+
|
25
|
+
let(:start_prod) { grammar_abc.start_production }
|
26
|
+
|
27
|
+
let(:tokens_abc) do
|
28
|
+
%w(a a b c c).map do |letter|
|
29
|
+
Token.new(letter, grammar_abc.name2symbol[letter])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
let(:sample_parsing) do
|
34
|
+
parser = EarleyParser.new(grammar_abc)
|
35
|
+
parser.parse(tokens_abc)
|
36
|
+
end
|
37
|
+
|
38
|
+
subject { ParseTreeBuilder.new(start_prod, { low: 0, high: 5 }) }
|
39
|
+
|
40
|
+
context 'Initialization:' do
|
41
|
+
it 'should be created with a proposition and a range' do
|
42
|
+
expect { ParseTreeBuilder.new(start_prod, {}) }.not_to raise_error
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should have a root node at start' do
|
46
|
+
expect(subject.root.symbol).to eq(capital_s)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should have current path at start' do
|
50
|
+
expect(subject.current_path).not_to be_empty
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should have current node at start' do
|
54
|
+
expect(subject.current_node.symbol).to eq(capital_a)
|
55
|
+
end
|
56
|
+
end # context
|
57
|
+
|
58
|
+
context 'Adding nodes to parse tree:' do
|
59
|
+
it 'should process parse state for a non-terminal node' do
|
60
|
+
# Expectation:
|
61
|
+
# S[0, 5]
|
62
|
+
# +- A[0,5]
|
63
|
+
expect(subject.root.symbol).to eq(capital_s)
|
64
|
+
expect(subject.root.children.size).to eq(1)
|
65
|
+
child1 = subject.root.children[0]
|
66
|
+
expect(child1.symbol).to eq(capital_a)
|
67
|
+
expect(child1.range.low).to eq(0)
|
68
|
+
expect(child1.range.high).to eq(5)
|
69
|
+
expect(subject.current_node).to eq(child1)
|
70
|
+
|
71
|
+
# Add children to A
|
72
|
+
other_state = sample_parsing.chart.state_sets.last.states.first
|
73
|
+
subject.use_complete_state(other_state)
|
74
|
+
|
75
|
+
# Tree is:
|
76
|
+
# S[0,5]
|
77
|
+
# +- A[0,5]
|
78
|
+
# +- a[0, ?]
|
79
|
+
# +- A[?, ?]
|
80
|
+
# +- c[?, 5]
|
81
|
+
expect(child1.children.size).to eq(3) # a A c
|
82
|
+
%w(a A c).each_with_index do |letter, i|
|
83
|
+
grm_symbol = grammar_abc.name2symbol[letter]
|
84
|
+
expect(child1.children[i].symbol).to eq(grm_symbol)
|
85
|
+
end
|
86
|
+
expect(child1.children[0].range.low).to eq(0)
|
87
|
+
expect(child1.children[-1].range.high).to eq(5)
|
88
|
+
|
89
|
+
subject.move_down # ... to c
|
90
|
+
subject.range = { low: 4 }
|
91
|
+
expect(child1.children[-1].range.low).to eq(4)
|
92
|
+
expect(child1.children.last).to eq(subject.current_node)
|
93
|
+
subject.move_back # ... to A
|
94
|
+
expect(subject.current_node).to eq(child1.children[1])
|
95
|
+
grand_child_A = subject.current_node
|
96
|
+
|
97
|
+
other_state = sample_parsing.chart.state_sets[4].first
|
98
|
+
subject.use_complete_state(other_state)
|
99
|
+
expect(grand_child_A.children.size).to eq(3) # a A c
|
100
|
+
%w(a A c).each_with_index do |letter, i|
|
101
|
+
grm_symbol = grammar_abc.name2symbol[letter]
|
102
|
+
expect(grand_child_A.children[i].symbol).to eq(grm_symbol)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end # context
|
106
|
+
|
107
|
+
context 'Moving the current node:' do
|
108
|
+
it 'should move down to last child' do
|
109
|
+
# Tree is:
|
110
|
+
# S[0,?]
|
111
|
+
# +- A[0,?]
|
112
|
+
|
113
|
+
# Add children to A
|
114
|
+
parse_state = sample_parsing.chart.state_sets.last.states.first
|
115
|
+
subject.use_complete_state(parse_state)
|
116
|
+
|
117
|
+
# Tree is:
|
118
|
+
# S[0,?]
|
119
|
+
# +- A[0,?]
|
120
|
+
# +- a[0, ?]
|
121
|
+
# +- A[?, ?]
|
122
|
+
# +- c[?, ?]
|
123
|
+
subject.move_down # ...to grand-child c
|
124
|
+
expect(subject.current_node.symbol).to eq(small_c)
|
125
|
+
|
126
|
+
|
127
|
+
subject.move_back # ...to grand-child A
|
128
|
+
expect(subject.current_node.symbol).to eq(capital_a)
|
129
|
+
|
130
|
+
# Add more children
|
131
|
+
other_state = sample_parsing.chart.state_sets[4].states.first
|
132
|
+
subject.use_complete_state(other_state)
|
133
|
+
|
134
|
+
# Tree is:
|
135
|
+
# S[0,?]
|
136
|
+
# +- A[0,?]
|
137
|
+
# +- a[0, ?]
|
138
|
+
# +- A[?, ?]
|
139
|
+
# +- a[?, ?]
|
140
|
+
# +- A[?, ?]
|
141
|
+
# +- c [?, ?]
|
142
|
+
# +- c[?, ?]
|
143
|
+
|
144
|
+
subject.move_down # ...to grand-grand-child c
|
145
|
+
expect(subject.current_node.symbol).to eq(small_c)
|
146
|
+
|
147
|
+
subject.move_back # ...to grand-grand-child A
|
148
|
+
expect(subject.current_node.symbol).to eq(capital_a)
|
149
|
+
|
150
|
+
subject.move_back # ...to grand-grand-child a
|
151
|
+
expect(subject.current_node.symbol).to eq(small_a)
|
152
|
+
|
153
|
+
subject.move_back # ...to grand-child A
|
154
|
+
expect(subject.current_node.symbol).to eq(capital_a)
|
155
|
+
|
156
|
+
subject.move_back # ...to grand-child a
|
157
|
+
expect(subject.current_node.symbol).to eq(small_a)
|
158
|
+
|
159
|
+
subject.move_back # ...to child A
|
160
|
+
expect(subject.current_node.symbol).to eq(capital_a)
|
161
|
+
|
162
|
+
subject.move_back # ...to S
|
163
|
+
expect(subject.current_node.symbol).to eq(capital_s)
|
164
|
+
end
|
165
|
+
end # context
|
166
|
+
|
167
|
+
context 'Parse tree building:' do
|
168
|
+
it 'should build a parse tree' do
|
169
|
+
expect(subject.parse_tree).to be_kind_of(PTree::ParseTree)
|
170
|
+
actual = subject.parse_tree
|
171
|
+
expect(actual.root).to eq(subject.root)
|
172
|
+
end
|
173
|
+
end # context
|
174
|
+
|
175
|
+
end # describe
|
176
|
+
end # module
|
177
|
+
end # module
|
178
|
+
|
179
|
+
# End of file
|
@@ -116,7 +116,6 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
116
116
|
expect(new_state.dotted_rule).to eq(item1)
|
117
117
|
expect(new_state.origin).to eq(0)
|
118
118
|
end
|
119
|
-
|
120
119
|
end # context
|
121
120
|
|
122
121
|
context 'Parse tree building:' do
|
@@ -175,13 +174,13 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
175
174
|
|
176
175
|
(node_s, node_plus, node_m) = node.children
|
177
176
|
expect(node_s.symbol).to eq(grm_symbol('S'))
|
178
|
-
expect(node_s.range).to eq(
|
177
|
+
expect(node_s.range).to eq(low: 0, high: 1)
|
179
178
|
expect(node_s.children.size).to eq(1)
|
180
179
|
expect(node_plus.symbol).to eq(grm_symbol('+'))
|
181
|
-
expect(node_plus.range).to eq(
|
180
|
+
expect(node_plus.range).to eq(low: 0, high: 1) # TODO: fix this
|
182
181
|
expect(node_plus.token.lexeme). to eq('+')
|
183
182
|
expect(node_m.symbol).to eq(grm_symbol('M'))
|
184
|
-
expect(node_m.range).to eq(
|
183
|
+
expect(node_m.range).to eq(low: 2, high: 5)
|
185
184
|
expect(node_m.children.size).to eq(3)
|
186
185
|
|
187
186
|
node = node_s.children[0] # M
|
@@ -21,19 +21,19 @@ module GrammarBExprHelper
|
|
21
21
|
|
22
22
|
# Basic expression tokenizer
|
23
23
|
def expr_tokenizer(aText, aGrammar)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
24
|
+
tokens = aText.scan(/\S+/).map do |lexeme|
|
25
|
+
case lexeme
|
26
|
+
when '+', '*'
|
27
|
+
terminal = aGrammar.name2symbol[lexeme]
|
28
|
+
when /^[-+]?\d+$/
|
29
|
+
terminal = aGrammar.name2symbol['integer']
|
30
|
+
else
|
31
|
+
msg = "Unknown input text '#{lexeme}'"
|
32
|
+
fail StandardError, msg
|
33
|
+
end
|
34
|
+
Rley::Parser::Token.new(lexeme, terminal)
|
33
35
|
end
|
34
|
-
|
36
|
+
|
37
|
+
return tokens
|
35
38
|
end
|
36
|
-
|
37
|
-
return tokens
|
38
|
-
end
|
39
39
|
end # module
|