rley 0.6.06 → 0.6.07

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