rley 0.2.00 → 0.2.01

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