rley 0.2.15 → 0.3.00
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/gfg/call_edge.rb +30 -0
- data/lib/rley/gfg/edge.rb +4 -0
- data/lib/rley/gfg/end_vertex.rb +1 -1
- data/lib/rley/gfg/epsilon_edge.rb +0 -4
- data/lib/rley/gfg/grm_flow_graph.rb +32 -7
- data/lib/rley/gfg/item_vertex.rb +71 -25
- data/lib/rley/gfg/non_terminal_vertex.rb +10 -1
- data/lib/rley/gfg/return_edge.rb +31 -0
- data/lib/rley/gfg/scan_edge.rb +2 -1
- data/lib/rley/gfg/shortcut_edge.rb +26 -0
- data/lib/rley/gfg/start_vertex.rb +2 -2
- data/lib/rley/gfg/vertex.rb +27 -1
- data/lib/rley/parse_forest_visitor.rb +115 -0
- data/lib/rley/parser/base_parser.rb +27 -0
- data/lib/rley/parser/dotted_item.rb +11 -0
- data/lib/rley/parser/earley_parser.rb +3 -15
- data/lib/rley/parser/gfg_chart.rb +106 -0
- data/lib/rley/parser/gfg_earley_parser.rb +139 -0
- data/lib/rley/parser/gfg_parsing.rb +384 -0
- data/lib/rley/parser/parse_entry.rb +148 -0
- data/lib/rley/parser/parse_entry_set.rb +104 -0
- data/lib/rley/parser/parse_entry_tracker.rb +56 -0
- data/lib/rley/parser/parse_forest_builder.rb +229 -0
- data/lib/rley/parser/parse_forest_factory.rb +54 -0
- data/lib/rley/parser/parse_walker_factory.rb +237 -0
- data/lib/rley/ptree/token_range.rb +14 -1
- data/lib/rley/sppf/alternative_node.rb +34 -0
- data/lib/rley/sppf/composite_node.rb +27 -0
- data/lib/rley/sppf/epsilon_node.rb +27 -0
- data/lib/rley/sppf/leaf_node.rb +12 -0
- data/lib/rley/sppf/non_terminal_node.rb +38 -0
- data/lib/rley/sppf/parse_forest.rb +48 -0
- data/lib/rley/sppf/sppf_node.rb +24 -0
- data/lib/rley/sppf/token_node.rb +29 -0
- data/lib/rley/syntax/grammar_builder.rb +16 -12
- data/lib/rley/syntax/grm_symbol.rb +6 -0
- data/lib/rley/syntax/terminal.rb +5 -0
- data/spec/rley/gfg/call_edge_spec.rb +51 -0
- data/spec/rley/gfg/end_vertex_spec.rb +1 -0
- data/spec/rley/gfg/grm_flow_graph_spec.rb +24 -2
- data/spec/rley/gfg/item_vertex_spec.rb +75 -6
- data/spec/rley/gfg/non_terminal_vertex_spec.rb +14 -0
- data/spec/rley/gfg/return_edge_spec.rb +51 -0
- data/spec/rley/gfg/shortcut_edge_spec.rb +43 -0
- data/spec/rley/gfg/vertex_spec.rb +52 -37
- data/spec/rley/parse_forest_visitor_spec.rb +238 -0
- data/spec/rley/parser/dotted_item_spec.rb +29 -8
- data/spec/rley/parser/gfg_chart_spec.rb +138 -0
- data/spec/rley/parser/gfg_earley_parser_spec.rb +918 -0
- data/spec/rley/parser/gfg_parsing_spec.rb +565 -0
- data/spec/rley/parser/parse_entry_set_spec.rb +179 -0
- data/spec/rley/parser/parse_entry_spec.rb +208 -0
- data/spec/rley/parser/parse_forest_builder_spec.rb +382 -0
- data/spec/rley/parser/parse_forest_factory_spec.rb +81 -0
- data/spec/rley/parser/parse_walker_factory_spec.rb +235 -0
- data/spec/rley/parser/state_set_spec.rb +4 -0
- data/spec/rley/sppf/alternative_node_spec.rb +72 -0
- data/spec/rley/sppf/antecedence_graph.rb +87 -0
- data/spec/rley/sppf/forest_representation.rb +136 -0
- data/spec/rley/sppf/gfg_representation.rb +111 -0
- data/spec/rley/sppf/non_terminal_node_spec.rb +64 -0
- data/spec/rley/support/ambiguous_grammar_helper.rb +36 -36
- data/spec/rley/support/expectation_helper.rb +36 -0
- data/spec/rley/support/grammar_helper.rb +28 -0
- data/spec/rley/support/grammar_sppf_helper.rb +25 -0
- data/spec/rley/syntax/grammar_builder_spec.rb +5 -0
- data/spec/rley/syntax/non_terminal_spec.rb +4 -0
- data/spec/rley/syntax/terminal_spec.rb +4 -0
- metadata +58 -2
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
require_relative '../../spec_helper'
|
|
2
|
+
|
|
3
|
+
require_relative '../../../lib/rley/parser/gfg_earley_parser'
|
|
4
|
+
|
|
5
|
+
require_relative '../../../lib/rley/syntax/grammar_builder'
|
|
6
|
+
require_relative '../support/grammar_helper'
|
|
7
|
+
require_relative '../support/expectation_helper'
|
|
8
|
+
|
|
9
|
+
# Load the class under test
|
|
10
|
+
require_relative '../../../lib/rley/parser/parse_forest_factory'
|
|
11
|
+
|
|
12
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
|
13
|
+
module Parser
|
|
14
|
+
describe ParseForestFactory do
|
|
15
|
+
include GrammarHelper # Mix-in with token factory method
|
|
16
|
+
include ExpectationHelper # Mix-in with expectation on parse entry sets
|
|
17
|
+
|
|
18
|
+
let(:sample_grammar) do
|
|
19
|
+
# Grammar based on paper from Elisabeth Scott
|
|
20
|
+
# "SPPF=Style Parsing From Earley Recognizers" in
|
|
21
|
+
# Notes in Theoretical Computer Science 203, (2008), pp. 53-67
|
|
22
|
+
# contains a hidden left recursion and a cycle
|
|
23
|
+
builder = Syntax::GrammarBuilder.new
|
|
24
|
+
builder.add_terminals('a', 'b')
|
|
25
|
+
builder.add_production('Phi' => %'S')
|
|
26
|
+
builder.add_production('S' => %w[A T])
|
|
27
|
+
builder.add_production('S' => %w[a T])
|
|
28
|
+
builder.add_production('A' => 'a')
|
|
29
|
+
builder.add_production('A' => %w[B A])
|
|
30
|
+
builder.add_production('B' => [])
|
|
31
|
+
builder.add_production('T' => %w( b b b))
|
|
32
|
+
builder.grammar
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
let(:sample_tokens) do
|
|
36
|
+
build_token_sequence(%w(a b b b), sample_grammar)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
let(:sample_result) do
|
|
40
|
+
parser = Parser::GFGEarleyParser.new(sample_grammar)
|
|
41
|
+
parser.parse(sample_tokens)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
subject do
|
|
46
|
+
ParseForestFactory.new(sample_result)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Emit a text representation of the current path.
|
|
50
|
+
def path_to_s()
|
|
51
|
+
text_parts = subject.curr_path.map { |path_element| path_element.to_string(0) }
|
|
52
|
+
return text_parts.join('/')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
context 'Initialization:' do
|
|
57
|
+
it 'should be created with a GFGParsing' do
|
|
58
|
+
expect { ParseForestFactory.new(sample_result) }.not_to raise_error
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'should know the parse result' do
|
|
62
|
+
expect(subject.parsing).to eq(sample_result)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
=begin
|
|
66
|
+
context 'Parse forest construction' do
|
|
67
|
+
it 'should build a parse forest' do
|
|
68
|
+
forest = subject.build_parse_forest
|
|
69
|
+
|
|
70
|
+
require 'yaml'
|
|
71
|
+
require_relative '../sppf/forest_representation'
|
|
72
|
+
File.open("forest.yml", "w") { |f| YAML.dump(forest, f) }
|
|
73
|
+
pen = ForestRepresentation.new
|
|
74
|
+
pen.generate_graph(forest, File.open("forest.dot", "w"))
|
|
75
|
+
end
|
|
76
|
+
end # context
|
|
77
|
+
=end
|
|
78
|
+
end # describe
|
|
79
|
+
end # module
|
|
80
|
+
end # module
|
|
81
|
+
# End of file
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
require_relative '../../spec_helper'
|
|
2
|
+
|
|
3
|
+
require_relative '../../../lib/rley/syntax/grammar_builder'
|
|
4
|
+
require_relative '../support/grammar_helper'
|
|
5
|
+
require_relative '../support/expectation_helper'
|
|
6
|
+
|
|
7
|
+
require_relative '../../../lib/rley/parser/gfg_earley_parser'
|
|
8
|
+
|
|
9
|
+
# Load the class under test
|
|
10
|
+
require_relative '../../../lib/rley/parser/parse_walker_factory'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
|
14
|
+
module Parser
|
|
15
|
+
describe ParseWalkerFactory do
|
|
16
|
+
include GrammarHelper # Mix-in with token factory method
|
|
17
|
+
include ExpectationHelper # Mix-in with expectation on parse entry sets
|
|
18
|
+
|
|
19
|
+
# Helper method use to check wether a visit event
|
|
20
|
+
# matches the given expectations
|
|
21
|
+
# The expectations form an array with the given elements:
|
|
22
|
+
# [0] => the event symbol (:visit)
|
|
23
|
+
# [1] => the parse entry being visited
|
|
24
|
+
# [2] => the index of the parse entry set to which the visitee belongs to
|
|
25
|
+
def event_expectations(anEvent, expectations)
|
|
26
|
+
visit_event, entry, index = anEvent
|
|
27
|
+
expect(visit_event).to eq(expectations[0])
|
|
28
|
+
if expectations[1].is_a?(String)
|
|
29
|
+
case entry
|
|
30
|
+
when Parser::ParseEntry
|
|
31
|
+
expect(entry.to_s).to eq(expectations[1])
|
|
32
|
+
when Parser::Token
|
|
33
|
+
expect(entry.lexeme).to eq(expectations[1])
|
|
34
|
+
end
|
|
35
|
+
else
|
|
36
|
+
expect(entry).to eq(expectations[1])
|
|
37
|
+
end
|
|
38
|
+
expect(index).to eq(expectations[2])
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
let(:sample_grammar) do
|
|
42
|
+
# Grammar based on paper from Elisabeth Scott
|
|
43
|
+
# "SPPF=Style Parsing From Earley Recognizers" in
|
|
44
|
+
# Notes in Theoretical Computer Science 203, (2008), pp. 53-67
|
|
45
|
+
# contains a hidden left recursion and a cycle
|
|
46
|
+
builder = Syntax::GrammarBuilder.new
|
|
47
|
+
builder.add_terminals('a', 'b')
|
|
48
|
+
builder.add_production('Phi' => %'S')
|
|
49
|
+
builder.add_production('S' => %w[A T])
|
|
50
|
+
builder.add_production('S' => %w[a T])
|
|
51
|
+
builder.add_production('A' => 'a')
|
|
52
|
+
builder.add_production('A' => %w[B A])
|
|
53
|
+
builder.add_production('B' => [])
|
|
54
|
+
builder.add_production('T' => %w( b b b))
|
|
55
|
+
builder.grammar
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
let(:sample_tokens) do
|
|
59
|
+
build_token_sequence(%w(a b b b), sample_grammar)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
let(:sample_result) do
|
|
63
|
+
parser = Parser::GFGEarleyParser.new(sample_grammar)
|
|
64
|
+
parser.parse(sample_tokens)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
let(:subject) { ParseWalkerFactory.new }
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
context 'Initialization:' do
|
|
71
|
+
it 'should be created without argument' do
|
|
72
|
+
expect { ParseWalkerFactory.new }.not_to raise_error
|
|
73
|
+
end
|
|
74
|
+
end # context
|
|
75
|
+
|
|
76
|
+
context 'Parse graph traversal:' do
|
|
77
|
+
it 'should create an Enumerator as a walker' do
|
|
78
|
+
expect(subject.build_walker(sample_result)).to be_kind_of(Enumerator)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'should return the accepting parse entry in the first place' do
|
|
82
|
+
walker = subject.build_walker(sample_result)
|
|
83
|
+
first_event = walker.next
|
|
84
|
+
expectations = [:visit, sample_result.accepting_entry, 4]
|
|
85
|
+
event_expectations(first_event, expectations)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'should traverse the parse graph backwards' do
|
|
89
|
+
walker = subject.build_walker(sample_result)
|
|
90
|
+
event1 = walker.next
|
|
91
|
+
expectations = [:visit, 'Phi. | 0', 4]
|
|
92
|
+
event_expectations(event1, expectations)
|
|
93
|
+
|
|
94
|
+
event2 = walker.next
|
|
95
|
+
expectations = [:visit, 'Phi => S . | 0', 4]
|
|
96
|
+
event_expectations(event2, expectations)
|
|
97
|
+
|
|
98
|
+
event3 = walker.next
|
|
99
|
+
expectations = [:visit, 'S. | 0', 4]
|
|
100
|
+
event_expectations(event3, expectations)
|
|
101
|
+
|
|
102
|
+
# Backtrack created: first alternative selected
|
|
103
|
+
event4 = walker.next
|
|
104
|
+
expectations = [:visit, 'S => a T . | 0', 4]
|
|
105
|
+
event_expectations(event4, expectations)
|
|
106
|
+
|
|
107
|
+
event5 = walker.next
|
|
108
|
+
expectations = [:visit, 'T. | 1', 4]
|
|
109
|
+
event_expectations(event5, expectations)
|
|
110
|
+
|
|
111
|
+
event6 = walker.next
|
|
112
|
+
expectations = [:visit, 'T => b b b . | 1', 4]
|
|
113
|
+
event_expectations(event6, expectations)
|
|
114
|
+
|
|
115
|
+
event7 = walker.next
|
|
116
|
+
expectations = [:visit, 'T => b b . b | 1', 3]
|
|
117
|
+
event_expectations(event7, expectations)
|
|
118
|
+
|
|
119
|
+
event8 = walker.next
|
|
120
|
+
expectations = [:visit, 'T => b . b b | 1', 2]
|
|
121
|
+
event_expectations(event8, expectations)
|
|
122
|
+
|
|
123
|
+
event9 = walker.next
|
|
124
|
+
expectations = [:visit, 'T => . b b b | 1', 1]
|
|
125
|
+
event_expectations(event9, expectations)
|
|
126
|
+
|
|
127
|
+
event10 = walker.next
|
|
128
|
+
expectations = [:visit, '.T | 1', 1]
|
|
129
|
+
event_expectations(event10, expectations)
|
|
130
|
+
|
|
131
|
+
event11 = walker.next
|
|
132
|
+
expectations = [:visit, 'S => a . T | 0', 1]
|
|
133
|
+
event_expectations(event11, expectations)
|
|
134
|
+
|
|
135
|
+
event12 = walker.next
|
|
136
|
+
expectations = [:visit, 'S => . a T | 0', 0]
|
|
137
|
+
event_expectations(event12, expectations)
|
|
138
|
+
|
|
139
|
+
event13 = walker.next
|
|
140
|
+
expectations = [:visit, '.S | 0', 0]
|
|
141
|
+
event_expectations(event13, expectations)
|
|
142
|
+
|
|
143
|
+
event14 = walker.next
|
|
144
|
+
expectations = [:visit, 'Phi => . S | 0', 0]
|
|
145
|
+
event_expectations(event14, expectations)
|
|
146
|
+
|
|
147
|
+
event15 = walker.next
|
|
148
|
+
expectations = [:visit, '.Phi | 0', 0]
|
|
149
|
+
event_expectations(event15, expectations)
|
|
150
|
+
|
|
151
|
+
# Backtracking is occurring
|
|
152
|
+
event16 = walker.next
|
|
153
|
+
expectations = [:backtrack, 'S. | 0', 4]
|
|
154
|
+
event_expectations(event16, expectations)
|
|
155
|
+
|
|
156
|
+
event18 = walker.next
|
|
157
|
+
expectations = [:visit, 'S => A T . | 0', 4]
|
|
158
|
+
event_expectations(event18, expectations)
|
|
159
|
+
|
|
160
|
+
event19 = walker.next
|
|
161
|
+
expectations = [:revisit, 'T. | 1', 4]
|
|
162
|
+
event_expectations(event19, expectations)
|
|
163
|
+
|
|
164
|
+
# Multiple visit occurred: jump to antecedent of start entry
|
|
165
|
+
event20 = walker.next
|
|
166
|
+
expectations = [:visit, 'S => A . T | 0', 1]
|
|
167
|
+
event_expectations(event20, expectations)
|
|
168
|
+
|
|
169
|
+
event21 = walker.next
|
|
170
|
+
expectations = [:visit, 'A. | 0', 1]
|
|
171
|
+
event_expectations(event21, expectations)
|
|
172
|
+
|
|
173
|
+
# Backtrack created: first alternative selected
|
|
174
|
+
event22 = walker.next
|
|
175
|
+
expectations = [:visit, 'A => a . | 0', 1]
|
|
176
|
+
event_expectations(event22, expectations)
|
|
177
|
+
|
|
178
|
+
event23 = walker.next
|
|
179
|
+
expectations = [:visit, 'A => . a | 0', 0]
|
|
180
|
+
event_expectations(event23, expectations)
|
|
181
|
+
|
|
182
|
+
event24 = walker.next
|
|
183
|
+
expectations = [:visit, '.A | 0', 0]
|
|
184
|
+
event_expectations(event24, expectations)
|
|
185
|
+
|
|
186
|
+
event25 = walker.next
|
|
187
|
+
expectations = [:visit, 'S => . A T | 0', 0]
|
|
188
|
+
event_expectations(event25, expectations)
|
|
189
|
+
|
|
190
|
+
# Backtracking is occurring
|
|
191
|
+
event26 = walker.next
|
|
192
|
+
expectations = [:backtrack, 'A. | 0', 1]
|
|
193
|
+
event_expectations(event26, expectations)
|
|
194
|
+
|
|
195
|
+
event27 = walker.next
|
|
196
|
+
expectations = [:visit, 'A => B A . | 0', 1]
|
|
197
|
+
event_expectations(event27, expectations)
|
|
198
|
+
|
|
199
|
+
event28 = walker.next
|
|
200
|
+
expectations = [:revisit, 'A. | 0', 1]
|
|
201
|
+
event_expectations(event28, expectations)
|
|
202
|
+
|
|
203
|
+
event29 = walker.next
|
|
204
|
+
expectations = [:visit, 'A => B . A | 0', 0]
|
|
205
|
+
event_expectations(event29, expectations)
|
|
206
|
+
|
|
207
|
+
event30 = walker.next
|
|
208
|
+
expectations = [:visit, 'B. | 0', 0]
|
|
209
|
+
event_expectations(event30, expectations)
|
|
210
|
+
|
|
211
|
+
event31 = walker.next
|
|
212
|
+
expectations = [:visit, 'B => . | 0', 0]
|
|
213
|
+
event_expectations(event31, expectations)
|
|
214
|
+
|
|
215
|
+
event32 = walker.next
|
|
216
|
+
expectations = [:visit, '.B | 0', 0]
|
|
217
|
+
event_expectations(event32, expectations)
|
|
218
|
+
|
|
219
|
+
event33 = walker.next
|
|
220
|
+
expectations = [:visit, 'A => . B A | 0', 0]
|
|
221
|
+
event_expectations(event33, expectations)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
it 'should raise an exception at end of visit' do
|
|
225
|
+
walker = subject.build_walker(sample_result)
|
|
226
|
+
32.times { walker.next }
|
|
227
|
+
|
|
228
|
+
expect{ walker.next }.to raise_error(StopIteration)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
end # context
|
|
232
|
+
end # describe
|
|
233
|
+
end # module
|
|
234
|
+
end # module
|
|
235
|
+
# End of file
|
|
@@ -17,6 +17,10 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
|
17
17
|
it 'should be created without argument' do
|
|
18
18
|
expect { StateSet.new }.not_to raise_error
|
|
19
19
|
end
|
|
20
|
+
|
|
21
|
+
it 'should be empty at creation' do
|
|
22
|
+
expect(subject.states).to be_empty
|
|
23
|
+
end
|
|
20
24
|
end # context
|
|
21
25
|
|
|
22
26
|
context 'Provided services:' do
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'ostruct'
|
|
2
|
+
require_relative '../../spec_helper'
|
|
3
|
+
|
|
4
|
+
require_relative '../../../lib/rley/gfg/item_vertex'
|
|
5
|
+
require_relative '../../../lib/rley/syntax/terminal'
|
|
6
|
+
require_relative '../../../lib/rley/syntax/production'
|
|
7
|
+
require_relative '../../../lib/rley/ptree/token_range'
|
|
8
|
+
require_relative '../../../lib/rley/parser/dotted_item'
|
|
9
|
+
|
|
10
|
+
# Load the class under test
|
|
11
|
+
require_relative '../../../lib/rley/sppf/alternative_node'
|
|
12
|
+
|
|
13
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
|
14
|
+
module SPPF # Open this namespace to avoid module qualifier prefixes
|
|
15
|
+
describe AlternativeNode do
|
|
16
|
+
# Factory method. Generate a range from its boundary values.
|
|
17
|
+
def range(low, high)
|
|
18
|
+
return PTree::TokenRange.new(low: low, high: high)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
let(:t_a) { Syntax::Terminal.new('A') }
|
|
22
|
+
let(:t_b) { Syntax::Terminal.new('B') }
|
|
23
|
+
let(:t_c) { Syntax::Terminal.new('C') }
|
|
24
|
+
let(:nt_sentence) { Syntax::NonTerminal.new('sentence') }
|
|
25
|
+
let(:sample_prod) do
|
|
26
|
+
Syntax::Production.new(nt_sentence, [t_a, t_b, t_c])
|
|
27
|
+
end
|
|
28
|
+
let(:sample_item) { Parser::DottedItem.new(sample_prod, 3) }
|
|
29
|
+
let(:sample_vertex) { GFG::ItemVertex.new(sample_item) }
|
|
30
|
+
let(:sample_range) { range(0, 3) }
|
|
31
|
+
|
|
32
|
+
subject { AlternativeNode.new(sample_vertex, sample_range) }
|
|
33
|
+
|
|
34
|
+
context 'Construction:' do
|
|
35
|
+
it 'should be created with a item vertex and a token range' do
|
|
36
|
+
expect { AlternativeNode.new(sample_vertex, sample_range) }.not_to raise_error
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
context 'Initialization:' do
|
|
41
|
+
it 'should know its token range' do
|
|
42
|
+
expect(subject.range).to eq(sample_range)
|
|
43
|
+
expect(subject.origin).to eq(sample_range.low)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "shouldn't have children yet" do
|
|
47
|
+
expect(subject.subnodes).to be_empty
|
|
48
|
+
end
|
|
49
|
+
end # context
|
|
50
|
+
|
|
51
|
+
context 'Provided services:' do
|
|
52
|
+
it 'should accept the addition of subnodes' do
|
|
53
|
+
subnode1 = double('first_subnode')
|
|
54
|
+
subnode2 = double('second_subnode')
|
|
55
|
+
subnode3 = double('third_subnode')
|
|
56
|
+
expect { subject.add_subnode(subnode1) }.not_to raise_error
|
|
57
|
+
subject.add_subnode(subnode2)
|
|
58
|
+
subject.add_subnode(subnode3)
|
|
59
|
+
expect(subject.subnodes).to eq([subnode3, subnode2, subnode1])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
it 'should have a string representation' do
|
|
64
|
+
expect(subject.to_string(0)).to eq('Alt(sentence => A B C .)[0, 3]')
|
|
65
|
+
end
|
|
66
|
+
end # context
|
|
67
|
+
|
|
68
|
+
end # describe
|
|
69
|
+
end # module
|
|
70
|
+
end # module
|
|
71
|
+
|
|
72
|
+
# End of file
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Mix-in module that generates a Graphviz's DOT file
|
|
2
|
+
# that represents the precedence graph of parse entries.
|
|
3
|
+
module AntecedenceGraph
|
|
4
|
+
|
|
5
|
+
def generate_graph(aParsing, aFile)
|
|
6
|
+
heading = build_heading()
|
|
7
|
+
aFile.puts(heading)
|
|
8
|
+
|
|
9
|
+
fill_graph(aParsing, aFile)
|
|
10
|
+
|
|
11
|
+
trailing = build_trailing()
|
|
12
|
+
aFile.puts(trailing)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def build_heading()
|
|
18
|
+
text = <<-END_STRING
|
|
19
|
+
digraph entries {
|
|
20
|
+
size = "7,9.5";
|
|
21
|
+
page = "8.5,11";
|
|
22
|
+
ratio = fill;
|
|
23
|
+
rankdir = "BT"; // Draw arrows from bottom to top
|
|
24
|
+
END_STRING
|
|
25
|
+
|
|
26
|
+
return text
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def build_trailing()
|
|
30
|
+
return '}'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def fill_graph(aParsing, aFile)
|
|
34
|
+
# Associate to each parse entry a node id
|
|
35
|
+
oid2node_id = build_nodes_id(aParsing)
|
|
36
|
+
aParsing.chart.sets.each_with_index do |entry_set, chart_index|
|
|
37
|
+
# Create the graph nodes
|
|
38
|
+
aFile.puts ''
|
|
39
|
+
aFile.puts(cluster_heading(chart_index))
|
|
40
|
+
|
|
41
|
+
entry_set.entries.each do |entry|
|
|
42
|
+
aFile.puts %Q( #{oid2node_id[entry]}[label="#{entry}"];)
|
|
43
|
+
end
|
|
44
|
+
aFile.puts ' }' # Close cluster
|
|
45
|
+
|
|
46
|
+
# Create the edges
|
|
47
|
+
aFile.puts ''
|
|
48
|
+
entry_set.entries.each do |entry|
|
|
49
|
+
antecedents = aParsing.antecedence[entry]
|
|
50
|
+
antecedents.each do |antec|
|
|
51
|
+
aFile.puts " #{oid2node_id[antec]} -> #{oid2node_id[entry]};"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# For each parse entry, associate a graph node id
|
|
58
|
+
def build_nodes_id(aParsing)
|
|
59
|
+
# Create a Hash with pairs of the form: object id => node id
|
|
60
|
+
oid2node_id = {}
|
|
61
|
+
|
|
62
|
+
aParsing.chart.sets.each_with_index do |entry_set, chart_index|
|
|
63
|
+
entry_set.entries.each_with_index do |entry, entry_index|
|
|
64
|
+
oid2node_id[entry] = "node_#{chart_index}_#{entry_index}"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
return oid2node_id
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def cluster_heading(anIndex)
|
|
73
|
+
text = <<-END_STRING
|
|
74
|
+
subgraph cluster_chart_#{anIndex} {
|
|
75
|
+
style = rounded;
|
|
76
|
+
color = blue;
|
|
77
|
+
fontsize = 24.0;
|
|
78
|
+
labeljust = "r";
|
|
79
|
+
label="chart[#{anIndex}]";
|
|
80
|
+
END_STRING
|
|
81
|
+
|
|
82
|
+
return text
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
end # module
|