rley 0.7.06 → 0.8.01

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +362 -62
  3. data/.travis.yml +6 -6
  4. data/CHANGELOG.md +20 -4
  5. data/LICENSE.txt +1 -1
  6. data/README.md +7 -7
  7. data/examples/NLP/engtagger.rb +193 -190
  8. data/examples/NLP/nano_eng/nano_en_demo.rb +7 -11
  9. data/examples/NLP/nano_eng/nano_grammar.rb +21 -21
  10. data/examples/NLP/pico_en_demo.rb +2 -2
  11. data/examples/data_formats/JSON/cli_options.rb +1 -1
  12. data/examples/data_formats/JSON/json_ast_builder.rb +21 -27
  13. data/examples/data_formats/JSON/json_ast_nodes.rb +12 -21
  14. data/examples/data_formats/JSON/json_demo.rb +1 -2
  15. data/examples/data_formats/JSON/json_grammar.rb +13 -13
  16. data/examples/data_formats/JSON/json_lexer.rb +8 -8
  17. data/examples/data_formats/JSON/json_minifier.rb +1 -1
  18. data/examples/general/calc_iter1/calc_ast_builder.rb +13 -10
  19. data/examples/general/calc_iter1/calc_ast_nodes.rb +23 -37
  20. data/examples/general/calc_iter1/calc_grammar.rb +7 -6
  21. data/examples/general/calc_iter1/calc_lexer.rb +6 -4
  22. data/examples/general/calc_iter1/spec/calculator_spec.rb +5 -5
  23. data/examples/general/calc_iter2/calc_ast_builder.rb +5 -3
  24. data/examples/general/calc_iter2/calc_ast_nodes.rb +27 -43
  25. data/examples/general/calc_iter2/calc_grammar.rb +12 -12
  26. data/examples/general/calc_iter2/calc_lexer.rb +11 -10
  27. data/examples/general/calc_iter2/spec/calculator_spec.rb +26 -26
  28. data/examples/general/left.rb +2 -2
  29. data/examples/general/right.rb +2 -2
  30. data/lib/rley.rb +1 -1
  31. data/lib/rley/base/dotted_item.rb +28 -31
  32. data/lib/rley/base/grm_items_builder.rb +6 -0
  33. data/lib/rley/constants.rb +2 -2
  34. data/lib/rley/engine.rb +22 -25
  35. data/lib/rley/formatter/asciitree.rb +3 -3
  36. data/lib/rley/formatter/bracket_notation.rb +1 -8
  37. data/lib/rley/formatter/debug.rb +6 -6
  38. data/lib/rley/formatter/json.rb +2 -2
  39. data/lib/rley/gfg/call_edge.rb +1 -1
  40. data/lib/rley/gfg/edge.rb +5 -5
  41. data/lib/rley/gfg/end_vertex.rb +2 -6
  42. data/lib/rley/gfg/epsilon_edge.rb +1 -5
  43. data/lib/rley/gfg/grm_flow_graph.rb +27 -23
  44. data/lib/rley/gfg/item_vertex.rb +10 -10
  45. data/lib/rley/gfg/non_terminal_vertex.rb +4 -4
  46. data/lib/rley/gfg/scan_edge.rb +1 -1
  47. data/lib/rley/gfg/shortcut_edge.rb +2 -2
  48. data/lib/rley/gfg/start_vertex.rb +4 -8
  49. data/lib/rley/gfg/vertex.rb +43 -39
  50. data/lib/rley/interface.rb +16 -0
  51. data/lib/rley/lexical/token_range.rb +6 -6
  52. data/lib/rley/notation/all_notation_nodes.rb +2 -0
  53. data/lib/rley/notation/ast_builder.rb +191 -0
  54. data/lib/rley/notation/ast_node.rb +44 -0
  55. data/lib/rley/notation/ast_visitor.rb +113 -0
  56. data/lib/rley/notation/grammar.rb +49 -0
  57. data/lib/rley/notation/grammar_builder.rb +504 -0
  58. data/lib/rley/notation/grouping_node.rb +23 -0
  59. data/lib/rley/notation/parser.rb +56 -0
  60. data/lib/rley/notation/sequence_node.rb +35 -0
  61. data/lib/rley/notation/symbol_node.rb +29 -0
  62. data/lib/rley/notation/tokenizer.rb +192 -0
  63. data/lib/rley/parse_forest_visitor.rb +5 -5
  64. data/lib/rley/parse_rep/ast_base_builder.rb +48 -11
  65. data/lib/rley/parse_rep/cst_builder.rb +5 -6
  66. data/lib/rley/parse_rep/parse_forest_builder.rb +22 -18
  67. data/lib/rley/parse_rep/parse_forest_factory.rb +3 -3
  68. data/lib/rley/parse_rep/parse_rep_creator.rb +14 -16
  69. data/lib/rley/parse_rep/parse_tree_builder.rb +4 -4
  70. data/lib/rley/parse_rep/parse_tree_factory.rb +27 -27
  71. data/lib/rley/parse_tree_visitor.rb +1 -1
  72. data/lib/rley/parser/error_reason.rb +4 -5
  73. data/lib/rley/parser/gfg_chart.rb +118 -26
  74. data/lib/rley/parser/gfg_parsing.rb +22 -33
  75. data/lib/rley/parser/parse_entry.rb +25 -31
  76. data/lib/rley/parser/parse_entry_set.rb +19 -16
  77. data/lib/rley/parser/parse_entry_tracker.rb +4 -4
  78. data/lib/rley/parser/parse_tracer.rb +13 -13
  79. data/lib/rley/parser/parse_walker_factory.rb +23 -28
  80. data/lib/rley/ptree/non_terminal_node.rb +7 -5
  81. data/lib/rley/ptree/parse_tree.rb +3 -3
  82. data/lib/rley/ptree/parse_tree_node.rb +5 -5
  83. data/lib/rley/ptree/terminal_node.rb +7 -7
  84. data/lib/rley/rley_error.rb +12 -12
  85. data/lib/rley/sppf/alternative_node.rb +6 -6
  86. data/lib/rley/sppf/composite_node.rb +7 -7
  87. data/lib/rley/sppf/epsilon_node.rb +3 -3
  88. data/lib/rley/sppf/leaf_node.rb +3 -3
  89. data/lib/rley/sppf/parse_forest.rb +16 -16
  90. data/lib/rley/sppf/sppf_node.rb +7 -8
  91. data/lib/rley/sppf/token_node.rb +3 -3
  92. data/lib/rley/syntax/{grammar_builder.rb → base_grammar_builder.rb} +61 -23
  93. data/lib/rley/syntax/grammar.rb +5 -5
  94. data/lib/rley/syntax/grm_symbol.rb +7 -7
  95. data/lib/rley/syntax/match_closest.rb +43 -0
  96. data/lib/rley/syntax/non_terminal.rb +9 -15
  97. data/lib/rley/syntax/production.rb +16 -10
  98. data/lib/rley/syntax/symbol_seq.rb +7 -9
  99. data/lib/rley/syntax/terminal.rb +4 -5
  100. data/lib/rley/syntax/verbatim_symbol.rb +3 -3
  101. data/lib/support/base_tokenizer.rb +19 -18
  102. data/spec/rley/base/dotted_item_spec.rb +2 -2
  103. data/spec/rley/engine_spec.rb +23 -21
  104. data/spec/rley/formatter/asciitree_spec.rb +7 -7
  105. data/spec/rley/formatter/bracket_notation_spec.rb +13 -13
  106. data/spec/rley/formatter/json_spec.rb +1 -1
  107. data/spec/rley/gfg/end_vertex_spec.rb +5 -5
  108. data/spec/rley/gfg/grm_flow_graph_spec.rb +2 -2
  109. data/spec/rley/gfg/item_vertex_spec.rb +10 -10
  110. data/spec/rley/gfg/non_terminal_vertex_spec.rb +3 -3
  111. data/spec/rley/gfg/shortcut_edge_spec.rb +1 -1
  112. data/spec/rley/gfg/start_vertex_spec.rb +5 -5
  113. data/spec/rley/gfg/vertex_spec.rb +3 -3
  114. data/spec/rley/lexical/token_range_spec.rb +16 -16
  115. data/spec/rley/lexical/token_spec.rb +2 -2
  116. data/spec/rley/notation/grammar_builder_spec.rb +302 -0
  117. data/spec/rley/notation/parser_spec.rb +184 -0
  118. data/spec/rley/notation/tokenizer_spec.rb +370 -0
  119. data/spec/rley/parse_forest_visitor_spec.rb +165 -163
  120. data/spec/rley/parse_rep/ambiguous_parse_spec.rb +44 -44
  121. data/spec/rley/parse_rep/ast_builder_spec.rb +6 -7
  122. data/spec/rley/parse_rep/cst_builder_spec.rb +5 -5
  123. data/spec/rley/parse_rep/groucho_spec.rb +24 -26
  124. data/spec/rley/parse_rep/parse_forest_builder_spec.rb +27 -27
  125. data/spec/rley/parse_rep/parse_forest_factory_spec.rb +8 -8
  126. data/spec/rley/parse_rep/parse_tree_factory_spec.rb +3 -3
  127. data/spec/rley/parse_tree_visitor_spec.rb +10 -8
  128. data/spec/rley/parser/dangling_else_spec.rb +445 -0
  129. data/spec/rley/parser/error_reason_spec.rb +6 -6
  130. data/spec/rley/parser/gfg_earley_parser_spec.rb +120 -12
  131. data/spec/rley/parser/gfg_parsing_spec.rb +6 -13
  132. data/spec/rley/parser/parse_entry_spec.rb +19 -19
  133. data/spec/rley/parser/parse_walker_factory_spec.rb +10 -10
  134. data/spec/rley/ptree/non_terminal_node_spec.rb +5 -3
  135. data/spec/rley/ptree/parse_tree_node_spec.rb +4 -4
  136. data/spec/rley/ptree/terminal_node_spec.rb +6 -6
  137. data/spec/rley/sppf/alternative_node_spec.rb +6 -6
  138. data/spec/rley/sppf/non_terminal_node_spec.rb +3 -3
  139. data/spec/rley/sppf/token_node_spec.rb +4 -4
  140. data/spec/rley/support/ambiguous_grammar_helper.rb +4 -5
  141. data/spec/rley/support/grammar_abc_helper.rb +3 -5
  142. data/spec/rley/support/grammar_ambig01_helper.rb +5 -6
  143. data/spec/rley/support/grammar_arr_int_helper.rb +5 -6
  144. data/spec/rley/support/grammar_b_expr_helper.rb +5 -6
  145. data/spec/rley/support/grammar_int_seq_helper.rb +51 -0
  146. data/spec/rley/support/grammar_l0_helper.rb +14 -17
  147. data/spec/rley/support/grammar_pb_helper.rb +8 -7
  148. data/spec/rley/support/grammar_sppf_helper.rb +3 -3
  149. data/spec/rley/syntax/{grammar_builder_spec.rb → base_grammar_builder_spec.rb} +35 -16
  150. data/spec/rley/syntax/grammar_spec.rb +6 -6
  151. data/spec/rley/syntax/grm_symbol_spec.rb +1 -1
  152. data/spec/rley/syntax/match_closest_spec.rb +46 -0
  153. data/spec/rley/syntax/non_terminal_spec.rb +8 -8
  154. data/spec/rley/syntax/production_spec.rb +17 -13
  155. data/spec/rley/syntax/symbol_seq_spec.rb +2 -2
  156. data/spec/rley/syntax/terminal_spec.rb +5 -5
  157. data/spec/rley/syntax/verbatim_symbol_spec.rb +1 -1
  158. data/spec/spec_helper.rb +0 -12
  159. data/spec/support/base_tokenizer_spec.rb +7 -2
  160. metadata +48 -74
  161. data/.simplecov +0 -7
  162. data/lib/rley/parser/parse_state.rb +0 -83
  163. data/lib/rley/parser/parse_state_tracker.rb +0 -59
  164. data/lib/rley/parser/state_set.rb +0 -101
  165. data/spec/rley/parser/parse_state_spec.rb +0 -125
  166. data/spec/rley/parser/parse_tracer_spec.rb +0 -200
  167. data/spec/rley/parser/state_set_spec.rb +0 -130
@@ -16,13 +16,13 @@ module Rley # This module is used as a namespace
16
16
  # nodes) and using a step by step approach.
17
17
  class CSTBuilder < ParseTreeBuilder
18
18
  protected
19
-
19
+
20
20
  # Method to override
21
21
  # Create a parse tree object with given
22
22
  # node as root node.
23
23
  def create_tree(aRootNode)
24
24
  return Rley::PTree::ParseTree.new(aRootNode)
25
- end
25
+ end
26
26
 
27
27
  # Method to override
28
28
  # Factory method for creating a node object for the given
@@ -42,10 +42,9 @@ module Rley # This module is used as a namespace
42
42
  # @param theChildren [Array] Children nodes (one per rhs symbol)
43
43
  def new_parent_node(aProduction, aRange, _tokens, theChildren)
44
44
  node = Rley::PTree::NonTerminalNode.new(aProduction.lhs, aRange)
45
- if theChildren
46
- theChildren.reverse_each { |child| node.add_subnode(child) }
47
- end
48
- return node
45
+ theChildren&.reverse_each { |child| node.add_subnode(child) }
46
+
47
+ node
49
48
  end
50
49
  end # class
51
50
  end # module
@@ -44,7 +44,7 @@ module Rley # This module is used as a namespace
44
44
  end
45
45
 
46
46
  # Notify the builder that the construction is over
47
- def done!()
47
+ def done!
48
48
  result.done!
49
49
  end
50
50
 
@@ -64,7 +64,7 @@ module Rley # This module is used as a namespace
64
64
  end
65
65
 
66
66
  # Return the current_parent node
67
- def curr_parent()
67
+ def curr_parent
68
68
  curr_path.last
69
69
  end
70
70
 
@@ -110,18 +110,18 @@ module Rley # This module is used as a namespace
110
110
  def process_item_entry(anEvent, anEntry, anIndex)
111
111
  case anEvent
112
112
  when :visit
113
- if anEntry.exit_entry?
113
+ if anEntry.exit_entry? && last_visitee.end_entry? &&
114
+ last_visitee.antecedents.size > 1
114
115
  # Previous entry was an end entry (X. pattern)
115
116
  # Does the previous entry have multiple antecedent?
116
- if last_visitee.end_entry? && last_visitee.antecedents.size > 1
117
- # Store current path for later backtracking
118
- # puts "Store backtrack context #{last_visitee}"
119
- # puts "path [#{curr_path.map{|e|e.to_string(0)}.join(', ')}]"
120
- entry2path_to_alt[last_visitee] = curr_path.dup
121
- curr_parent.refinement = :or
122
-
123
- create_alternative_node(anEntry)
124
- end
117
+
118
+ # Store current path for later backtracking
119
+ # puts "Store backtrack context #{last_visitee}"
120
+ # puts "path [#{curr_path.map{|e|e.to_string(0)}.join(', ')}]"
121
+ entry2path_to_alt[last_visitee] = curr_path.dup
122
+ curr_parent.refinement = :or
123
+
124
+ create_alternative_node(anEntry)
125
125
  end
126
126
 
127
127
  # Does this entry have multiple antecedent?
@@ -155,6 +155,7 @@ module Rley # This module is used as a namespace
155
155
  # Restore path
156
156
  @curr_path = entry2path_to_alt[anEntry].dup
157
157
  raise StandardError, 'path is nil' if curr_path.nil?
158
+
158
159
  create_alternative_node(anEntry)
159
160
 
160
161
  when :revisit
@@ -178,7 +179,7 @@ module Rley # This module is used as a namespace
178
179
 
179
180
  # Create an empty parse forest
180
181
  def create_forest(aRootNode)
181
- return Rley::SPPF::ParseForest.new(aRootNode)
182
+ Rley::SPPF::ParseForest.new(aRootNode)
182
183
  end
183
184
 
184
185
  # Factory method. Build and return an SPPF non-terminal node.
@@ -189,7 +190,7 @@ module Rley # This module is used as a namespace
189
190
  add_subnode(new_node)
190
191
  # puts "FOREST ADD #{curr_parent.key if curr_parent}/#{new_node.key}"
191
192
 
192
- return new_node
193
+ new_node
193
194
  end
194
195
 
195
196
  # Add an alternative node to the forest
@@ -201,7 +202,7 @@ module Rley # This module is used as a namespace
201
202
  result.is_ambiguous = true
202
203
  # puts "FOREST ADD #{alternative.key}"
203
204
 
204
- return alternative
205
+ alternative
205
206
  end
206
207
 
207
208
  # create a token node,
@@ -215,7 +216,7 @@ module Rley # This module is used as a namespace
215
216
  candidate = add_node_to_forest(new_node)
216
217
  entry2node[anEntry] = candidate
217
218
 
218
- return candidate
219
+ candidate
219
220
  end
220
221
 
221
222
  def create_epsilon_node(anEntry, anIndex)
@@ -223,7 +224,7 @@ module Rley # This module is used as a namespace
223
224
  candidate = add_node_to_forest(new_node)
224
225
  entry2node[anEntry] = candidate
225
226
 
226
- return candidate
227
+ candidate
227
228
  end
228
229
 
229
230
  # Add the given node if not yet present in parse forest
@@ -238,16 +239,19 @@ module Rley # This module is used as a namespace
238
239
  end
239
240
  add_subnode(new_node, false)
240
241
 
241
- return new_node
242
+ new_node
242
243
  end
243
244
 
244
245
  # Add the given node as sub-node of current parent node
245
246
  # Optionally add the node to the current path
247
+ # rubocop: disable Style/OptionalBooleanParameter
246
248
  def add_subnode(aNode, addToPath = true)
247
249
  raise StandardError, 'node is nil' if aNode.nil?
250
+
248
251
  curr_parent.add_subnode(aNode) unless curr_path.empty?
249
252
  curr_path << aNode if addToPath
250
253
  end
254
+ # rubocop: enable Style/OptionalBooleanParameter
251
255
  end # class
252
256
  end # module
253
257
  end # module
@@ -15,13 +15,13 @@ module Rley # This module is used as a namespace
15
15
  def builder(aParseResult, _builder = nil)
16
16
  ParseForestBuilder.new(aParseResult.tokens)
17
17
  end
18
-
18
+
19
19
  # When an end vertex is re-visited then jump
20
20
  # its corresponding start vertex. This behaviour
21
21
  # makes sense for sharing nodes.
22
- def jump_to_start()
22
+ def jump_to_start
23
23
  true
24
- end
24
+ end
25
25
  end # class
26
26
  end # module
27
27
  end # module
@@ -7,18 +7,18 @@ module Rley # This module is used as a namespace
7
7
  # Utility class that helps to create a representation of a parse from
8
8
  # a given Parsing object.
9
9
  class ParseRepCreator
10
- # @return [Rley::Parser::GFGParsing]
10
+ # @return [Rley::Parser::GFGParsing]
11
11
  # Link to Parsing object (= results of recognizer)
12
12
  attr_reader(:parsing)
13
-
13
+
14
14
  # Constructor. Creates and initialize a ParseRepCreator instance.
15
15
  # @return [ParseRepCreator]
16
16
  def initialize(aParsingResult)
17
17
  @parsing = aParsingResult
18
- end
19
-
18
+ end
19
+
20
20
  # Factory method that produces the representation of the parse.
21
- # @return [Rley::PTree::ParseTree, Rley::SPPF::ParseForest]
21
+ # @return [Rley::PTree::ParseTree, Rley::SPPF::ParseForest]
22
22
  # The parse representation.
23
23
  def create(aBuilder = nil)
24
24
  a_walker = walker(parsing)
@@ -32,20 +32,20 @@ module Rley # This module is used as a namespace
32
32
  end
33
33
  rescue StopIteration
34
34
  # Do nothing: gobble the exception
35
- rescue StandardError => exc
36
- if exc.message =~ /^Ambiguous/
35
+ rescue StandardError => e
36
+ if e.message =~ /^Ambiguous/
37
37
  $stderr.puts parsing
38
38
  end
39
- raise exc
39
+ raise e
40
40
  end
41
-
41
+
42
42
  a_builder.done!
43
43
 
44
- return a_builder.result
44
+ a_builder.result
45
45
  end
46
-
46
+
47
47
  private
48
-
48
+
49
49
  # Create a Parsing walker, that is, an object
50
50
  # that will iterate over the relevant nodes (= parsing entries)
51
51
  # of a GFGParsing
@@ -58,13 +58,11 @@ module Rley # This module is used as a namespace
58
58
 
59
59
  # By default, when a end vertex is re-visited don't jump
60
60
  # its corresponding start vertex.
61
- def jump_to_start()
61
+ def jump_to_start
62
62
  false
63
63
  end
64
-
65
64
  end # class
66
65
  end # module
67
66
  end # module
68
67
 
69
- # End of file
70
-
68
+ # End of file
@@ -50,7 +50,7 @@ module Rley # This module is used as a namespace
50
50
  end
51
51
 
52
52
  # Notify the builder that the parse tree construction is complete.
53
- def done!()
53
+ def done!
54
54
  result.done!
55
55
  end
56
56
 
@@ -92,7 +92,7 @@ module Rley # This module is used as a namespace
92
92
  private
93
93
 
94
94
  # Return the top of stack element.
95
- def tos()
95
+ def tos
96
96
  @stack.last
97
97
  end
98
98
 
@@ -207,7 +207,7 @@ module Rley # This module is used as a namespace
207
207
  # @param anEntry [ParseEntry] The entry being visited
208
208
  def terminal_before_dot?(anEntry)
209
209
  prev_symbol = anEntry.prev_symbol
210
- return prev_symbol && prev_symbol.terminal?
210
+ prev_symbol&.terminal?
211
211
  end
212
212
 
213
213
  # A terminal symbol was detected at left of dot.
@@ -260,7 +260,7 @@ module Rley # This module is used as a namespace
260
260
  non_terminal = anEntry.vertex.lhs
261
261
  end
262
262
 
263
- return non_terminal
263
+ non_terminal
264
264
  end
265
265
  end # class
266
266
  end # module
@@ -1,27 +1,27 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'parse_rep_creator'
4
- # require_relative 'parse_tree_builder' # TODO remove this line
5
- require_relative 'cst_builder'
6
-
7
- module Rley # This module is used as a namespace
8
- module ParseRep # This module is used as a namespace
9
- # Utility class that helps to create a ParseTree from
10
- # a given Parsing object.
11
- class ParseTreeFactory < ParseRepCreator
12
- protected
13
-
14
- # Create a Builder, that is, an object
15
- # that will create piece by piece the forest
16
- def builder(aParseResult, aBuilder = nil)
17
- if aBuilder
18
- aBuilder.new(aParseResult.tokens)
19
- else
20
- CSTBuilder.new(aParseResult.tokens)
21
- end
22
- end
23
- end # class
24
- end # module
25
- end # module
26
-
27
- # End of file
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'parse_rep_creator'
4
+ # require_relative 'parse_tree_builder' # TODO remove this line
5
+ require_relative 'cst_builder'
6
+
7
+ module Rley # This module is used as a namespace
8
+ module ParseRep # This module is used as a namespace
9
+ # Utility class that helps to create a ParseTree from
10
+ # a given Parsing object.
11
+ class ParseTreeFactory < ParseRepCreator
12
+ protected
13
+
14
+ # Create a Builder, that is, an object
15
+ # that will create piece by piece the forest
16
+ def builder(aParseResult, aBuilder = nil)
17
+ if aBuilder
18
+ aBuilder.new(aParseResult.tokens)
19
+ else
20
+ CSTBuilder.new(aParseResult.tokens)
21
+ end
22
+ end
23
+ end # class
24
+ end # module
25
+ end # module
26
+
27
+ # End of file
@@ -37,7 +37,7 @@ module Rley # This module is used as a namespace
37
37
  end
38
38
 
39
39
  # The signal to begin the visit of the parse tree.
40
- def start()
40
+ def start
41
41
  ptree.accept(self)
42
42
  end
43
43
 
@@ -17,7 +17,7 @@ module Rley # Module used as a namespace
17
17
  end
18
18
 
19
19
  # @return [String] the result of invoking reason.to_s
20
- def message()
20
+ def message
21
21
  return to_s
22
22
  end
23
23
 
@@ -27,11 +27,10 @@ module Rley # Module used as a namespace
27
27
  end
28
28
  end # class
29
29
 
30
-
31
30
  # This parse error occurs when no input for parsing was provided
32
31
  # while the grammar requires some non-empty input.
33
32
  class NoInput < ErrorReason
34
- def initialize()
33
+ def initialize
35
34
  super(0)
36
35
  end
37
36
 
@@ -61,7 +60,7 @@ module Rley # Module used as a namespace
61
60
 
62
61
  protected
63
62
 
64
- def position()
63
+ def position
65
64
  return last_token.position if last_token.respond_to?(:position)
66
65
 
67
66
  rank + 1
@@ -76,7 +75,7 @@ module Rley # Module used as a namespace
76
75
  else
77
76
  "'#{term_names[0]}'"
78
77
  end
79
- return explain
78
+ explain
80
79
  end
81
80
  end # class
82
81
 
@@ -12,38 +12,52 @@ module Rley # This module is used as a namespace
12
12
  # the chart is an array with n + 1 entry sets.
13
13
  class GFGChart
14
14
  # @return [Array<ParseEntrySet>] entry sets (one per input token + 1)
15
- attr_reader(:sets)
15
+ attr_reader :sets
16
+
17
+ # @return [Array<Array<Syntax::MatchClosest>>]
18
+ attr_reader :constraints
16
19
 
17
20
  # @param aGFGraph [GFG::GrmFlowGraph] The GFG for the grammar in use.
18
21
  def initialize(aGFGraph)
19
- @sets = [ ParseEntrySet.new ]
22
+ @sets = [ParseEntrySet.new]
23
+ @constraints = [[]]
20
24
  push_entry(aGFGraph.start_vertex, 0, 0, :start_rule)
21
25
  end
22
26
 
23
27
  # @return [Syntax::NonTerminal] the start symbol of the grammar.
24
- def start_symbol()
25
- return sets.first.entries[0].vertex.non_terminal
28
+ def start_symbol
29
+ sets.first.entries[0].vertex.non_terminal
26
30
  end
27
31
 
28
32
  # @param index [Integer]
29
33
  # @return [ParseEntrySet] Access the entry set at given position.
30
34
  def [](index)
31
- return sets[index]
35
+ sets[index]
32
36
  end
33
37
 
34
38
  # Return the index value of the last non-empty entry set.
35
39
  # @return [Integer]
36
- def last_index()
40
+ def last_index
37
41
  first_empty = sets.find_index(&:empty?)
38
- index = if first_empty.nil?
39
- sets.size - 1
40
- else
41
- first_empty.zero? ? 0 : first_empty - 1
42
- end
43
-
44
- return index
42
+ if first_empty.nil?
43
+ sets.size - 1
44
+ else
45
+ first_empty.zero? ? 0 : first_empty - 1
46
+ end
45
47
  end
46
48
 
49
+ # if an entry corresponds to dotted item with a constraint
50
+ # make this constraint active for this index
51
+ # :before 'IF'
52
+ # search backwards to find nearest 'IF' scan rule
53
+ # in n+1, retrieve all items with IF . pattern
54
+ # create a lambda
55
+ # for every subsequent push_entry with same index,
56
+ # the lambda checks the condition (i.e pattern: ELSE . )
57
+ # if the condition is false, then push new entry
58
+ # if the condition is true but the consequent is false, then discard push action
59
+ # consequent: candidate refers to same dotted_item and same origin, then condition is false
60
+
47
61
  # Push a parse entry for the chart entry with given index
48
62
  # @param anIndex [Integer] The rank of the token in the input stream.
49
63
  # @return [ParseEntry] the passed parse entry if it is pushed
@@ -53,27 +67,59 @@ module Rley # This module is used as a namespace
53
67
  # puts " anOrigin: #{anOrigin}"
54
68
  # puts " anIndex: #{anIndex}"
55
69
  # puts " _reason: #{_reason}"
56
- new_entry = ParseEntry.new(aVertex, anOrigin)
57
70
  if anIndex == sets.size
58
- err_msg = "Internal error: unexpected push reason #{reason}"
59
- raise StandardError, err_msg if reason != :scan_rule
71
+ if reason == :scan_rule
72
+ add_entry_set
73
+ else
74
+ err_msg = "Internal error: unexpected push reason #{reason}"
75
+ raise StandardError, err_msg
76
+ end
77
+ end
78
+
79
+ reject = false
80
+ unless constraints[anIndex].empty?
81
+ constraints[anIndex].each do |ct|
82
+ case ct
83
+ when Syntax::MatchClosest
84
+ not_found = sets[anIndex][0].prev_symbol != aVertex.prev_symbol
85
+ next if not_found
86
+
87
+ some_mismatch = ct.entries.find do |en|
88
+ (en.vertex.dotted_item.production == aVertex.dotted_item.production) &&
89
+ (en.origin != anOrigin)
90
+ end
91
+ reject = true if some_mismatch
92
+ end
93
+ end
94
+ end
95
+
96
+ return nil if reject
97
+
98
+ new_entry = ParseEntry.new(aVertex, anOrigin)
99
+ result = self[anIndex].push_entry(new_entry)
60
100
 
61
- add_entry_set
101
+ if aVertex.kind_of?(GFG::ItemVertex) && aVertex.dotted_item.constraint
102
+ ct = aVertex.dotted_item.constraint
103
+
104
+ case ct
105
+ when Syntax::MatchClosest
106
+ update_match_closest(ct, anIndex)
107
+ end
108
+ constraints[anIndex] << ct
62
109
  end
63
- pushed = self[anIndex].push_entry(new_entry)
64
110
 
65
- return pushed
111
+ result
66
112
  end
67
113
 
68
114
  # Retrieve the first parse entry added to this chart
69
115
  # @return [ParseEntry]
70
- def initial_entry()
71
- return sets[0].first
116
+ def initial_entry
117
+ sets[0].first
72
118
  end
73
119
 
74
120
  # Retrieve the entry that corresponds to a complete and successful parse
75
121
  # @return [ParseEntry]
76
- def accepting_entry()
122
+ def accepting_entry
77
123
  # Success can be detected as follows:
78
124
  # The last chart entry set has at least one complete parse entry
79
125
  # for the start symbol with an origin == 0
@@ -95,7 +141,7 @@ module Rley # This module is used as a namespace
95
141
 
96
142
  success_entries.first
97
143
  end
98
-
144
+
99
145
  # @return [Integer] The number of states.
100
146
  def count_states
101
147
  sets.size
@@ -103,6 +149,7 @@ module Rley # This module is used as a namespace
103
149
 
104
150
  # @return [Integer] The total number of entries.
105
151
  def count_entries
152
+ # rubocop: disable Lint/UselessAssignment
106
153
  sets.reduce(0) do |sub_result, a_set|
107
154
  sub_result += a_set.size
108
155
  end
@@ -114,6 +161,26 @@ module Rley # This module is used as a namespace
114
161
  sub_result += a_set.count_edges
115
162
  end
116
163
  end
164
+ # rubocop: enable Lint/UselessAssignment
165
+
166
+ # Retrieve all entries that have a given terminal before the dot.
167
+ # @param criteria [Hash{Symbol => String}]
168
+ def search_entries(atIndex, criteria)
169
+ entries = sets[atIndex].entries
170
+ keyword = criteria.keys[0]
171
+ found = []
172
+ entries.each do |e|
173
+ case keyword
174
+ when :before # terminal before dot
175
+ term_name = criteria[keyword]
176
+ if e.dotted_entry? && e.vertex.dotted_item.position > -2
177
+ found << e if e.prev_symbol&.name == criteria[keyword]
178
+ end
179
+ end
180
+ end
181
+
182
+ found
183
+ end
117
184
 
118
185
  # @ return [String] A human-readable representation of the chart.
119
186
  def to_s
@@ -121,17 +188,42 @@ module Rley # This module is used as a namespace
121
188
  sets.each_with_index do |a_set, i|
122
189
  result << "State[#{i}]\n"
123
190
  a_set.entries.each do |item|
124
- result << ' ' + item.to_s + "\n"
191
+ result << " #{item}\n"
125
192
  end
126
193
  end
127
-
194
+
128
195
  result
129
196
  end
130
197
 
131
198
  private
132
199
 
133
- def add_entry_set()
200
+ def add_entry_set
134
201
  @sets << ParseEntrySet.new
202
+ @constraints << []
203
+ end
204
+
205
+ def update_match_closest(aConstraint, anIndex)
206
+ # Locate in the chart the closest matching terminal...
207
+ i = anIndex - 1
208
+ loop do
209
+ first_entry = sets[i][0]
210
+ prev_symbol = first_entry.prev_symbol
211
+ break if prev_symbol.name == aConstraint.closest_symb
212
+ i -= 1
213
+ break if i < 0
214
+ end
215
+
216
+ # Retrieve all entries of the kind: closest_symb .
217
+ if i > 0
218
+ entries = sets[i].entries.select do |en|
219
+ if en.prev_symbol
220
+ en.prev_symbol.name == aConstraint.closest_symb
221
+ else
222
+ false
223
+ end
224
+ end
225
+ aConstraint.entries = entries
226
+ end
135
227
  end
136
228
  end # class
137
229
  end # module