rley 0.6.06 → 0.6.07

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,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e1e6215ec93e9404550f4ac55ac71cb294d5d3ef
4
- data.tar.gz: 73b5089ae521c84ec6999096054dc1212e646d28
3
+ metadata.gz: 25c9a2612411bdb3767901c9b21d1e38f5279c94
4
+ data.tar.gz: 9c3ffe74bda63f31d40421b74a6f976ac3db49ad
5
5
  SHA512:
6
- metadata.gz: e954203459567724574ae05d976d5c836c21febdacc730971630a585ae73809bff5fa240238151c704ba51ccda44d9c2434f3202bdeddda75abb1b17cafa1a04
7
- data.tar.gz: cc61845755b29425dfe538b7f7fd1605e21fb43be2d8e33956688bfca5f7626c257d31b6264ae44066875dadb4e9e4bb53a7e8a3adce16f76c14af2207ad9007
6
+ metadata.gz: d289304c2a693fb1eb14747751e205bc23cccb9941c5f416af1966e6b3fcf3d2ff58f52ae6d56a2cae40752bcccbc2cd9a2c9015cc555a5ad3ef1890306f15e9
7
+ data.tar.gz: c59a9d80ff3b941efabed869de7e5d16b6ebc597c3a63d914de3976aa3c709a6cfde0630c8c931e14ae559e4c3fcbc5dddb935fa2d58c12c71fafad2fc817f4e
@@ -1,111 +1,111 @@
1
- AllCops:
2
- Exclude:
3
- - 'features/**/*'
4
- - 'exp/**/*'
5
- - 'gems/**/*'
6
- - 'refs/**/*'
7
-
8
- # This is disabled because some demos use UTF-8
9
- AsciiComments:
10
- Enabled: false
11
-
12
- Attr:
13
- Enabled: false
14
-
15
- BlockComments:
16
- Enabled: false
17
-
18
- CaseIndentation:
19
- EnforcedStyle: end
20
- IndentOneStep: true
21
-
22
- # Rubocop enforces the use of is_a? instead of kind_of?
23
- # Which is contrary to modelling practice.
24
- ClassCheck:
25
- Enabled: false
26
-
27
- ClassLength:
28
- Max: 250
29
- CountComments: false
30
-
31
- ConstantName:
32
- Enabled: false
33
-
34
- CyclomaticComplexity:
35
- Enabled: false
36
-
37
- DefWithParentheses:
38
- Enabled: false
39
-
40
- Documentation:
41
- Enabled: false
42
-
43
- EmptyLines:
44
- Enabled: false
45
-
46
- Encoding:
47
- Enabled: false
48
-
49
- EndOfLine:
50
- Enabled: false
51
- # SupportedStyles: lf
52
-
53
-
54
- IndentationWidth :
55
- Enabled: false
56
-
57
- # Disable this because it produces false negatives
58
- Naming/HeredocDelimiterNaming:
59
- Enabled: false
60
-
61
- # Enabled after end of support of Rubies < 2.3
62
- Layout/IndentHeredoc:
63
- Enabled: false
64
-
65
- Metrics/AbcSize:
66
- Max: 50
67
-
68
- # Avoid methods longer than 50 lines of code
69
- Metrics/MethodLength:
70
- Max: 50
71
- CountComments: false
72
-
73
- # Avoid modules longer than 200 lines of code
74
- Metrics/ModuleLength:
75
- CountComments: false
76
- Max: 200
77
-
78
- Metrics/PerceivedComplexity:
79
- Enabled: true
80
- Max: 50
81
-
82
- Naming/MethodName:
83
- Enabled: false
84
-
85
- NonNilCheck:
86
- Enabled: false
87
-
88
- NumericLiterals:
89
- Enabled: false
90
-
91
- RaiseArgs:
92
- Enabled: false
93
-
94
- RedundantReturn:
95
- Enabled: false
96
-
97
- SpaceInsideBrackets:
98
- Enabled: false
99
-
100
- TrailingWhitespace:
101
- Enabled: false
102
-
103
- VariableName:
104
- Enabled: false
105
-
106
- VariableNumber:
107
- Enabled: false
108
-
109
- Style/CommentedKeyword:
110
- Enabled: false
111
-
1
+ AllCops:
2
+ Exclude:
3
+ - 'features/**/*'
4
+ - 'exp/**/*'
5
+ - 'gems/**/*'
6
+ - 'refs/**/*'
7
+
8
+ # This is disabled because some demos use UTF-8
9
+ AsciiComments:
10
+ Enabled: false
11
+
12
+ Attr:
13
+ Enabled: false
14
+
15
+ BlockComments:
16
+ Enabled: false
17
+
18
+ CaseIndentation:
19
+ EnforcedStyle: end
20
+ IndentOneStep: true
21
+
22
+ # Rubocop enforces the use of is_a? instead of kind_of?
23
+ # Which is contrary to modelling practice.
24
+ ClassCheck:
25
+ Enabled: false
26
+
27
+ ClassLength:
28
+ Max: 250
29
+ CountComments: false
30
+
31
+ ConstantName:
32
+ Enabled: false
33
+
34
+ CyclomaticComplexity:
35
+ Enabled: false
36
+
37
+ DefWithParentheses:
38
+ Enabled: false
39
+
40
+ Documentation:
41
+ Enabled: false
42
+
43
+ EmptyLines:
44
+ Enabled: false
45
+
46
+ Encoding:
47
+ Enabled: false
48
+
49
+ EndOfLine:
50
+ Enabled: false
51
+ # SupportedStyles: lf
52
+
53
+
54
+ IndentationWidth :
55
+ Enabled: false
56
+
57
+ # Disable this because it produces false negatives
58
+ Naming/HeredocDelimiterNaming:
59
+ Enabled: false
60
+
61
+ # Enabled after end of support of Rubies < 2.3
62
+ Layout/IndentHeredoc:
63
+ Enabled: false
64
+
65
+ Metrics/AbcSize:
66
+ Max: 50
67
+
68
+ # Avoid methods longer than 50 lines of code
69
+ Metrics/MethodLength:
70
+ Max: 50
71
+ CountComments: false
72
+
73
+ # Avoid modules longer than 200 lines of code
74
+ Metrics/ModuleLength:
75
+ CountComments: false
76
+ Max: 200
77
+
78
+ Metrics/PerceivedComplexity:
79
+ Enabled: true
80
+ Max: 50
81
+
82
+ Naming/MethodName:
83
+ Enabled: false
84
+
85
+ NonNilCheck:
86
+ Enabled: false
87
+
88
+ NumericLiterals:
89
+ Enabled: false
90
+
91
+ RaiseArgs:
92
+ Enabled: false
93
+
94
+ RedundantReturn:
95
+ Enabled: false
96
+
97
+ SpaceInsideBrackets:
98
+ Enabled: false
99
+
100
+ TrailingWhitespace:
101
+ Enabled: false
102
+
103
+ VariableName:
104
+ Enabled: false
105
+
106
+ VariableNumber:
107
+ Enabled: false
108
+
109
+ Style/CommentedKeyword:
110
+ Enabled: false
111
+
@@ -1,3 +1,8 @@
1
+ ### 0.6.07 / 2018-05-29
2
+ * [CHANGE] Method `GFGEarleyParser#parse` doesn't assume that the exact sequence of tokens to be known in advance.
3
+ * [FIXED] Method `GFGParsing#nullable_rule`: in very specific cases a wrong parse entry was added to the parse chart.
4
+ * [FIXED] File `examples/general/left.rb` is working back.
5
+
1
6
  ### 0.6.06 / 2018-04-12
2
7
  * [ADDED] Method `done!`: added to classes involved parse tree and parse forest creation. This method signals the end of parsing.
3
8
  * [CHANGE] Method `ParseTreeBuilder#process_item_entry`: added an explicit message in case of ambiguous parse.
data/Gemfile CHANGED
@@ -5,5 +5,5 @@ group :development do
5
5
  gem 'coveralls', '>= 0.7.0'
6
6
  gem 'rake', '>= 10.0.0'
7
7
  gem 'rspec', '>= 3.5.0'
8
- gem 'simplecov', '>= 0.1.0'
8
+ gem 'simplecov', '>= 0.8.0'
9
9
  end
@@ -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.6.06'.freeze
6
+ Version = '0.6.07'.freeze
7
7
 
8
8
  # Brief description of the gem.
9
9
  Description = "Ruby implementation of the Earley's parsing algorithm".freeze
@@ -21,14 +21,14 @@ module Rley # This module is used as a namespace
21
21
  def terminal2node()
22
22
  raise NotImplementedError
23
23
  end
24
-
24
+
25
25
  # Method to override in subclass.
26
26
  # Default class for representing terminal nodes.
27
27
  # @return [Class]
28
28
  def terminalnode_class()
29
29
  PTree::TerminalNode
30
30
  end
31
-
31
+
32
32
  # Default method name to invoke when production
33
33
  # with given name is invoked.
34
34
  # Override this method for other method naming convention.
@@ -51,7 +51,7 @@ module Rley # This module is used as a namespace
51
51
  # Simply return the second child node
52
52
  # @param _range [Lexical::TokenRange]
53
53
  # @param _tokens [Array<Lexical::Token>]
54
- # @param theChildren [Array<Object>]
54
+ # @param theChildren [Array<Object>]
55
55
  def return_second_child(_range, _tokens, theChildren)
56
56
  return theChildren[1]
57
57
  end
@@ -60,7 +60,7 @@ module Rley # This module is used as a namespace
60
60
  # Simply return the last child node
61
61
  # @param _range [Lexical::TokenRange]
62
62
  # @param _tokens [Array<Lexical::Token>]
63
- # @param theChildren [Array<Object>]
63
+ # @param theChildren [Array<Object>]
64
64
  def return_last_child(_range, _tokens, theChildren)
65
65
  return theChildren[-1]
66
66
  end
@@ -68,13 +68,13 @@ module Rley # This module is used as a namespace
68
68
  # Simply return an epsilon symbol
69
69
  # @param _range [Lexical::TokenRange]
70
70
  # @param _tokens [Array<Lexical::Token>]
71
- # @param _children [Array<Object>]
71
+ # @param _children [Array<Object>]
72
72
  def return_epsilon(_range, _tokens, _children)
73
73
  return nil
74
74
  end
75
-
75
+
76
76
  protected
77
-
77
+
78
78
  # Overriding method.
79
79
  # Create a parse tree object with given
80
80
  # node as root node.
@@ -98,7 +98,7 @@ module Rley # This module is used as a namespace
98
98
 
99
99
  return node
100
100
  end
101
-
101
+
102
102
  # Method to override.
103
103
  # Factory method for creating a parent node object.
104
104
  # @param aProduction [Production] Production rule
@@ -119,7 +119,7 @@ module Rley # This module is used as a namespace
119
119
  else
120
120
  msg = "Don't know production '#{aProduction.name}'"
121
121
  raise StandardError, msg
122
- end
122
+ end
123
123
  end
124
124
  return node
125
125
  end
@@ -47,7 +47,7 @@ module Rley # This module is used as a namespace
47
47
  @dummy_node = Object.new.freeze
48
48
  end
49
49
 
50
- # Notify the builder that the construction is over
50
+ # Notify the builder that the parse tree construction is complete.
51
51
  def done!()
52
52
  result.done!
53
53
  end
@@ -77,7 +77,7 @@ module Rley # This module is used as a namespace
77
77
 
78
78
  # Return the stack
79
79
  def stack()
80
- return @stack
80
+ @stack
81
81
  end
82
82
 
83
83
  # Overriding method.
@@ -92,7 +92,7 @@ module Rley # This module is used as a namespace
92
92
 
93
93
  # Return the top of stack element.
94
94
  def tos()
95
- return @stack.last
95
+ @stack.last
96
96
  end
97
97
 
98
98
  # Handler for visit events for ParseEntry matching N. pattern
@@ -12,10 +12,9 @@ module Rley # This module is used as a namespace
12
12
  # @return [Array<ParseEntrySet>] entry sets (one per input token + 1)
13
13
  attr_reader(:sets)
14
14
 
15
- # @param tokenCount [Integer] The number of lexemes in the input to parse.
16
15
  # @param aGFGraph [GFG::GrmFlowGraph] The GFG for the grammar in use.
17
- def initialize(tokenCount, aGFGraph)
18
- @sets = Array.new(tokenCount + 1) { |_| ParseEntrySet.new }
16
+ def initialize( aGFGraph)
17
+ @sets = [ ParseEntrySet.new ]
19
18
  push_entry(aGFGraph.start_vertex, 0, 0, :start_rule)
20
19
  end
21
20
 
@@ -25,7 +24,7 @@ module Rley # This module is used as a namespace
25
24
  end
26
25
 
27
26
  # @param index [Integer]
28
- # @return [ParseEntrySet] Access the entry set at given position
27
+ # @return [ParseEntrySet] Access the entry set at given position.
29
28
  def [](index)
30
29
  return sets[index]
31
30
  end
@@ -53,6 +52,11 @@ module Rley # This module is used as a namespace
53
52
  # puts " anIndex: #{anIndex}"
54
53
  # puts " _reason: #{_reason}"
55
54
  new_entry = ParseEntry.new(aVertex, anOrigin)
55
+ if anIndex == sets.size
56
+ err_msg = "Internal error: unexpected push reason #{_reason}"
57
+ raise StandardError, err_msg if _reason != :scan_rule
58
+ add_entry_set
59
+ end
56
60
  pushed = self[anIndex].push_entry(new_entry)
57
61
 
58
62
  return pushed
@@ -88,6 +92,13 @@ module Rley # This module is used as a namespace
88
92
 
89
93
  return success_entries.first
90
94
  end
95
+
96
+ private
97
+
98
+ def add_entry_set()
99
+ @sets << ParseEntrySet.new
100
+ end
101
+
91
102
  end # class
92
103
  end # module
93
104
  end # module
@@ -22,36 +22,40 @@ module Rley # This module is used as a namespace
22
22
  # tokenizer/scanner/lexer.
23
23
  # @return [GFGParsing] an object that embeds the parse results.
24
24
  def parse(aTokenSequence)
25
- result = GFGParsing.new(gf_graph, aTokenSequence)
26
- last_token_index = aTokenSequence.size
27
- if last_token_index.zero? && !grammar.start_symbol.nullable?
25
+ result = GFGParsing.new(gf_graph)
26
+ token_count = aTokenSequence.size
27
+ if token_count.zero? && !grammar.start_symbol.nullable?
28
28
  return unexpected_empty_input(result)
29
29
  end
30
30
 
31
- (0..last_token_index).each do |i|
32
- result.chart[i].each do |entry|
33
- # Is entry of the form? [A => alpha . B beta, k]...
34
- next_symbol = entry.next_symbol
35
- if next_symbol && next_symbol.kind_of?(Syntax::NonTerminal)
36
- # ...apply the Call rule
37
- call_rule(result, entry, i)
38
- end
39
-
40
- exit_rule(result, entry, i) if entry.exit_entry?
41
- start_rule(result, entry, i) if entry.start_entry?
42
- end_rule(result, entry, i) if entry.end_entry?
43
- end
44
- if i < last_token_index
45
- scan_success = scan_rule(result, i)
46
- break unless scan_success
47
- end
31
+ aTokenSequence.each_with_index do |token, i|
32
+ parse_for_token(result, i)
33
+ scan_success = scan_rule(result, i, token)
34
+ break unless scan_success
48
35
  end
36
+ parse_for_token(result, token_count) unless result.failure_reason
37
+
49
38
  result.done # End of parsing process
50
39
  return result
51
40
  end
52
41
 
53
42
  private
54
43
 
44
+ def parse_for_token(result, index)
45
+ result.chart[index].each do |entry|
46
+ # Is entry of the form? [A => alpha . B beta, k]...
47
+ next_symbol = entry.next_symbol
48
+ if next_symbol && next_symbol.kind_of?(Syntax::NonTerminal)
49
+ # ...apply the Call rule
50
+ call_rule(result, entry, index)
51
+ end
52
+
53
+ exit_rule(result, entry, index) if entry.exit_entry?
54
+ start_rule(result, entry, index) if entry.start_entry?
55
+ end_rule(result, entry, index) if entry.end_entry?
56
+ end
57
+ end
58
+
55
59
  # Let the current sigma set be the ith parse entry set.
56
60
  # This method is invoked when an entry is added to the parse entry set
57
61
  # and is of the form [A => alpha . B beta, k].
@@ -95,17 +99,17 @@ module Rley # This module is used as a namespace
95
99
  # [A => α . t γ, i]
96
100
  # and allow them to cross the edge, adding the node on the back side
97
101
  # of the edge as an entry to the next sigma set:
98
- # add an entry to the next sigma set [A => α t . γ, i]
99
- def scan_rule(aParsing, aPosition)
100
- aParsing.scan_rule(aPosition)
102
+ # add an entry to the next sigma set [A => α t . γ, i + 1]
103
+ def scan_rule(aParsing, aPosition, aToken)
104
+ aParsing.scan_rule(aPosition, aToken)
101
105
  end
102
-
106
+
103
107
  # Parse error detected: no input tokens provided while the grammar
104
108
  # forbids this this.
105
109
  def unexpected_empty_input(aParsing)
106
110
  aParsing.faulty(NoInput.new)
107
111
  return aParsing
108
- end
112
+ end
109
113
  end # class
110
114
  end # module
111
115
  end # module
@@ -33,11 +33,10 @@ module Rley # This module is used as a namespace
33
33
 
34
34
  # Constructor
35
35
  # @param theGFG [GFG::GrmFlowGraph] the Grammar Flow Graph
36
- # @param theTokens [Array<Token>] the array of input tokens
37
- def initialize(theGFG, theTokens)
36
+ def initialize(theGFG)
38
37
  @gf_graph = theGFG
39
- @tokens = theTokens.dup
40
- @chart = GFGChart.new(tokens.size, gf_graph)
38
+ @tokens = []
39
+ @chart = GFGChart.new(gf_graph)
41
40
  @antecedence = Hash.new { |hash, key| hash[key] = [] }
42
41
  antecedence[chart[0].first]
43
42
  end
@@ -57,7 +56,7 @@ module Rley # This module is used as a namespace
57
56
 
58
57
  if next_symbol.nullable? && anEntry.dotted_entry?
59
58
  size_after = chart[pos].size
60
- # ...apply the Nullable rule
59
+ # ...apply the Nullable rule, if not already invoked for this entry
61
60
  nullable_rule(anEntry, aPosition) if size_after == size_before
62
61
  end
63
62
  end
@@ -81,14 +80,15 @@ module Rley # This module is used as a namespace
81
80
 
82
81
  start.edges.each do |edge|
83
82
  succ = edge.successor # succ always an ItemVertex
84
- next if succ.dotted_item.production.generative?
85
- succ_entry = apply_rule(start_entry, succ, pos, pos, :nullable_rule)
86
- apply_rule(succ_entry, end_vertex, pos, pos, :nullable_rule)
83
+ if succ.dotted_item.production.nullable?
84
+ succ_entry = apply_rule(start_entry, succ, pos, pos, :nullable_rule)
85
+ apply_rule(succ_entry, end_vertex, pos, pos, :nullable_rule)
86
+ end
87
87
  end
88
88
 
89
89
  curr_vertex = anEntry.vertex
90
90
  next_vertex = curr_vertex.shortcut.successor
91
- end_entry = push_entry(end_vertex, pos, pos, :nullable_rule)
91
+ end_entry = push_entry(end_vertex, pos, pos, :nullable_rule)
92
92
  apply_rule(end_entry, next_vertex, anEntry.origin, pos, :nullable_rule)
93
93
  end
94
94
 
@@ -144,10 +144,11 @@ module Rley # This module is used as a namespace
144
144
  # [A => α . t γ, i]
145
145
  # and allow them to cross the edge, adding the node on the back side
146
146
  # of the edge as an entry to the next sigma set:
147
- # add an entry to the next sigma set [A => α t . γ, i]
147
+ # add an entry to the next sigma set [A => α t . γ, i + 1]
148
148
  # returns true if next token matches the expectations, false otherwise.
149
- def scan_rule(aPosition)
150
- terminal = tokens[aPosition].terminal
149
+ def scan_rule(aPosition, aToken)
150
+ tokens << aToken
151
+ terminal = aToken.terminal
151
152
 
152
153
  # Retrieve all the entries that expect the given terminal
153
154
  expecting_term = chart[aPosition].entries4term(terminal)
@@ -271,11 +272,11 @@ END_MSG
271
272
  # Rule: has 1..* antecedents, all of them are exit items
272
273
  antecedents.each do |antec|
273
274
  next if antec.exit_entry?
274
- msg_prefix = "Parse entry #{consequent}"
275
- msg_suffix = " has for antecedent #{antec}"
275
+ msg_prefix = "End vertex parse entry #{consequent}"
276
+ msg_suffix = " has for antecedent #{antec}"
276
277
  raise StandardError, msg_prefix + msg_suffix
277
278
  end
278
-
279
+
279
280
  when Rley::GFG::ItemVertex
280
281
  # Rule: has exactly one antecedent
281
282
  # if antecedents.size > 1
@@ -284,15 +285,15 @@ END_MSG
284
285
  # msg_suffix = antecedents.map(&:to_s).join("\n")
285
286
  # raise(StandardError, msg_prefix + msg + msg_suffix)
286
287
  # end
287
-
288
+
288
289
  when Rley::GFG::StartVertex
289
290
  # Rule: has 0..* antecedents, all of them are item vertices but not exit items
290
291
  antecedents.each do |antec|
291
292
  next if antec.dotted_entry? && !antec.end_entry?
292
293
  msg_prefix = "Parse entry #{consequent}"
293
- msg_suffix = " has for antecedent #{antec}"
294
+ msg_suffix = " has for antecedent #{antec}"
294
295
  raise StandardError, msg_prefix + msg_suffix
295
- end
296
+ end
296
297
  end
297
298
 
298
299
  consequent.add_antecedent(antecedentEntry)
@@ -52,6 +52,8 @@ module Rley # This module is used as a namespace
52
52
  # @return [Enumerator] yields visit events when walking over the
53
53
  # parse result
54
54
  def build_walker(acceptingEntry, maxIndex, lazyWalk = false)
55
+ msg = 'Internal error: nil entry argument'
56
+ raise StandardError, msg if acceptingEntry.nil?
55
57
  # Local context for the enumerator
56
58
  ctx = init_context(acceptingEntry, maxIndex, lazyWalk)
57
59
 
@@ -13,7 +13,7 @@ module Rley # This module is used as a namespace
13
13
  # The root node corresponds to the main/start symbol of the grammar.
14
14
  class ParseTree
15
15
  # @return [ParseTreeNode] The root node of the tree.
16
- attr_reader(:root)
16
+ attr_accessor(:root)
17
17
 
18
18
  # @param theRootNode [ParseTreeNode] The root node of the parse tree.
19
19
  def initialize(theRootNode)
@@ -214,20 +214,36 @@ module Rley # This module is used as a namespace
214
214
  filtered_rules.reject! { |prod| prod.lhs.nullable? }
215
215
  nullable_sets[i] = nullable_sets[i - 1].merge(new_nullables)
216
216
  end
217
+
218
+ mark_nullable
217
219
  end
218
220
 
219
221
  # Return the set of nonterminals which have one of their
220
222
  # production rules empty
221
223
  def direct_nullable()
222
- nullable = Set.new
224
+ nullables = Set.new
223
225
  # Direct nullable nonterminals correspond to empty productions
224
226
  rules.each do |prod|
225
227
  next unless prod.empty?
226
228
  prod.lhs.nullable = true
227
- nullable << prod.lhs
229
+ nullables << prod.lhs
228
230
  end
229
231
 
230
- return nullable
232
+ return nullables
233
+ end
234
+
235
+
236
+ # For each prodction determine whether it is nullable or not.
237
+ # A nullable production is a production that can match an empty string.
238
+ def mark_nullable
239
+ rules.each do |prod|
240
+ if prod.empty?
241
+ prod.nullable = true
242
+ else
243
+ # If all rhs members are all nullable, then rule is nullable
244
+ prod.nullable = prod.rhs.members.all?(&:nullable?)
245
+ end
246
+ end
231
247
  end
232
248
 
233
249
  def add_symbol(aSymbol)
@@ -15,15 +15,19 @@ module Rley # This module is used as a namespace
15
15
 
16
16
  # @return [NonTerminal] The left-hand side of the rule.
17
17
  attr_reader(:lhs)
18
-
19
- # @return [String]
18
+
19
+ # @return [String]
20
20
  # The name of the production rule. It must be unique in a grammar.
21
21
  attr_accessor(:name)
22
-
23
- # @return [Boolean] A production is generative when all of its
22
+
23
+ # @return [Boolean] A production is generative when all of its
24
24
  # rhs members are generative (that is, they can each generate/derive
25
25
  # a non-empty string of terminals).
26
- attr_writer(:generative)
26
+ attr_writer(:generative)
27
+
28
+ # @return [Boolean] A production is nullable when all of its
29
+ # rhs members are nullable.
30
+ attr_writer(:nullable)
27
31
 
28
32
  # Provide common alternate names to lhs and rhs accessors
29
33
 
@@ -32,10 +36,10 @@ module Rley # This module is used as a namespace
32
36
 
33
37
  # Create a Production instance.
34
38
  # @param aNonTerminal [NonTerminal] The left-hand side of the rule.
35
- # @param theSymbols [list<Terminal | NonTerminal>] symbols of rhs.
39
+ # @param theSymbols [list<Terminal | NonTerminal>] symbols of rhs.
36
40
  def initialize(aNonTerminal, theSymbols)
37
41
  @lhs = valid_lhs(aNonTerminal)
38
- @rhs = valid_rhs(theSymbols)
42
+ @rhs = valid_rhs(theSymbols)
39
43
  end
40
44
 
41
45
  # Is the rhs empty?
@@ -43,16 +47,21 @@ module Rley # This module is used as a namespace
43
47
  def empty?()
44
48
  return rhs.empty?
45
49
  end
46
-
47
- # Return true iff the production is generative
50
+
51
+ # Return true iff the production is generative
48
52
  def generative?()
49
53
  if @generative.nil?
50
54
  end
51
-
55
+
52
56
  return @generative
53
57
  end
54
58
 
55
- # Returns a string containing a human-readable representation of the
59
+ # @return [Boolen] true iff the production is nullable
60
+ def nullable?()
61
+ return @nullable
62
+ end
63
+
64
+ # Returns a string containing a human-readable representation of the
56
65
  # production.
57
66
  # @return [String]
58
67
  def inspect()
@@ -63,7 +72,7 @@ module Rley # This module is used as a namespace
63
72
  result << " @generative=#{@generative}>"
64
73
  return result
65
74
  end
66
-
75
+
67
76
  # A setter for the production name
68
77
  # @param aName [String] the name of the production
69
78
  def as(aName)
@@ -83,14 +92,14 @@ module Rley # This module is used as a namespace
83
92
 
84
93
  return aNonTerminal
85
94
  end
86
-
95
+
87
96
  def valid_rhs(theSymbols)
88
97
  if theSymbols.nil?
89
98
  msg_prefix = 'Right side of a production of the kind '
90
99
  msg_suffix = "'#{lhs.name}' => ... is nil."
91
- raise StandardError, msg_prefix + msg_suffix
92
- end
93
-
100
+ raise StandardError, msg_prefix + msg_suffix
101
+ end
102
+
94
103
  return SymbolSeq.new(theSymbols)
95
104
  end
96
105
  end # class
@@ -47,38 +47,50 @@ module Rley # Open this namespace to avoid module qualifier prefixes
47
47
  let(:items_from_grammar) { build_items_for_grammar(grammar_abc) }
48
48
  let(:sample_gfg) { GFG::GrmFlowGraph.new(items_from_grammar) }
49
49
  let(:sample_start_symbol) { sample_gfg.start_vertex.non_terminal }
50
+ let(:second_vertex) { sample_gfg.start_vertex.edges[0].successor }
50
51
 
51
52
 
52
53
  # Default instantiation rule
53
- subject { GFGChart.new(count_token, sample_gfg) }
54
+ subject { GFGChart.new(sample_gfg) }
54
55
 
55
56
 
56
57
  context 'Initialization:' do
57
58
  it 'should be created with start vertex, token count' do
58
- expect { GFGChart.new(count_token, sample_gfg) }
59
- .not_to raise_error
59
+ expect { GFGChart.new(sample_gfg) }.not_to raise_error
60
60
  end
61
61
 
62
- it 'should have correct entry set count' do
63
- expect(subject.sets.size).to eq(count_token + 1)
62
+ it 'should have one entry set' do
63
+ expect(subject.sets.size).to eq(1)
64
64
  end
65
65
 
66
66
  it 'should know the start symbol' do
67
67
  expect(subject.start_symbol).to eq(sample_start_symbol)
68
68
  end
69
-
69
+
70
70
  it 'should know the initial parse entry' do
71
71
  expect(subject.initial_entry.vertex).to eq(sample_gfg.start_vertex)
72
72
  expect(subject.initial_entry.origin).to eq(0)
73
73
  end
74
- =begin
74
+ end # context
75
75
 
76
- it 'should know the start dotted rule' do
77
- expect(subject.start_dotted_rule).to eq(dotted_rule)
76
+ context 'Provided services:' do
77
+ it 'should accept the pushing of a parse entry in existing set' do
78
+ expect(subject.sets[0].entries.size).to eq(1)
79
+ subject.push_entry(second_vertex, 0, 0, :scan_rule)
80
+ expect(subject.sets[0].entries.size).to eq(2)
81
+ end
82
+
83
+ it 'should accept the pushing of a parse entry in new set' do
84
+ expect(subject.sets[0].entries.size).to eq(1)
85
+ subject.push_entry(second_vertex, 0, 1, :scan_rule)
86
+ expect(subject.sets[0].entries.size).to eq(1)
87
+ expect(subject.sets.size).to eq(2)
88
+ expect(subject.sets[1].entries.size).to eq(1)
78
89
  end
79
90
 
80
-
81
- =end
91
+ it 'should retrieve an existing set at given position' do
92
+ expect(subject[0]).to eq(subject.sets[0])
93
+ end
82
94
  end # context
83
95
  end # describe
84
96
  end # module
@@ -41,10 +41,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
41
41
  builder.grammar
42
42
  end
43
43
 
44
- let(:grm1_tokens) do
45
- build_token_sequence(%w[a a b c c], grm1)
46
- end
47
-
44
+ let(:grm1_tokens) { build_token_sequence(%w[a a b c c], grm1) }
48
45
  let(:grm1_token_b) { build_token_sequence(['b'], grm1) }
49
46
 
50
47
  # Helper method. Create an array of dotted items
@@ -55,18 +52,15 @@ module Rley # Open this namespace to avoid module qualifier prefixes
55
52
  let(:output) { StringIO.new('', 'w') }
56
53
 
57
54
  # Default instantiation rule
58
- subject do
59
- GFGParsing.new(sample_gfg, grm1_tokens)
60
- end
55
+ subject { GFGParsing.new(sample_gfg) }
61
56
 
62
57
  context 'Initialization:' do
63
- it 'should be created with a GFG, tokens' do
64
- expect { GFGParsing.new(sample_gfg, grm1_tokens) }
65
- .not_to raise_error
58
+ it 'should be created with a GFG' do
59
+ expect { GFGParsing.new(sample_gfg) }.not_to raise_error
66
60
  end
67
61
 
68
- it 'should know the input tokens' do
69
- expect(subject.tokens).to eq(grm1_tokens)
62
+ it 'should have an empty tokens array' do
63
+ expect(subject.tokens).to be_empty
70
64
  end
71
65
 
72
66
  it 'should know its chart object' do
@@ -107,11 +101,9 @@ SNIPPET
107
101
 
108
102
  # Utility method to initialize the second entry set...
109
103
  def seed_second_set()
110
- # Cheating: we change surreptitiously the tokens to scan...
111
- subject.instance_variable_set(:@tokens, grm1_token_b)
112
-
104
+ # Cheating: we change the tokens to scan...
113
105
  # Seeding second entry set...
114
- subject.scan_rule(0)
106
+ subject.scan_rule(0, grm1_token_b[0])
115
107
  end
116
108
 
117
109
  # Utility method used to invoke the private method 'push_entry'
@@ -120,19 +112,19 @@ SNIPPET
120
112
  end
121
113
 
122
114
  it 'should push a parse entry to a given chart entry set' do
123
- expect(subject.chart[1]).to be_empty
115
+ expect(subject.chart.sets[1]).to be_nil
124
116
  a_vertex = sample_gfg.find_vertex('A => a . A c')
125
117
 
126
- push_entry(subject, a_vertex, 1, 1, :scanning)
118
+ push_entry(subject, a_vertex, 1, 1, :scan_rule)
127
119
  expect(subject.chart[1].size).to eq(1)
128
120
  expect(subject.chart[1].first.vertex).to eq(a_vertex)
129
121
 
130
122
  # Pushing twice the same state must be no-op
131
- push_entry(subject, a_vertex, 1, 1, :scanning)
123
+ push_entry(subject, a_vertex, 1, 1, :scan_rule)
132
124
  expect(subject.chart[1].size).to eq(1)
133
125
 
134
126
  # Pushing to another entry set
135
- push_entry(subject, a_vertex, 1, 2, :scanning)
127
+ push_entry(subject, a_vertex, 1, 2, :scan_rule)
136
128
  expect(subject.chart[2].size).to eq(1)
137
129
  end
138
130
 
@@ -191,8 +183,8 @@ SNIPPET
191
183
  # ['A => . a A c', 'A => . b']
192
184
  fourth_entry = subject.chart[0].entries[3] # 'A => . a A c'
193
185
 
194
- expect(subject.chart[1]).to be_empty
195
- subject.scan_rule(0)
186
+ expect(subject.chart.sets[1]).to be_nil
187
+ subject.scan_rule(0, grm1_tokens[0])
196
188
  # Given that the scanned token is 'a'...
197
189
  # Then a new entry is added in next entry set
198
190
  expect(subject.chart[1].size).to eq(1)
@@ -316,9 +308,15 @@ SNIPPET
316
308
  tokens = expr_tokenizer('2 + 3 * 4', b_expr_grammar)
317
309
  parser.parse(tokens)
318
310
  end
311
+
312
+ it 'should indicate whether a parse succeeded' do
313
+ expect(subject.success?).to be_truthy
314
+ end
319
315
 
320
316
  it 'should build a parse forest' do
321
- expect { subject.parse_forest }.not_to raise_error
317
+ if subject.success?
318
+ expect { subject.parse_forest }.not_to raise_error
319
+ end
322
320
  end
323
321
  =begin
324
322
  it 'should create the root of a parse forest' do
@@ -246,6 +246,13 @@ module Rley # Open this namespace to avoid module qualifier prefixes
246
246
  expect(nterm).to be_nullable
247
247
  end
248
248
  end
249
+
250
+ it 'should mark its nullable productions' do
251
+ # Given the above productions, here are our expectations:
252
+ expectations = [true, false, false, true]
253
+ actuals = subject.rules.map(&:nullable?)
254
+ expect(actuals).to eq(expectations)
255
+ end
249
256
  end # context
250
257
  end # describe
251
258
  end # module
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rley
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.06
4
+ version: 0.6.07
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-12 00:00:00.000000000 Z
11
+ date: 2018-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: coveralls