dendroid 0.1.00 → 0.2.00

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/lib/dendroid/formatters/ascii_tree.rb +142 -0
  4. data/lib/dendroid/formatters/base_formatter.rb +25 -0
  5. data/lib/dendroid/formatters/bracket_notation.rb +50 -0
  6. data/lib/dendroid/grm_analysis/dotted_item.rb +46 -30
  7. data/lib/dendroid/grm_analysis/grm_analyzer.rb +2 -4
  8. data/lib/dendroid/grm_analysis/{choice_items.rb → rule_items.rb} +10 -10
  9. data/lib/dendroid/grm_dsl/base_grm_builder.rb +3 -4
  10. data/lib/dendroid/parsing/and_node.rb +56 -0
  11. data/lib/dendroid/parsing/chart_walker.rb +293 -0
  12. data/lib/dendroid/parsing/composite_parse_node.rb +21 -0
  13. data/lib/dendroid/parsing/empty_rule_node.rb +28 -0
  14. data/lib/dendroid/parsing/or_node.rb +51 -0
  15. data/lib/dendroid/parsing/parse_node.rb +26 -0
  16. data/lib/dendroid/parsing/parse_tree_visitor.rb +127 -0
  17. data/lib/dendroid/parsing/parser.rb +185 -0
  18. data/lib/dendroid/parsing/terminal_node.rb +32 -0
  19. data/lib/dendroid/parsing/walk_progress.rb +117 -0
  20. data/lib/dendroid/recognizer/chart.rb +8 -0
  21. data/lib/dendroid/recognizer/e_item.rb +21 -2
  22. data/lib/dendroid/recognizer/item_set.rb +7 -2
  23. data/lib/dendroid/recognizer/recognizer.rb +33 -20
  24. data/lib/dendroid/syntax/grammar.rb +1 -1
  25. data/lib/dendroid/syntax/rule.rb +71 -13
  26. data/spec/dendroid/grm_analysis/dotted_item_spec.rb +59 -47
  27. data/spec/dendroid/grm_analysis/{choice_items_spec.rb → rule_items_spec.rb} +5 -6
  28. data/spec/dendroid/parsing/chart_walker_spec.rb +223 -0
  29. data/spec/dendroid/parsing/terminal_node_spec.rb +36 -0
  30. data/spec/dendroid/recognizer/e_item_spec.rb +5 -5
  31. data/spec/dendroid/recognizer/item_set_spec.rb +16 -8
  32. data/spec/dendroid/recognizer/recognizer_spec.rb +57 -5
  33. data/spec/dendroid/support/sample_grammars.rb +2 -0
  34. data/spec/dendroid/syntax/grammar_spec.rb +16 -21
  35. data/spec/dendroid/syntax/rule_spec.rb +56 -7
  36. data/version.txt +1 -1
  37. metadata +20 -13
  38. data/lib/dendroid/grm_analysis/alternative_item.rb +0 -70
  39. data/lib/dendroid/grm_analysis/production_items.rb +0 -55
  40. data/lib/dendroid/syntax/choice.rb +0 -95
  41. data/lib/dendroid/syntax/production.rb +0 -82
  42. data/spec/dendroid/grm_analysis/alternative_item_spec.rb +0 -12
  43. data/spec/dendroid/grm_analysis/production_items_spec.rb +0 -68
  44. data/spec/dendroid/syntax/choice_spec.rb +0 -68
  45. data/spec/dendroid/syntax/production_spec.rb +0 -92
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 722b27a6f20e87c43de339b3f0c45e2bcc77c464d5dd9ecd56bbb686c4857b61
4
- data.tar.gz: ce6ffd0c100ea7b7c336044e2877617eebb85c7bb306d5de9d1d4395200320aa
3
+ metadata.gz: bb2bba5cca25fb6a95bfa820891edb4882595062be6a3a439a0fee8c2515efb1
4
+ data.tar.gz: 52643afdeded2e2944e93124267c009d6c496ab6d645d4878087072f24685e67
5
5
  SHA512:
6
- metadata.gz: 69870ade1f77e7fe0b9faf20b7943a500abdf2b41d383a4e048438e431e6f65bf4b418806ec4ba325a6839ee4eb1337085772fe7fa5c594b59663cd653cdeac6
7
- data.tar.gz: 137bbf46a71dcb603f3866f51b5be25a8557fb574937e2e1b3f8b40980cd6e7fd6a851c84b229b7b7ecb692d2899d721e3bcc041fe09e213318f46efdb041ea7
6
+ metadata.gz: 47db9f266723dc8d292bdc8cbb7006bd8e9da8471bd89e933312231bf26ba90e5c40e7bf3e6eead665918a968f476705936507b66b753debde18391a4fa6936a
7
+ data.tar.gz: 2fdc6b316b2f250644818af70ea40bf68c263776fcbb751ac2b05e03555194a3d86344f28574d3d6efd1a303f0d89c619f7ac199d57468c5e8eddff9f397f123
data/CHANGELOG.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.2.00] - 2023-12-16
6
+ Version bump: Very crude parser implementation (generate shared parse forests in case of ambiguity).
7
+
5
8
  ## [0.1.00] - 2023-11-03
6
9
  Version bump: the Earley recognizer is functional.
7
10
 
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_formatter'
4
+
5
+ # A formatter class that draws parse trees by using characters
6
+ class Asciitree < BaseFormatter
7
+ # TODO
8
+ attr_reader(:curr_path)
9
+
10
+ # For each node in curr_path, there is a corresponding string value.
11
+ # Allowed string values are: 'first', 'last', 'first_and_last', 'other'
12
+ attr_reader(:ranks)
13
+
14
+ # @return [String] The character pattern used for rendering
15
+ # a parent - child nesting
16
+ attr_reader(:and_nesting_prefix)
17
+
18
+ # TODO: comment
19
+ attr_reader(:or_nesting_prefix)
20
+
21
+ # @return [String] The character pattern used for a blank indentation
22
+ attr_reader(:blank_indent)
23
+
24
+ # @return [String] The character pattern for indentation and nesting
25
+ # continuation.
26
+ attr_reader(:continuation_indent)
27
+
28
+ # Constructor.
29
+ # @param anIO [IO] The output stream to which the parse tree
30
+ # is written.
31
+ def initialize(anIO)
32
+ super(anIO)
33
+ @curr_path = []
34
+ @ranks = []
35
+
36
+ @and_nesting_prefix = '+-- '
37
+ @or_nesting_prefix = '/-- '
38
+ @blank_indent = ' '
39
+ @continuation_indent = '| '
40
+ end
41
+
42
+ # Method called by a ParseTreeVisitor to which the formatter subscribed.
43
+ # Notification of a visit event: the visitor is about to visit
44
+ # the children of a non-terminal node
45
+ # @param parent [NonTerminalNode]
46
+ # @param _children [Array<ParseTreeNode>] array of children nodes
47
+ def before_subnodes(parent, _children)
48
+ rank_of(parent)
49
+ curr_path << parent
50
+ end
51
+
52
+ # Method called by a ParseTreeVisitor to which the formatter subscribed.
53
+ # Notification of a visit event: the visitor is about to visit
54
+ # a non-terminal node
55
+ # @param aNonTerm [NonTerminalNode]
56
+ def before_and_node(aNonTerm)
57
+ emit_and(aNonTerm)
58
+ end
59
+
60
+ def before_or_node(aNonTerm)
61
+ emit_or(aNonTerm)
62
+ end
63
+
64
+ def before_empty_rule_node(anEmptyRuleNode)
65
+ emit_and(anEmptyRuleNode, ': .')
66
+ end
67
+
68
+ # Method called by a ParseTreeVisitor to which the formatter subscribed.
69
+ # Notification of a visit event: the visitor is about to visit
70
+ # a terminal node
71
+ # @param aTerm [TerminalNode]
72
+ def before_terminal(aTerm)
73
+ emit_terminal(aTerm, ": '#{aTerm.token.source}'")
74
+ end
75
+
76
+ # Method called by a ParseTreeVisitor to which the formatter subscribed.
77
+ # Notification of a visit event: the visitor completed the visit of
78
+ # the children of a non-terminal node.
79
+ # @param _parent [NonTerminalNode]
80
+ # @param _children [Array] array of children nodes
81
+ def after_subnodes(_parent, _children)
82
+ curr_path.pop
83
+ ranks.pop
84
+ end
85
+
86
+ private
87
+
88
+ # Parent node is last node in current path
89
+ # or current path is empty (then aChild is root node)
90
+ def rank_of(aChild)
91
+ if curr_path.empty?
92
+ rank = 'root'
93
+ elsif curr_path[-1].children.size == 1
94
+ rank = 'first_and_last'
95
+ else
96
+ parent = curr_path[-1]
97
+ siblings = parent.children
98
+ siblings_last_index = siblings.size - 1
99
+ rank = case siblings.find_index(aChild)
100
+ when 0 then 'first'
101
+ when siblings_last_index then 'last'
102
+ else
103
+ 'other'
104
+ end
105
+ end
106
+ ranks << rank
107
+ end
108
+
109
+ # 'root', 'first', 'first_and_last', 'last', 'other'
110
+ def path_prefix(connector)
111
+ return '' if ranks.empty?
112
+
113
+ prefix = +''
114
+ @ranks.each_with_index do |rank, i|
115
+ next if i.zero?
116
+
117
+ case rank
118
+ when 'first', 'other'
119
+ prefix << continuation_indent
120
+
121
+ when 'last', 'first_and_last', 'root'
122
+ prefix << blank_indent
123
+ end
124
+ end
125
+
126
+ nesting = (connector == :and) ? and_nesting_prefix : or_nesting_prefix
127
+ prefix << nesting
128
+ prefix
129
+ end
130
+
131
+ def emit_and(aNode, aSuffix = '')
132
+ output.puts("#{path_prefix(:and)}#{aNode.rule.lhs.name}#{aSuffix}")
133
+ end
134
+
135
+ def emit_or(aNode, aSuffix = '')
136
+ output.puts("#{path_prefix(:or)}OR #{aNode.symbol.name}#{aSuffix}")
137
+ end
138
+
139
+ def emit_terminal(aNode, aSuffix = '')
140
+ output.puts("#{path_prefix(:and)}#{aNode.symbol.name}#{aSuffix}")
141
+ end
142
+ end # class
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+
4
+ # Superclass for parse tree formatters.
5
+ class BaseFormatter
6
+ # The IO output stream in which the formatter's result will be sent.
7
+ # @return [IO] The output stream for the formatter.
8
+ attr_reader(:output)
9
+
10
+ # Constructor.
11
+ # @param anIO [IO] an output IO where the formatter's result will
12
+ # be placed.
13
+ def initialize(anIO)
14
+ @output = anIO
15
+ end
16
+
17
+ # Given a parse tree visitor, perform the visit
18
+ # and render the visit events in the output stream.
19
+ # @param aVisitor [ParseTreeVisitor]
20
+ def render(aVisitor)
21
+ aVisitor.subscribe(self)
22
+ aVisitor.start
23
+ aVisitor.unsubscribe(self)
24
+ end
25
+ end # class
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_formatter'
4
+
5
+ class BracketNotation < BaseFormatter
6
+ # Method called by a ParseTreeVisitor to which the formatter subscribed.
7
+ # Notification of a visit event: the visitor is about to visit
8
+ # a non-terminal node
9
+ # @param and_node [ANDNode]
10
+ def before_and_node(and_node)
11
+ write("[#{and_node.rule.lhs.name} ")
12
+ end
13
+
14
+ def before_empty_rule_node(anEmptyRuleNode)
15
+ write("[#{anEmptyRuleNode.rule.lhs.name}]")
16
+ end
17
+
18
+ # Method called by a ParseTreeVisitor to which the formatter subscribed.
19
+ # Notification of a visit event: the visitor is about to visit
20
+ # a terminal node
21
+ # @param aTerm [TerminalNode]
22
+ def before_terminal(aTerm)
23
+ write("[#{aTerm.symbol.name} ")
24
+ end
25
+
26
+ # Method called by a ParseTreeVisitor to which the formatter subscribed.
27
+ # Notification of a visit event: the visitor completed the visit of
28
+ # a terminal node.
29
+ # @param aTerm [TerminalNode]
30
+ def after_terminal(aTerm)
31
+ # Escape all opening and closing square brackets
32
+ escape_lbrackets = aTerm.token.source.gsub(/\[/, '\[')
33
+ escaped = escape_lbrackets.gsub(/\]/, '\]')
34
+ write("#{escaped}]")
35
+ end
36
+
37
+ # Method called by a ParseTreeVisitor to which the formatter subscribed.
38
+ # Notification of a visit event: the visitor completed the visit of
39
+ # a non-terminal node
40
+ # @param _nonterm [NonTerminalNode]
41
+ def after_and_node(_nonterm)
42
+ write(']')
43
+ end
44
+
45
+ private
46
+
47
+ def write(aText)
48
+ output.write(aText)
49
+ end
50
+ end # class
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'weakref'
4
+
3
5
  module Dendroid
4
- # This module contains classes that from the analysis of grammar rules help to build objects
5
- # needed by a recognizer or a parser for the language.
6
6
  module GrmAnalysis
7
7
  # For a given production rule, a dotted item represents a recognition state.
8
8
  # The dot partitions the rhs of the rule in two parts:
@@ -20,42 +20,40 @@ module Dendroid
20
20
  # An item with a dot in front of a terminal is called a shift item.
21
21
  # An item with the dot not at the beginning is sometimes referred to as a kernel item
22
22
  class DottedItem
23
- # Reference to the production rule
23
+ # (Weak) reference to the production rule
24
24
  # @return [Dendroid::Syntax::Production]
25
25
  attr_reader :rule
26
26
 
27
27
  # @return [Integer] the dot position
28
28
  attr_reader :position
29
29
 
30
+ # @return [Integer] the alternative number
31
+ attr_reader :alt_index
32
+
30
33
  # Constructor.
31
- # @param aRule [Dendroid::Syntax::Rule]
34
+ # @param aChoice [Dendroid::Syntax::Rule]
32
35
  # @param aPosition [Integer] Position of the dot in rhs of production.
33
- def initialize(aRule, aPosition)
34
- @rule = aRule
36
+ # @param index [Integer] the rank of the alternative at hand
37
+ def initialize(aChoice, aPosition, index)
38
+ @alt_index = index
39
+ @rule = WeakRef.new(aChoice)
35
40
  @position = valid_position(aPosition)
36
41
  end
37
42
 
38
- # Return a String representation of the dotted item.
43
+ # Return a String representation of the alternative item.
39
44
  # @return [String]
40
45
  def to_s
41
- rhs_names = rule.body.map(&:to_s)
46
+ rhs_names = rule.alternatives[alt_index].members.map(&:to_s)
42
47
  dotted_rhs = rhs_names.insert(position, '.')
43
48
  "#{rule.head} => #{dotted_rhs.join(' ')}"
44
49
  end
45
50
 
46
- # Indicate whether the rhs of the rule is empty
51
+ alias inspect to_s
52
+
53
+ # Indicate whether the rhs of the alternative is empty
47
54
  # @return [Boolean]
48
55
  def empty?
49
- rule.empty?
50
- end
51
-
52
- # Terminology inspired from Luger's book
53
- # @return [Symbol] one of: :initial, :initial_and_completed, :partial, :completed
54
- def state
55
- return :initial_and_completed if empty?
56
- return :initial if position.zero?
57
-
58
- position == rule.body.size ? :completed : :partial
56
+ rule.alternatives[alt_index].empty?
59
57
  end
60
58
 
61
59
  # Indicate whether the dot is at the start of rhs
@@ -64,28 +62,46 @@ module Dendroid
64
62
  position.zero? || empty?
65
63
  end
66
64
 
67
- # Indicate whether the dot is at the end of rhs
65
+ # Indicate the dot isn't at start nor at end position
66
+ # @return [Boolean]
67
+ def intermediate_pos?
68
+ return false if empty? || position.zero?
69
+
70
+ position < rule.alternatives[alt_index].size
71
+ end
72
+
73
+ # Indicate whether the dot is at the start of rhs
68
74
  # @return [Boolean]
69
75
  def final_pos?
70
- empty? || position == rule.body.size
76
+ empty? || position == rule.alternatives[alt_index].size
71
77
  end
72
78
 
73
79
  alias completed? final_pos?
74
80
 
75
- # Indicate the dot isn't at start nor at end position
76
- # @return [Boolean]
77
- def intermediate_pos?
78
- return false if empty? || position.zero?
81
+ # Terminology inspired from Luger's book
82
+ # @return [Symbol] one of: :initial, :initial_and_completed, :partial, :completed
83
+ def state
84
+ return :initial_and_completed if empty?
85
+ return :initial if position.zero?
79
86
 
80
- position < rule.body.size
87
+ position == rule.alternatives[alt_index].size ? :completed : :partial
81
88
  end
82
89
 
90
+
83
91
  # Return the symbol right after the dot (if any)
84
92
  # @return [Dendroid::Syntax::GrmSymbol, NilClass]
85
93
  def next_symbol
86
94
  return nil if empty? || completed?
87
95
 
88
- rule.body[position]
96
+ rule.alternatives[alt_index].members[position]
97
+ end
98
+
99
+ # Return the symbol right before the dot (if any)
100
+ # @return [Dendroid::Syntax::GrmSymbol, NilClass]
101
+ def prev_symbol
102
+ return nil if empty? || position.zero?
103
+
104
+ rule.alternatives[alt_index].members[position - 1]
89
105
  end
90
106
 
91
107
  # Check whether the given symbol is the same as after the dot.
@@ -99,7 +115,7 @@ module Dendroid
99
115
  end
100
116
 
101
117
  # Check whether the dotted item is a shift item.
102
- # In other words, it expects a terminal to be next symbol
118
+ # In other words, it expects a terminal to be the next symbol
103
119
  # @return [Boolean]
104
120
  def pre_scan?
105
121
  next_symbol&.terminal?
@@ -112,13 +128,13 @@ module Dendroid
112
128
  def ==(other)
113
129
  return true if eql?(other)
114
130
 
115
- (position == other.position) && rule.eql?(other.rule)
131
+ (position == other.position) && rule.eql?(other.rule) && (alt_index == other.alt_index)
116
132
  end
117
133
 
118
134
  private
119
135
 
120
136
  def valid_position(aPosition)
121
- raise StandardError if aPosition.negative? || aPosition > rule.body.size
137
+ raise StandardError if aPosition.negative? || aPosition > rule.alternatives[alt_index].size
122
138
 
123
139
  aPosition
124
140
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../grm_analysis/production_items'
4
- require_relative '../grm_analysis/choice_items'
3
+ require_relative '../grm_analysis/rule_items.rb'
5
4
 
6
5
  module Dendroid
7
6
  module GrmAnalysis
@@ -62,8 +61,7 @@ module Dendroid
62
61
 
63
62
  def build_dotted_items
64
63
  grammar.rules.each do |prod|
65
- mixin = prod.choice? ? ChoiceItems : ProductionItems
66
- prod.extend(mixin)
64
+ prod.extend(RuleItems)
67
65
  prod.build_items
68
66
  rule_items = prod.items.flatten
69
67
  items.concat(rule_items)
@@ -1,24 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'alternative_item'
3
+ require_relative 'dotted_item.rb'
4
4
 
5
5
  module Dendroid
6
6
  module GrmAnalysis
7
7
  # Mix-in module for extending the Syntax::Choice class
8
8
  # with dotted items manipulation methods
9
- module ChoiceItems
9
+ module RuleItems
10
10
  # Build the alternative items for this choice and assign them
11
11
  # to the `items` attributes
12
- # @return [Array<Array<GrmAnalysis::AlternativeItem>>]
12
+ # @return [Array<Array<GrmAnalysis::Dottedtem>>]
13
13
  def build_items
14
14
  # AlternativeItem
15
15
  @items = Array.new(alternatives.size) { |_| [] }
16
16
  alternatives.each_with_index do |alt_seq, index|
17
17
  if alt_seq.empty?
18
- @items[index] << AlternativeItem.new(self, 0, index)
18
+ @items[index] << DottedItem.new(self, 0, index)
19
19
  else
20
20
  (0..alt_seq.size).each do |pos|
21
- @items[index] << AlternativeItem.new(self, pos, index)
21
+ @items[index] << DottedItem.new(self, pos, index)
22
22
  end
23
23
  end
24
24
  end
@@ -26,29 +26,29 @@ module Dendroid
26
26
 
27
27
  # Read accessor for the `items` attribute.
28
28
  # Return the dotted items for this production
29
- # @return [Array<Array<GrmAnalysis::AlternativeItem>>]
29
+ # @return [Array<Array<GrmAnalysis::Dottedtem>>]
30
30
  def items
31
31
  @items
32
32
  end
33
33
 
34
34
  # Return the predicted items (i.e. the alternative items with the dot at start)
35
35
  # for this choice.
36
- # @return [Array<GrmAnalysis::AlternativeItem>]
36
+ # @return [Array<GrmAnalysis::Dottedtem>]
37
37
  def predicted_items
38
38
  @items.map(&:first)
39
39
  end
40
40
 
41
41
  # Return the reduce items (i.e. the alternative items with the dot at end)
42
42
  # for this choice.
43
- # @return [Array<GrmAnalysis::AlternativeItem>]
43
+ # @return [Array<GrmAnalysis::Dottedtem>]
44
44
  def reduce_items
45
45
  @items.map(&:last)
46
46
  end
47
47
 
48
48
  # Return the next item given the provided item.
49
49
  # In other words, advance the dot by one position.
50
- # @param anItem [GrmAnalysis::AlternativeItem]
51
- # @return [GrmAnalysis::AlternativeItem|NilClass]
50
+ # @param anItem [GrmAnalysis::Dottedtem]
51
+ # @return [GrmAnalysis::Dottedtem|NilClass]
52
52
  def next_item(anItem)
53
53
  items_arr = items[anItem.alt_index]
54
54
  return nil if anItem == items_arr.last
@@ -3,8 +3,7 @@
3
3
  require_relative '..\syntax\terminal'
4
4
  require_relative '..\syntax\non_terminal'
5
5
  require_relative '..\syntax\symbol_seq'
6
- require_relative '..\syntax\production'
7
- require_relative '..\syntax\choice'
6
+ require_relative '..\syntax\rule'
8
7
  require_relative '..\syntax\grammar'
9
8
 
10
9
  module Dendroid
@@ -90,10 +89,10 @@ module Dendroid
90
89
  raw_rhs = productionRuleRepr.values.first
91
90
 
92
91
  if raw_rhs.is_a? String
93
- new_prod = Dendroid::Syntax::Production.new(lhs, build_symbol_seq(raw_rhs))
92
+ new_prod = Dendroid::Syntax::Rule.new(lhs, [build_symbol_seq(raw_rhs)])
94
93
  else
95
94
  rhs = raw_rhs.map { |raw| build_symbol_seq(raw) }
96
- new_prod = Dendroid::Syntax::Choice.new(lhs, rhs)
95
+ new_prod = Dendroid::Syntax::Rule.new(lhs, rhs)
97
96
  end
98
97
  rules << new_prod
99
98
  new_prod
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'composite_parse_node'
4
+
5
+ module Dendroid
6
+ module Parsing
7
+ class ANDNode < CompositeParseNode
8
+ attr_reader :rule
9
+ attr_reader :alt_index
10
+
11
+ def initialize(anEItem, rank)
12
+ @rule = WeakRef.new(anEItem.dotted_item.rule)
13
+ @alt_index = anEItem.dotted_item.alt_index
14
+ upper_bound = rank
15
+ super(anEItem.origin, upper_bound, rule.rhs[alt_index].size)
16
+ end
17
+
18
+ def add_child(child_node, index)
19
+ if children[index].nil? # Is slot available?
20
+ super(child_node, index)
21
+ else
22
+ raise StandardError
23
+ end
24
+ end
25
+
26
+ def match(anEItem)
27
+ return false if range[0] != anEItem.origin
28
+
29
+ dotted = anEItem.dotted_item
30
+ same_rule = (rule.lhs == dotted.rule.lhs) && (alt_index == dotted.alt_index)
31
+ return false unless same_rule
32
+
33
+ dotted.initial_pos? ? true : partial?
34
+ end
35
+
36
+ def expecting?(symbol, position)
37
+ symb_seq = rule.rhs[alt_index]
38
+ symb_seq[position] == symbol
39
+ end
40
+
41
+ def partial?
42
+ children.any?(&:nil?)
43
+ end
44
+
45
+ def to_s
46
+ "#{rule.lhs} => #{rule.rhs[alt_index]} #{range}"
47
+ end
48
+
49
+ # Part of the 'visitee' role in Visitor design pattern.
50
+ # @param aVisitor[ParseTreeVisitor] the visitor
51
+ def accept(aVisitor)
52
+ aVisitor.visit_and_node(self)
53
+ end
54
+ end # class
55
+ end # module
56
+ end # module