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