rley 0.6.07 → 0.6.08
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/examples/NLP/nano_eng/nano_en_demo.rb +32 -15
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/engine.rb +47 -18
- data/lib/rley/formatter/debug.rb +21 -71
- data/lib/rley/parse_forest_visitor.rb +112 -28
- data/lib/rley/parse_rep/parse_rep_creator.rb +4 -2
- data/lib/rley/parse_tree_visitor.rb +3 -3
- data/lib/rley/ptree/parse_tree.rb +3 -3
- data/lib/rley/sppf/alternative_node.rb +6 -0
- data/lib/rley/sppf/composite_node.rb +2 -0
- data/lib/rley/sppf/epsilon_node.rb +6 -0
- data/lib/rley/sppf/non_terminal_node.rb +9 -3
- data/lib/rley/sppf/parse_forest.rb +12 -1
- data/lib/rley/sppf/token_node.rb +6 -0
- data/spec/rley/engine_spec.rb +32 -6
- data/spec/rley/formatter/debug_spec.rb +126 -13
- data/spec/rley/parse_forest_visitor_spec.rb +357 -129
- data/spec/rley/parse_rep/parse_forest_factory_spec.rb +0 -9
- data/spec/rley/parse_tree_visitor_spec.rb +2 -1
- metadata +2 -2
@@ -5,7 +5,8 @@ module Rley # This module is used as a namespace
|
|
5
5
|
# Utility class that helps to create a representation of a parse from
|
6
6
|
# a given Parsing object.
|
7
7
|
class ParseRepCreator
|
8
|
-
# @return [GFGParsing]
|
8
|
+
# @return [Rley::Parser::GFGParsing]
|
9
|
+
# Link to Parsing object (= results of recognizer)
|
9
10
|
attr_reader(:parsing)
|
10
11
|
|
11
12
|
# Constructor. Creates and initialize a ParseRepCreator instance.
|
@@ -15,7 +16,8 @@ module Rley # This module is used as a namespace
|
|
15
16
|
end
|
16
17
|
|
17
18
|
# Factory method that produces the representation of the parse.
|
18
|
-
# @return [ParseTree]
|
19
|
+
# @return [Rley::PTree::ParseTree, Rley::SPPF::ParseForest]
|
20
|
+
# The parse representation.
|
19
21
|
def create(aBuilder = nil)
|
20
22
|
a_walker = walker(parsing)
|
21
23
|
a_builder = builder(parsing, aBuilder)
|
@@ -97,9 +97,9 @@ module Rley # This module is used as a namespace
|
|
97
97
|
# @param msg [Symbol] event to notify
|
98
98
|
# @param args [Array] arguments of the notification.
|
99
99
|
def broadcast(msg, *args)
|
100
|
-
subscribers.each do |
|
101
|
-
next unless
|
102
|
-
|
100
|
+
subscribers.each do |subscr|
|
101
|
+
next unless subscr.respond_to?(msg) || subscr.respond_to?(:accept_all)
|
102
|
+
subscr.send(msg, *args)
|
103
103
|
end
|
104
104
|
end
|
105
105
|
end # class
|
@@ -20,9 +20,9 @@ module Rley # This module is used as a namespace
|
|
20
20
|
@root = theRootNode
|
21
21
|
end
|
22
22
|
|
23
|
-
#
|
24
|
-
# This method can be overriden
|
25
|
-
def done!
|
23
|
+
# Notification from the builder telling that the parse tree construction
|
24
|
+
# is over. This method can be overriden.
|
25
|
+
def done!
|
26
26
|
@root.done!
|
27
27
|
end
|
28
28
|
|
@@ -28,6 +28,12 @@ module Rley # This module is used as a namespace
|
|
28
28
|
def to_string(indentation)
|
29
29
|
return "Alt(#{label})#{range.to_string(indentation)}"
|
30
30
|
end
|
31
|
+
|
32
|
+
# Part of the 'visitee' role in Visitor design pattern.
|
33
|
+
# @param aVisitor[ParseTreeVisitor] the visitor
|
34
|
+
def accept(aVisitor)
|
35
|
+
aVisitor.visit_alternative(self)
|
36
|
+
end
|
31
37
|
end # class
|
32
38
|
end # module
|
33
39
|
end # module
|
@@ -17,6 +17,12 @@ module Rley # This module is used as a namespace
|
|
17
17
|
def to_string(indentation)
|
18
18
|
return "_#{range.to_string(indentation)}"
|
19
19
|
end
|
20
|
+
|
21
|
+
# Part of the 'visitee' role in Visitor design pattern.
|
22
|
+
# @param aVisitor[ParseTreeVisitor] the visitor
|
23
|
+
def accept(aVisitor)
|
24
|
+
aVisitor.visit_epsilon(self)
|
25
|
+
end
|
20
26
|
end # class
|
21
27
|
end # module
|
22
28
|
end # module
|
@@ -14,7 +14,7 @@ module Rley # This module is used as a namespace
|
|
14
14
|
|
15
15
|
# Constructor
|
16
16
|
# @param aNonTerminal [Syntax::NonTerminal]
|
17
|
-
# @param aRange [Lexical::TokenRange]
|
17
|
+
# @param aRange [Lexical::TokenRange]
|
18
18
|
def initialize(aNonTerminal, aRange)
|
19
19
|
super(aRange)
|
20
20
|
@symbol = aNonTerminal
|
@@ -22,7 +22,7 @@ module Rley # This module is used as a namespace
|
|
22
22
|
end
|
23
23
|
|
24
24
|
# Add a sub-node (child) to this one.
|
25
|
-
# @param aSubnode [SPPFNode]
|
25
|
+
# @param aSubnode [SPPFNode]
|
26
26
|
def add_subnode(aSubnode)
|
27
27
|
if refinement == :or
|
28
28
|
subnodes << aSubnode
|
@@ -33,10 +33,16 @@ module Rley # This module is used as a namespace
|
|
33
33
|
|
34
34
|
# Emit a (formatted) string representation of the node.
|
35
35
|
# Mainly used for diagnosis/debugging purposes.
|
36
|
-
# @return [String] a text representation of the node.
|
36
|
+
# @return [String] a text representation of the node.
|
37
37
|
def to_string(indentation)
|
38
38
|
return "#{symbol.name}#{range.to_string(indentation)}"
|
39
39
|
end
|
40
|
+
|
41
|
+
# Part of the 'visitee' role in Visitor design pattern.
|
42
|
+
# @param aVisitor[ParseTreeVisitor] the visitor
|
43
|
+
def accept(aVisitor)
|
44
|
+
aVisitor.visit_nonterminal(self)
|
45
|
+
end
|
40
46
|
end # class
|
41
47
|
end # module
|
42
48
|
end # module
|
@@ -30,7 +30,7 @@ module Rley # This module is used as a namespace
|
|
30
30
|
end
|
31
31
|
|
32
32
|
# Notification that the SPPF construction is over
|
33
|
-
def done!
|
33
|
+
def done!
|
34
34
|
end
|
35
35
|
|
36
36
|
# Returns true if the given node is present in the forest.
|
@@ -43,6 +43,17 @@ module Rley # This module is used as a namespace
|
|
43
43
|
def ambiguous?()
|
44
44
|
return @is_ambiguous
|
45
45
|
end
|
46
|
+
|
47
|
+
# Create an Enumerator that helps to iterate over the possible parse trees.
|
48
|
+
# That enumerator will generate a parse tree when called with `next` method.
|
49
|
+
# @return [Enumerator]
|
50
|
+
def to_ptree_enum()
|
51
|
+
# How to implement?
|
52
|
+
# One visits the forest => beware of dependency
|
53
|
+
# At each visited item create a corresponding tree node.
|
54
|
+
# At end of visit & stack not empty
|
55
|
+
# Re-generate another ptree
|
56
|
+
end
|
46
57
|
|
47
58
|
# Part of the 'visitee' role in the Visitor design pattern.
|
48
59
|
# A visitee is expected to accept the visit from a visitor object
|
data/lib/rley/sppf/token_node.rb
CHANGED
@@ -25,6 +25,12 @@ module Rley # This module is used as a namespace
|
|
25
25
|
def to_string(indentation)
|
26
26
|
return "#{token.terminal.name}#{range.to_string(indentation)}"
|
27
27
|
end
|
28
|
+
|
29
|
+
# Part of the 'visitee' role in Visitor design pattern.
|
30
|
+
# @param aVisitor[ParseTreeVisitor] the visitor
|
31
|
+
def accept(aVisitor)
|
32
|
+
aVisitor.visit_terminal(self)
|
33
|
+
end
|
28
34
|
end # class
|
29
35
|
end # module
|
30
36
|
end # module
|
data/spec/rley/engine_spec.rb
CHANGED
@@ -3,7 +3,6 @@ require_relative '../spec_helper'
|
|
3
3
|
|
4
4
|
require_relative '../../lib/rley/lexical/token'
|
5
5
|
require_relative '../../lib/rley/parse_rep/cst_builder'
|
6
|
-
require_relative '../../lib/rley/parse_tree_visitor'
|
7
6
|
|
8
7
|
# Load the class under test
|
9
8
|
require_relative '../../lib/rley/engine'
|
@@ -93,10 +92,10 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
93
92
|
add_sample_grammar(instance)
|
94
93
|
instance
|
95
94
|
end
|
96
|
-
|
95
|
+
|
97
96
|
let(:sample_tokenizer) do
|
98
97
|
sample_text = 'a a b c c'
|
99
|
-
ABCTokenizer.new(sample_text)
|
98
|
+
ABCTokenizer.new(sample_text)
|
100
99
|
end
|
101
100
|
|
102
101
|
it 'should build default parse trees' do
|
@@ -107,18 +106,45 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
107
106
|
|
108
107
|
it 'should build custom parse trees' do
|
109
108
|
# Cheating: we point to default tree builder (CST)
|
110
|
-
subject.configuration.repr_builder = ParseRep::CSTBuilder
|
109
|
+
subject.configuration.repr_builder = ParseRep::CSTBuilder
|
111
110
|
raw_result = subject.parse(sample_tokenizer)
|
112
111
|
ptree = subject.convert(raw_result)
|
113
112
|
expect(ptree).to be_kind_of(PTree::ParseTree)
|
114
113
|
end
|
115
|
-
|
114
|
+
|
116
115
|
it 'should provide a parse visitor' do
|
117
116
|
raw_result = subject.parse(sample_tokenizer)
|
118
117
|
ptree = subject.to_ptree(raw_result)
|
119
118
|
visitor = subject.ptree_visitor(ptree)
|
120
119
|
expect(visitor).to be_kind_of(ParseTreeVisitor)
|
121
|
-
end
|
120
|
+
end
|
121
|
+
end # context
|
122
|
+
|
123
|
+
context 'Parse forest manipulation:' do
|
124
|
+
subject do
|
125
|
+
instance = Engine.new
|
126
|
+
add_sample_grammar(instance)
|
127
|
+
instance
|
128
|
+
end
|
129
|
+
|
130
|
+
let(:sample_tokenizer) do
|
131
|
+
sample_text = 'a a b c c'
|
132
|
+
ABCTokenizer.new(sample_text)
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'should build parse forest' do
|
136
|
+
raw_result = subject.parse(sample_tokenizer)
|
137
|
+
pforest = subject.to_pforest(raw_result)
|
138
|
+
expect(pforest).to be_kind_of(SPPF::ParseForest)
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'should provide a parse visitor' do
|
142
|
+
raw_result = subject.parse(sample_tokenizer)
|
143
|
+
ptree = subject.to_pforest(raw_result)
|
144
|
+
visitor = subject.pforest_visitor(ptree)
|
145
|
+
expect(visitor).to be_kind_of(ParseForestVisitor)
|
146
|
+
end
|
147
|
+
|
122
148
|
end # context
|
123
149
|
end # describe
|
124
150
|
end # module
|
@@ -1,19 +1,20 @@
|
|
1
1
|
require_relative '../../spec_helper'
|
2
2
|
require 'stringio'
|
3
3
|
|
4
|
-
require_relative '../support/grammar_abc_helper'
|
5
4
|
require_relative '../../../lib/rley/lexical/token'
|
6
|
-
require_relative '../../../lib/rley/parser/gfg_earley_parser'
|
7
|
-
require_relative '../../../lib/rley/ptree/parse_tree'
|
8
|
-
require_relative '../../../lib/rley/parse_tree_visitor'
|
9
5
|
require_relative '../../../lib/rley/engine'
|
6
|
+
require_relative '../support/grammar_abc_helper'
|
7
|
+
require_relative '../support/grammar_sppf_helper'
|
8
|
+
|
10
9
|
# Load the class under test
|
11
10
|
require_relative '../../../lib/rley/formatter/debug'
|
12
11
|
|
13
12
|
module Rley # Re-open the module to get rid of qualified names
|
14
13
|
module Formatter
|
15
14
|
describe Debug do
|
16
|
-
|
15
|
+
include GrammarSPPFHelper # Mix-in module with builder for grammar sppf
|
16
|
+
|
17
|
+
# Factory method. Build a production with the given sequence
|
17
18
|
# of symbols as its rhs.
|
18
19
|
let(:grammar_abc) do
|
19
20
|
sandbox = Object.new
|
@@ -21,18 +22,27 @@ module Rley # Re-open the module to get rid of qualified names
|
|
21
22
|
builder = sandbox.grammar_abc_builder
|
22
23
|
builder.grammar
|
23
24
|
end
|
24
|
-
|
25
|
+
|
25
26
|
# Variables for the terminal symbols
|
26
27
|
let(:a_) { grammar_abc.name2symbol['a'] }
|
27
28
|
let(:b_) { grammar_abc.name2symbol['b'] }
|
28
29
|
let(:c_) { grammar_abc.name2symbol['c'] }
|
29
|
-
|
30
|
+
|
30
31
|
# Helper method that mimicks the output of a tokenizer
|
31
32
|
# for the language specified by grammar_abc
|
32
33
|
let(:grm_abc_tokens1) do
|
33
34
|
%w[a a b c c].map { |ch| Lexical::Token.new(ch, ch) }
|
34
35
|
end
|
35
36
|
|
37
|
+
let(:grammar_sppf) do
|
38
|
+
builder = grammar_sppf_builder
|
39
|
+
builder.grammar
|
40
|
+
end
|
41
|
+
|
42
|
+
let(:sample_tokens) do
|
43
|
+
%w[a b b b].map { |ch| Lexical::Token.new(ch, ch) }
|
44
|
+
end
|
45
|
+
|
36
46
|
# Factory method that builds a sample parse tree.
|
37
47
|
# Generated tree has the following structure:
|
38
48
|
# S[0,5]
|
@@ -47,30 +57,43 @@ module Rley # Re-open the module to get rid of qualified names
|
|
47
57
|
# Capital letters represent non-terminal nodes
|
48
58
|
let(:grm_abc_ptree1) do
|
49
59
|
engine = Rley::Engine.new
|
50
|
-
engine.use_grammar(grammar_abc)
|
60
|
+
engine.use_grammar(grammar_abc)
|
51
61
|
parse_result = engine.parse(grm_abc_tokens1)
|
52
62
|
ptree = engine.convert(parse_result)
|
53
63
|
ptree
|
54
64
|
end
|
55
|
-
|
65
|
+
|
66
|
+
# Factory method that builds a sample parse forest.
|
67
|
+
# Generated forest has the following structure:
|
68
|
+
let(:grm_sppf_pforest1) do
|
69
|
+
engine = Rley::Engine.new
|
70
|
+
engine.use_grammar(grammar_sppf)
|
71
|
+
parse_result = engine.parse(sample_tokens)
|
72
|
+
engine.to_pforest(parse_result)
|
73
|
+
end
|
74
|
+
|
56
75
|
let(:destination) { StringIO.new('', 'w') }
|
57
76
|
|
58
77
|
context 'Standard creation & initialization:' do
|
59
78
|
it 'should be initialized with an IO argument' do
|
60
79
|
expect { Debug.new(StringIO.new('', 'w')) }.not_to raise_error
|
61
80
|
end
|
62
|
-
|
81
|
+
|
63
82
|
it 'should know its output destination' do
|
64
83
|
instance = Debug.new(destination)
|
65
84
|
expect(instance.output).to eq(destination)
|
66
85
|
end
|
86
|
+
|
87
|
+
it 'should have a zero indentation' do
|
88
|
+
instance = Debug.new(destination)
|
89
|
+
expect(instance.indentation).to be_zero
|
90
|
+
end
|
67
91
|
end # context
|
68
|
-
|
69
92
|
|
70
|
-
|
71
|
-
context 'Formatting events:' do
|
93
|
+
context 'Formatting events:' do
|
72
94
|
it 'should support visit events of a parse tree' do
|
73
95
|
instance = Debug.new(destination)
|
96
|
+
expect(instance.output).to eq(destination)
|
74
97
|
visitor = Rley::ParseTreeVisitor.new(grm_abc_ptree1)
|
75
98
|
instance.render(visitor)
|
76
99
|
expectations = <<-SNIPPET
|
@@ -102,6 +125,96 @@ before_ptree
|
|
102
125
|
after_subnodes
|
103
126
|
after_non_terminal
|
104
127
|
after_ptree
|
128
|
+
SNIPPET
|
129
|
+
expect(destination.string).to eq(expectations)
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'should support visit events of a parse forest' do
|
133
|
+
instance = Debug.new(destination)
|
134
|
+
expect(instance.output).to eq(destination)
|
135
|
+
visitor = Rley::ParseForestVisitor.new(grm_sppf_pforest1)
|
136
|
+
instance.render(visitor)
|
137
|
+
expectations = <<-SNIPPET
|
138
|
+
before_pforest
|
139
|
+
before_non_terminal
|
140
|
+
before_subnodes
|
141
|
+
before_non_terminal
|
142
|
+
before_subnodes
|
143
|
+
before_alternative
|
144
|
+
before_subnodes
|
145
|
+
before_terminal
|
146
|
+
after_terminal
|
147
|
+
before_non_terminal
|
148
|
+
before_subnodes
|
149
|
+
before_terminal
|
150
|
+
after_terminal
|
151
|
+
before_terminal
|
152
|
+
after_terminal
|
153
|
+
before_terminal
|
154
|
+
after_terminal
|
155
|
+
after_subnodes
|
156
|
+
after_non_terminal
|
157
|
+
after_subnodes
|
158
|
+
after_alternative
|
159
|
+
before_alternative
|
160
|
+
before_subnodes
|
161
|
+
before_non_terminal
|
162
|
+
before_subnodes
|
163
|
+
before_alternative
|
164
|
+
before_subnodes
|
165
|
+
before_terminal
|
166
|
+
after_terminal
|
167
|
+
after_subnodes
|
168
|
+
after_alternative
|
169
|
+
before_alternative
|
170
|
+
before_subnodes
|
171
|
+
before_non_terminal
|
172
|
+
before_subnodes
|
173
|
+
before_epsilon
|
174
|
+
after_epsilon
|
175
|
+
after_subnodes
|
176
|
+
after_non_terminal
|
177
|
+
before_non_terminal
|
178
|
+
before_subnodes
|
179
|
+
before_alternative
|
180
|
+
before_subnodes
|
181
|
+
before_terminal
|
182
|
+
after_terminal
|
183
|
+
after_subnodes
|
184
|
+
after_alternative
|
185
|
+
before_alternative
|
186
|
+
before_subnodes
|
187
|
+
before_non_terminal
|
188
|
+
before_subnodes
|
189
|
+
before_epsilon
|
190
|
+
after_epsilon
|
191
|
+
after_subnodes
|
192
|
+
after_non_terminal
|
193
|
+
after_subnodes
|
194
|
+
after_alternative
|
195
|
+
after_subnodes
|
196
|
+
after_non_terminal
|
197
|
+
after_subnodes
|
198
|
+
after_alternative
|
199
|
+
after_subnodes
|
200
|
+
after_non_terminal
|
201
|
+
before_non_terminal
|
202
|
+
before_subnodes
|
203
|
+
before_terminal
|
204
|
+
after_terminal
|
205
|
+
before_terminal
|
206
|
+
after_terminal
|
207
|
+
before_terminal
|
208
|
+
after_terminal
|
209
|
+
after_subnodes
|
210
|
+
after_non_terminal
|
211
|
+
after_subnodes
|
212
|
+
after_alternative
|
213
|
+
after_subnodes
|
214
|
+
after_non_terminal
|
215
|
+
after_subnodes
|
216
|
+
after_non_terminal
|
217
|
+
after_pforest
|
105
218
|
SNIPPET
|
106
219
|
expect(destination.string).to eq(expectations)
|
107
220
|
end
|
@@ -4,8 +4,10 @@ require_relative './support/grammar_helper'
|
|
4
4
|
require_relative './support/grammar_sppf_helper'
|
5
5
|
require_relative '../../lib/rley/lexical/token'
|
6
6
|
require_relative '../../lib/rley/parser/gfg_earley_parser'
|
7
|
+
require_relative '../../lib/rley/parse_rep/parse_forest_factory'
|
7
8
|
require_relative '../../lib/rley/sppf/non_terminal_node'
|
8
9
|
require_relative '../../lib/rley/sppf/parse_forest'
|
10
|
+
require_relative '../../lib/rley/formatter/debug'
|
9
11
|
|
10
12
|
# Load the class under test
|
11
13
|
require_relative '../../lib/rley/parse_forest_visitor'
|
@@ -31,7 +33,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
31
33
|
end
|
32
34
|
|
33
35
|
# A forest with just a root node
|
34
|
-
let(:
|
36
|
+
let(:forest_root) do
|
35
37
|
parser = Parser::GFGEarleyParser.new(grammar_sppf)
|
36
38
|
parse_result = parser.parse(sample_tokens)
|
37
39
|
accepting_entry = parse_result.accepting_entry
|
@@ -40,37 +42,27 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
40
42
|
Rley::SPPF::ParseForest.new(root_node)
|
41
43
|
end
|
42
44
|
|
43
|
-
=begin
|
44
45
|
# Factory method that builds a sample parse forest.
|
45
46
|
# Generated forest has the following structure:
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
# | +- A[2,3]
|
52
|
-
# | | +- b[2,3]
|
53
|
-
# | +- c[3,4]
|
54
|
-
# +- c[4,5]
|
55
|
-
# Capital letters represent non-terminal nodes
|
56
|
-
let(:grm_abc_pforest1) do
|
57
|
-
parser = Parser::EarleyParser.new(grammar_abc)
|
58
|
-
parse_result = parser.parse(grm_abc_tokens1)
|
59
|
-
parse_result.parse_forest
|
47
|
+
let(:grm_sppf_pforest1) do
|
48
|
+
parser = Parser::GFGEarleyParser.new(grammar_sppf)
|
49
|
+
parse_result = parser.parse(sample_tokens)
|
50
|
+
factory = ParseRep::ParseForestFactory.new(parse_result)
|
51
|
+
factory.create
|
60
52
|
end
|
61
|
-
|
53
|
+
|
62
54
|
|
63
55
|
# Default instantiation rule
|
64
|
-
subject { ParseForestVisitor.new(
|
56
|
+
subject { ParseForestVisitor.new(forest_root) }
|
65
57
|
|
66
58
|
|
67
59
|
context 'Standard creation & initialization:' do
|
68
60
|
it 'should be initialized with a parse forest argument' do
|
69
|
-
expect { ParseForestVisitor.new(
|
61
|
+
expect { ParseForestVisitor.new(forest_root) }.not_to raise_error
|
70
62
|
end
|
71
63
|
|
72
64
|
it 'should know the parse forest to visit' do
|
73
|
-
expect(subject.pforest).to eq(
|
65
|
+
expect(subject.pforest).to eq(forest_root)
|
74
66
|
end
|
75
67
|
|
76
68
|
it "shouldn't have subscribers at start" do
|
@@ -104,134 +96,370 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
104
96
|
end
|
105
97
|
end # context
|
106
98
|
|
99
|
+
class EventDispatcher
|
100
|
+
# return [Array<Proc>]
|
101
|
+
attr_accessor(:expectations)
|
102
|
+
attr_reader(:event_count)
|
107
103
|
|
108
|
-
|
109
|
-
|
110
|
-
let(:listener1) { double('fake-subscriber1') }
|
111
|
-
let(:listener2) { double('fake-subscriber2') }
|
112
|
-
|
113
|
-
it 'should react to the start_visit_pforest message' do
|
114
|
-
subject.subscribe(listener1)
|
115
|
-
|
116
|
-
# Notify subscribers when start the visit of the pforest
|
117
|
-
expect(listener1).to receive(:before_pforest).with(rooted_forest)
|
118
|
-
subject.start_visit_pforest(rooted_forest)
|
119
|
-
end
|
120
|
-
=begin
|
121
|
-
# Default instantiation rule
|
122
|
-
subject do
|
123
|
-
instance = ParseForestVisitor.new(grm_abc_pforest1)
|
124
|
-
instance.subscribe(listener1)
|
125
|
-
instance
|
104
|
+
def initialize
|
105
|
+
@event_count = 0
|
126
106
|
end
|
127
107
|
|
128
|
-
|
129
|
-
|
130
|
-
# Sample non-terminal node
|
131
|
-
let(:nterm_node) do
|
132
|
-
first_big_a = grm_abc_pforest1.root.children[0]
|
133
|
-
second_big_a = first_big_a.children[1]
|
134
|
-
second_big_a.children[1]
|
108
|
+
def accept_all
|
109
|
+
true
|
135
110
|
end
|
136
111
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
112
|
+
def method_missing(mth, *args)
|
113
|
+
if expectations.at(event_count)
|
114
|
+
if mth =~ /_subnodes$/
|
115
|
+
parent = args[0]
|
116
|
+
children = args[1]
|
117
|
+
expectations[event_count].call(mth, parent, children)
|
118
|
+
else
|
119
|
+
actual_item = args[0]
|
120
|
+
expectations[event_count].call(mth, actual_item)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
@event_count += 1
|
144
124
|
end
|
125
|
+
end # class
|
145
126
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
127
|
+
context 'Notifying visit events:' do
|
128
|
+
# expectations [Array<Array<Symbol, String>>]
|
129
|
+
def check_event(actual_event, actual_item, expectations)
|
130
|
+
(event, item) = expectations
|
131
|
+
expect(actual_event).to eq(event)
|
132
|
+
case item
|
133
|
+
when String
|
134
|
+
expect(actual_item.to_string(0)).to eq(item)
|
135
|
+
end
|
150
136
|
end
|
151
137
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
expect(listener1).to receive(:before_children).with(*args)
|
157
|
-
expect(listener1).to receive(:before_terminal).with(children[0])
|
158
|
-
expect(listener1).to receive(:after_terminal).with(children[0])
|
159
|
-
expect(listener1).to receive(:after_children).with(nterm_node, children)
|
160
|
-
subject.send(:traverse_children, nterm_node)
|
138
|
+
def check_legs(expectations)
|
139
|
+
(parent, path_signature) = subject.legs[-1]
|
140
|
+
expect(parent.to_string(0)).to eq(expectations[0])
|
141
|
+
expect(path_signature).to eq(expectations[1])
|
161
142
|
end
|
162
143
|
|
163
|
-
|
164
|
-
|
165
|
-
expect(
|
166
|
-
subject.end_visit_nonterminal(nterm_node)
|
144
|
+
def check_node_accesses(node, paths)
|
145
|
+
actual_paths = subject.node_accesses.fetch(node)
|
146
|
+
expect(actual_paths).to eq(paths)
|
167
147
|
end
|
168
148
|
|
169
|
-
|
170
|
-
# Notify subscribers when start & ending the visit of a terminal node
|
171
|
-
expect(listener1).to receive(:before_terminal).with(term_node)
|
172
|
-
expect(listener1).to receive(:after_terminal).with(term_node)
|
173
|
-
subject.visit_terminal(term_node)
|
174
|
-
end
|
149
|
+
let(:checker) { EventDispatcher.new }
|
175
150
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
151
|
+
# Default instantiation rule
|
152
|
+
subject do
|
153
|
+
instance = ParseForestVisitor.new(grm_sppf_pforest1)
|
154
|
+
instance.subscribe(checker)
|
155
|
+
instance
|
180
156
|
end
|
181
157
|
|
182
|
-
it 'should
|
183
|
-
#
|
184
|
-
#
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
158
|
+
it 'should react to the start_visit_pforest message' do
|
159
|
+
# Notify subscribers when start the visit of the pforest
|
160
|
+
# expect(listener1).to receive(:before_pforest).with(forest_root)
|
161
|
+
checker.expectations = [
|
162
|
+
->(event, item) {
|
163
|
+
check_event(event, item, [:before_pforest, grm_sppf_pforest1])
|
164
|
+
},
|
165
|
+
->(event, item) {
|
166
|
+
check_event(event, item, [:before_non_terminal, 'Phi[0, 4]'])
|
167
|
+
},
|
168
|
+
->(event, parent, children) {
|
169
|
+
check_event(event, parent, [:before_subnodes, 'Phi[0, 4]'])
|
170
|
+
expect(children.size).to eq(1)
|
171
|
+
},
|
172
|
+
->(event, item) {
|
173
|
+
check_event(event, item, [:before_non_terminal, 'S[0, 4]'])
|
174
|
+
check_legs(['S[0, 4]', 2]) # 2
|
175
|
+
check_node_accesses(item, [2])
|
176
|
+
},
|
177
|
+
->(event, parent, children) {
|
178
|
+
check_event(event, parent, [:before_subnodes, 'S[0, 4]'])
|
179
|
+
expect(children.size).to eq(2)
|
180
|
+
},
|
181
|
+
->(event, item) {
|
182
|
+
check_event(event, item, [:before_alternative, 'Alt(S => a T .)[0, 4]'])
|
183
|
+
check_legs(['Alt(S => a T .)[0, 4]', 6]) # 2 * 3
|
184
|
+
check_node_accesses(item, [6])
|
185
|
+
},
|
186
|
+
->(event, parent, children) {
|
187
|
+
check_event(event, parent, [:before_subnodes, 'Alt(S => a T .)[0, 4]'])
|
188
|
+
expect(children.size).to eq(2)
|
189
|
+
},
|
190
|
+
->(event, item) {
|
191
|
+
check_event(event, item, [:before_terminal, 'a[0, 1]'])
|
192
|
+
},
|
193
|
+
->(event, item) {
|
194
|
+
check_event(event, item, [:after_terminal, 'a[0, 1]'])
|
195
|
+
},
|
196
|
+
->(event, item) {
|
197
|
+
check_event(event, item, [:before_non_terminal, 'T[1, 4]'])
|
198
|
+
check_legs(['T[1, 4]', 66]) # 2 * 3 * 11
|
199
|
+
check_node_accesses(item, [66])
|
200
|
+
},
|
201
|
+
->(event, parent, children) {
|
202
|
+
check_event(event, parent, [:before_subnodes, 'T[1, 4]'])
|
203
|
+
expect(children.size).to eq(3)
|
204
|
+
},
|
205
|
+
->(event, item) {
|
206
|
+
check_event(event, item, [:before_terminal, 'b[1, 2]'])
|
207
|
+
},
|
208
|
+
->(event, item) {
|
209
|
+
check_event(event, item, [:after_terminal, 'b[1, 2]'])
|
210
|
+
},
|
211
|
+
->(event, item) {
|
212
|
+
check_event(event, item, [:before_terminal, 'b[2, 3]'])
|
213
|
+
},
|
214
|
+
->(event, item) {
|
215
|
+
check_event(event, item, [:after_terminal, 'b[2, 3]'])
|
216
|
+
},
|
217
|
+
->(event, item) {
|
218
|
+
check_event(event, item, [:before_terminal, 'b[3, 4]'])
|
219
|
+
},
|
220
|
+
->(event, item) {
|
221
|
+
check_event(event, item, [:after_terminal, 'b[3, 4]'])
|
222
|
+
},
|
223
|
+
->(event, parent, children) {
|
224
|
+
check_event(event, parent, [:after_subnodes, 'T[1, 4]'])
|
225
|
+
},
|
226
|
+
->(event, item) {
|
227
|
+
check_event(event, item, [:after_non_terminal, 'T[1, 4]'])
|
228
|
+
},
|
229
|
+
->(event, parent, children) {
|
230
|
+
check_event(event, parent, [:after_subnodes, 'Alt(S => a T .)[0, 4]'])
|
231
|
+
expect(children.size).to eq(2)
|
232
|
+
check_legs(['Alt(S => a T .)[0, 4]', 6]) # 2 * 3
|
233
|
+
},
|
234
|
+
->(event, item) {
|
235
|
+
check_event(event, item, [:after_alternative, 'Alt(S => a T .)[0, 4]'])
|
236
|
+
},
|
237
|
+
->(event, item) {
|
238
|
+
check_event(event, item, [:before_alternative, 'Alt(S => A T .)[0, 4]'])
|
239
|
+
check_legs(['Alt(S => A T .)[0, 4]', 10]) # 2 * 5
|
240
|
+
check_node_accesses(item, [10])
|
241
|
+
},
|
242
|
+
->(event, parent, children) {
|
243
|
+
check_event(event, parent, [:before_subnodes, 'Alt(S => A T .)[0, 4]'])
|
244
|
+
expect(children.size).to eq(2)
|
245
|
+
},
|
246
|
+
->(event, item) {
|
247
|
+
check_event(event, item, [:before_non_terminal, 'A[0, 1]'])
|
248
|
+
check_legs(['A[0, 1]', 230]) # 2 * 5 * 23
|
249
|
+
check_node_accesses(item, [230])
|
250
|
+
},
|
251
|
+
->(event, parent, children) {
|
252
|
+
check_event(event, parent, [:before_subnodes, 'A[0, 1]'])
|
253
|
+
expect(children.size).to eq(2)
|
254
|
+
},
|
255
|
+
->(event, item) {
|
256
|
+
check_event(event, item, [:before_alternative, 'Alt(A => a .)[0, 1]'])
|
257
|
+
check_legs(['Alt(A => a .)[0, 1]', 7130]) # 2 * 5 * 23 * 31
|
258
|
+
check_node_accesses(item, [7130])
|
259
|
+
# p(subject.legs)
|
260
|
+
},
|
261
|
+
->(event, parent, children) {
|
262
|
+
check_event(event, parent, [:before_subnodes, 'Alt(A => a .)[0, 1]'])
|
263
|
+
expect(children.size).to eq(1)
|
264
|
+
},
|
265
|
+
->(event, item) {
|
266
|
+
check_event(event, item, [:before_terminal, 'a[0, 1]'])
|
267
|
+
},
|
268
|
+
->(event, item) {
|
269
|
+
check_event(event, item, [:after_terminal, 'a[0, 1]'])
|
270
|
+
},
|
271
|
+
->(event, parent, children) {
|
272
|
+
check_event(event, parent, [:after_subnodes, 'Alt(A => a .)[0, 1]'])
|
273
|
+
check_legs(['Alt(A => a .)[0, 1]', 7130]) # 2 * 5 * 23 * 31
|
274
|
+
},
|
275
|
+
->(event, item) {
|
276
|
+
check_event(event, item, [:after_alternative, 'Alt(A => a .)[0, 1]'])
|
277
|
+
},
|
278
|
+
->(event, item) {
|
279
|
+
check_event(event, item, [:before_alternative, 'Alt(A => B A .)[0, 1]'])
|
280
|
+
check_legs(['Alt(A => B A .)[0, 1]', 8510]) # 2 * 5 * 23 * 37
|
281
|
+
check_node_accesses(item, [8510])
|
282
|
+
},
|
283
|
+
->(event, parent, children) {
|
284
|
+
check_event(event, parent, [:before_subnodes, 'Alt(A => B A .)[0, 1]'])
|
285
|
+
expect(children.size).to eq(2)
|
286
|
+
},
|
287
|
+
->(event, item) {
|
288
|
+
check_event(event, item, [:before_non_terminal, 'B[0, 0]'])
|
289
|
+
check_legs(['B[0, 0]', 365930]) # 2 * 5 * 23 * 37 * 43
|
290
|
+
check_node_accesses(item, [365930])
|
291
|
+
},
|
292
|
+
->(event, parent, children) {
|
293
|
+
check_event(event, parent, [:before_subnodes, 'B[0, 0]'])
|
294
|
+
expect(children.size).to eq(1)
|
295
|
+
},
|
296
|
+
->(event, item) {
|
297
|
+
check_event(event, item, [:before_epsilon, '_[0, 0]'])
|
298
|
+
},
|
299
|
+
->(event, item) {
|
300
|
+
check_event(event, item, [:after_epsilon, '_[0, 0]'])
|
301
|
+
},
|
302
|
+
->(event, parent, children) {
|
303
|
+
check_event(event, parent, [:after_subnodes, 'B[0, 0]'])
|
304
|
+
check_legs(['B[0, 0]', 365930]) # 2 * 5 * 23 * 37 * 43
|
305
|
+
},
|
306
|
+
->(event, item) {
|
307
|
+
check_event(event, item, [:after_non_terminal, 'B[0, 0]'])
|
308
|
+
},
|
309
|
+
->(event, item) {
|
310
|
+
check_event(event, item, [:before_non_terminal, 'A[0, 1]'])
|
311
|
+
check_legs(['A[0, 1]', 399970]) # 2 * 5 * 23 * 37 * 47
|
312
|
+
check_node_accesses(item, [230, 399970])
|
313
|
+
},
|
314
|
+
->(event, parent, children) {
|
315
|
+
check_event(event, parent, [:before_subnodes, 'A[0, 1]'])
|
316
|
+
expect(children.size).to eq(2)
|
317
|
+
},
|
318
|
+
->(event, item) {
|
319
|
+
check_event(event, item, [:before_alternative, 'Alt(A => a .)[0, 1]'])
|
320
|
+
check_legs(['Alt(A => a .)[0, 1]', 12399070]) # 2 * 5 * 23 * 37 * 47 * 31
|
321
|
+
check_node_accesses(item, [7130, 12399070])
|
322
|
+
},
|
323
|
+
->(event, parent, children) {
|
324
|
+
check_event(event, parent, [:before_subnodes, 'Alt(A => a .)[0, 1]'])
|
325
|
+
expect(children.size).to eq(1)
|
326
|
+
},
|
327
|
+
->(event, item) {
|
328
|
+
check_event(event, item, [:before_terminal, 'a[0, 1]'])
|
329
|
+
},
|
330
|
+
->(event, item) {
|
331
|
+
check_event(event, item, [:after_terminal, 'a[0, 1]'])
|
332
|
+
},
|
333
|
+
->(event, parent, children) {
|
334
|
+
check_event(event, parent, [:after_subnodes, 'Alt(A => a .)[0, 1]'])
|
335
|
+
check_legs(['Alt(A => a .)[0, 1]', 12399070]) # 2 * 5 * 23 * 37 * 47 * 31
|
336
|
+
},
|
337
|
+
->(event, item) {
|
338
|
+
check_event(event, item, [:after_alternative, 'Alt(A => a .)[0, 1]'])
|
339
|
+
},
|
340
|
+
->(event, item) {
|
341
|
+
check_event(event, item, [:before_alternative, 'Alt(A => B A .)[0, 1]'])
|
342
|
+
# For prime factoring: https://www.calculatorsoup.com/calculators/math/prime-factors.php
|
343
|
+
check_legs(['Alt(A => B A .)[0, 1]', 399970]) # 2 * 5 * 23 * 37 * 47
|
344
|
+
check_node_accesses(item, [8510, 399970])
|
345
|
+
},
|
346
|
+
->(event, parent, children) {
|
347
|
+
check_event(event, parent, [:before_subnodes, 'Alt(A => B A .)[0, 1]'])
|
348
|
+
expect(children.size).to eq(2)
|
349
|
+
},
|
350
|
+
->(event, item) {
|
351
|
+
check_event(event, item, [:before_non_terminal, 'B[0, 0]'])
|
352
|
+
check_legs(['B[0, 0]', 17198710]) # 2 * 5 * 23 * 37 * 47 * 43
|
353
|
+
check_node_accesses(item, [365930, 17198710])
|
354
|
+
},
|
355
|
+
->(event, parent, children) {
|
356
|
+
check_event(event, parent, [:before_subnodes, 'B[0, 0]'])
|
357
|
+
expect(children.size).to eq(1)
|
358
|
+
},
|
359
|
+
->(event, item) {
|
360
|
+
check_event(event, item, [:before_epsilon, '_[0, 0]'])
|
361
|
+
},
|
362
|
+
->(event, item) {
|
363
|
+
check_event(event, item, [:after_epsilon, '_[0, 0]'])
|
364
|
+
},
|
365
|
+
->(event, parent, children) {
|
366
|
+
check_event(event, parent, [:after_subnodes, 'B[0, 0]'])
|
367
|
+
check_legs(['B[0, 0]', 17198710]) # 2 * 5 * 23 * 37 * 43 * 47
|
368
|
+
},
|
369
|
+
->(event, item) {
|
370
|
+
check_event(event, item, [:after_non_terminal, 'B[0, 0]'])
|
371
|
+
},
|
372
|
+
->(event, parent, children) {
|
373
|
+
check_event(event, parent, [:after_subnodes, 'Alt(A => B A .)[0, 1]'])
|
374
|
+
check_legs(['Alt(A => B A .)[0, 1]', 399970]) # 2 * 5 * 23 * 37 * 47
|
375
|
+
check_node_accesses(parent, [8510, 399970])
|
376
|
+
},
|
377
|
+
->(event, item) {
|
378
|
+
check_event(event, item, [:after_alternative, 'Alt(A => B A .)[0, 1]'])
|
379
|
+
},
|
380
|
+
->(event, parent, children) {
|
381
|
+
check_event(event, parent, [:after_subnodes, 'A[0, 1]'])
|
382
|
+
check_legs(['A[0, 1]', 399970]) # 2 * 5 * 23 * 37 * 47
|
383
|
+
},
|
384
|
+
->(event, item) {
|
385
|
+
check_event(event, item, [:after_non_terminal, 'A[0, 1]'])
|
386
|
+
},
|
387
|
+
->(event, parent, children) {
|
388
|
+
check_event(event, parent, [:after_subnodes, 'Alt(A => B A .)[0, 1]'])
|
389
|
+
check_legs(['Alt(A => B A .)[0, 1]', 8510]) # 2 * 5 * 23 * 37
|
390
|
+
},
|
391
|
+
->(event, item) {
|
392
|
+
check_event(event, item, [:after_alternative, 'Alt(A => B A .)[0, 1]'])
|
393
|
+
},
|
394
|
+
->(event, parent, children) {
|
395
|
+
check_event(event, parent, [:after_subnodes, 'A[0, 1]'])
|
396
|
+
check_legs(['A[0, 1]', 230]) # 2 * 5 * 23
|
397
|
+
},
|
398
|
+
->(event, item) {
|
399
|
+
check_event(event, item, [:after_non_terminal, 'A[0, 1]'])
|
400
|
+
},
|
401
|
+
->(event, item) {
|
402
|
+
check_event(event, item, [:before_non_terminal, 'T[1, 4]'])
|
403
|
+
check_legs(['T[1, 4]', 290]) # 2 * 5 * 29
|
404
|
+
check_node_accesses(item, [66, 290])
|
405
|
+
},
|
406
|
+
->(event, parent, children) {
|
407
|
+
check_event(event, parent, [:before_subnodes, 'T[1, 4]'])
|
408
|
+
expect(children.size).to eq(3)
|
409
|
+
},
|
410
|
+
->(event, item) {
|
411
|
+
check_event(event, item, [:before_terminal, 'b[1, 2]'])
|
412
|
+
},
|
413
|
+
->(event, item) {
|
414
|
+
check_event(event, item, [:after_terminal, 'b[1, 2]'])
|
415
|
+
},
|
416
|
+
->(event, item) {
|
417
|
+
check_event(event, item, [:before_terminal, 'b[2, 3]'])
|
418
|
+
},
|
419
|
+
->(event, item) {
|
420
|
+
check_event(event, item, [:after_terminal, 'b[2, 3]'])
|
421
|
+
},
|
422
|
+
->(event, item) {
|
423
|
+
check_event(event, item, [:before_terminal, 'b[3, 4]'])
|
424
|
+
},
|
425
|
+
->(event, item) {
|
426
|
+
check_event(event, item, [:after_terminal, 'b[3, 4]'])
|
427
|
+
},
|
428
|
+
->(event, parent, children) {
|
429
|
+
check_event(event, parent, [:after_subnodes, 'T[1, 4]'])
|
430
|
+
},
|
431
|
+
->(event, item) {
|
432
|
+
check_event(event, item, [:after_non_terminal, 'T[1, 4]'])
|
433
|
+
},
|
434
|
+
->(event, parent, children) {
|
435
|
+
check_event(event, parent, [:after_subnodes, 'Alt(S => A T .)[0, 4]'])
|
436
|
+
expect(children.size).to eq(2)
|
437
|
+
check_legs(['Alt(S => A T .)[0, 4]', 10]) # 2 * 5
|
438
|
+
},
|
439
|
+
->(event, item) {
|
440
|
+
check_event(event, item, [:after_alternative, 'Alt(S => A T .)[0, 4]'])
|
441
|
+
},
|
442
|
+
->(event, parent, children) {
|
443
|
+
check_event(event, parent, [:after_subnodes, 'S[0, 4]'])
|
444
|
+
expect(children.size).to eq(2)
|
445
|
+
check_legs(['S[0, 4]', 2]) # 2
|
446
|
+
},
|
447
|
+
->(event, item) {
|
448
|
+
check_event(event, item, [:after_non_terminal, 'S[0, 4]'])
|
449
|
+
},
|
450
|
+
->(event, parent, children) {
|
451
|
+
check_event(event, parent, [:after_subnodes, 'Phi[0, 4]'])
|
452
|
+
expect(children.size).to eq(1)
|
453
|
+
},
|
454
|
+
->(event, item) {
|
455
|
+
check_event(event, item, [:after_non_terminal, 'Phi[0, 4]'])
|
456
|
+
},
|
457
|
+
->(event, item) {
|
458
|
+
check_event(event, item, [:after_pforest, grm_sppf_pforest1])
|
459
|
+
}
|
226
460
|
]
|
227
|
-
expectations.each do |(msg, args)|
|
228
|
-
expect(listener1).to receive(msg).with(*args).ordered
|
229
|
-
end
|
230
|
-
|
231
|
-
# Here we go...
|
232
461
|
subject.start
|
233
462
|
end
|
234
|
-
=end
|
235
463
|
end # context
|
236
464
|
end # describe
|
237
465
|
end # module
|