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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZTAxMjk3Yzc3YTFjZmUxY2Q2OGI1MDFhZjMxMTk5M2Q1YmMzZWMwNw==
4
+ NzlkNmE1NzA2OWEzMzVlNTNjOTBkNDI3ZGQ1MDljYTI4M2I3ZmNhNQ==
5
5
  data.tar.gz: !binary |-
6
- ODMxNDQ0ODZjYmI0YWRlYTE5NWQwZjE3Njg2ZGI2MzBjMjc5ZmVlMQ==
6
+ NGNlMGZhM2UyNjNiYmMwZTBjMGFiNmEwM2U3NWI2MjUxMzFlYmY5YQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- NmVkNTY4MzE3ZWNhMDhlNWI5OTJlODBkM2ZmOTg1Y2RjODViOThiMzAyYmUy
10
- ZTY5N2RhN2EyNzFhMmY3MjhlNmJkNjI4MGVlOTBjZDgwYmM4ZTY0ZjhlNjE2
11
- OGU5ZTRlNzAzOGRiOTlmMmZkZDg0NzcyMGQwZDFlYjVmYjAwMzI=
9
+ MjA1YjJmYjRiNjFjZTViNmFhMDk4ZjA3MDVkOWJjNDBhMjExYjhkMDg5MTUy
10
+ ZTJkNzhjN2I1NjY5M2I2MWY5NzEyZmJkMjJiZTIwYjEyNTk2MzFmN2ViZWU2
11
+ YTYzYzkzNTUxYWE1MjU3NGI5NTk3YzAzNmI0MGNkNGExYmEyMDE=
12
12
  data.tar.gz: !binary |-
13
- N2E0OGJhNjVjOWM3YTMzMzk5YjdjZDdiY2IzMGE5N2RkNTJkNDlkMDcyYTI3
14
- YzY5Y2YzZTE4NjcxYjFlNzhhMmM4NDExYzk0NGEzMTE1ZmMwNDUzNzExMTA5
15
- OGI3MDFlMzFkYWQ2Y2UzNmI1MGQ5ZGEwNGYyNTg4N2MzMTk3NTk=
13
+ N2U4Y2I5MjA0YWQyMWZmMmU4MDM0N2ZlNGU1NzA5MzViMDAxZGI1YTc5Zjcy
14
+ M2MzMDFmOWU5ZGI1NjllODliZTkwNjE2MmMyOTQzZjRkODBjNzUwNDg2MDJl
15
+ NmExN2FiMjA4ZjBhNTdkNDE4YWUxMzgxZTQ1Y2M4ZDE0OTE4NGY=
@@ -5,7 +5,11 @@ AllCops:
5
5
  - 'gems/**/*'
6
6
  - 'refs/**/*'
7
7
 
8
- # This is disabled because some demos use UTF-8
8
+ AbcSize:
9
+ Max: 45
10
+ Enabled: true
11
+
12
+ # This is disabled because some demos use UTF-8
9
13
  AsciiComments:
10
14
  Enabled: false
11
15
 
@@ -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)
@@ -3,7 +3,7 @@
3
3
 
4
4
  module Rley # Module used as a namespace
5
5
  # The version number of the gem.
6
- Version = '0.2.00'
6
+ Version = '0.2.01'
7
7
 
8
8
  # Brief description of the gem.
9
9
  Description = "Ruby implementation of the Earley's parsing algorithm"
@@ -1,5 +1,4 @@
1
1
  module Rley # This module is used as a namespace
2
-
3
2
  # Namespace dedicated to parse tree formatters.
4
3
  module Formatter
5
4
  # Superclass for parse tree formatters.
@@ -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 { |set| set.empty? }
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
- self.processed_states[parse_state] = true
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| ! processed_states.include?(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 = self.state_set_index - 1
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
- require 'ostruct' # TODO delete this
2
- require_relative '../ptree/terminal_node'
3
- require_relative '../ptree/non_terminal_node'
4
- require_relative '../ptree/parse_tree'
5
-
6
-
7
- module Rley # This module is used as a namespace
8
- module Parser # This module is used as a namespace
9
- # Builder GoF pattern. Builder pattern builds a complex object
10
- # (say, a parse tree) from simpler objects (terminal and non-terminal
11
- # nodes) and using a step by step approach.
12
- class ParseTreeBuilder
13
- attr_reader(:root)
14
- attr_reader(:current_path)
15
-
16
- def initialize(aStartProduction, aRange)
17
- @current_path = []
18
- start_symbol = aStartProduction.lhs
19
- add_node(start_symbol, aRange)
20
- use_production(aStartProduction, aRange)
21
- move_down
22
- end
23
-
24
- # Return the active node.
25
- def current_node()
26
- return current_path.last
27
- end
28
-
29
- # Factory method.
30
- def parse_tree()
31
- return PTree::ParseTree.new(root)
32
- end
33
-
34
-
35
- # Given that the current node is also lhs of the production
36
- # associated with the complete parse state,
37
- # Then add the rhs constituents as child nodes of the current node.
38
- # Assumption: current node is lhs of the production association
39
- # with the parse state.
40
- # @param aCompleteState [ParseState] A complete parse state
41
- # (dot is at end of rhs)
42
- def use_complete_state(aCompleteState)
43
- prod = aCompleteState.dotted_rule.production
44
- use_production(prod, {low: aCompleteState.origin})
45
- end
46
-
47
- # Given that the current node is a non-terminal
48
- # Make its last child node the current node.
49
- def move_down()
50
- curr_node = current_node
51
- unless curr_node.is_a?(PTree::NonTerminalNode)
52
- msg = "Current node isn't a non-terminal node #{curr_node.class}"
53
- fail StandardError, msg
54
- end
55
- children = curr_node.children
56
- path_increment = [children.size - 1, children.last]
57
- @current_path.concat(path_increment)
58
- end
59
-
60
-
61
- # Make the predecessor of current node the
62
- # new current node.
63
- def move_back()
64
- begin
65
- if current_path.length == 1
66
- msg = 'Cannot move further back'
67
- fail StandardError, msg
68
- end
69
- (parent, pos, child_node) = current_path[-3, 3]
70
- current_path.pop(2)
71
- if pos > 0
72
- new_pos = pos - 1
73
- new_curr_node = parent.children[new_pos]
74
- current_path << new_pos
75
- current_path << new_curr_node
76
- range = high_bound(child_node.range.low)
77
- end
78
- end while pos == 0 && new_curr_node.is_a?(PTree::NonTerminalNode)
79
- end
80
-
81
-
82
- # Add a child node to the current node.
83
- def add_node(aSymbol, aRange)
84
- # Create the node
85
- a_node = new_node(aSymbol, aRange)
86
-
87
- # Add it to the current node
88
- add_child(a_node)
89
- end
90
-
91
- # Set unbound endpoints of current node range
92
- # to the given range.
93
- def range=(aRange)
94
- curr_node = current_node
95
- return if curr_node.nil?
96
- lower = low_bound(aRange)
97
- unless lower.nil?
98
- current_node.range = lower
99
- if curr_node.is_a?(PTree::TerminalNode)
100
- current_node.range = high_bound(lower[:low] + 1)
101
- end
102
- end
103
- upper = high_bound(aRange)
104
- current_node.range = upper unless upper.nil?
105
- end
106
-
107
- private
108
-
109
- def new_node(aSymbol, aRange)
110
- case aSymbol
111
- when Syntax::Terminal
112
- new_node = PTree::TerminalNode.new(aSymbol, aRange)
113
- when Syntax::NonTerminal
114
- new_node = PTree::NonTerminalNode.new(aSymbol, aRange)
115
- end
116
-
117
- return new_node
118
- end
119
-
120
- # Add children nodes to current one.
121
- # The children correspond to the members of the rhs of the production.
122
- def use_production(aProduction, aRange)
123
- prod = aProduction
124
- curr_node = current_node
125
-
126
- if curr_node.symbol != prod.lhs
127
- msg = "Current node is a #{curr_node.symbol} instead of #{prod.lhs}"
128
- fail StandardError, msg
129
- end
130
- self.range = aRange
131
- prod.rhs.each { |symb| add_node(symb, {}) }
132
-
133
- unless curr_node.children.empty?
134
- curr_node.children.first.range.assign({ low: curr_node.range.low })
135
- curr_node.children.last.range.assign({ high: curr_node.range.high })
136
- end
137
- end
138
-
139
- # Add the given node as child node of current node
140
- def add_child(aNode)
141
- curr_node = current_node
142
-
143
- if curr_node.nil?
144
- self.root = aNode
145
- else
146
- curr_node.children << aNode
147
- end
148
- end
149
-
150
- # Set the root node of the tree.
151
- def root=(aNode)
152
- @root = aNode
153
- @current_path = [ @root ]
154
- root.range = low_bound(0)
155
- end
156
-
157
-
158
- def low_bound(aRange)
159
- result = case aRange
160
- when Fixnum then aRange
161
- when Hash then aRange[:low]
162
- when PTree::TokenRange then aRange.low
163
- end
164
-
165
- return { low: result }
166
- end
167
-
168
- def high_bound(aRange)
169
- result = case aRange
170
- when Fixnum then aRange
171
- when Hash then aRange[:high]
172
- when PTree::TokenRange then aRange.high
173
- end
174
-
175
- return { high: result }
176
- end
177
- end # class
178
- end # module
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
@@ -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 { |state| state.predicted? }
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(a_symb, aStateTracker, aTreeBuilder)
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, "Expected terminal node"
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, aStateTracker, aTreeBuilder)
178
- new_states = chart[aStateTracker.state_set_index].states_rewriting(a_symb)
179
- aStateTracker.select_state(new_states)
180
- aTreeBuilder.range = { high: aStateTracker.state_set_index }
181
- aTreeBuilder.use_complete_state(aStateTracker.parse_state)
182
- link_node_to_token(aTreeBuilder, aStateTracker.state_set_index - 1)
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
- if aTreeBuilder.current_node.is_a?(PTree::TerminalNode)
208
- a_node = aTreeBuilder.current_node
209
- a_node.token = tokens[aStateSetIndex] unless a_node.token
210
- end
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
@@ -24,7 +24,6 @@ module Rley # This module is used as a namespace
24
24
 
25
25
  aVisitor.end_visit_ptree(self)
26
26
  end
27
-
28
27
  end # class
29
28
  end # module
30
29
  end # module
@@ -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
- unless name2symbol.include? its_name
120
- @symbols << aSymbol
121
- @name2symbol[its_name] = aSymbol
122
- end
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
- result = 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
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({low: 0, high: 1})
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({low: 0, high: 1}) # TODO: fix this
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({low: 2, high: 5})
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
- 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
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
- Rley::Parser::Token.new(lexeme, terminal)
36
+
37
+ return tokens
35
38
  end
36
-
37
- return tokens
38
- end
39
39
  end # module
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rley
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.00
4
+ version: 0.2.01
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef