File Coverage

blib/lib/Marpa/R2/ASF.pm
Criterion Covered Total %
statement 896 998 89.7
branch 171 238 71.8
condition 26 34 76.4
subroutine 70 83 84.3
pod 3 19 15.7
total 1166 1372 84.9


line stmt bran cond sub pod time code
1             # Copyright 2022 Jeffrey Kegler
2             # This file is part of Marpa::R2. Marpa::R2 is free software: you can
3             # redistribute it and/or modify it under the terms of the GNU Lesser
4             # General Public License as published by the Free Software Foundation,
5             # either version 3 of the License, or (at your option) any later version.
6             #
7             # Marpa::R2 is distributed in the hope that it will be useful,
8             # but WITHOUT ANY WARRANTY; without even the implied warranty of
9             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10             # Lesser General Public License for more details.
11             #
12             # You should have received a copy of the GNU Lesser
13             # General Public License along with Marpa::R2. If not, see
14             # http://www.gnu.org/licenses/.
15              
16             package Marpa::R2::ASF;
17              
18 132     132   2404 use 5.010001;
  132         571  
19 132     132   885 use strict;
  132         408  
  132         2875  
20 132     132   736 use warnings;
  132         514  
  132         3804  
21 132     132   758 no warnings qw(recursion);
  132         19020  
  132         7211  
22              
23 132     132   901 use vars qw($VERSION $STRING_VERSION);
  132         311  
  132         355744  
24             $VERSION = '12.000000';
25             $STRING_VERSION = $VERSION;
26             ## no critic(BuiltinFunctions::ProhibitStringyEval)
27             $VERSION = eval $VERSION;
28             ## use critic
29              
30             # The code in this file, for now, breaks "the rules". It makes use
31             # of internal methods not documented as part of Libmarpa.
32             # It is intended to create documented Libmarpa methods to underlie
33             # this interface, and rewrite it to use them
34              
35             package Marpa::R2::Internal::ASF;
36              
37             # This is more complicated that it needs to be for the current implementation.
38             # It allows for LHS terminals (implemented in Libmarpa but not allowed by the SLIF).
39             # It also assumes that every or-node which can be constructed from preceding or-nodes
40             # and the input will be present. This is currently the case, but in the future
41             # rules and/or symbols may have extra-syntactic conditions attached making this
42             # assumption false.
43              
44             # Terms:
45              
46             # NID (Node ID): Encoded ID of either an or-node or an and-node.
47             #
48             # Extensions:
49             # Set "powers": A set of power 0 is an "atom" -- a single NID.
50             # A set of power 1 is a set of NID's -- a nidset.
51             # A set of power 2 is a set of sets of NID's, also called a powerset.
52             # A set of power 3 is a set of powersets, etc.
53             #
54             # The whole ID of NID is the external rule id of an or-node, or -1
55             # if the NID is for a token and-node.
56             #
57             # Intensions:
58             # A Symch is a nidset, where all the NID's share the same "whole ID"
59             # and the same span. NID's in a symch may differ in their internal rule,
60             # or have different causes. If the symch contains and-node NID's they
61             # will all have the same symbol.
62             #
63             # A choicepoint is a powerset -- a set of symches all of which share
64             # the same set of predecessors. (This set of predecessors is a power 3 set of
65             # choicepoints.) All symches in a choicepoint also share the same span,
66             # and the same symch-symbol. A symch's symbol is the LHS of the rule,
67             # or the symbol of the token in the token and-nodes.
68              
69             sub intset_id {
70 13311     13311   22482 my ( $asf, @ids ) = @_;
71 13311         26718 my $key = join q{ }, sort { $a <=> $b } @ids;
  99         426  
72 13311         19946 my $intset_by_key = $asf->[Marpa::R2::Internal::ASF::INTSET_BY_KEY];
73 13311         20136 my $intset_id = $intset_by_key->{$key};
74 13311 100       30479 return $intset_id if defined $intset_id;
75 1643         2645 $intset_id = $asf->[Marpa::R2::Internal::ASF::NEXT_INTSET_ID]++;
76 1643         3773 $intset_by_key->{$key} = $intset_id;
77 1643         3041 return $intset_id;
78             } ## end sub intset_id
79              
80             sub Marpa::R2::Nidset::obtain {
81 12453     12453   25619 my ( $class, $asf, @nids ) = @_;
82 12453         20799 my $id = intset_id( $asf, @nids );
83 12453         18105 my $nidset_by_id = $asf->[Marpa::R2::Internal::ASF::NIDSET_BY_ID];
84 12453         16324 my $nidset = $nidset_by_id->[$id];
85 12453 100       25136 return $nidset if defined $nidset;
86 937         1732 $nidset = bless [], $class;
87 937         1894 $nidset->[Marpa::R2::Internal::Nidset::ID] = $id;
88             $nidset->[Marpa::R2::Internal::Nidset::NIDS] =
89 937         2137 [ sort { $a <=> $b } @nids ];
  51         171  
90 937         1679 $nidset_by_id->[$id] = $nidset;
91 937         1699 return $nidset;
92             } ## end sub Marpa::R2::Nidset::obtain
93              
94             sub Marpa::R2::Nidset::nids {
95 858     858   1375 my ($nidset) = @_;
96 858         2127 return $nidset->[Marpa::R2::Internal::Nidset::NIDS];
97             }
98              
99             sub Marpa::R2::Nidset::nid {
100 3263     3263   5264 my ( $nidset, $ix ) = @_;
101 3263         6026 return $nidset->[Marpa::R2::Internal::Nidset::NIDS]->[$ix];
102             }
103              
104             sub Marpa::R2::Nidset::count {
105 285     285   465 my ($nidset) = @_;
106 285         401 return scalar @{ $nidset->[Marpa::R2::Internal::Nidset::NIDS] };
  285         478  
107             }
108              
109             sub Marpa::R2::Nidset::id {
110 12453     12453   20106 my ($nidset) = @_;
111 12453         18644 return $nidset->[Marpa::R2::Internal::Nidset::ID];
112             }
113              
114             sub Marpa::R2::Nidset::show {
115 0     0   0 my ($nidset) = @_;
116 0         0 my $id = $nidset->id();
117 0         0 my $nids = $nidset->nids();
118 0         0 return "Nidset #$id: " . join q{ }, @{$nids};
  0         0  
119             } ## end sub Marpa::R2::Nidset::show
120              
121             sub Marpa::R2::Powerset::obtain {
122 858     858   1647 my ( $class, $asf, @nidset_ids ) = @_;
123 858         1461 my $id = intset_id( $asf, @nidset_ids );
124 858         1445 my $powerset_by_id = $asf->[Marpa::R2::Internal::ASF::POWERSET_BY_ID];
125 858         1261 my $powerset = $powerset_by_id->[$id];
126 858 50       1622 return $powerset if defined $powerset;
127 858         1694 $powerset = bless [], $class;
128 858         1622 $powerset->[Marpa::R2::Internal::Powerset::ID] = $id;
129             $powerset->[Marpa::R2::Internal::Powerset::NIDSET_IDS] =
130 858         1692 [ sort { $a <=> $b } @nidset_ids ];
  19         64  
131 858         1437 $powerset_by_id->[$id] = $powerset;
132 858         1513 return $powerset;
133             } ## end sub Marpa::R2::Powerset::obtain
134              
135             sub Marpa::R2::Powerset::nidset_ids {
136 0     0   0 my ($powerset) = @_;
137 0         0 return $powerset->[Marpa::R2::Internal::Powerset::NIDSET_IDS];
138             }
139              
140             sub Marpa::R2::Powerset::count {
141 858     858   1424 my ($powerset) = @_;
142 858         1111 return scalar @{ $powerset->[Marpa::R2::Internal::Powerset::NIDSET_IDS] };
  858         1637  
143             }
144              
145             sub Marpa::R2::Powerset::nidset_id {
146 0     0   0 my ( $powerset, $ix ) = @_;
147 0         0 my $nidset_ids = $powerset->[Marpa::R2::Internal::Powerset::NIDSET_IDS];
148 0 0       0 return if $ix > $#{$nidset_ids};
  0         0  
149 0         0 return $powerset->[Marpa::R2::Internal::Powerset::NIDSET_IDS]->[$ix];
150             } ## end sub Marpa::R2::Powerset::nidset_id
151              
152             sub Marpa::R2::Powerset::nidset {
153 1160     1160   1987 my ( $powerset, $asf, $ix ) = @_;
154 1160         1591 my $nidset_ids = $powerset->[Marpa::R2::Internal::Powerset::NIDSET_IDS];
155 1160 50       1437 return if $ix > $#{$nidset_ids};
  1160         2304  
156 1160         1870 my $nidset_id = $powerset->[Marpa::R2::Internal::Powerset::NIDSET_IDS]->[$ix];
157 1160         1554 my $nidset_by_id = $asf->[Marpa::R2::Internal::ASF::NIDSET_BY_ID];
158 1160         1926 return $nidset_by_id->[$nidset_id];
159             } ## end sub Marpa::R2::Powerset::nidset_id
160              
161             sub Marpa::R2::Powerset::id {
162 0     0   0 my ($powerset) = @_;
163 0         0 return $powerset->[Marpa::R2::Internal::Powerset::ID];
164             }
165              
166             sub Marpa::R2::Powerset::show {
167 0     0   0 my ($powerset) = @_;
168 0         0 my $id = $powerset->id();
169 0         0 my $nidset_ids = $powerset->nidset_ids();
170 0         0 return "Powerset #$id: " . join q{ }, @{$nidset_ids};
  0         0  
171             } ## end sub Marpa::R2::Powerset::show
172              
173             sub set_last_choice {
174 26134     26134   39659 my ( $asf, $nook ) = @_;
175 26134         34934 my $or_nodes = $asf->[Marpa::R2::Internal::ASF::OR_NODES];
176 26134         34404 my $or_node_id = $nook->[Marpa::R2::Internal::Nook::OR_NODE];
177 26134         33227 my $and_nodes = $or_nodes->[$or_node_id];
178 26134         32100 my $choice = $nook->[Marpa::R2::Internal::Nook::FIRST_CHOICE];
179 26134 100       31359 return if $choice > $#{$and_nodes};
  26134         66228  
180 13598 100       23835 if ( nook_has_semantic_cause( $asf, $nook ) ) {
181 10934         14633 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
182 10934         14370 my $recce = $slr->[Marpa::R2::Internal::Scanless::R::THICK_G1_RECCE];
183 10934         13578 my $grammar = $recce->[Marpa::R2::Internal::Recognizer::GRAMMAR];
184 10934         13473 my $grammar_c = $grammar->[Marpa::R2::Internal::Grammar::C];
185 10934         13712 my $bocage = $recce->[Marpa::R2::Internal::Recognizer::B_C];
186 10934         14445 my $and_node_id = $and_nodes->[$choice];
187 10934   100     31952 my $current_predecessor =
188             $bocage->_marpa_b_and_node_predecessor($and_node_id) // -1;
189 10934         13996 AND_NODE: while (1) {
190 10987         13602 $choice++;
191 10987         14471 $and_node_id = $and_nodes->[$choice];
192 10987 100       20424 last AND_NODE if not defined $and_node_id;
193 59   100     239 my $next_predecessor =
194             $bocage->_marpa_b_and_node_predecessor($and_node_id) // -1;
195 59 100       171 last AND_NODE if $current_predecessor != $next_predecessor;
196             } ## end AND_NODE: while (1)
197 10934         16309 $choice--;
198             } ## end if ( nook_has_semantic_cause( $asf, $nook ) )
199 13598         18630 $nook->[Marpa::R2::Internal::Nook::LAST_CHOICE] = $choice;
200 13598         19328 return $choice;
201             } ## end sub set_last_choice
202              
203             sub nook_new {
204 12536     12536   23002 my ( $asf, $or_node_id, $parent_or_node_id ) = @_;
205 12536         17999 my $nook = [];
206 12536         23645 $nook->[Marpa::R2::Internal::Nook::OR_NODE] = $or_node_id;
207 12536   100     23084 $nook->[Marpa::R2::Internal::Nook::PARENT] = $parent_or_node_id // -1;
208 12536         16754 $nook->[Marpa::R2::Internal::Nook::FIRST_CHOICE] = 0;
209 12536         24563 set_last_choice( $asf, $nook );
210 12536         17768 return $nook;
211             } ## end sub nook_new
212              
213             sub nook_increment {
214 13598     13598   21873 my ( $asf, $nook ) = @_;
215 13598   50     23398 $nook->[Marpa::R2::Internal::Nook::LAST_CHOICE] //= 0;
216 13598         17316 $nook->[Marpa::R2::Internal::Nook::FIRST_CHOICE] =
217             $nook->[Marpa::R2::Internal::Nook::LAST_CHOICE] + 1;
218 13598 100       20727 return if not defined set_last_choice( $asf, $nook );
219 1062         2153 return 1;
220             } ## end sub nook_increment
221              
222             sub nook_has_semantic_cause {
223 45994     45994   73055 my ( $asf, $nook ) = @_;
224 45994         64837 my $or_node = $nook->[Marpa::R2::Internal::Nook::OR_NODE];
225 45994         59714 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
226 45994         55873 my $recce = $slr->[Marpa::R2::Internal::Scanless::R::THICK_G1_RECCE];
227 45994         56975 my $grammar = $recce->[Marpa::R2::Internal::Recognizer::GRAMMAR];
228 45994         59153 my $grammar_c = $grammar->[Marpa::R2::Internal::Grammar::C];
229 45994         57122 my $bocage = $recce->[Marpa::R2::Internal::Recognizer::B_C];
230              
231 45994         90255 my $irl_id = $bocage->_marpa_b_or_node_irl($or_node);
232 45994         83553 my $predot_position = $bocage->_marpa_b_or_node_position($or_node) - 1;
233 45994         84654 my $predot_isyid =
234             $grammar_c->_marpa_g_irl_rhs( $irl_id, $predot_position );
235 45994         120434 return $grammar_c->_marpa_g_nsy_is_semantic($predot_isyid);
236             } ## end sub nook_has_semantic_cause
237              
238             # No check for conflicting usage -- value(), asf(), etc.
239             # at this point
240             sub Marpa::R2::ASF::peak {
241 52     52 0 126 my ($asf) = @_;
242 52         115 my $or_nodes = $asf->[Marpa::R2::Internal::ASF::OR_NODES];
243 52         102 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
244 52         94 my $recce = $slr->[Marpa::R2::Internal::Scanless::R::THICK_G1_RECCE];
245              
246 52         94 my $bocage = $recce->[Marpa::R2::Internal::Recognizer::B_C];
247 52 50       163 die 'No Bocage' if not $bocage;
248 52         204 my $augment_or_node_id = $bocage->_marpa_b_top_or_node();
249 52         108 my $augment_and_node_id = $or_nodes->[$augment_or_node_id]->[0];
250 52         182 my $start_or_node_id =
251             $bocage->_marpa_b_and_node_cause($augment_and_node_id);
252              
253 52         205 my $base_nidset = Marpa::R2::Nidset->obtain( $asf, $start_or_node_id );
254 52         176 my $glade_id = $base_nidset->id();
255              
256             # Cannot "obtain" the glade if it is not registered
257 52         136 $asf->[Marpa::R2::Internal::ASF::GLADES]->[$glade_id]
258             ->[Marpa::R2::Internal::Glade::REGISTERED] = 1;
259 52         176 glade_obtain( $asf, $glade_id );
260 52         152 return $glade_id;
261             } ## end sub Marpa::R2::ASF::peak
262              
263             our $NID_LEAF_BASE = -43;
264              
265             # Range from -1 to -42 reserved for special values
266 5459     5459   13226 sub and_node_to_nid { return -$_[0] + $NID_LEAF_BASE; }
267 1346     1346   2269 sub nid_to_and_node { return -$_[0] + $NID_LEAF_BASE; }
268              
269             sub normalize_asf_blessing {
270 648     648   1075 my ($name) = @_;
271 648         1859 $name =~ s/\A \s * //xms;
272 648         2097 $name =~ s/ \s * \z//xms;
273 648         1202 $name =~ s/ \s+ / /gxms;
274 648         1338 $name =~ s/ \W /_/gxms;
275 648         2423 return $name;
276             } ## end sub normalize_asf_blessing
277              
278             sub Marpa::R2::Internal::ASF::blessings_set {
279 49     49   124 my ( $asf, $default_blessing ) = @_;
280 49         106 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
281 49         94 my $recce = $slr->[Marpa::R2::Internal::Scanless::R::THICK_G1_RECCE];
282 49         94 my $grammar = $recce->[Marpa::R2::Internal::Recognizer::GRAMMAR];
283 49         83 my $grammar_c = $grammar->[Marpa::R2::Internal::Grammar::C];
284 49         83 my $rules = $grammar->[Marpa::R2::Internal::Grammar::RULES];
285 49         86 my $symbols = $grammar->[Marpa::R2::Internal::Grammar::SYMBOLS];
286              
287 49         89 my $default_token_blessing_package =
288             $asf->[ Marpa::R2::Internal::ASF::DEFAULT_TOKEN_BLESSING_PACKAGE ];
289 49         84 my $default_rule_blessing_package =
290             $asf->[Marpa::R2::Internal::ASF::DEFAULT_RULE_BLESSING_PACKAGE];
291              
292 49         90 my @rule_blessing = ();
293 49         142 my $highest_rule_id = $grammar_c->highest_rule_id();
294 49         247 RULE: for ( my $rule_id = 0; $rule_id <= $highest_rule_id; $rule_id++ ) {
295 368         527 my $blessing;
296 368         503 my $rule = $rules->[$rule_id];
297 368 50       690 $blessing = $rule->[Marpa::R2::Internal::Rule::BLESSING]
298             if defined $rule;
299 368 100 66     868 if ( defined $blessing and q{::} ne substr $blessing, 0, 2 ) {
300 120         180 $rule_blessing[$rule_id] = $blessing;
301 120         232 next RULE;
302             }
303 248         588 my $lhs_id = $grammar_c->rule_lhs($rule_id);
304 248         681 my $name = $grammar->symbol_name($lhs_id);
305 248         562 $rule_blessing[$rule_id] = join q{::}, $default_rule_blessing_package,
306             normalize_asf_blessing($name);
307             } ## end RULE: for ( my $rule_id = 0; $rule_id <= $highest_rule_id; ...)
308              
309 49         132 my @symbol_blessing = ();
310 49         173 my $highest_symbol_id = $grammar_c->highest_symbol_id();
311             SYMBOL:
312 49         156 for ( my $symbol_id = 0; $symbol_id <= $highest_symbol_id; $symbol_id++ )
313             {
314 424         587 my $blessing;
315 424         583 my $symbol = $symbols->[$symbol_id];
316 424 50       782 $blessing = $symbol->[Marpa::R2::Internal::Symbol::BLESSING]
317             if defined $symbol;
318 424 100 66     814 if ( defined $blessing and q{::} ne substr $blessing, 0, 2 ) {
319 24         41 $symbol_blessing[$symbol_id] = $blessing;
320 24         51 next SYMBOL;
321             }
322 400         830 my $name = $grammar->symbol_name($symbol_id);
323 400         700 $symbol_blessing[$symbol_id] = join q{::},
324             $default_token_blessing_package,
325             normalize_asf_blessing($name);
326             } ## end SYMBOL: for ( my $symbol_id = 0; $symbol_id <= $highest_symbol_id...)
327 49         145 $asf->[Marpa::R2::Internal::ASF::RULE_BLESSINGS] = \@rule_blessing;
328 49         127 $asf->[Marpa::R2::Internal::ASF::SYMBOL_BLESSINGS] = \@symbol_blessing;
329 49         115 return $asf;
330             } ## end sub Marpa::R2::Internal::ASF::blessings_set
331              
332             # Returns undef if no parse
333             sub Marpa::R2::ASF::new {
334 49     49 1 531 my ( $class, @arg_hashes ) = @_;
335 49         134 my $asf = bless [], $class;
336              
337 49         91 my $slr;
338              
339 49         121 for my $arg_hash (@arg_hashes) {
340 49         87 ARG: for my $arg ( keys %{$arg_hash} ) {
  49         176  
341 74 100       214 if ( $arg eq 'slr' ) {
342             $asf->[Marpa::R2::Internal::ASF::SLR] = $slr =
343 49         185 $arg_hash->{$arg};
344 49         122 next ARG;
345             }
346 25 50       74 if ( $arg eq 'factoring_max' ) {
347             $asf->[Marpa::R2::Internal::ASF::FACTORING_MAX] =
348 25         55 $arg_hash->{$arg};
349 25         52 next ARG;
350             }
351             Marpa::R2::exception(
352 0         0 qq{Unknown named arg to $asf->new(): "$arg"});
353             } ## end ARG: for my $arg ( keys %{$arg_hash} )
354             } ## end for my $arg_hash (@arg_hashes)
355              
356             Marpa::R2::exception(
357 49 50       162 q{The "slr" named argument must be specified with the Marpa::R2::ASF::new method}
358             ) if not defined $slr;
359 49         117 $asf->[Marpa::R2::Internal::ASF::SLR] = $slr;
360 49   100     200 $asf->[Marpa::R2::Internal::ASF::FACTORING_MAX] //= 42;
361              
362 49         93 my $recce = $slr->[Marpa::R2::Internal::Scanless::R::THICK_G1_RECCE];
363              
364 49 50       150 if ( defined $recce->[Marpa::R2::Internal::Recognizer::TREE_MODE] ) {
365              
366             # If we already in ASF mode, or are in valuation mode, we cannot create an ASF
367 0         0 Marpa::R2::exception(
368             "An attempt was made to create an ASF for a SLIF recognizer already in use\n",
369             " The recognizer must be reset first\n",
370             ' The current SLIF recognizer mode is "',
371             $recce->[Marpa::R2::Internal::Recognizer::TREE_MODE],
372             qq{"\n}
373             );
374             } ## end if ( defined $recce->[Marpa::R2::Internal::Recognizer::TREE_MODE...])
375 49         102 $recce->[Marpa::R2::Internal::Recognizer::TREE_MODE] = 'forest';
376              
377 49         221 ( $asf->[Marpa::R2::Internal::ASF::RULE_RESOLUTIONS],
378             $asf->[Marpa::R2::Internal::ASF::LEXEME_RESOLUTIONS]
379             ) = Marpa::R2::Internal::Value::resolve_recce( $recce, $slr );
380              
381 49         171 $asf->[Marpa::R2::Internal::ASF::SYMCH_BLESSING_PACKAGE] = 'My_Symch';
382 49         121 $asf->[Marpa::R2::Internal::ASF::FACTORING_BLESSING_PACKAGE] =
383             'My_Factoring';
384 49         108 $asf->[Marpa::R2::Internal::ASF::PROBLEM_BLESSING_PACKAGE] = 'My_Problem';
385 49         110 $asf->[Marpa::R2::Internal::ASF::DEFAULT_RULE_BLESSING_PACKAGE] =
386             'My_Rule';
387 49         110 $asf->[Marpa::R2::Internal::ASF::DEFAULT_TOKEN_BLESSING_PACKAGE] =
388             'My_Token';
389              
390 49         109 $asf->[Marpa::R2::Internal::ASF::NEXT_INTSET_ID] = 0;
391 49         125 $asf->[Marpa::R2::Internal::ASF::INTSET_BY_KEY] = {};
392              
393 49         91 $asf->[Marpa::R2::Internal::ASF::NIDSET_BY_ID] = [];
394 49         103 $asf->[Marpa::R2::Internal::ASF::POWERSET_BY_ID] = [];
395              
396 49         112 $asf->[Marpa::R2::Internal::ASF::GLADES] = [];
397              
398 49         99 my $slg = $slr->[Marpa::R2::Internal::Scanless::R::GRAMMAR];
399 49         87 my $thin_slr = $slr->[Marpa::R2::Internal::Scanless::R::C];
400 49         111 my $grammar = $recce->[Marpa::R2::Internal::Recognizer::GRAMMAR];
401 49         92 my $grammar_c = $grammar->[Marpa::R2::Internal::Grammar::C];
402 49         78 my $recce_c = $recce->[Marpa::R2::Internal::Recognizer::C];
403              
404 49         187 my $ordering = $recce->ordering_get();
405 49 50       142 Marpa::R2::exception( "Parse failed\n") if not $ordering;
406              
407 49 50       216 Marpa::R2::exception(
408             "An attempt was make to create an ASF for a null parse\n",
409             " A null parse is a successful parse of a zero-length string\n",
410             " ASF's are not defined for null parses\n"
411             ) if $ordering->is_null();
412              
413 49         100 my $bocage = $recce->[Marpa::R2::Internal::Recognizer::B_C];
414              
415 49         125 my $or_nodes = $asf->[Marpa::R2::Internal::ASF::OR_NODES] = [];
416 132     132   70620 use sort 'stable';
  132         77712  
  132         870  
417 49         115 OR_NODE: for ( my $or_node_id = 0;; $or_node_id++ ) {
418 1759         4109 my @and_node_ids =
419             $ordering->_marpa_o_or_node_and_node_ids($or_node_id);
420 1759 100       3210 last OR_NODE if not scalar @and_node_ids;
421 2065         3869 my @sorted_and_node_ids = map { $_->[-1] } sort { $a <=> $b } map {
  357         793  
422 1710   100     2501 [ ( $bocage->_marpa_b_and_node_predecessor($_) // -1 ), $_ ]
  2065         7064  
423             } @and_node_ids;
424 1710         3738 $or_nodes->[$or_node_id] = \@and_node_ids;
425             } ## end OR_NODE: for ( my $or_node_id = 0;; $or_node_id++ )
426              
427 49         277 blessings_set($asf);
428 49         180 return $asf;
429              
430             } ## end sub Marpa::R2::ASF::new
431              
432             sub Marpa::R2::ASF::glade_is_visited {
433 0     0 0 0 my ( $asf, $glade_id ) = @_;
434 0         0 my $glade = $asf->[Marpa::R2::Internal::ASF::GLADES]->[$glade_id];
435 0 0       0 return if not $glade;
436 0         0 return $glade->[Marpa::R2::Internal::Glade::VISITED];
437             } ## end sub Marpa::R2::ASF::glade_is_visited
438              
439             sub Marpa::R2::ASF::glade_visited_clear {
440 0     0 0 0 my ( $asf, $glade_id ) = @_;
441 0 0       0 my $glade_list =
442             defined $glade_id
443             ? [ $asf->[Marpa::R2::Internal::ASF::GLADES]->[$glade_id] ]
444             : $asf->[Marpa::R2::Internal::ASF::GLADES];
445             $_->[Marpa::R2::Internal::Glade::VISITED] = undef
446 0         0 for grep {defined} @{$glade_list};
  0         0  
  0         0  
447 0         0 return;
448             } ## end sub Marpa::R2::ASF::glade_visited_clear
449              
450             sub nid_sort_ix {
451 900     900   1558 my ( $asf, $nid ) = @_;
452 900         1463 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
453 900         1267 my $recce = $slr->[Marpa::R2::Internal::Scanless::R::THICK_G1_RECCE];
454 900         1220 my $grammar = $recce->[Marpa::R2::Internal::Recognizer::GRAMMAR];
455 900         1249 my $grammar_c = $grammar->[Marpa::R2::Internal::Grammar::C];
456 900         1174 my $bocage = $recce->[Marpa::R2::Internal::Recognizer::B_C];
457 900 100       2350 if ( $nid >= 0 ) {
458 310         954 my $irl_id = $bocage->_marpa_b_or_node_irl($nid);
459 310         1207 return $grammar_c->_marpa_g_source_xrl($irl_id);
460             }
461 590         1164 my $and_node_id = nid_to_and_node($nid);
462 590         1879 my $token_nsy_id = $bocage->_marpa_b_and_node_symbol($and_node_id);
463 590         1591 my $token_id = $grammar_c->_marpa_g_source_xsy($token_nsy_id);
464              
465             # -2 is reserved for 'end of data'
466 590         1191 return -$token_id - 3;
467             } ## end sub nid_sort_ix
468              
469             sub Marpa::R2::ASF::grammar {
470 223     223 1 544 my ($asf) = @_;
471 223         325 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
472 223         358 my $slg = $slr->[Marpa::R2::Internal::Scanless::R::GRAMMAR];
473 223         394 return $slg;
474             } ## end sub Marpa::R2::ASF::grammar
475              
476             sub nid_rule_id {
477 875     875   1887 my ( $asf, $nid ) = @_;
478 875 100       3136 return if $nid < 0;
479 285         425 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
480 285         445 my $recce = $slr->[Marpa::R2::Internal::Scanless::R::THICK_G1_RECCE];
481 285         381 my $bocage = $recce->[Marpa::R2::Internal::Recognizer::B_C];
482 285         427 my $grammar = $recce->[Marpa::R2::Internal::Recognizer::GRAMMAR];
483 285         414 my $grammar_c = $grammar->[Marpa::R2::Internal::Grammar::C];
484 285         836 my $irl_id = $bocage->_marpa_b_or_node_irl($nid);
485 285         634 my $xrl_id = $grammar_c->_marpa_g_source_xrl($irl_id);
486 285         724 return $xrl_id;
487             }
488              
489             sub or_node_es_span {
490 1651     1651   2804 my ( $asf, $choicepoint ) = @_;
491 1651         2326 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
492 1651         2402 my $recce = $slr->[Marpa::R2::Internal::Scanless::R::THICK_G1_RECCE];
493 1651         2392 my $bocage = $recce->[Marpa::R2::Internal::Recognizer::B_C];
494 1651         4172 my $origin_es = $bocage->_marpa_b_or_node_origin($choicepoint);
495 1651         3264 my $current_es = $bocage->_marpa_b_or_node_set($choicepoint);
496 1651         4882 return $origin_es, $current_es - $origin_es;
497             } ## end sub or_node_es_span
498              
499             sub token_es_span {
500 638     638   1181 my ( $asf, $and_node_id ) = @_;
501 638         962 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
502 638         859 my $recce = $slr->[Marpa::R2::Internal::Scanless::R::THICK_G1_RECCE];
503 638         976 my $grammar = $recce->[Marpa::R2::Internal::Recognizer::GRAMMAR];
504 638         845 my $grammar_c = $grammar->[Marpa::R2::Internal::Grammar::C];
505 638         813 my $bocage = $recce->[Marpa::R2::Internal::Recognizer::B_C];
506 638         1827 my $predecessor_id = $bocage->_marpa_b_and_node_predecessor($and_node_id);
507 638         1383 my $parent_or_node_id = $bocage->_marpa_b_and_node_parent($and_node_id);
508              
509 638 100       1378 if ( defined $predecessor_id ) {
510 249         536 my $origin_es = $bocage->_marpa_b_or_node_set($predecessor_id);
511 249         834 my $current_es = $bocage->_marpa_b_or_node_set($parent_or_node_id);
512 249         633 return ( $origin_es, $current_es - $origin_es );
513             }
514 389         745 return or_node_es_span( $asf, $parent_or_node_id );
515             } ## end sub token_es_span
516              
517             sub nid_literal {
518 1805     1805   3033 my ( $asf, $nid ) = @_;
519 1805         2542 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
520 1805 100       3801 if ( $nid <= $NID_LEAF_BASE ) {
521 606         1079 my $and_node_id = nid_to_and_node($nid);
522 606         1204 my ( $start, $length ) = token_es_span( $asf, $and_node_id );
523 606 100       2128 return q{} if $length == 0;
524 186         676 return $slr->substring( $start, $length );
525             } ## end if ( $nid <= $NID_LEAF_BASE )
526 1199 50       2213 if ( $nid >= 0 ) {
527 1199         2100 return $slr->substring( or_node_es_span( $asf, $nid ) );
528             }
529 0         0 Marpa::R2::exception("No literal for node ID: $nid");
530             }
531              
532             sub nid_span {
533 95     95   146 my ( $asf, $nid ) = @_;
534 95         129 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
535 95 100       204 if ( $nid <= $NID_LEAF_BASE ) {
536 32         65 my $and_node_id = nid_to_and_node($nid);
537 32         59 my ( $start, $length ) = token_es_span( $asf, $and_node_id );
538 32 50       93 return ($start, 0) if $length == 0;
539 0         0 return $slr->es_to_input_span( $start, $length );
540             } ## end if ( $nid <= $NID_LEAF_BASE )
541 63 50       121 if ( $nid >= 0 ) {
542 63         129 return $slr->es_to_input_span( or_node_es_span( $asf, $nid ) );
543             }
544 0         0 Marpa::R2::exception("No literal for node ID: $nid");
545             }
546              
547             sub nid_token_id {
548 178     178   345 my ( $asf, $nid ) = @_;
549 178 100       418 return if $nid > $NID_LEAF_BASE;
550 118         233 my $and_node_id = nid_to_and_node($nid);
551 118         177 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
552 118         166 my $recce = $slr->[Marpa::R2::Internal::Scanless::R::THICK_G1_RECCE];
553 118         179 my $grammar = $recce->[Marpa::R2::Internal::Recognizer::GRAMMAR];
554 118         151 my $grammar_c = $grammar->[Marpa::R2::Internal::Grammar::C];
555 118         162 my $bocage = $recce->[Marpa::R2::Internal::Recognizer::B_C];
556 118         319 my $token_nsy_id = $bocage->_marpa_b_and_node_symbol($and_node_id);
557 118         289 my $token_id = $grammar_c->_marpa_g_source_xsy($token_nsy_id);
558 118         238 return $token_id;
559             }
560              
561             sub nid_symbol_id {
562 178     178   305 my ( $asf, $nid ) = @_;
563 178         343 my $token_id = nid_token_id($asf, $nid);
564 178 100       439 return $token_id if defined $token_id;
565 60 50       154 Marpa::R2::exception("No symbol ID for node ID: $nid") if $nid < 0;
566              
567             # Not a token, so return the LHS of the rule
568 60         89 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
569 60         88 my $recce = $slr->[Marpa::R2::Internal::Scanless::R::THICK_G1_RECCE];
570 60         93 my $grammar = $recce->[Marpa::R2::Internal::Recognizer::GRAMMAR];
571 60         87 my $grammar_c = $grammar->[Marpa::R2::Internal::Grammar::C];
572 60         84 my $bocage = $recce->[Marpa::R2::Internal::Recognizer::B_C];
573 60         173 my $irl_id = $bocage->_marpa_b_or_node_irl($nid);
574 60         156 my $xrl_id = $grammar_c->_marpa_g_source_xrl($irl_id);
575 60         144 my $lhs_id = $grammar_c->rule_lhs($xrl_id);
576 60         196 return $lhs_id;
577             }
578              
579             sub nid_symbol_name {
580 0     0   0 my ( $asf, $nid ) = @_;
581 0         0 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
582 0         0 my $recce = $slr->[Marpa::R2::Internal::Scanless::R::THICK_G1_RECCE];
583 0         0 my $grammar = $recce->[Marpa::R2::Internal::Recognizer::GRAMMAR];
584 0         0 my $symbol_id = nid_symbol_id($asf, $nid);
585 0         0 return $grammar->symbol_name($symbol_id);
586             }
587              
588             sub nid_token_name {
589 0     0   0 my ( $asf, $nid ) = @_;
590 0         0 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
591 0         0 my $recce = $slr->[Marpa::R2::Internal::Scanless::R::THICK_G1_RECCE];
592 0         0 my $grammar = $recce->[Marpa::R2::Internal::Recognizer::GRAMMAR];
593 0         0 my $token_id = nid_token_id($asf, $nid);
594 0 0       0 return if not defined $token_id;
595 0         0 return $grammar->symbol_name($token_id);
596             }
597              
598             # Memoization is heavily used -- it needs to be to keep the worst cases from
599             # going exponential. The need to memoize is the reason for the very heavy use of
600             # hashes. For example, quite often an HOH (hash of hashes) is used where
601             # an HoL (hash of lists) would usually be preferred. But the HOL would leave me
602             # with the problem of having duplicates, which if followed up upon, would make
603             # the algorithm go exponential.
604              
605             # For the "seen" hashes, the intent, in C, is to use a bit vector. Since typically
606             # choicepoints will only use a tiny fraction of the or- and and-node space, I'll create
607             # a per-choicepoint index in the bit vector for each or- and and-node. The index will
608             # per-ASF, and to avoid the overhead of clearing it, it will track, or each node, the
609             # current CP indexing it. It is assumed that the indexes need only remain valid within
610             # the method call that constructs the CPI (choicepoint iterator).
611              
612             sub first_factoring {
613 310     310   575 my ($choicepoint, $nid_of_choicepoint) = @_;
614              
615             # Current NID of current SYMCH
616             # The caller should ensure that we are never called unless the current
617             # NID is for a rule.
618 310 50       661 Marpa::R2::exception(
619             "Internal error: first_factoring() called for negative NID: $nid_of_choicepoint"
620             ) if $nid_of_choicepoint < 0;
621              
622             # Due to skipping, even the top or-node can have no valid choices
623 310         482 my $asf = $choicepoint->[Marpa::R2::Internal::Choicepoint::ASF];
624 310         439 my $or_nodes = $asf->[Marpa::R2::Internal::ASF::OR_NODES];
625 310 50       415 if ( not scalar @{ $or_nodes->[$nid_of_choicepoint] } ) {
  310         732  
626 0         0 $choicepoint->[Marpa::R2::Internal::Choicepoint::FACTORING_STACK] =
627             undef;
628 0         0 return;
629             }
630              
631             $choicepoint->[Marpa::R2::Internal::Choicepoint::OR_NODE_IN_USE]
632 310         802 ->{$nid_of_choicepoint} = 1;
633 310         645 my $nook = nook_new( $asf, $nid_of_choicepoint );
634 310         692 $choicepoint->[Marpa::R2::Internal::Choicepoint::FACTORING_STACK] =
635             [$nook];
636              
637             # Iterate as long as we cannot finish this stack
638 310         800 while ( not factoring_finish($choicepoint, $nid_of_choicepoint) ) {
639 0 0       0 return if not factoring_iterate($choicepoint);
640             }
641 310         556 return 1;
642              
643             }
644              
645             sub next_factoring {
646 1372     1372   2520 my ($choicepoint, $nid_of_choicepoint) = @_;
647 1372         2110 my $factoring_stack =
648             $choicepoint->[Marpa::R2::Internal::Choicepoint::FACTORING_STACK];
649 1372 50       2655 Marpa::R2::exception(
650             'Attempt to iterate factoring of uninitialized checkpoint')
651             if not $factoring_stack;
652              
653 1372         2438 while ( factoring_iterate($choicepoint) ) {
654 1062 50       2126 return 1 if factoring_finish($choicepoint, $nid_of_choicepoint);
655             }
656              
657             # Found nothing to iterate
658 310         524 return;
659             }
660              
661             sub factoring_iterate {
662 1372     1372   2111 my ($choicepoint) = @_;
663 1372         2013 my $asf = $choicepoint->[Marpa::R2::Internal::Choicepoint::ASF];
664 1372         1892 my $factoring_stack =
665             $choicepoint->[Marpa::R2::Internal::Choicepoint::FACTORING_STACK];
666 1372         1687 FIND_NODE_TO_ITERATE: while (1) {
667 13908 100       16747 if ( not scalar @{$factoring_stack} ) {
  13908         24098  
668 310         501 $choicepoint->[Marpa::R2::Internal::Choicepoint::FACTORING_STACK]
669             = undef;
670 310         790 return;
671             }
672 13598         17897 my $top_nook = $factoring_stack->[-1];
673 13598 100       20350 if ( nook_increment( $asf, $top_nook ) ) {
674 1062         2056 last FIND_NODE_TO_ITERATE; # in C, a "break" will do this
675             }
676              
677             # Could not iterate
678             # "Dirty" the corresponding bits in the parent and pop this nook
679 12536         16854 my $stack_ix_of_parent_nook =
680             $top_nook->[Marpa::R2::Internal::Nook::PARENT];
681 12536 100       19773 if ( $stack_ix_of_parent_nook >= 0 ) {
682 12226         15428 my $parent_nook = $factoring_stack->[$stack_ix_of_parent_nook];
683 12226 100       20376 $parent_nook->[Marpa::R2::Internal::Nook::CAUSE_IS_EXPANDED] = 0
684             if $top_nook->[Marpa::R2::Internal::Nook::IS_CAUSE];
685 12226 100       21625 $parent_nook->[Marpa::R2::Internal::Nook::PREDECESSOR_IS_EXPANDED]
686             = 0
687             if $top_nook->[Marpa::R2::Internal::Nook::IS_PREDECESSOR];
688             } ## end if ( $stack_ix_of_parent_nook >= 0 )
689              
690 12536         17121 my $top_or_node = $top_nook->[Marpa::R2::Internal::Nook::OR_NODE];
691             $choicepoint->[Marpa::R2::Internal::Choicepoint::OR_NODE_IN_USE]
692 12536         20438 ->{$top_or_node} = undef;
693 12536         14948 pop @{$factoring_stack};
  12536         22575  
694             } ## end FIND_NODE_TO_ITERATE: while (1)
695 1062         2195 return 1;
696             } ## end sub factoring_iterate
697              
698             sub factoring_finish {
699 1372     1372   2638 my ($choicepoint, $nid_of_choicepoint) = @_;
700 1372         2099 my $asf = $choicepoint->[Marpa::R2::Internal::Choicepoint::ASF];
701 1372         1799 my $or_nodes = $asf->[Marpa::R2::Internal::ASF::OR_NODES];
702 1372         2001 my $factoring_stack =
703             $choicepoint->[Marpa::R2::Internal::Choicepoint::FACTORING_STACK];
704              
705 1372         1787 my $nidset_by_id = $asf->[Marpa::R2::Internal::ASF::NIDSET_BY_ID];
706 1372         1902 my $powerset_by_id = $asf->[Marpa::R2::Internal::ASF::POWERSET_BY_ID];
707              
708 1372         1734 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
709 1372         1976 my $recce = $slr->[Marpa::R2::Internal::Scanless::R::THICK_G1_RECCE];
710 1372         1743 my $grammar = $recce->[Marpa::R2::Internal::Recognizer::GRAMMAR];
711 1372         1830 my $grammar_c = $grammar->[Marpa::R2::Internal::Grammar::C];
712 1372         1798 my $bocage = $recce->[Marpa::R2::Internal::Recognizer::B_C];
713              
714 1372         1913 my @worklist = ( 0 .. $#{$factoring_stack} );
  1372         2942  
715              
716 1372         3452 DO_WORKLIST: while ( scalar @worklist ) {
717 31030         40297 my $stack_ix_of_work_nook = $worklist[-1];
718 31030         39133 my $work_nook = $factoring_stack->[$stack_ix_of_work_nook];
719 31030         42783 my $work_or_node = $work_nook->[Marpa::R2::Internal::Nook::OR_NODE];
720 31030         37529 my $working_choice =
721             $work_nook->[Marpa::R2::Internal::Nook::FIRST_CHOICE];
722 31030         41508 my $work_and_node_id = $or_nodes->[$work_or_node]->[$working_choice];
723 31030         60143 my $child_or_node;
724             my $child_is_cause;
725 31030         0 my $child_is_predecessor;
726             FIND_CHILD_OR_NODE: {
727              
728 31030 100       36816 if ( !$work_nook->[Marpa::R2::Internal::Nook::CAUSE_IS_EXPANDED] )
  31030         50694  
729             {
730 13592 100       22735 if ( not nook_has_semantic_cause( $asf, $work_nook ) ) {
731 2664         5451 $child_or_node =
732             $bocage->_marpa_b_and_node_cause($work_and_node_id);
733 2664         3699 $child_is_cause = 1;
734 2664         3858 last FIND_CHILD_OR_NODE;
735             } ## end if ( not nook_has_semantic_cause( $asf, $work_nook ))
736             } ## end if ( !$work_nook->[...])
737 28366         41752 $work_nook->[Marpa::R2::Internal::Nook::CAUSE_IS_EXPANDED] = 1;
738 28366 100       46285 if ( !$work_nook
739             ->[Marpa::R2::Internal::Nook::PREDECESSOR_IS_EXPANDED] )
740             {
741 18794         33481 $child_or_node =
742             $bocage->_marpa_b_and_node_predecessor($work_and_node_id);
743 18794 100       32636 if ( defined $child_or_node ) {
744 9562         11744 $child_is_predecessor = 1;
745 9562         13517 last FIND_CHILD_OR_NODE;
746             }
747             } ## end if ( !$work_nook->[...])
748 18804         23303 $work_nook->[Marpa::R2::Internal::Nook::PREDECESSOR_IS_EXPANDED] =
749             1;
750 18804         22491 pop @worklist;
751 18804         38673 next DO_WORKLIST;
752             } ## end FIND_CHILD_OR_NODE:
753              
754             return 0
755             if
756             $choicepoint->[Marpa::R2::Internal::Choicepoint::OR_NODE_IN_USE]
757 12226 50       26023 ->{$child_or_node};
758              
759             return 0
760 12226 50       15039 if not scalar @{ $or_nodes->[$work_or_node] };
  12226         24875  
761              
762 12226         21544 my $new_nook =
763             nook_new( $asf, $child_or_node, $stack_ix_of_work_nook );
764 12226 100       22055 if ($child_is_cause) {
765 2664         4386 $new_nook->[Marpa::R2::Internal::Nook::IS_CAUSE] = 1;
766 2664         4061 $work_nook->[Marpa::R2::Internal::Nook::CAUSE_IS_EXPANDED] = 1;
767             }
768 12226 100       20214 if ($child_is_predecessor) {
769 9562         15825 $new_nook->[Marpa::R2::Internal::Nook::IS_PREDECESSOR] = 1;
770 9562         12915 $work_nook->[Marpa::R2::Internal::Nook::PREDECESSOR_IS_EXPANDED] =
771             1;
772             }
773 12226         14905 push @{$factoring_stack}, $new_nook;
  12226         18552  
774 12226         15919 push @worklist, $#{$factoring_stack};
  12226         28722  
775              
776             } ## end DO_WORKLIST: while ( scalar @worklist )
777              
778 1372         4158 return 1;
779              
780             } ## end sub factoring_finish
781              
782             sub and_nodes_to_cause_nids {
783 10936     10936   20134 my ( $asf, @and_node_ids ) = @_;
784 10936         14562 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
785 10936         13620 my $recce = $slr->[Marpa::R2::Internal::Scanless::R::THICK_G1_RECCE];
786 10936         13370 my $bocage = $recce->[Marpa::R2::Internal::Recognizer::B_C];
787 10936         14937 my %causes = ();
788 10936         16838 for my $and_node_id (@and_node_ids) {
789 10989   66     30130 my $cause_nid = $bocage->_marpa_b_and_node_cause($and_node_id)
790             // and_node_to_nid($and_node_id);
791 10989         25623 $causes{$cause_nid} = 1;
792             }
793 10936         34678 return [ keys %causes ];
794             } ## end sub and_nodes_to_cause_nids
795              
796             sub glade_id_factors {
797 1682     1682   3029 my ($choicepoint) = @_;
798 1682         2500 my $asf = $choicepoint->[Marpa::R2::Internal::Choicepoint::ASF];
799 1682         2369 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
800 1682         2199 my $recce = $slr->[Marpa::R2::Internal::Scanless::R::THICK_G1_RECCE];
801 1682         2319 my $grammar = $recce->[Marpa::R2::Internal::Recognizer::GRAMMAR];
802 1682         2299 my $grammar_c = $grammar->[Marpa::R2::Internal::Grammar::C];
803 1682         2411 my $bocage = $recce->[Marpa::R2::Internal::Recognizer::B_C];
804 1682         2279 my $or_nodes = $asf->[Marpa::R2::Internal::ASF::OR_NODES];
805              
806 1682         2053 my @result;
807 1682         2442 my $factoring_stack =
808             $choicepoint->[Marpa::R2::Internal::Choicepoint::FACTORING_STACK];
809 1682 100       4072 return if not $factoring_stack;
810             FACTOR:
811 1372         2052 for (
812             my $factor_ix = 0;
813 20176         42044 $factor_ix <= $#{$factoring_stack};
814             $factor_ix++
815             )
816             {
817 18804         25042 my $nook = $factoring_stack->[$factor_ix];
818 18804 100       29292 next FACTOR if not nook_has_semantic_cause( $asf, $nook );
819 10936         16331 my $or_node = $nook->[Marpa::R2::Internal::Nook::OR_NODE];
820 10936         14367 my $and_nodes = $or_nodes->[$or_node];
821             my $cause_nids = and_nodes_to_cause_nids(
822             $asf,
823 10936         19509 map { $and_nodes->[$_] } (
  10989         22831  
824             $nook->[Marpa::R2::Internal::Nook::FIRST_CHOICE]
825             .. $nook->[Marpa::R2::Internal::Nook::LAST_CHOICE]
826             )
827             );
828 10936         16599 my $base_nidset = Marpa::R2::Nidset->obtain( $asf, @{$cause_nids} );
  10936         20993  
829 10936         20666 my $glade_id = $base_nidset->id();
830              
831 10936         18605 $asf->[Marpa::R2::Internal::ASF::GLADES]->[$glade_id]
832             ->[Marpa::R2::Internal::Glade::REGISTERED] = 1;
833 10936         26403 push @result, $glade_id;
834             } ## end FACTOR: for ( my $factor_ix = 0; $factor_ix <= $#{...})
835 1372         4368 return \@result;
836             } ## end sub glade_id_factors
837              
838             sub glade_obtain {
839 3893     3893   6269 my ( $asf, $glade_id ) = @_;
840              
841 3893         5589 my $factoring_max = $asf->[Marpa::R2::Internal::ASF::FACTORING_MAX];
842              
843 3893         5067 my $glades = $asf->[Marpa::R2::Internal::ASF::GLADES];
844 3893         5699 my $glade = $glades->[$glade_id];
845 3893 50 33     11973 if ( not defined $glade
846             or not $glade->[Marpa::R2::Internal::Glade::REGISTERED] )
847             {
848 0         0 say Data::Dumper::Dumper($glade);
849 0         0 Marpa::R2::exception(
850             "Attempt to use an invalid glade, one whose ID is $glade_id");
851             } ## end if ( not defined $glade or not $glade->[...])
852              
853             # Return the glade if it is already set up
854 3893 100       8700 return $glade if $glade->[Marpa::R2::Internal::Glade::SYMCHES];
855              
856 858         1420 my $base_nidset =
857             $asf->[Marpa::R2::Internal::ASF::NIDSET_BY_ID]->[$glade_id];
858 858         1323 my $choicepoint;
859             my $choicepoint_powerset;
860             {
861 858         1130 my @source_data = ();
  858         1260  
862 858         1075 for my $source_nid ( @{ $base_nidset->nids() } ) {
  858         1959  
863 900         1828 my $sort_ix = nid_sort_ix( $asf, $source_nid );
864 900         2993 push @source_data, [ $sort_ix, $source_nid ];
865             }
866 858         2047 my @sorted_source_data = sort { $a->[0] <=> $b->[0] } @source_data;
  44         179  
867 858         1296 my $nid_ix = 0;
868             my ( $sort_ix_of_this_nid, $this_nid ) =
869 858         1193 @{ $sorted_source_data[ $nid_ix++ ] };
  858         1883  
870 858         1320 my @nids_with_current_sort_ix = ();
871 858         1224 my $current_sort_ix = $sort_ix_of_this_nid;
872 858         1157 my @symch_ids = ();
873 858         1069 NID: while (1) {
874              
875 1758 100       3322 if ( $sort_ix_of_this_nid != $current_sort_ix ) {
876              
877             # Currently only whole id break logic
878 875         2065 my $nidset_for_sort_ix = Marpa::R2::Nidset->obtain( $asf,
879             @nids_with_current_sort_ix );
880 875         1893 push @symch_ids, $nidset_for_sort_ix->id();
881 875         1620 @nids_with_current_sort_ix = ();
882 875         1323 $current_sort_ix = $sort_ix_of_this_nid;
883             } ## end if ( $sort_ix_of_this_nid != $current_sort_ix )
884 1758 100       3472 last NID if not defined $this_nid;
885 900         1344 push @nids_with_current_sort_ix, $this_nid;
886 900         1239 my $sorted_entry = $sorted_source_data[ $nid_ix++ ];
887 900 100       1655 if ( defined $sorted_entry ) {
888 42         113 ( $sort_ix_of_this_nid, $this_nid ) = @{$sorted_entry};
  42         98  
889 42         123 next NID;
890             }
891 858         1175 $this_nid = undef;
892 858         1206 $sort_ix_of_this_nid = -2;
893             } ## end NID: while (1)
894 858         2056 $choicepoint_powerset = Marpa::R2::Powerset->obtain( $asf, @symch_ids );
895 858         1762 $choicepoint->[Marpa::R2::Internal::Choicepoint::ASF] = $asf;
896 858         2014 $choicepoint->[Marpa::R2::Internal::Choicepoint::FACTORING_STACK] =
897             undef;
898             }
899              
900             # Check if choicepoint already seen?
901 858         1366 my @symches = ();
902 858         1730 my $symch_count = $choicepoint_powerset->count();
903 858         2009 SYMCH: for ( my $symch_ix = 0; $symch_ix < $symch_count; $symch_ix++ ) {
904 875         1304 $choicepoint->[Marpa::R2::Internal::Choicepoint::FACTORING_STACK] =
905             undef;
906 875         1781 my $symch_nidset = $choicepoint_powerset->nidset($asf, $symch_ix);
907 875         1773 my $choicepoint_nid = $symch_nidset->nid(0);
908 875   100     1711 my $symch_rule_id = nid_rule_id($asf, $choicepoint_nid) // -1;
909              
910             # Initial undef indicates no factorings omitted
911 875         1889 my @factorings = ( $symch_rule_id, undef );
912              
913             # For a token
914             # There will not be multiple factorings or nids,
915             # it is assumed, for a token
916 875 100       1853 if ( $symch_rule_id < 0 ) {
917 590         1186 my $base_nidset = Marpa::R2::Nidset->obtain( $asf, $choicepoint_nid );
918 590         1201 my $glade_id = $base_nidset->id();
919              
920 590         1002 $asf->[Marpa::R2::Internal::ASF::GLADES]->[$glade_id]
921             ->[Marpa::R2::Internal::Glade::REGISTERED] = 1;
922 590         1091 push @factorings, [$glade_id];
923 590         1133 push @symches, \@factorings;
924 590         1759 next SYMCH;
925             } ## end if ( $symch_rule_id < 0 )
926              
927 285         639 my $symch = $choicepoint_powerset->nidset($asf, $symch_ix);
928 285         603 my $nid_count = $symch->count();
929 285         436 my $factorings_omitted;
930             FACTORINGS_LOOP:
931 285         657 for ( my $nid_ix = 0; $nid_ix < $nid_count; $nid_ix++ ) {
932 310         614 $choicepoint_nid = $symch_nidset->nid($nid_ix);
933 310         828 first_factoring($choicepoint, $choicepoint_nid);
934 310         615 my $factoring = glade_id_factors($choicepoint);
935              
936 310         664 FACTOR: while ( defined $factoring ) {
937 1372 50       2840 if ( scalar @factorings > $factoring_max ) {
938              
939             # update factorings omitted flag
940 0         0 $factorings[1] = 1;
941 0         0 last FACTORINGS_LOOP;
942             }
943 1372         2181 my @factoring = ();
944 1372         1689 for (
945 1372         3134 my $item_ix = $#{$factoring};
946             $item_ix >= 0;
947             $item_ix--
948             )
949             {
950 10936         19696 push @factoring, $factoring->[$item_ix];
951             } ## end for ( my $item_ix = $#{$factoring}; $item_ix >= 0; ...)
952 1372         2245 push @factorings, \@factoring;
953 1372         3439 next_factoring($choicepoint, $choicepoint_nid);
954 1372         2618 $factoring = glade_id_factors($choicepoint);
955             } ## end FACTOR: while ( defined $factoring )
956             } ## end FACTORINGS_LOOP: for ( my $nid_ix = 0; $nid_ix < $nid_count; $nid_ix...)
957 285         931 push @symches, \@factorings;
958             } ## end SYMCH: for ( my $symch_ix = 0; $symch_ix < $symch_count; ...)
959              
960 858         1474 $glade->[Marpa::R2::Internal::Glade::SYMCHES] = \@symches;
961              
962 858         1362 $glade->[Marpa::R2::Internal::Glade::ID] = $glade_id;
963 858         1199 $asf->[Marpa::R2::Internal::ASF::GLADES]->[$glade_id] = $glade;
964 858         2416 return $glade;
965             } ## end sub glade_obtain
966              
967             sub Marpa::R2::ASF::glade_symch_count {
968 829     829 0 1512 my ( $asf, $glade_id ) = @_;
969 829         1462 my $glade = glade_obtain( $asf, $glade_id );
970 829 50       1656 Marpa::R2::exception("No glade found for glade ID $glade_id)") if not defined $glade;
971 829         1074 return scalar @{ $glade->[Marpa::R2::Internal::Glade::SYMCHES] };
  829         1493  
972             }
973              
974             sub Marpa::R2::ASF::glade_literal {
975 1805     1805 0 2976 my ( $asf, $glade_id ) = @_;
976 1805         2613 my $nidset_by_id = $asf->[Marpa::R2::Internal::ASF::NIDSET_BY_ID];
977 1805         2605 my $nidset = $nidset_by_id->[$glade_id];
978 1805 50       3335 Marpa::R2::exception("No glade found for glade ID $glade_id)") if not defined $nidset;
979 1805         3697 my $nid0 = $nidset->nid(0);
980 1805         3706 return nid_literal($asf, $nid0);
981             } ## end sub Marpa::R2::ASF::glade_literal
982              
983             sub Marpa::R2::ASF::glade_span {
984 95     95 0 222 my ( $asf, $glade_id ) = @_;
985 95         132 my $nidset_by_id = $asf->[Marpa::R2::Internal::ASF::NIDSET_BY_ID];
986 95         128 my $nidset = $nidset_by_id->[$glade_id];
987 95 50       179 Marpa::R2::exception("No glade found for glade ID $glade_id)") if not defined $nidset;
988 95         164 my $nid0 = $nidset->nid(0);
989 95         187 return nid_span($asf, $nid0);
990             }
991              
992             sub Marpa::R2::ASF::glade_symbol_id {
993 178     178 0 304 my ( $asf, $glade_id ) = @_;
994 178         263 my $nidset_by_id = $asf->[Marpa::R2::Internal::ASF::NIDSET_BY_ID];
995 178         244 my $nidset = $nidset_by_id->[$glade_id];
996 178 50       368 Marpa::R2::exception("No glade found for glade ID $glade_id)") if not defined $nidset;
997 178         323 my $nid0 = $nidset->nid(0);
998 178         423 return nid_symbol_id($asf, $nid0);
999             }
1000              
1001             sub Marpa::R2::ASF::symch_rule_id {
1002 170     170 0 459 my ( $asf, $glade_id, $symch_ix ) = @_;
1003 170         338 my $glade = glade_obtain( $asf, $glade_id );
1004 170         274 my $symches = $glade->[Marpa::R2::Internal::Glade::SYMCHES];
1005 170 50       233 return if $symch_ix > $#{$symches};
  170         339  
1006 170         246 my ($rule_id) = @{ $symches->[$symch_ix] };
  170         282  
1007 170         323 return $rule_id;
1008             } ## end sub Marpa::R2::ASF::symch_rule_id
1009              
1010             sub Marpa::R2::ASF::symch_factoring_count {
1011 1819     1819 0 2937 my ( $asf, $glade_id, $symch_ix ) = @_;
1012 1819         3337 my $glade = glade_obtain( $asf, $glade_id );
1013 1819 50       3605 Marpa::R2::exception("No glade found for glade ID $glade_id)") if not defined $glade;
1014 1819         2735 my $symches = $glade->[Marpa::R2::Internal::Glade::SYMCHES];
1015 1819 50       2680 return if $symch_ix > $#{$symches};
  1819         3650  
1016 1819         2656 return $#{ $symches->[$symch_ix] } - 1; # length minus 2
  1819         3813  
1017             } ## end sub Marpa::R2::ASF::symch_factoring_count
1018              
1019             sub Marpa::R2::ASF::factoring_downglades {
1020 290     290 0 590 my ( $asf, $glade_id, $symch_ix, $factoring_ix ) = @_;
1021 290         495 my $glade = glade_obtain( $asf, $glade_id );
1022 290 50       551 Marpa::R2::exception("No glade found for glade ID $glade_id)") if not defined $glade;
1023 290         402 my $symches = $glade->[Marpa::R2::Internal::Glade::SYMCHES];
1024             Marpa::R2::exception("No symch #$symch_ix exists for glade ID $glade_id")
1025 290 50       359 if $symch_ix > $#{$symches};
  290         545  
1026 290         396 my $symch = $symches->[$symch_ix];
1027 290         384 my ( $rule_id, undef, @factorings ) = @{$symch};
  290         555  
1028 290 50       519 Marpa::R2::exception("No downglades for glade ID $glade_id, symch #$symch_ix: it is a token symch")
1029             if $rule_id < 0;
1030 290 50       567 return if $factoring_ix >= scalar @factorings;
1031 290         416 my $factoring = $factorings[$factoring_ix];
1032 290         532 return $factoring;
1033             }
1034              
1035             sub Marpa::R2::ASF::factoring_symbol_count {
1036 85     85 0 152 my ( $asf, $glade_id, $symch_ix, $factoring_ix ) = @_;
1037 85         187 my $factoring = $asf->factoring_downglades($glade_id, $symch_ix, $factoring_ix);
1038 85 50       161 return if not defined $factoring;
1039 85         106 return scalar @{$factoring};
  85         143  
1040             } ## end sub Marpa::R2::ASF::factoring_symbol_count
1041              
1042             sub Marpa::R2::ASF::factor_downglade {
1043 152     152 0 295 my ( $asf, $glade_id, $symch_ix, $factoring_ix, $symbol_ix ) = @_;
1044 152         274 my $factoring = $asf->factoring_downglades($glade_id, $symch_ix, $factoring_ix);
1045 152 50       255 return if not defined $factoring;
1046 152         245 return $factoring->[$symbol_ix];
1047             } ## end sub Marpa::R2::ASF::factor_downglade
1048              
1049             sub Marpa::R2::Internal::ASF::ambiguities {
1050 8     8   47 my ($asf) = @_;
1051 8         29 my $peak = $asf->peak();
1052 8         45 return Marpa::R2::Internal::ASF::glade_ambiguities( $asf, $peak, [] );
1053             }
1054              
1055             sub Marpa::R2::Internal::ASF::glade_ambiguities {
1056 28     28   62 my ( $asf, $glade, $seen ) = @_;
1057 28 50       62 return [] if $seen->[$glade]; # empty on revisit
1058 28         51 $seen->[$glade] = 1;
1059 28         65 my $grammar = $asf->grammar();
1060 28         72 my $symch_count = $asf->glade_symch_count($glade);
1061 28 100       73 if ( $symch_count > 1 ) {
1062 4         24 my $literal = $asf->glade_literal($glade);
1063 4         14 my $symbol_id = $asf->glade_symbol_id($glade);
1064 4         18 my $display_form = $grammar->symbol_display_form($symbol_id);
1065 4         26 return [ [ 'symch', $glade, ] ];
1066             } ## end if ( $symch_count > 1 )
1067 24         69 my $rule_id = $asf->symch_rule_id( $glade, 0 );
1068 24 100       60 return [] if $rule_id < 0; # no ambiguities if a token
1069              
1070             # ignore any truncation of the factorings
1071              
1072 19         53 my $factoring_count = $asf->symch_factoring_count( $glade, 0 );
1073 19 100       43 if ( $factoring_count <= 1 ) {
1074 15         42 my $downglades = $asf->factoring_downglades( $glade, 0, 0 );
1075             my @problems =
1076 15         62 map { @{ glade_ambiguities( $asf, $_, $seen ) } } @{$downglades};
  19         22  
  19         168  
  15         36  
1077 15         55 return \@problems;
1078             } ## end if ( $factoring_count <= 1 )
1079 4         10 my @results = ();
1080              
1081 4         13 my $downglades = $asf->factoring_downglades( $glade, 0, 0 );
1082 4         11 my $min_factors = $#{$downglades} + 1;
  4         9  
1083 4         18 my ( $upglade_start, $upglade_length ) = $asf->glade_span($glade);
1084 4         11 my $sync_location = $upglade_start + $upglade_length;
1085              
1086 4         10 my @factors_by_factoring = ($downglades);
1087 4         15 for (
1088             my $factoring_ix = 1;
1089             $factoring_ix < $factoring_count;
1090             $factoring_ix++
1091             )
1092             {
1093 12         28 my $downglades =
1094             $asf->factoring_downglades( $glade, 0, $factoring_ix );
1095 12         18 my $factor_count = $#{$downglades} + 1;
  12         23  
1096 12 100       28 $min_factors =
1097             $min_factors > $factor_count ? $factor_count : $min_factors;
1098              
1099             # Determine a first potential
1100             # "sync location of the factors" from
1101             # the earliest start of the first downglade of any factoring.
1102             # Currently this will be the start of the parent glade, but this
1103             # method will be safe against any future hacks.
1104 12         25 my ($this_sync_location) = $asf->glade_span( $downglades->[0] );
1105 12         35 $sync_location =
1106             List::Util::min( $this_sync_location, $sync_location );
1107              
1108 12         33 push @factors_by_factoring, $downglades;
1109             } ## end for ( my $factoring_ix = 1; $factoring_ix < $factoring_count...)
1110              
1111 4         17 my @factor_ix = (0) x $factoring_count;
1112 4         16 SYNC_PASS: while (1) {
1113              
1114             # Assume synced and unambiguous until we see otherwise.
1115 14         24 my $is_synced = 1;
1116              
1117             # First find a synch'ed set of factors, if we can
1118             FACTORING:
1119 14         32 for (
1120             my $factoring_ix = 0;
1121             $factoring_ix < $factoring_count;
1122             $factoring_ix++
1123             )
1124             {
1125 43         62 my $this_factor_ix = $factor_ix[$factoring_ix];
1126 43         64 my $this_downglade =
1127             $factors_by_factoring[$factoring_ix][$this_factor_ix];
1128 43         72 my ($this_start) = $asf->glade_span($this_downglade);
1129              
1130             # To keep time complexity down we limit the number of times we deal
1131             # with a factoring at a sync location to 3, worst case -- a pass which
1132             # identifies it as a potential sync location, a pass which
1133             # (if possible) brings all the factors to that location, and a
1134             # pass which leaves all factor IX's where they are, and determines
1135             # we have found a sync location. This makes out time O(f*n), where
1136             # f is the factoring count and n is the mininum number of factors.
1137              
1138 43         96 while ( $this_start < $sync_location ) {
1139 11         17 $factor_ix[$factoring_ix]++;
1140 11 100       27 last SYNC_PASS if $factor_ix[$factoring_ix] >= $min_factors;
1141 7         11 $this_start = $asf->glade_span($this_downglade);
1142             } ## end if ( $this_start < $sync_location )
1143 39 100       95 if ( $this_start > $sync_location ) {
1144 7         11 $is_synced = 0;
1145 7         16 $sync_location = $this_start;
1146             }
1147             } ## end FACTORING: for ( my $factoring_ix = 0; $factoring_ix < ...)
1148              
1149 10 100       33 next SYNC_PASS if not $is_synced;
1150              
1151             # If here, every factor starts at the sync location
1152              
1153             SYNCED_RESULT: {
1154              
1155 6         6 my $ambiguous_factors;
  6         10  
1156 6         9 my $first_factor_ix = $factor_ix[0];
1157 6         12 my $first_downglade = $factors_by_factoring[0][$first_factor_ix];
1158              
1159             FACTORING:
1160 6         17 for (
1161             my $factoring_ix = 1;
1162             $factoring_ix < $factoring_count;
1163             $factoring_ix++
1164             )
1165             {
1166 8         13 my $this_factor_ix = $factor_ix[$factoring_ix];
1167 8         13 my $this_downglade =
1168             $factors_by_factoring[$factoring_ix][$this_factor_ix];
1169 8 100       21 if ( $this_downglade != $first_downglade ) {
1170 5         13 $ambiguous_factors = [
1171             $first_factor_ix, $factoring_ix,
1172             $this_factor_ix
1173             ];
1174 5         10 last FACTORING;
1175             } ## end if ( $this_downglade != $first_downglade )
1176              
1177             } ## end FACTORING: for ( my $factoring_ix = 1; $factoring_ix < ...)
1178              
1179             # If here, all the the downglades are identical
1180 6 100       15 if ( not defined $ambiguous_factors ) {
1181             push @results,
1182 1         2 @{ glade_ambiguities( $asf, $first_downglade, $seen ) };
  1         19  
1183 1         13 last SYNCED_RESULT;
1184             }
1185              
1186             # First factoring IX is always zero
1187             push @results,
1188 5         10 [ 'factoring', $glade, 0, @{$ambiguous_factors} ];
  5         14  
1189             } ## end SYNCED_RESULT:
1190              
1191 6         43 $factor_ix[$_]++ for 0 .. $factoring_count;
1192 6 50       31 last SYNC_PASS if List::Util::max(@factor_ix) >= $min_factors;
1193              
1194             } ## end SYNC_PASS: while (1)
1195              
1196 4         21 return \@results;
1197              
1198             } ## end sub Marpa::R2::Internal::ASF::glade_ambiguities
1199              
1200             # A generic display routine for ambiguities -- complex application will
1201             # want to replace this, using it perhaps as a fallback.
1202             sub Marpa::R2::Internal::ASF::ambiguities_show {
1203 8     8   86 my ( $asf, $ambiguities ) = @_;
1204 8         24 my $grammar = $asf->grammar();
1205 8         31 my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
1206 8         17 my $p_input = $slr->[Marpa::R2::Internal::Scanless::R::P_INPUT_STRING];
1207 8         17 my $result = q{};
1208 8         15 AMBIGUITY: for my $ambiguity ( @{$ambiguities} ) {
  8         20  
1209 9         18 my $type = $ambiguity->[0];
1210 9 100       31 if ( $type eq 'symch' ) {
1211              
1212             # Not tested !!!!
1213 4         8 my ( undef, $glade ) = @{$ambiguity};
  4         10  
1214 4         10 my $symbol_display_form =
1215             $grammar->symbol_display_form(
1216             $asf->glade_symbol_id($glade) );
1217 4         16 my ( $start, $length ) = $asf->glade_span($glade);
1218 4         25 my ( $start_line, $start_column ) = $slr->line_column($start);
1219 4         18 my ( $end_line, $end_column ) =
1220             $slr->line_column( $start + $length - 1 );
1221 4         27 my $display_length = List::Util::min( $length, 60 );
1222 4         20 $result
1223             .= qq{Ambiguous symch at Glade=$glade, Symbol=<$symbol_display_form>:\n};
1224 4         23 $result
1225             .= qq{ The ambiguity is from line $start_line, column $start_column }
1226             . qq{to line $end_line, column $end_column\n};
1227 4 50       23 my $literal_label =
1228             $display_length == $length ? 'Text is: ' : 'Text begins: ';
1229 4         18 $result
1230             .= q{ }
1231             . $literal_label
1232             . Marpa::R2::Internal::Scanless::input_escape( $p_input,
1233             $start, $display_length )
1234             . qq{\n};
1235              
1236 4         18 my $symch_count = $asf->glade_symch_count($glade);
1237 4         37 my $display_symch_count = List::Util::min( 5, $symch_count );
1238 4 50       17 $result .=
1239             $symch_count == $display_symch_count
1240             ? " There are $symch_count symches\n"
1241             : " There are $symch_count symches -- showing only the first $display_symch_count\n";
1242 4         21 SYMCH_IX: for my $symch_ix ( 0 .. $display_symch_count - 1 ) {
1243 8         19 my $rule_id = $asf->symch_rule_id( $glade, $symch_ix );
1244 8 50       35 if ( $rule_id < 0 ) {
1245 0         0 $result .= " Symch $symch_ix is a token\n";
1246 0         0 next SYMCH_IX;
1247             }
1248 8         51 $result .= " Symch $symch_ix is a rule: "
1249             . $grammar->rule_show($rule_id) . "\n";
1250             } ## end SYMCH_IX: for my $symch_ix ( 0 .. $display_symch_count - 1 )
1251              
1252 4         15 next AMBIGUITY;
1253             } ## end if ( $type eq 'symch' )
1254 5 50       15 if ( $type eq 'factoring' ) {
1255 5         7 my $factoring_ix1 = 0;
1256             my ( undef, $glade, $symch_ix, $factor_ix1, $factoring_ix2,
1257             $factor_ix2 )
1258 5         11 = @{$ambiguity};
  5         12  
1259 5         14 my $first_downglades =
1260             $asf->factoring_downglades( $glade, $symch_ix, 0 );
1261 5         9 my $first_downglade = $first_downglades->[$factor_ix1];
1262             {
1263 5         9 my $these_downglades =
  5         13  
1264             $asf->factoring_downglades( $glade, $symch_ix,
1265             $factoring_ix2 );
1266 5         12 my $this_downglade = $these_downglades->[$factor_ix2];
1267 5         14 my $symbol_display_form =
1268             $grammar->symbol_display_form(
1269             $asf->glade_symbol_id($first_downglade) );
1270 5         15 my ( $start, $first_length ) =
1271             $asf->glade_span($first_downglade);
1272 5         14 my ( undef, $this_length ) =
1273             $asf->glade_span($this_downglade);
1274 5         24 my ( $start_line, $start_column ) = $slr->line_column($start);
1275 5         16 my $display_length =
1276             List::Util::min( $first_length, $this_length, 60 );
1277 5         24 $result
1278             .= qq{Length of symbol "$symbol_display_form" at line $start_line, column $start_column is ambiguous\n};
1279              
1280 5 100       24 if ( $display_length > 0 ) {
1281 1         17 $result .= qq{ Choices start with: }
1282             . Marpa::R2::Internal::Scanless::input_escape(
1283             $p_input, $start, $display_length )
1284             . qq{\n};
1285             } ## end if ( $display_length > 0 )
1286              
1287 5         12 my @display_downglade = ( $first_downglade, $this_downglade );
1288             DISPLAY_GLADE:
1289 5         19 for (
1290             my $glade_ix = 0;
1291             $glade_ix <= $#display_downglade;
1292             $glade_ix++
1293             )
1294             {
1295             # Choices may be zero length
1296 10         20 my $choice_number = $glade_ix + 1;
1297 10         16 my $glade_id = $display_downglade[$glade_ix];
1298 10         24 my ( undef, $length ) = $asf->glade_span($glade_id);
1299 10 100       23 if ( $length <= 0 ) {
1300 4         14 $result
1301             .= qq{ Choice $choice_number is zero length\n};
1302 4         12 next DISPLAY_GLADE;
1303             }
1304 6         21 my ( $end_line, $end_column ) =
1305             $slr->line_column( $start + $length - 1 );
1306 6         25 $result
1307             .= qq{ Choice $choice_number, length=$length, ends at line $end_line, column $end_column\n};
1308 6 50       45 if ( $length > 60 ) {
1309 0         0 $result .= qq{ Choice $choice_number ending: }
1310             . Marpa::R2::Internal::Scanless::reversed_input_escape(
1311             $p_input, $start + $length, 60 )
1312             . qq{\n};
1313 0         0 next DISPLAY_GLADE;
1314             } ## end if ( $length > 60 )
1315 6         27 $result .= qq{ Choice $choice_number: }
1316             . Marpa::R2::Internal::Scanless::input_escape(
1317             $p_input, $start, $length )
1318             . qq{\n};
1319              
1320             } ## end DISPLAY_GLADE: for ( my $glade_ix = 0; $glade_ix <= ...)
1321 5         18 next AMBIGUITY;
1322             } ## end FACTORING: for ( my $factoring_ix = 1; $factoring_ix < ...)
1323 0         0 next AMBIGUITY;
1324             } ## end if ( $type eq 'factoring' )
1325             $result
1326 0         0 .= qq{Ambiguities of type "$type" not implemented:\n}
1327             . Data::Dumper::dumper($ambiguity);
1328 0         0 next AMBIGUITY;
1329              
1330             } ## end AMBIGUITY: for my $ambiguity ( @{$ambiguities} )
1331 8         344 return $result;
1332             } ## end sub Marpa::R2::Internal::ASF::ambiguities_show
1333              
1334             # The higher level calls
1335              
1336             sub Marpa::R2::ASF::traverse {
1337 31     31 1 981 my ( $asf, $per_traverse_object, $method ) = @_;
1338 31 50       112 if ( ref $method ne 'CODE' ) {
1339 0         0 Marpa::R2::exception(
1340             'Argument to $asf->traverse() must be an anonymous subroutine');
1341             }
1342 31 50       102 if ( not ref $per_traverse_object ) {
1343 0         0 Marpa::R2::exception(
1344             'Argument to $asf->traverse() must be a reference');
1345             }
1346 31         103 my $peak = $asf->peak();
1347 31         138 my $peak_glade = glade_obtain( $asf, $peak );
1348 31         158 my $traverser = bless [], "Marpa::R2::Internal::ASF::Traverse";
1349 31         102 $traverser->[Marpa::R2::Internal::ASF::Traverse::ASF] = $asf;
1350 31         58 $traverser->[Marpa::R2::Internal::ASF::Traverse::CODE] = $method;
1351 31         62 $traverser->[Marpa::R2::Internal::ASF::Traverse::PER_TRAVERSE_OBJECT] = $per_traverse_object;
1352 31         51 $traverser->[Marpa::R2::Internal::ASF::Traverse::VALUES] = [];
1353 31         63 $traverser->[Marpa::R2::Internal::ASF::Traverse::GLADE] = $peak_glade;
1354 31         62 $traverser->[Marpa::R2::Internal::ASF::Traverse::SYMCH_IX] = 0;
1355 31         74 $traverser->[Marpa::R2::Internal::ASF::Traverse::FACTORING_IX] = 0;
1356 31         114 return $method->( $traverser, $per_traverse_object );
1357             } ## end sub Marpa::R2::ASF::traverse
1358              
1359             sub Marpa::R2::Internal::ASF::Traverse::all_choices {
1360 12     12   92 my ( $traverser ) = @_;
1361              
1362 12         30 my @values = Marpa::R2::Internal::ASF::Traverse::rh_values( $traverser );
1363 12         25 my @results = ( [] );
1364 12         32 for my $rh_ix ( 0 .. @values - 1 ) {
1365 24         33 my @new_results = ();
1366 24         38 for my $old_result (@results) {
1367 26         37 my $child_value = $values[$rh_ix];
1368 26 50       55 $child_value = [ $child_value ] unless ref $child_value eq 'ARRAY';
1369 26         34 for my $new_value ( @{ $child_value } ) {
  26         39  
1370 30         35 push @new_results, [ @{$old_result}, $new_value ];
  30         83  
1371             }
1372             }
1373 24         49 @results = @new_results;
1374             } ## end for my $rh_ix ( 0 .. $length - 1 )
1375              
1376 12         28 return @results;
1377             }
1378              
1379              
1380             sub Marpa::R2::Internal::ASF::Traverse::literal {
1381 1742     1742   7797 my ( $traverser ) = @_;
1382 1742         2548 my $asf = $traverser->[Marpa::R2::Internal::ASF::Traverse::ASF];
1383 1742         2297 my $glade = $traverser->[Marpa::R2::Internal::ASF::Traverse::GLADE];
1384 1742         2273 my $glade_id = $glade->[Marpa::R2::Internal::Glade::ID];
1385 1742         3340 return $asf->glade_literal($glade_id);
1386             }
1387              
1388             sub Marpa::R2::Internal::ASF::Traverse::span {
1389 4     4   27 my ( $traverser ) = @_;
1390 4         8 my $asf = $traverser->[Marpa::R2::Internal::ASF::Traverse::ASF];
1391 4         9 my $glade = $traverser->[Marpa::R2::Internal::ASF::Traverse::GLADE];
1392 4         6 my $glade_id = $glade->[Marpa::R2::Internal::Glade::ID];
1393 4         10 return $asf->glade_span($glade_id);
1394             }
1395              
1396             sub Marpa::R2::Internal::ASF::Traverse::symbol_id {
1397 103     103   311 my ( $traverser ) = @_;
1398 103         194 my $asf = $traverser->[Marpa::R2::Internal::ASF::Traverse::ASF];
1399 103         154 my $glade = $traverser->[Marpa::R2::Internal::ASF::Traverse::GLADE];
1400 103         137 my $glade_id = $glade->[Marpa::R2::Internal::Glade::ID];
1401 103         200 return $asf->glade_symbol_id($glade_id);
1402             }
1403              
1404             sub Marpa::R2::Internal::ASF::Traverse::rule_id {
1405 1799     1799   9456 my ( $traverser ) = @_;
1406 1799         2791 my $glade = $traverser->[Marpa::R2::Internal::ASF::Traverse::GLADE];
1407 1799         2490 my $symch_ix =
1408             $traverser->[Marpa::R2::Internal::ASF::Traverse::SYMCH_IX];
1409 1799         2558 my $symch = $glade->[Marpa::R2::Internal::Glade::SYMCHES]->[$symch_ix];
1410 1799         2345 my ( $rule_id ) = @{$symch};
  1799         3225  
1411 1799 100       4075 return if $rule_id < 0;
1412 1252         2112 return $rule_id;
1413             } ## end sub Marpa::R2::Internal::ASF::Traverse::rule_id
1414              
1415             sub Marpa::R2::Internal::ASF::Traverse::rh_length {
1416 1249     1249   4176 my ( $traverser ) = @_;
1417 1249         1799 my $glade = $traverser->[Marpa::R2::Internal::ASF::Traverse::GLADE];
1418 1249         1690 my $symch_ix =
1419             $traverser->[Marpa::R2::Internal::ASF::Traverse::SYMCH_IX];
1420 1249         1670 my $symch = $glade->[Marpa::R2::Internal::Glade::SYMCHES]->[$symch_ix];
1421 1249         1605 my ( $rule_id, undef, @factorings ) = @{$symch};
  1249         8135  
1422 1249 50       2697 Marpa::R2::exception(
1423             '$glade->rh_length($rh_ix) called for a token -- that is not allowed')
1424             if $rule_id < 0;
1425 1249         1761 my $factoring_ix =
1426             $traverser->[Marpa::R2::Internal::ASF::Traverse::FACTORING_IX];
1427 1249         1817 my $factoring = $factorings[$factoring_ix];
1428 1249         1541 return scalar @{$factoring};
  1249         6901  
1429             } ## end sub Marpa::R2::Internal::ASF::Traverse::rh_length
1430              
1431             sub Marpa::R2::Internal::ASF::Traverse::rh_value {
1432 10708     10708   26774 my ( $traverser, $rh_ix ) = @_;
1433 10708         14810 my $glade = $traverser->[Marpa::R2::Internal::ASF::Traverse::GLADE];
1434 10708         13316 my $symch_ix =
1435             $traverser->[Marpa::R2::Internal::ASF::Traverse::SYMCH_IX];
1436 10708         14054 my $symch = $glade->[Marpa::R2::Internal::Glade::SYMCHES]->[$symch_ix];
1437 10708         12883 my ( $rule_id, undef, @factorings ) = @{$symch};
  10708         59225  
1438 10708 50       21169 Marpa::R2::exception(
1439             '$glade->rh_value($rh_ix) called for a token -- that is not allowed')
1440             if $rule_id < 0;
1441 10708         15004 my $factoring_ix =
1442             $traverser->[Marpa::R2::Internal::ASF::Traverse::FACTORING_IX];
1443 10708         13883 my $factoring = $factorings[$factoring_ix];
1444 10708 50       12882 return if $rh_ix > $#{$factoring};
  10708         19479  
1445 10708         15826 my $downglade_id = $factoring->[$rh_ix];
1446 10708         13676 my $memoized_value = $traverser->[Marpa::R2::Internal::ASF::Traverse::VALUES]->[$downglade_id];
1447 10708 100       58615 return $memoized_value if defined $memoized_value;
1448 702         1015 my $asf = $traverser->[Marpa::R2::Internal::ASF::Traverse::ASF];
1449 702         1209 my $downglade = glade_obtain( $asf, $downglade_id );
1450 702         1329 my $blessing = ref $traverser;
1451              
1452             # A shallow clone
1453 702         949 my $child_traverser = bless [ @{$traverser} ], $blessing;
  702         1895  
1454 702         1135 $child_traverser->[Marpa::R2::Internal::ASF::Traverse::GLADE] =
1455             $downglade;
1456 702         942 $child_traverser->[Marpa::R2::Internal::ASF::Traverse::SYMCH_IX] = 0;
1457 702         1008 $child_traverser->[Marpa::R2::Internal::ASF::Traverse::FACTORING_IX] = 0;
1458 702         959 my $code = $traverser->[Marpa::R2::Internal::ASF::Traverse::CODE];
1459 702         2228 my $value = $code->(
1460             $child_traverser,
1461             $traverser->[Marpa::R2::Internal::ASF::Traverse::PER_TRAVERSE_OBJECT]
1462             );
1463 702 50       2974 Marpa::R2::exception(
1464             'The ASF traversing method returned undef -- that is not allowed')
1465             if not defined $value;
1466 702         1229 $traverser->[Marpa::R2::Internal::ASF::Traverse::VALUES]->[$downglade_id]
1467             = $value;
1468 702         3829 return $value;
1469             } ## end sub Marpa::R2::Internal::ASF::Traverse::rh_value
1470              
1471             sub Marpa::R2::Internal::ASF::Traverse::rh_values {
1472 17     17   43 my ( $traverser ) = @_;
1473 17         36 return map { Marpa::R2::Internal::ASF::Traverse::rh_value( $traverser, $_ ) }
  35         77  
1474             0 .. Marpa::R2::Internal::ASF::Traverse::rh_length( $traverser ) - 1;
1475             }
1476              
1477             sub Marpa::R2::Internal::ASF::Traverse::next_factoring {
1478 1721     1721   2711 my ($traverser) = @_;
1479 1721         2764 my $glade = $traverser->[Marpa::R2::Internal::ASF::Traverse::GLADE];
1480 1721         2505 my $glade_id = $glade->[Marpa::R2::Internal::Glade::ID];
1481 1721         2264 my $asf = $traverser->[Marpa::R2::Internal::ASF::Traverse::ASF];
1482 1721         2224 my $symch_ix = $traverser->[Marpa::R2::Internal::ASF::Traverse::SYMCH_IX];
1483 1721         3197 my $last_factoring =
1484             $asf->symch_factoring_count( $glade_id, $symch_ix ) - 1;
1485 1721         2341 my $factoring_ix =
1486             $traverser->[Marpa::R2::Internal::ASF::Traverse::FACTORING_IX];
1487 1721 100       4106 return if $factoring_ix >= $last_factoring;
1488 1056         1451 $factoring_ix++;
1489 1056         1568 $traverser->[Marpa::R2::Internal::ASF::Traverse::FACTORING_IX] =
1490             $factoring_ix;
1491 1056         2662 return $factoring_ix;
1492             } ## end sub Marpa::R2::Internal::ASF::Traverse::next_factoring
1493              
1494             sub Marpa::R2::Internal::ASF::Traverse::next_symch {
1495 665     665   1222 my ($traverser) = @_;
1496 665         1133 my $glade = $traverser->[Marpa::R2::Internal::ASF::Traverse::GLADE];
1497 665         871 my $glade_id = $glade->[Marpa::R2::Internal::Glade::ID];
1498 665         952 my $asf = $traverser->[Marpa::R2::Internal::ASF::Traverse::ASF];
1499 665         876 my $symch_ix = $traverser->[Marpa::R2::Internal::ASF::Traverse::SYMCH_IX];
1500 665         2755 my $last_symch = $asf->glade_symch_count( $glade_id ) - 1;
1501 665 100       2148 return if $symch_ix >= $last_symch;
1502 7         18 $symch_ix++;
1503 7         14 $traverser->[Marpa::R2::Internal::ASF::Traverse::SYMCH_IX] = $symch_ix;
1504 7         16 $traverser->[Marpa::R2::Internal::ASF::Traverse::FACTORING_IX] = 0;
1505 7         25 return $symch_ix;
1506             } ## end sub Marpa::R2::Internal::ASF::Traverse::next_symch
1507              
1508             sub Marpa::R2::Internal::ASF::Traverse::next {
1509 1721     1721   3828 my ($traverser) = @_;
1510 1721   100     3211 return $traverser->next_factoring() // $traverser->next_symch();
1511             }
1512              
1513             # GLADE_SEEN is a local -- this is to silence warnings
1514             our %GLADE_SEEN;
1515              
1516             sub form_choice {
1517 35     35   60 my ( $parent_choice, $sub_choice ) = @_;
1518 35 100       88 return $sub_choice if not defined $parent_choice;
1519 10         29 return join q{.}, $parent_choice, $sub_choice;
1520             }
1521              
1522             sub Marpa::R2::ASF::dump_glade {
1523 163     163 0 320 my ( $asf, $glade_id, $parent_choice, $item_ix ) = @_;
1524 163 100       392 if ( $GLADE_SEEN{$glade_id} ) {
1525 44         131 return [ [0, $glade_id, "already displayed"] ];
1526             }
1527 119         242 $GLADE_SEEN{$glade_id} = 1;
1528              
1529 119         226 my $grammar = $asf->grammar();
1530 119         180 my @lines = ();
1531 119         167 my $symch_indent = 0;
1532              
1533 119         228 my $symch_count = $asf->glade_symch_count($glade_id);
1534 119         178 my $symch_choice = $parent_choice;
1535 119 100       232 if ( $symch_count > 1 ) {
1536 3   50     12 $item_ix //= 0;
1537 3         16 push @lines,
1538             [ 0, undef, "Symbol #$item_ix "
1539             . $grammar->symbol_display_form($asf->glade_symbol_id($glade_id))
1540             . " has $symch_count symches" ];
1541 3         8 $symch_indent += 2;
1542 3         8 $symch_choice = form_choice( $parent_choice, $item_ix );
1543             } ## end if ( $symch_count > 1 )
1544 119         259 for ( my $symch_ix = 0; $symch_ix < $symch_count; $symch_ix++ ) {
1545 122 100       257 my $current_choice =
1546             $symch_count > 1
1547             ? form_choice( $symch_choice, $symch_ix )
1548             : $symch_choice;
1549 122         165 my $indent = $symch_indent;
1550 122 100       234 if ( $symch_count > 1 ) {
1551 6         17 push @lines, [ $symch_indent , undef, "Symch #$current_choice" ];
1552             }
1553 122         280 my $rule_id = $asf->symch_rule_id( $glade_id, $symch_ix );
1554 122 100       222 if ( $rule_id >= 0 ) {
1555 67         269 push @lines,
1556             [
1557             $symch_indent, $glade_id,
1558             "Rule $rule_id: " . $grammar->rule_show($rule_id)
1559             ];
1560 67         115 for my $line (
1561 67         198 @{ dump_factorings(
1562             $asf, $glade_id, $symch_ix, $current_choice
1563             ) }
1564             )
1565             {
1566 515         634 my ( $line_indent, @rest_of_line ) = @{$line};
  515         937  
1567 515         1320 push @lines, [ $line_indent + $symch_indent + 2, @rest_of_line ];
1568             } ## end for my $line ( dump_factorings( $asf, $glade_id, ...))
1569             } ## end if ( $rule_id >= 0 )
1570             else {
1571 55         152 my $line = dump_terminal( $asf, $glade_id, $current_choice );
1572 55         84 my ( $line_indent, @rest_of_line ) = @{$line};
  55         135  
1573 55         259 push @lines, [ $line_indent + $symch_indent, @rest_of_line ];
1574             } ## end else [ if ( $rule_id >= 0 ) ]
1575             } ## end for ( my $symch_ix = 0; $symch_ix < $symch_count; $symch_ix...)
1576 119         281 return \@lines;
1577             }
1578              
1579             # Show all the factorings of a SYMCH
1580             sub dump_factorings {
1581 67     67   140 my ( $asf, $glade_id, $symch_ix, $parent_choice ) = @_;
1582              
1583 67         92 my @lines;
1584 67         145 my $factoring_count = $asf->symch_factoring_count( $glade_id, $symch_ix );
1585 67         166 for (
1586             my $factoring_ix = 0;
1587             $factoring_ix < $factoring_count;
1588             $factoring_ix++
1589             )
1590             {
1591 85         116 my $indent = 0;
1592 85         131 my $current_choice = $parent_choice;
1593 85 100       152 if ( $factoring_count > 1 ) {
1594 26         38 $indent = 2;
1595 26         49 $current_choice = form_choice( $parent_choice, $factoring_ix );
1596 26         76 push @lines, [ 0, undef, "Factoring #$current_choice" ];
1597             }
1598 85         196 my $symbol_count =
1599             $asf->factoring_symbol_count( $glade_id, $symch_ix,
1600             $factoring_ix );
1601 85         213 SYMBOL: for my $symbol_ix ( 0 .. $symbol_count - 1 ) {
1602 152         324 my $downglade =
1603             $asf->factor_downglade( $glade_id, $symch_ix, $factoring_ix,
1604             $symbol_ix );
1605 152         191 for my $line (
1606 152         357 @{ $asf->dump_glade( $downglade, $current_choice,
1607             $symbol_ix )
1608             }
1609             )
1610             {
1611 489         628 my ( $line_indent, @rest_of_line ) = @{$line};
  489         870  
1612 489         1342 push @lines, [ $line_indent + $indent, @rest_of_line ];
1613              
1614             } ## end for my $line ( @{ $asf->dump_glade( $downglade, ...)})
1615             } ## end SYMBOL: for my $symbol_ix ( 0 .. $symbol_count - 1 )
1616             } ## end for ( my $factoring_ix = 0; $factoring_ix < $factoring_count...)
1617 67         145 return \@lines;
1618             } ## end sub dump_factorings
1619              
1620             sub dump_terminal {
1621 55     55   106 my ( $asf, $glade_id, $symch_ix, $parent_choice ) = @_;
1622              
1623             # There can only be one symbol in a terminal and therefore only one factoring
1624 55         78 my $current_choice = $parent_choice;
1625 55         112 my $literal = $asf->glade_literal($glade_id);
1626 55         132 my $symbol_id = $asf->glade_symbol_id($glade_id);
1627 55         125 my $grammar = $asf->grammar();
1628 55         153 my $display_form = $grammar->symbol_display_form($symbol_id);
1629 55         238 return [0, $glade_id, qq{Symbol $display_form: "$literal"}];
1630             } ## end sub dump_terminal
1631              
1632             sub Marpa::R2::ASF::dump {
1633 11     11 0 80 my ($asf) = @_;
1634 11         26 my $peak = $asf->peak();
1635 11         24 local %GLADE_SEEN = (); ## no critic (Variables::ProhibitLocalVars)
1636 11         33 my $lines = $asf->dump_glade( $peak );
1637 11         19 my $next_sequenced_id = 1; # one-based
1638 11         19 my %sequenced_id = ();
1639 11   66     17 $sequenced_id{$_} //= $next_sequenced_id++ for grep { defined } map { $_->[1] } @{$lines};
  201         498  
  201         299  
  11         21  
1640 11         47 my $text = q{};
1641 11         26 for my $line ( @{$lines}[ 1 .. $#$lines ] ) {
  11         34  
1642 190         222 my ( $line_indent, $glade_id, $body ) = @{$line};
  190         307  
1643 190         240 $line_indent -= 2;
1644 190         270 $text .= q{ } x $line_indent;
1645 190 100       380 $text .= 'GL' . $sequenced_id{$glade_id} . q{ } if defined $glade_id;
1646 190         326 $text .= "$body\n";
1647             }
1648 11         111 return $text;
1649             } ## end sub show
1650              
1651             sub Marpa::R2::ASF::show_nidsets {
1652 0     0 0   my ($asf) = @_;
1653 0           my $text = q{};
1654 0           my $nidsets = $asf->[Marpa::R2::Internal::ASF::NIDSET_BY_ID];
1655 0           for my $nidset ( grep {defined} @{$nidsets} ) {
  0            
  0            
1656 0           $text .= $nidset->show() . "\n";
1657             }
1658 0           return $text;
1659             } ## end sub Marpa::R2::ASF::show_nidsets
1660              
1661             sub Marpa::R2::ASF::show_powersets {
1662 0     0 0   my ($asf) = @_;
1663 0           my $text = q{};
1664 0           my $powersets = $asf->[Marpa::R2::Internal::ASF::POWERSET_BY_ID];
1665 0           for my $powerset ( grep {defined} @{$powersets} ) {
  0            
  0            
1666 0           $text .= $powerset->show() . "\n";
1667             }
1668 0           return $text;
1669             } ## end sub Marpa::R2::ASF::show_powersets
1670              
1671             sub dump_nook {
1672 0     0     my ( $asf, $nook ) = @_;
1673 0           my $slr = $asf->[Marpa::R2::Internal::ASF::SLR];
1674 0           my $or_nodes = $asf->[Marpa::R2::Internal::ASF::OR_NODES];
1675 0           my $recce = $slr->[Marpa::R2::Internal::Scanless::R::THICK_G1_RECCE];
1676 0           my $or_node_id = $nook->[Marpa::R2::Internal::Nook::OR_NODE];
1677 0           my $and_node_count = scalar @{ $or_nodes->[$or_node_id] };
  0            
1678 0           my $text = 'Nook ';
1679 0           my @text = ();
1680 0 0         push @text, $nook->[Marpa::R2::Internal::Nook::IS_CAUSE] ? q{C} : q{-};
1681 0 0         push @text,
1682             $nook->[Marpa::R2::Internal::Nook::IS_PREDECESSOR] ? q{P} : q{-};
1683 0 0         push @text,
1684             $nook->[Marpa::R2::Internal::Nook::CAUSE_IS_EXPANDED] ? q{C+} : q{--};
1685 0 0         push @text,
1686             $nook->[Marpa::R2::Internal::Nook::PREDECESSOR_IS_EXPANDED]
1687             ? q{P+}
1688             : q{--};
1689 0           $text .= join q{ }, @text;
1690 0           $text
1691             .= ' @'
1692             . $nook->[Marpa::R2::Internal::Nook::FIRST_CHOICE] . q{-}
1693             . $nook->[Marpa::R2::Internal::Nook::LAST_CHOICE]
1694             . qq{ of $and_node_count: };
1695 0           $text .= $recce->verbose_or_node($or_node_id);
1696 0           return $text;
1697             } ## end sub dump_nook
1698              
1699             # For debugging
1700             sub dump_factoring_stack {
1701 0     0     my ( $asf, $stack ) = @_;
1702 0           my $text = q{};
1703 0           for ( my $stack_ix = 0; $stack_ix <= $#{$stack}; $stack_ix++ ) {
  0            
1704              
1705             # Nook already has newline at end
1706 0           $text .= "$stack_ix: " . dump_nook( $asf, $stack->[$stack_ix] );
1707             }
1708 0           return $text . "\n";
1709             } ## end sub dump_factoring_stack
1710              
1711             1;
1712              
1713             # vim: expandtab shiftwidth=4: