dendroid 0.2.02 → 0.2.04

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.
@@ -1,17 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dendroid
4
+ # Namespace for all classes needed for implementing a generic parser.
5
+ # The goal is to take the output from the Earley recognizer (i.e. a chart object),
6
+ # visit it and build a data structure (a parse tree or a shared parse forest) that is much
7
+ # more convenient for subsequent processing (e.g. semantic analysis of a compiler/interpreter).
4
8
  module Parsing
9
+ # An abstract class (i.e. a generalization) for elements forming a parse tree (forest).
10
+ # A parse tree is a graph data structure that represents the parsed input text into a tree-like hierarchy
11
+ # of elements constructed by applying syntax rules of the language at hand.
12
+ # A parse forest is a data structure that merges a number parse trees into one graph.
13
+ # Contrary to parse trees, a parse forests can represent the results of an ambiguous parsing.
5
14
  class ParseNode
6
- # @return [Array<Integer>] The range of input tokens that match this node.
15
+ # @return [Range] The range of indexes of the input tokens that match this node.
7
16
  attr_reader :range
8
17
 
18
+ # @param lowerBound [Integer] Rank of first input token that is matched by this node
19
+ # @param upperBound [Integer] Rank of last input token that is matched by this node
9
20
  def initialize(lowerBound, upperBound)
10
21
  @range = valid_range(lowerBound, upperBound)
11
22
  end
12
23
 
13
- def to_s
14
- "[#{range[0]}, #{range[1]}]"
24
+ protected
25
+
26
+ def range_to_s
27
+ "[#{range}]"
15
28
  end
16
29
 
17
30
  private
@@ -19,7 +32,7 @@ module Dendroid
19
32
  def valid_range(lowerBound, upperBound)
20
33
  raise StandardError unless lowerBound.is_a?(Integer) && upperBound.is_a?(Integer)
21
34
 
22
- [lowerBound, upperBound]
35
+ lowerBound..upperBound
23
36
  end
24
37
  end # class
25
38
  end # module
@@ -45,7 +45,7 @@ class ParseTreeVisitor
45
45
  end
46
46
 
47
47
  # Visit event. The visitor is about to visit the given non terminal node.
48
- # @param aNonTerminalNode [ANDNode] the node to visit.
48
+ # @param aNonTerminalNode [AndNode] the node to visit.
49
49
  def visit_and_node(aNonTerminalNode)
50
50
  if @traversal == :post_order
51
51
  broadcast(:before_and_node, aNonTerminalNode)
@@ -4,6 +4,7 @@ require_relative 'parse_node'
4
4
 
5
5
  module Dendroid
6
6
  module Parsing
7
+ # A parse tree/forest node that is related to one input token.
7
8
  class TerminalNode < ParseNode
8
9
  # @return [Dendroid::Syntax::Terminal] Terminal symbol of matching token.
9
10
  attr_reader :symbol
@@ -23,9 +24,11 @@ module Dendroid
23
24
  aVisitor.visit_terminal(self)
24
25
  end
25
26
 
27
+ # Render a String representation of itself
28
+ # @return [String]
26
29
  def to_s
27
- display_val = token.is_a?(Dendroid::Lexical::Literal) ? ": #{token.value}" : ''
28
- "#{symbol.name}#{display_val} #{super}"
30
+ display_val = token.literal? ? ": #{token.value}" : ''
31
+ "#{symbol.name}#{display_val} #{range_to_s}"
29
32
  end
30
33
  end # class
31
34
  end # module
@@ -7,16 +7,30 @@ require_relative 'empty_rule_node'
7
7
 
8
8
  module Dendroid
9
9
  module Parsing
10
+ # This object holds the current state of the visit of a Chart by one
11
+ # ChartWalker through one single visit path. A path corresponds to a
12
+ # chain from the current item back to the initial item(s) through the predecessors links.
13
+ # It is used to construct (part of) the parse tree beginning from the root node.
10
14
  class WalkProgress
15
+ # @return [Symbol] One of: :New, :Running, :Waiting, :Complete, :Forking, :Delegating
11
16
  attr_accessor :state
17
+
18
+ # @return [Integer] rank of the item set from the chart being visited
12
19
  attr_accessor :curr_rank
20
+
21
+ # @return [Dendroid::Recognizer::EItem] the chart entry being visited
13
22
  attr_reader :curr_item
23
+
24
+ # When not nil, override the predecessors links of the current item
25
+ # @return [Dendroid::Recognizer::EItem|NilClass]
14
26
  attr_accessor :predecessor
15
- attr_reader :parents
16
27
 
17
- # rubocop: disable Metrics/CyclomaticComplexity
18
- # rubocop: disable Metrics/PerceivedComplexity
28
+ # @return [Array<Dendroid::Parsing::CompositeParseNode>] The ancestry of current parse node.
29
+ attr_reader :parents
19
30
 
31
+ # @param start_rank [Integer] Initial rank at the start of the visit
32
+ # @param start_item [Dendroid::Recognizer::EItem] Initial chart entry to visit
33
+ # @param parents [Array<Dendroid::Parsing::CompositeParseNode>]
20
34
  def initialize(start_rank, start_item, parents)
21
35
  @state = :New
22
36
  @curr_rank = start_rank
@@ -34,32 +48,46 @@ module Dendroid
34
48
  @parents = orig.parents.dup
35
49
  end
36
50
 
51
+ # Current item has multiple predecessors: set the state to Forking and
52
+ # force one of the predecessor to be the next entry to visit.
53
+ # @param thePredecessor [Dendroid::Recognizer::EItem]
37
54
  def fork(thePredecessor)
38
55
  @state = :Forking
39
56
  @predecessor = thePredecessor
40
57
  end
41
58
 
59
+ # Set the current entry being visited to the given one
60
+ # @param anEntry [Dendroid::Recognizer::EItem]
42
61
  def curr_item=(anEntry)
43
62
  raise StandardError if anEntry.nil?
44
63
 
45
64
  @curr_item = anEntry
46
65
  end
47
66
 
67
+ # Add a child leaf node for the given chart entry that corresponds
68
+ # to an empty rule.
69
+ # @param anEntry [Dendroid::Recognizer::EItem]
70
+ # @return [Dendroid::Parsing::EmptyRuleNode]
48
71
  def add_node_empty(anEntry)
49
72
  node_empty = EmptyRuleNode.new(anEntry, curr_rank)
50
73
  add_child_node(node_empty)
51
74
  end
52
75
 
53
- # Add a terminal node for terminal at current rank as a child of last parent
76
+ # Add a leaf terminal node for the token at current rank as a child of last parent.
77
+ # @param token [Dendroid::Lexical::Token]
78
+ # @return [Dendroid::Parsing::TerminalNode]
54
79
  def add_terminal_node(token)
55
80
  @curr_rank -= 1
56
81
  term_node = TerminalNode.new(curr_item.prev_symbol, token, curr_rank)
57
82
  add_child_node(term_node)
58
83
  end
59
84
 
60
- # Add an AND node for given entry as a child of last parent
85
+ # Make an AND node for the given entry as a child of last parent and
86
+ # push this node in the ancestry
87
+ # @param anEntry [Dendroid::Recognizer::EItem]
88
+ # @return [Dendroid::Parsing::AndNode]
61
89
  def push_and_node(anEntry)
62
- node = ANDNode.new(anEntry, curr_rank)
90
+ node = AndNode.new(anEntry, curr_rank)
63
91
  raise StandardError unless anEntry.rule == node.rule # Fails
64
92
 
65
93
  add_child_node(node)
@@ -68,6 +96,11 @@ module Dendroid
68
96
  node
69
97
  end
70
98
 
99
+ # Make an OR node as a child of last parent and
100
+ # push this node in the ancestry.
101
+ # @param origin [Integer] Start rank
102
+ # #param arity [Integer] The number of alternative derivations
103
+ # @return [Dendroid::Parsing::OrNode]
71
104
  def push_or_node(origin, arity)
72
105
  node = OrNode.new(curr_item.prev_symbol, origin, curr_rank, arity)
73
106
  add_child_node(node)
@@ -76,14 +109,22 @@ module Dendroid
76
109
  node
77
110
  end
78
111
 
112
+ # Add the given node as a child of the last parent node.
113
+ # @param aNode [Dendroid::Parsing::ParseNode]
114
+ # @return [Dendroid::Parsing::ParseNode]
79
115
  def add_child_node(aNode)
80
- parents.last.add_child(aNode, curr_item.position - 1)
116
+ parents.last.add_child(aNode, curr_item.position - 1) unless parents.empty?
81
117
  aNode
82
118
  end
83
119
 
120
+ # rubocop: disable Metrics/CyclomaticComplexity
121
+ # rubocop: disable Metrics/PerceivedComplexity
122
+
84
123
  # Do the given EItems match one of the parent?
85
124
  # Matching = corresponds to the same rule and range
86
- # @return [Array<EItem>]
125
+ # @param entries [Dendroid::Recognizer::EItem]
126
+ # @param stop_at_first [Boolean] Must be true
127
+ # @return [Array<Array<EItem, Integer>>]
87
128
  def match_parent?(entries, stop_at_first)
88
129
  matching = []
89
130
  min_origin = entries[0].origin
@@ -106,7 +147,7 @@ module Dendroid
106
147
  break if stop_at_first && !matching.empty?
107
148
 
108
149
  # Stop loop when parent.origin < min(entries.origin)
109
- break if node.range[0] < min_origin
150
+ break if node.range.begin < min_origin
110
151
 
111
152
  offset += 1
112
153
  end
@@ -8,6 +8,7 @@ module Dendroid
8
8
  # An Earley item is essentially a pair consisting of a dotted item and the rank of a token.
9
9
  # It helps to keep track the progress of an Earley recognizer.
10
10
  class EItem
11
+ # Mix-in module used to forward some method calls to the related dotted item.
11
12
  extend Forwardable
12
13
 
13
14
  # (Weak) reference to the dotted item
@@ -17,7 +18,8 @@ module Dendroid
17
18
  # @return [Integer] the rank of the token that correspond to the start of the rule.
18
19
  attr_reader :origin
19
20
 
20
- # TODO: :predictor, :completer, :scanner
21
+ # Specifies the algorithm with which this entry can be derived from its predecessor(s).
22
+ # @return [Symbol] of one: :predictor, :completer, :scanner
21
23
  attr_accessor :algo
22
24
 
23
25
  # @return [Array<WeakRef>] predecessors sorted by decreasing origin value
@@ -12,7 +12,7 @@ module Dendroid
12
12
  # @return [Array<Dendroid::Syntax::SymbolSeq>]
13
13
  attr_reader :alternatives
14
14
 
15
- # Create a Choice instance.
15
+ # Create a Rule instance.
16
16
  # @param theLhs [Dendroid::Syntax::NonTerminal] The left-hand side of the rule.
17
17
  # @param alt [Array<Dendroid::Syntax::SymbolSeq>] the alternatives (each as a sequence of symbols).
18
18
  def initialize(theLhs, alt)
data/lib/dendroid.rb CHANGED
@@ -6,10 +6,8 @@
6
6
  module Dendroid
7
7
  end # module
8
8
 
9
-
10
9
  # This file acts as a jumping-off point for loading dependencies expected
11
10
  # for a Dendroid client.
12
-
13
11
  require_relative './dendroid/grm_dsl/base_grm_builder'
14
12
  require_relative './dendroid/utils/base_tokenizer'
15
13
  require_relative './dendroid/recognizer/recognizer'
@@ -16,7 +16,11 @@ describe Dendroid::Lexical::Literal do
16
16
  expect { described_class.new(ex_source, ex_pos, ex_terminal, ex_value) }.not_to raise_error
17
17
  end
18
18
 
19
- it 'knows its value' do
19
+ it 'knows it is a token with a literal value attached to it' do
20
+ expect(subject).to be_literal
21
+ end
22
+
23
+ it 'knows its literal value' do
20
24
  expect(subject.value).to eq(ex_value)
21
25
  end
22
26
  end # context
@@ -27,5 +27,9 @@ describe Dendroid::Lexical::Token do
27
27
  it 'knows the terminal name' do
28
28
  expect(subject.terminal).to eq(ex_terminal)
29
29
  end
30
+
31
+ it 'has no literal value attached to it' do
32
+ expect(subject).not_to be_literal
33
+ end
30
34
  end # context
31
35
  end # describe
@@ -5,14 +5,6 @@ require_relative '../support/sample_grammars'
5
5
  require_relative '../../../lib/dendroid/recognizer/recognizer'
6
6
  require_relative '../../../lib/dendroid/parsing/chart_walker'
7
7
 
8
- # require_relative '../grm_dsl/base_grm_builder'
9
- # require_relative '../utils/base_tokenizer'
10
- # require_relative '../recognizer/recognizer'
11
- # require_relative 'chart_walker'
12
- # require_relative 'parse_tree_visitor'
13
- # require_relative '../formatters/bracket_notation'
14
- # require_relative '../formatters/ascii_tree'
15
-
16
8
  RSpec.describe Dendroid::Parsing::ChartWalker do
17
9
  include SampleGrammars
18
10
 
@@ -45,37 +37,37 @@ RSpec.describe Dendroid::Parsing::ChartWalker do
45
37
  walker = described_class.new(chart)
46
38
  root = walker.walk(success_entry(chart, recognizer))
47
39
 
48
- expect(root.to_s).to eq('p => s [0, 5]')
40
+ expect(root.to_s).to eq('p => s [0..5]')
49
41
  expect(root.children.size).to eq(1)
50
- expect(root.children[-1].to_s).to eq('s => s PLUS m [0, 5]')
42
+ expect(root.children[-1].to_s).to eq('s => s PLUS m [0..5]')
51
43
  plus_expr = root.children[-1]
52
44
  expect(plus_expr.children.size).to eq(3)
53
- expect(plus_expr.children[0].to_s).to eq('s => m [0, 1]')
54
- expect(plus_expr.children[1].to_s).to eq('PLUS [1, 2]')
55
- expect(plus_expr.children[2].to_s).to eq('m => m STAR t [2, 5]')
45
+ expect(plus_expr.children[0].to_s).to eq('s => m [0..1]')
46
+ expect(plus_expr.children[1].to_s).to eq('PLUS [1..2]')
47
+ expect(plus_expr.children[2].to_s).to eq('m => m STAR t [2..5]')
56
48
 
57
49
  operand_plus = plus_expr.children[0]
58
50
  expect(operand_plus.children.size).to eq(1)
59
- expect(operand_plus.children[0].to_s).to eq('m => t [0, 1]')
51
+ expect(operand_plus.children[0].to_s).to eq('m => t [0..1]')
60
52
  expect(operand_plus.children[0].children.size).to eq(1)
61
- expect(operand_plus.children[0].children[0].to_s).to eq('t => INTEGER [0, 1]')
62
- expect(operand_plus.children[0].children[0].children[0].to_s).to eq('INTEGER: 2 [0, 1]')
53
+ expect(operand_plus.children[0].children[0].to_s).to eq('t => INTEGER [0..1]')
54
+ expect(operand_plus.children[0].children[0].children[0].to_s).to eq('INTEGER: 2 [0..1]')
63
55
 
64
- expect(plus_expr.children[1].to_s).to eq('PLUS [1, 2]')
56
+ expect(plus_expr.children[1].to_s).to eq('PLUS [1..2]')
65
57
 
66
58
  star_expr = plus_expr.children[2]
67
59
  expect(star_expr.children.size).to eq(3)
68
- expect(star_expr.children[0].to_s).to eq('m => t [2, 3]')
69
- expect(star_expr.children[1].to_s).to eq('STAR [3, 4]')
70
- expect(star_expr.children[2].to_s).to eq('t => INTEGER [4, 5]')
60
+ expect(star_expr.children[0].to_s).to eq('m => t [2..3]')
61
+ expect(star_expr.children[1].to_s).to eq('STAR [3..4]')
62
+ expect(star_expr.children[2].to_s).to eq('t => INTEGER [4..5]')
71
63
 
72
64
  operand_star = star_expr.children[0]
73
65
  expect(operand_star.children.size).to eq(1)
74
- expect(operand_star.children[0].to_s).to eq('t => INTEGER [2, 3]')
75
- expect(operand_star.children[0].children[0].to_s).to eq('INTEGER: 3 [2, 3]')
66
+ expect(operand_star.children[0].to_s).to eq('t => INTEGER [2..3]')
67
+ expect(operand_star.children[0].children[0].to_s).to eq('INTEGER: 3 [2..3]')
76
68
 
77
69
  expect(star_expr.children[2].children.size).to eq(1)
78
- expect(star_expr.children[2].children[0].to_s).to eq('INTEGER: 4 [4, 5]')
70
+ expect(star_expr.children[2].children[0].to_s).to eq('INTEGER: 4 [4..5]')
79
71
  end
80
72
 
81
73
  it 'generates a parse tree for grammar l10 (with left recursive rule)' do
@@ -84,27 +76,27 @@ RSpec.describe Dendroid::Parsing::ChartWalker do
84
76
  walker = described_class.new(chart)
85
77
  root = walker.walk(success_entry(chart, recognizer))
86
78
 
87
- expect(root.to_s).to eq('A => A a [0, 5]')
79
+ expect(root.to_s).to eq('A => A a [0..5]')
88
80
  expect(root.children.size).to eq(2)
89
- expect(root.children[0].to_s).to eq('A => A a [0, 4]')
90
- expect(root.children[1].to_s).to eq('a [4, 5]')
81
+ expect(root.children[0].to_s).to eq('A => A a [0..4]')
82
+ expect(root.children[1].to_s).to eq('a [4..5]')
91
83
 
92
84
  expect(root.children[0].children.size).to eq(2)
93
- expect(root.children[0].children[0].to_s).to eq('A => A a [0, 3]')
94
- expect(root.children[0].children[1].to_s).to eq('a [3, 4]')
85
+ expect(root.children[0].children[0].to_s).to eq('A => A a [0..3]')
86
+ expect(root.children[0].children[1].to_s).to eq('a [3..4]')
95
87
 
96
88
  grand_child = root.children[0].children[0]
97
89
  expect(grand_child.children.size).to eq(2)
98
- expect(grand_child.children[0].to_s).to eq('A => A a [0, 2]')
99
- expect(grand_child.children[1].to_s).to eq('a [2, 3]')
90
+ expect(grand_child.children[0].to_s).to eq('A => A a [0..2]')
91
+ expect(grand_child.children[1].to_s).to eq('a [2..3]')
100
92
 
101
93
  expect(grand_child.children[0].children.size).to eq(2)
102
- expect(grand_child.children[0].children[0].to_s).to eq('A => A a [0, 1]')
103
- expect(grand_child.children[0].children[1].to_s).to eq('a [1, 2]')
94
+ expect(grand_child.children[0].children[0].to_s).to eq('A => A a [0..1]')
95
+ expect(grand_child.children[0].children[1].to_s).to eq('a [1..2]')
104
96
 
105
97
  expect(grand_child.children[0].children[0].children.size).to eq(2)
106
- expect(grand_child.children[0].children[0].children[0].to_s).to eq('_ [0, 0]')
107
- expect(grand_child.children[0].children[0].children[1].to_s).to eq('a [0, 1]')
98
+ expect(grand_child.children[0].children[0].children[0].to_s).to eq('_ [0..0]')
99
+ expect(grand_child.children[0].children[0].children[1].to_s).to eq('a [0..1]')
108
100
  end
109
101
 
110
102
  it 'generates a parse tree for grammar l11 (with right recursive rule)' do
@@ -113,27 +105,27 @@ RSpec.describe Dendroid::Parsing::ChartWalker do
113
105
  walker = described_class.new(chart)
114
106
  root = walker.walk(success_entry(chart, recognizer))
115
107
 
116
- expect(root.to_s).to eq('A => a A [0, 5]')
108
+ expect(root.to_s).to eq('A => a A [0..5]')
117
109
  expect(root.children.size).to eq(2)
118
- expect(root.children[0].to_s).to eq('a [0, 1]')
119
- expect(root.children[1].to_s).to eq('A => a A [1, 5]')
110
+ expect(root.children[0].to_s).to eq('a [0..1]')
111
+ expect(root.children[1].to_s).to eq('A => a A [1..5]')
120
112
 
121
113
  expect(root.children[1].children.size).to eq(2)
122
- expect(root.children[1].children[0].to_s).to eq('a [1, 2]')
123
- expect(root.children[1].children[1].to_s).to eq('A => a A [2, 5]')
114
+ expect(root.children[1].children[0].to_s).to eq('a [1..2]')
115
+ expect(root.children[1].children[1].to_s).to eq('A => a A [2..5]')
124
116
 
125
117
  grand_child = root.children[1].children[1]
126
118
  expect(grand_child.children.size).to eq(2)
127
- expect(grand_child.children[0].to_s).to eq('a [2, 3]')
128
- expect(grand_child.children[1].to_s).to eq('A => a A [3, 5]')
119
+ expect(grand_child.children[0].to_s).to eq('a [2..3]')
120
+ expect(grand_child.children[1].to_s).to eq('A => a A [3..5]')
129
121
 
130
122
  expect(grand_child.children[1].children.size).to eq(2)
131
- expect(grand_child.children[1].children[0].to_s).to eq('a [3, 4]')
132
- expect(grand_child.children[1].children[1].to_s).to eq('A => a A [4, 5]')
123
+ expect(grand_child.children[1].children[0].to_s).to eq('a [3..4]')
124
+ expect(grand_child.children[1].children[1].to_s).to eq('A => a A [4..5]')
133
125
 
134
126
  expect(grand_child.children[1].children[1].children.size).to eq(2)
135
- expect(grand_child.children[1].children[1].children[0].to_s).to eq('a [4, 5]')
136
- expect(grand_child.children[1].children[1].children[1].to_s).to eq('_ [5, 5]')
127
+ expect(grand_child.children[1].children[1].children[0].to_s).to eq('a [4..5]')
128
+ expect(grand_child.children[1].children[1].children[1].to_s).to eq('_ [5..5]')
137
129
  end
138
130
  end # context
139
131
 
@@ -144,78 +136,78 @@ RSpec.describe Dendroid::Parsing::ChartWalker do
144
136
  walker = described_class.new(chart)
145
137
  root = walker.walk(success_entry(chart, recognizer))
146
138
 
147
- expect(root.to_s).to eq('OR: S [0, 4]')
139
+ expect(root.to_s).to eq('OR: S [0..4]')
148
140
  expect(root.children.size).to eq(3)
149
141
  root.children.each do |child|
150
142
  expect(child.children.size).to eq(2)
151
- expect(child.to_s).to eq('S => S S [0, 4]')
143
+ expect(child.to_s).to eq('S => S S [0..4]')
152
144
  end
153
145
  (a, b, c) = root.children
154
146
 
155
147
  # Test structure of tree a
156
148
  (child_a_0, child_a_1) = a.children
157
- expect(child_a_0.to_s).to eq('S => S S [0, 2]')
158
- expect(child_a_1.to_s).to eq('S => S S [2, 4]')
149
+ expect(child_a_0.to_s).to eq('S => S S [0..2]')
150
+ expect(child_a_1.to_s).to eq('S => S S [2..4]')
159
151
  expect(child_a_0.children.size).to eq(2)
160
152
  (child_a_0_0, child_a_0_1) = child_a_0.children
161
- expect(child_a_0_0.to_s).to eq('S => x [0, 1]')
162
- expect(child_a_0_1.to_s).to eq('S => x [1, 2]')
163
- expect(child_a_0_0.children[0].to_s).to eq('x [0, 1]')
164
- expect(child_a_0_1.children[0].to_s).to eq('x [1, 2]')
153
+ expect(child_a_0_0.to_s).to eq('S => x [0..1]')
154
+ expect(child_a_0_1.to_s).to eq('S => x [1..2]')
155
+ expect(child_a_0_0.children[0].to_s).to eq('x [0..1]')
156
+ expect(child_a_0_1.children[0].to_s).to eq('x [1..2]')
165
157
 
166
158
  expect(child_a_1.children.size).to eq(2)
167
159
  (child_a_1_0, child_a_1_1) = child_a_1.children
168
- expect(child_a_1_0.to_s).to eq('S => x [2, 3]')
169
- expect(child_a_1_1.to_s).to eq('S => x [3, 4]')
170
- expect(child_a_1_0.children[0].to_s).to eq('x [2, 3]')
171
- expect(child_a_1_1.children[0].to_s).to eq('x [3, 4]')
160
+ expect(child_a_1_0.to_s).to eq('S => x [2..3]')
161
+ expect(child_a_1_1.to_s).to eq('S => x [3..4]')
162
+ expect(child_a_1_0.children[0].to_s).to eq('x [2..3]')
163
+ expect(child_a_1_1.children[0].to_s).to eq('x [3..4]')
172
164
 
173
165
  # Test structure of forest b
174
166
  (child_b_0, child_b_1) = b.children
175
- expect(child_b_0.to_s).to eq('OR: S [0, 3]')
176
- expect(child_b_1.to_s).to eq('S => x [3, 4]')
167
+ expect(child_b_0.to_s).to eq('OR: S [0..3]')
168
+ expect(child_b_1.to_s).to eq('S => x [3..4]')
177
169
  expect(child_b_1.equal?(child_a_1_1)).to be_truthy # Sharing
178
170
  expect(child_b_0.children.size).to eq(2)
179
171
  (child_b_0_0, child_b_0_1) = child_b_0.children
180
- expect(child_b_0_0.to_s).to eq('S => S S [0, 3]')
181
- expect(child_b_0_1.to_s).to eq('S => S S [0, 3]')
172
+ expect(child_b_0_0.to_s).to eq('S => S S [0..3]')
173
+ expect(child_b_0_1.to_s).to eq('S => S S [0..3]')
182
174
  expect(child_b_0_0.children.size).to eq(2)
183
175
  (child_b_0_0_0, child_b_0_0_1) = child_b_0_0.children
184
- expect(child_b_0_0_0.to_s).to eq('S => x [0, 1]')
176
+ expect(child_b_0_0_0.to_s).to eq('S => x [0..1]')
185
177
  expect(child_b_0_0_0.equal?(child_a_0_0)).to be_truthy # Sharing
186
- expect(child_b_0_0_1.to_s).to eq('S => S S [1, 3]')
178
+ expect(child_b_0_0_1.to_s).to eq('S => S S [1..3]')
187
179
  expect(child_b_0_0_1.children.size).to eq(2)
188
- expect(child_b_0_0_1.children[0].to_s).to eq('S => x [1, 2]')
180
+ expect(child_b_0_0_1.children[0].to_s).to eq('S => x [1..2]')
189
181
  expect(child_b_0_0_1.children[0].equal?(child_a_0_1)).to be_truthy # Sharing
190
- expect(child_b_0_0_1.children[1].to_s).to eq('S => x [2, 3]')
182
+ expect(child_b_0_0_1.children[1].to_s).to eq('S => x [2..3]')
191
183
  expect(child_b_0_0_1.children[1].equal?(child_a_1_0)).to be_truthy # Sharing
192
184
 
193
185
  expect(child_b_0_1.children.size).to eq(2)
194
186
  (child_b_0_1_0, child_b_0_1_1) = child_b_0_1.children
195
- expect(child_b_0_1_0.to_s).to eq('S => S S [0, 2]')
187
+ expect(child_b_0_1_0.to_s).to eq('S => S S [0..2]')
196
188
  expect(child_b_0_1_0.equal?(child_a_0)).to be_truthy # Sharing
197
- expect(child_b_0_1_1.to_s).to eq('S => x [2, 3]')
189
+ expect(child_b_0_1_1.to_s).to eq('S => x [2..3]')
198
190
  expect(child_b_0_1_1.equal?(child_a_1_0)).to be_truthy # Sharing
199
191
 
200
192
  # Test structure of forest c
201
193
  (child_c_0, child_c_1) = c.children
202
- expect(child_c_0.to_s).to eq('S => x [0, 1]')
194
+ expect(child_c_0.to_s).to eq('S => x [0..1]')
203
195
  expect(child_c_0.equal?(child_a_0_0)).to be_truthy # Sharing
204
- expect(child_c_1.to_s).to eq('OR: S [1, 4]')
196
+ expect(child_c_1.to_s).to eq('OR: S [1..4]')
205
197
  expect(child_c_1.children.size).to eq(2)
206
198
  (child_c_1_0, child_c_1_1) = child_c_1.children
207
- expect(child_c_1_0.to_s).to eq('S => S S [1, 4]')
208
- expect(child_c_1_1.to_s).to eq('S => S S [1, 4]')
199
+ expect(child_c_1_0.to_s).to eq('S => S S [1..4]')
200
+ expect(child_c_1_1.to_s).to eq('S => S S [1..4]')
209
201
  expect(child_c_1_0.children.size).to eq(2)
210
202
  (child_c_1_0_0, child_c_1_0_1) = child_c_1_0.children
211
- expect(child_c_1_0_0.to_s).to eq('S => x [1, 2]')
203
+ expect(child_c_1_0_0.to_s).to eq('S => x [1..2]')
212
204
  expect(child_c_1_0_0.equal?(child_a_0_1)).to be_truthy # Sharing
213
- expect(child_c_1_0_1.to_s).to eq('S => S S [2, 4]')
205
+ expect(child_c_1_0_1.to_s).to eq('S => S S [2..4]')
214
206
  expect(child_c_1_0_1.equal?(child_a_1)).to be_truthy # Sharing
215
207
  (child_c_1_1_0, child_c_1_1_1) = child_c_1_1.children
216
- expect(child_c_1_1_0.to_s).to eq('S => S S [1, 3]')
208
+ expect(child_c_1_1_0.to_s).to eq('S => S S [1..3]')
217
209
  expect(child_c_1_1_0.equal?(child_b_0_0_1)).to be_truthy # Sharing
218
- expect(child_c_1_1_1.to_s).to eq('S => x [3, 4]')
210
+ expect(child_c_1_1_1.to_s).to eq('S => x [3..4]')
219
211
  expect(child_c_1_1_1.equal?(child_b_1)).to be_truthy # Sharing
220
212
  end
221
213
 
@@ -225,24 +217,24 @@ RSpec.describe Dendroid::Parsing::ChartWalker do
225
217
  walker = described_class.new(chart)
226
218
  root = walker.walk(success_entry(chart, recognizer))
227
219
 
228
- expect(root.to_s).to eq('OR: S [0, 2]')
220
+ expect(root.to_s).to eq('OR: S [0..2]')
229
221
  expect(root.children.size).to eq(2)
230
222
  root.children.each do |ch|
231
- expect(ch.to_s).to eq('S => S T [0, 2]')
223
+ expect(ch.to_s).to eq('S => S T [0..2]')
232
224
  expect(ch.children.size).to eq(2)
233
225
  end
234
226
  (child_0_0, child_0_1) = root.children[0].children
235
- expect(child_0_0.to_s).to eq('S => a [0, 1]')
236
- expect(child_0_1.to_s).to eq('T => a B [1, 2]')
227
+ expect(child_0_0.to_s).to eq('S => a [0..1]')
228
+ expect(child_0_1.to_s).to eq('T => a B [1..2]')
237
229
  expect(child_0_1.children.size).to eq(2)
238
- expect(child_0_1.children[0].to_s).to eq('a [1, 2]')
239
- expect(child_0_1.children[1].to_s).to eq('_ [2, 2]')
230
+ expect(child_0_1.children[0].to_s).to eq('a [1..2]')
231
+ expect(child_0_1.children[1].to_s).to eq('_ [2..2]')
240
232
 
241
233
  (child_1_0, child_1_1) = root.children[1].children
242
- expect(child_1_0.to_s).to eq('S => a [0, 1]')
243
- expect(child_1_1.to_s).to eq('T => a [1, 2]')
234
+ expect(child_1_0.to_s).to eq('S => a [0..1]')
235
+ expect(child_1_1.to_s).to eq('T => a [1..2]')
244
236
  expect(child_1_0.equal?(child_0_0)).to be_truthy # Sharing
245
- expect(child_1_1.children[0].to_s).to eq('a [1, 2]')
237
+ expect(child_1_1.children[0].to_s).to eq('a [1..2]')
246
238
  expect(child_1_1.children[0].equal?(child_0_1.children[0])).to be_truthy # Sharing
247
239
  end
248
240
  end # context
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../spec_helper'
4
+
5
+ require_relative '../../../lib/dendroid/parsing/composite_parse_node'
6
+
7
+ RSpec.describe Dendroid::Parsing::CompositeParseNode do
8
+ let(:sample_range) { 3..5 }
9
+ let(:sample_child_count) { 7 }
10
+ subject { described_class.new(sample_range.begin, sample_range.end, sample_child_count) }
11
+
12
+ context 'Initialization:' do
13
+ it 'should be initialized with two token positions and a child count' do
14
+ expect { described_class.new(sample_range.begin, sample_range.end, sample_child_count) }.not_to raise_error
15
+ end
16
+
17
+ it 'has its children array filled with nils' do
18
+ # Check number of elements...
19
+ expect(subject.children.size).to eq(sample_child_count)
20
+
21
+ # All elements are nil
22
+ expect(subject.children.all?(&:nil?)).to be_truthy
23
+ end
24
+ end # context
25
+
26
+ context 'provided services:' do
27
+ it 'adds a child node at a specific slot' do
28
+ child3 = Dendroid::Parsing::ParseNode.new(3, 4)
29
+ child5 = Dendroid::Parsing::ParseNode.new(5, 6)
30
+ subject.add_child(child3, 3)
31
+ subject.add_child(child5, 5)
32
+ expect(subject.children.reject(&:nil?).size).to eq(2)
33
+ expect(subject.children[3]).to eq(child3)
34
+ expect(subject.children[5]).to eq(child5)
35
+ end
36
+ end # context
37
+ end # describe
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../spec_helper'
4
+ require_relative '../../../lib/dendroid/grm_dsl/base_grm_builder'
5
+ require_relative '../../../lib/dendroid/grm_analysis/dotted_item'
6
+ require_relative '../../../lib/dendroid/recognizer/e_item'
7
+ require_relative '../../../lib/dendroid/parsing/empty_rule_node'
8
+
9
+ RSpec.describe Dendroid::Parsing::EmptyRuleNode do
10
+ let(:sample_rule) { sample_grammar.rules[0] }
11
+ let(:sample_dotted_item) { Dendroid::GrmAnalysis::DottedItem.new(sample_rule, 0, 1) }
12
+ let(:sample_entry) { Dendroid::Recognizer::EItem.new(sample_dotted_item, 5) }
13
+
14
+ subject { described_class.new(sample_entry, 5) }
15
+
16
+ let(:sample_grammar) do
17
+ builder = Dendroid::GrmDSL::BaseGrmBuilder.new do
18
+ declare_terminals('INTEGER')
19
+
20
+ rule('s' => ['INTEGER', ''])
21
+ end
22
+
23
+ builder.grammar
24
+ end
25
+
26
+ context 'Initialization:' do
27
+ it 'should be initialized with a chart entry and an end rank number' do
28
+ expect { described_class.new(sample_entry, 5) }.not_to raise_error
29
+ end
30
+
31
+ it 'knows the rule it is related to' do
32
+ expect(subject.rule).to eq(sample_grammar.rules[0])
33
+ end
34
+
35
+ it 'knows the alternative index it is related to' do
36
+ expect(subject.alt_index).to eq(1)
37
+ end
38
+
39
+ it 'knows its origin and end positions' do
40
+ expect(subject.range.begin).to eq(subject.range.end)
41
+ expect(subject.range.begin).to eq(5)
42
+ end
43
+ end # context
44
+
45
+ context 'Provided services:' do
46
+ it 'renders a String representation of its range' do
47
+ expect(subject.to_s).to eq('_ [5..5]')
48
+ end
49
+ end # context
50
+ end # describe