File Coverage

blib/lib/Marpa/R3/ASF2.pm
Criterion Covered Total %
statement 827 1004 82.3
branch 167 252 66.2
condition 19 38 50.0
subroutine 75 98 76.5
pod 0 31 0.0
total 1088 1423 76.4


line stmt bran cond sub pod time code
1             # Marpa::R3 is Copyright (C) 2018, Jeffrey Kegler.
2             #
3             # This module is free software; you can redistribute it and/or modify it
4             # under the same terms as Perl 5.10.1. For more details, see the full text
5             # of the licenses in the directory LICENSES.
6             #
7             # This program is distributed in the hope that it will be
8             # useful, but it is provided "as is" and without any express
9             # or implied warranties. For details, see the full text of
10             # of the licenses in the directory LICENSES.
11              
12             package Marpa::R3::ASF2;
13              
14 104     104   1614 use 5.010001;
  104         335  
15 104     104   504 use strict;
  104         183  
  104         1907  
16 104     104   422 use warnings;
  104         180  
  104         2401  
17 104     104   463 no warnings qw(recursion);
  104         192  
  104         3017  
18              
19 104     104   483 use vars qw($VERSION $STRING_VERSION);
  104         211  
  104         7834  
20             $VERSION = '4.001_054';
21             $STRING_VERSION = $VERSION;
22             ## no critic(BuiltinFunctions::ProhibitStringyEval)
23             $VERSION = eval $VERSION;
24             ## use critic
25              
26             # The code in this file, for now, breaks "the rules". It makes use
27             # of internal methods not documented as part of Libmarpa.
28             # It is intended to create documented Libmarpa methods to underlie
29             # this interface, and rewrite it to use them
30              
31             package Marpa::R3::Internal_ASF2;
32              
33 104     104   640 use Scalar::Util qw(blessed tainted);
  104         311  
  104         5838  
34 104     104   573 use English qw( -no_match_vars );
  104         226  
  104         624  
35              
36             our $PACKAGE = 'Marpa::R3::ASF2';
37              
38             # Set those common args which are at the Perl level.
39             # This is more complicated that it needs to be for the current implementation.
40             # It allows for LHS terminals (implemented in Libmarpa but not allowed by the SLIF).
41             # It also assumes that every or-node which can be constructed from preceding or-nodes
42             # and the input will be present. This is currently the case, but in the future
43             # rules and/or symbols may have extra-syntactic conditions attached making this
44             # assumption false.
45              
46             # Terms:
47              
48             # NID (Node ID): Encoded ID of either an or-node or an and-node.
49             #
50             # Extensions:
51             # Set "powers": A set of power 0 is an "atom" -- a single NID.
52             # A set of power 1 is a set of NID's -- a nidset.
53             # A set of power 2 is a set of sets of NID's, also called a powerset.
54             # A set of power 3 is a set of powersets, etc.
55             #
56             # The whole ID of NID is the external rule id of an or-node, or -1
57             # if the NID is for a token and-node.
58             #
59             # Intensions:
60             # A Symch is a nidset, where all the NID's share the same "whole ID"
61             # and the same span. NID's in a symch may differ in their internal rule,
62             # or have different causes. If the symch contains and-node NID's they
63             # will all have the same symbol.
64             #
65             # A choicepoint is a powerset -- a set of symches all of which share
66             # the same set of predecessors. (This set of predecessors is a power 3 set of
67             # choicepoints.) All symches in a choicepoint also share the same span,
68             # and the same symch-symbol. A symch's symbol is the LHS of the rule,
69             # or the symbol of the token in the token and-nodes.
70              
71             sub intset_id {
72 13543     13543   18952 my ( $asf, @ids ) = @_;
73 13543         23892 my $key = join q{ }, sort { $a <=> $b } @ids;
  116         394  
74 13543         15896 my $intset_by_key = $asf->[Marpa::R3::Internal_ASF2::INTSET_BY_KEY];
75 13543         17475 my $intset_id = $intset_by_key->{$key};
76 13543 100       25522 return $intset_id if defined $intset_id;
77 1764         2213 $intset_id = $asf->[Marpa::R3::Internal_ASF2::NEXT_INTSET_ID]++;
78 1764         3740 $intset_by_key->{$key} = $intset_id;
79 1764         2709 return $intset_id;
80             } ## end sub intset_id
81              
82             sub Marpa::R3::Nidset::obtain {
83 12651     12651   20037 my ( $class, $asf, @nids ) = @_;
84 12651         18353 my $id = intset_id( $asf, @nids );
85 12651         15199 my $nidset_by_id = $asf->[Marpa::R3::Internal_ASF2::NIDSET_BY_ID];
86 12651         15596 my $nidset = $nidset_by_id->[$id];
87 12651 100       20859 return $nidset if defined $nidset;
88 1034         1561 $nidset = bless [], $class;
89 1034         1630 $nidset->[Marpa::R3::Internal::Nidset::ID] = $id;
90             $nidset->[Marpa::R3::Internal::Nidset::NIDS] =
91 1034         1914 [ sort { $a <=> $b } @nids ];
  60         163  
92 1034         1609 $nidset_by_id->[$id] = $nidset;
93 1034         1649 return $nidset;
94             } ## end sub Marpa::R3::Nidset::obtain
95              
96             sub Marpa::R3::Nidset::nids {
97 892     892   1202 my ($nidset) = @_;
98 892         1956 return $nidset->[Marpa::R3::Internal::Nidset::NIDS];
99             }
100              
101             sub Marpa::R3::Nidset::nid {
102 3550     3550   4700 my ( $nidset, $ix ) = @_;
103 3550         5669 return $nidset->[Marpa::R3::Internal::Nidset::NIDS]->[$ix];
104             }
105              
106             sub Marpa::R3::Nidset::count {
107 313     313   467 my ($nidset) = @_;
108 313         351 return scalar @{ $nidset->[Marpa::R3::Internal::Nidset::NIDS] };
  313         488  
109             }
110              
111             sub Marpa::R3::Nidset::id {
112 12651     12651   16716 my ($nidset) = @_;
113 12651         15889 return $nidset->[Marpa::R3::Internal::Nidset::ID];
114             }
115              
116             sub Marpa::R3::Nidset::show {
117 0     0   0 my ($nidset) = @_;
118 0         0 my $id = $nidset->id();
119 0         0 my $nids = $nidset->nids();
120 0         0 return "Nidset #$id: " . join q{ }, @{$nids};
  0         0  
121             } ## end sub Marpa::R3::Nidset::show
122              
123             sub Marpa::R3::Powerset::obtain {
124 892     892   1394 my ( $class, $asf, @nidset_ids ) = @_;
125 892         1183 my $id = intset_id( $asf, @nidset_ids );
126 892         1223 my $powerset_by_id = $asf->[Marpa::R3::Internal_ASF2::POWERSET_BY_ID];
127 892         1115 my $powerset = $powerset_by_id->[$id];
128 892 50       1350 return $powerset if defined $powerset;
129 892         1329 $powerset = bless [], $class;
130 892         1604 $powerset->[Marpa::R3::Internal::Powerset::ID] = $id;
131             $powerset->[Marpa::R3::Internal::Powerset::NIDSET_IDS] =
132 892         1654 [ sort { $a <=> $b } @nidset_ids ];
  21         55  
133 892         1377 $powerset_by_id->[$id] = $powerset;
134 892         1618 return $powerset;
135             } ## end sub Marpa::R3::Powerset::obtain
136              
137             sub Marpa::R3::Powerset::nidset_ids {
138 0     0   0 my ($powerset) = @_;
139 0         0 return $powerset->[Marpa::R3::Internal::Powerset::NIDSET_IDS];
140             }
141              
142             sub Marpa::R3::Powerset::count {
143 892     892   1174 my ($powerset) = @_;
144 892         1029 return scalar @{ $powerset->[Marpa::R3::Internal::Powerset::NIDSET_IDS] };
  892         1334  
145             }
146              
147             sub Marpa::R3::Powerset::nidset_id {
148 0     0   0 my ( $powerset, $ix ) = @_;
149 0         0 my $nidset_ids = $powerset->[Marpa::R3::Internal::Powerset::NIDSET_IDS];
150 0 0       0 return if $ix > $#{$nidset_ids};
  0         0  
151 0         0 return $powerset->[Marpa::R3::Internal::Powerset::NIDSET_IDS]->[$ix];
152             } ## end sub Marpa::R3::Powerset::nidset_id
153              
154             sub Marpa::R3::Powerset::nidset {
155 1224     1224   1781 my ( $powerset, $asf, $ix ) = @_;
156 1224         1382 my $nidset_ids = $powerset->[Marpa::R3::Internal::Powerset::NIDSET_IDS];
157 1224 50       1398 return if $ix > $#{$nidset_ids};
  1224         2001  
158 1224         1640 my $nidset_id = $powerset->[Marpa::R3::Internal::Powerset::NIDSET_IDS]->[$ix];
159 1224         1415 my $nidset_by_id = $asf->[Marpa::R3::Internal_ASF2::NIDSET_BY_ID];
160 1224         1651 return $nidset_by_id->[$nidset_id];
161             } ## end sub Marpa::R3::Powerset::nidset_id
162              
163             sub Marpa::R3::Powerset::id {
164 0     0   0 my ($powerset) = @_;
165 0         0 return $powerset->[Marpa::R3::Internal::Powerset::ID];
166             }
167              
168             sub Marpa::R3::Powerset::show {
169 0     0   0 my ($powerset) = @_;
170 0         0 my $id = $powerset->id();
171 0         0 my $nidset_ids = $powerset->nidset_ids();
172 0         0 return "Powerset #$id: " . join q{ }, @{$nidset_ids};
  0         0  
173             } ## end sub Marpa::R3::Powerset::show
174              
175             sub set_last_choice {
176 26490     26490   30421 my ( $asf, $nook ) = @_;
177 26490         29237 my $or_nodes = $asf->[Marpa::R3::Internal_ASF2::OR_NODES];
178 26490         28198 my $or_node_id = $nook->[Marpa::R3::Internal::Nook::OR_NODE];
179 26490         28210 my $and_nodes = $or_nodes->[$or_node_id];
180 26490         28070 my $choice = $nook->[Marpa::R3::Internal::Nook::FIRST_CHOICE];
181 26490 100       27373 return if $choice > $#{$and_nodes};
  26490         52495  
182 13785 100       20817 if ( nook_has_semantic_cause( $asf, $nook ) ) {
183 11077         14031 my $slr = $asf->[Marpa::R3::Internal_ASF2::SLR];
184 11077         12471 my $slg = $slr->[Marpa::R3::Internal_R::SLG];
185 11077         13584 my $and_node_id = $and_nodes->[$choice];
186 11077         20880 my ($current_predecessor) = $asf->call_by_tag(
187             ('@' . __FILE__ . ':' . __LINE__),
188             <<'END_OF_LUA',
189             local asf, id = ...
190             local current = asf.lmw_b:_and_node_predecessor(id)
191             return current and current or -1
192             END_OF_LUA
193             'i', $and_node_id);
194 11077         12596 AND_NODE: while (1) {
195 11140         11354 $choice++;
196 11140         12761 $and_node_id = $and_nodes->[$choice];
197 11140 100       18322 last AND_NODE if not defined $and_node_id;
198 70   50     243 my ($next_predecessor) = $asf->call_by_tag(
199             ('@' . __FILE__ . ':' . __LINE__),
200             <<'END_OF_LUA',
201             local asf, id = ...
202             local next = asf.lmw_b:_and_node_predecessor(id)
203             return next and next or -1
204             END_OF_LUA
205             'i', ($and_node_id // -1));
206 70 100       204 last AND_NODE if $current_predecessor != $next_predecessor;
207             } ## end AND_NODE: while (1)
208 11077         13013 $choice--;
209             } ## end if ( nook_has_semantic_cause( $asf, $nook ) )
210 13785         16067 $nook->[Marpa::R3::Internal::Nook::LAST_CHOICE] = $choice;
211 13785         17069 return $choice;
212             } ## end sub set_last_choice
213              
214             sub nook_new {
215 12705     12705   18092 my ( $asf, $or_node_id, $parent_or_node_id ) = @_;
216 12705         16858 my $nook = [];
217 12705         18558 $nook->[Marpa::R3::Internal::Nook::OR_NODE] = $or_node_id;
218 12705   100     19401 $nook->[Marpa::R3::Internal::Nook::PARENT] = $parent_or_node_id // -1;
219 12705         13567 $nook->[Marpa::R3::Internal::Nook::FIRST_CHOICE] = 0;
220 12705         20291 set_last_choice( $asf, $nook );
221 12705         15167 return $nook;
222             } ## end sub nook_new
223              
224             sub nook_increment {
225 13785     13785   16063 my ( $asf, $nook ) = @_;
226 13785   50     18446 $nook->[Marpa::R3::Internal::Nook::LAST_CHOICE] //= 0;
227 13785         15579 $nook->[Marpa::R3::Internal::Nook::FIRST_CHOICE] =
228             $nook->[Marpa::R3::Internal::Nook::LAST_CHOICE] + 1;
229 13785 100       17456 return if not defined set_last_choice( $asf, $nook );
230 1080         2080 return 1;
231             } ## end sub nook_increment
232              
233             sub nook_has_semantic_cause {
234 46564     46564   55989 my ( $asf, $nook ) = @_;
235 46564         56176 my $or_node = $nook->[Marpa::R3::Internal::Nook::OR_NODE];
236              
237 46564         80493 my ($result) = $asf->call_by_tag(
238             ('@' . __FILE__ . ':' . __LINE__),
239             <<'END_OF_LUA', 'i', $or_node);
240             local asf, or_node = ...
241             local slr = asf.slr
242             local irl_id = asf.lmw_b:_or_node_nrl(or_node)
243             local predot_position = asf.lmw_b:_or_node_position(or_node) - 1
244             local predot_isyid = slr.slg.g1:_nrl_rhs(irl_id, predot_position)
245             return slr.slg.g1:_nsy_is_semantic(predot_isyid)
246             END_OF_LUA
247 46564         83566 return $result;
248             } ## end sub nook_has_semantic_cause
249              
250             # No check for conflicting usage -- value(), asf(), etc.
251             # at this point
252             sub Marpa::R3::ASF2::peak {
253 62     62 0 144 my ($asf) = @_;
254 62         124 my $or_nodes = $asf->[Marpa::R3::Internal_ASF2::OR_NODES];
255              
256 62         173 my ($augment_or_node_id) = $asf->call_by_tag(
257             ('@' . __FILE__ . ':' . __LINE__),
258             <<'END_OF_LUA', '');
259             local asf = ...
260             local bocage = asf.lmw_b
261             if not bocage then error('No Bocage') end
262             return bocage:_top_or_node()
263             END_OF_LUA
264              
265             # TODO: This logic is from previous Libmarpa which added its own
266             # augment rule -- commented out, just in case.
267             # my $augment_and_node_id = $or_nodes->[$augment_or_node_id]->[0];
268             # my ($start_or_node_id)
269             # = $asf->call_by_tag(
270             # ('@' . __FILE__ . ':' . __LINE__),
271             # 'local asf, id = ...; return asf.lmw_b:_and_node_cause(id)',
272             # 'i',
273             # $augment_and_node_id
274             # );
275              
276 62         306 my $base_nidset = Marpa::R3::Nidset->obtain( $asf, $augment_or_node_id );
277 62         259 my $glade_id = $base_nidset->id();
278              
279             # Cannot "obtain" the glade if it is not registered
280 62         331 $asf->[Marpa::R3::Internal_ASF2::GLADES]->[$glade_id]
281             ->[Marpa::R3::Internal::Glade::REGISTERED] = 1;
282 62         243 glade_obtain( $asf, $glade_id );
283 62         129 return $glade_id;
284             } ## end sub Marpa::R3::ASF2::peak
285              
286             our $NID_LEAF_BASE = -43;
287              
288             # Range from -1 to -42 reserved for special values
289 5523     5523   13763 sub and_node_to_nid { return -$_[0] + $NID_LEAF_BASE; }
290 1444     1444   2195 sub nid_to_and_node { return -$_[0] + $NID_LEAF_BASE; }
291              
292             # Set those common args which are at the Perl level.
293             sub asf_common_set {
294 59     59   136 my ( $asf, $flat_args ) = @_;
295 59 50       198 if ( my $value = $flat_args->{'trace_file_handle'} ) {
296 0         0 $asf->[Marpa::R3::Internal_ASF2::TRACE_FILE_HANDLE] = $value;
297             }
298 59         193 my $trace_file_handle =
299             $asf->[Marpa::R3::Internal_ASF2::TRACE_FILE_HANDLE];
300 59         118 delete $flat_args->{'trace_file_handle'};
301 59         111 return $flat_args;
302             }
303              
304             # Returns undef if no parse
305             sub Marpa::R3::ASF2::new {
306 59     59 0 2262 my ( $class, @args ) = @_;
307 59         159 my $asf = bless [], $class;
308              
309 59         98 my $end_of_parse;
310              
311 59         239 my ( $flat_args, $error_message ) = Marpa::R3::flatten_hash_args( \@args );
312 59 50       219 Marpa::R3::exception( sprintf $error_message, '$asf->new' )
313             if not $flat_args;
314 59         202 $flat_args = asf_common_set( $asf, $flat_args );
315              
316 59         132 my $slr = $flat_args->{recognizer};
317 59 50       177 Marpa::R3::exception(
318             qq{Marpa::R3::ASF2::new() called without a "recognizer" argument} )
319             if not defined $slr;
320 59         125 $asf->[Marpa::R3::Internal_ASF2::SLR] = $slr;
321 59         119 delete $flat_args->{recognizer};
322              
323 59         109 my $slr_class = 'Marpa::R3::Recognizer';
324 59 50 33     600 if ( not blessed $slr or not $slr->isa($slr_class) ) {
325 0         0 my $ref_type = ref $slr;
326 0 0       0 my $desc = $ref_type ? "a ref to $ref_type" : 'not a ref';
327 0         0 Marpa::R3::exception(
328             qq{'recognizer' named argument to new() is $desc\n},
329             " It should be a ref to $slr_class\n"
330             );
331             }
332              
333 59   33     346 $asf->[Marpa::R3::Internal_ASF2::TRACE_FILE_HANDLE] //=
334             $slr->[Marpa::R3::Internal_R::TRACE_FILE_HANDLE];
335              
336 59         112 my $trace_file_handle =
337             $asf->[Marpa::R3::Internal_ASF2::TRACE_FILE_HANDLE];
338              
339 59         108 my $lua = $slr->[Marpa::R3::Internal_R::L];
340 59         151 $asf->[Marpa::R3::Internal_ASF2::L] = $lua;
341              
342 59         259 ARG: for my $arg ( keys %{$flat_args} ) {
  59         190  
343 64 100       158 if ( $arg eq 'factoring_max' ) {
344             $asf->[Marpa::R3::Internal_ASF2::FACTORING_MAX] =
345 25         49 $flat_args->{$arg};
346 25         39 delete $flat_args->{$arg};
347 25         48 next ARG;
348             }
349             }
350              
351             my ( $regix ) = $slr->coro_by_tag(
352             ( '@' . __FILE__ . ':' . __LINE__ ),
353             {
354             signature => 's',
355             args => [$flat_args],
356             handlers => {
357             trace => sub {
358 0     0   0 my ($msg) = @_;
359 0         0 say {$trace_file_handle} $msg;
  0         0  
360 0         0 return 'ok';
361             },
362             }
363             },
364 59         633 <<'END_OF_LUA');
365             local slr, flat_args = ...
366             _M.wrap(function ()
367             local asf = slr:asf2_new(flat_args)
368             if not asf then return 'ok', -1 end
369             local order = asf.lmw_o
370             if not order then
371             error( 'Parse failed' )
372             end
373             if order:is_null() == 1 then
374             error([[
375             An attempt was make to create an ASF for a null parse\n\a
376             \u{20} A null parse is a successful parse of a zero-length string\n\z
377             \u{20} ASF's are not defined for null parses\n\z
378             ]])
379             end
380             return 'ok', asf.regix
381             end)
382             END_OF_LUA
383              
384 59 50       464 return if $regix < 0;
385 59         144 $asf->[Marpa::R3::Internal_ASF2::REGIX] = $regix;
386              
387 59   100     270 $asf->[Marpa::R3::Internal_ASF2::FACTORING_MAX] //= 42;
388 59         120 $asf->[Marpa::R3::Internal_ASF2::NEXT_INTSET_ID] = 0;
389 59         128 $asf->[Marpa::R3::Internal_ASF2::INTSET_BY_KEY] = {};
390 59         137 $asf->[Marpa::R3::Internal_ASF2::NIDSET_BY_ID] = [];
391 59         123 $asf->[Marpa::R3::Internal_ASF2::POWERSET_BY_ID] = [];
392 59         114 $asf->[Marpa::R3::Internal_ASF2::GLADES] = [];
393              
394 59         138 my $or_nodes = $asf->[Marpa::R3::Internal_ASF2::OR_NODES] = [];
395 59         120 OR_NODE: for ( my $or_node_id = 0;; $or_node_id++ ) {
396              
397 1892         4311 my ($and_node_ids) = $asf->call_by_tag(
398             ('@' . __FILE__ . ':' . __LINE__),
399             <<'END_OF_LUA', 'i>*', $or_node_id );
400             -- assumes throw mode
401             local asf, or_node_id = ...
402             local and_node_ids = {}
403             local order = asf.lmw_o
404             local count = order:_or_node_and_node_count(or_node_id)
405             if not count then return and_node_ids end
406             for ix = 1, count do
407             and_node_ids[ix] =
408             order:_or_node_and_node_id_by_ix(or_node_id, ix-1);
409             end
410             return and_node_ids
411             END_OF_LUA
412              
413 1892 100       2185 last OR_NODE if not scalar @{$and_node_ids};
  1892         2811  
414              
415             # Originally I had intended to sort the and node IDs by
416             # MAJOR: and-node predecessor (or -1 if no predecessor) and
417             # MINOR: and_node ID
418             # Don't know why, and in fact I screwed up the implementation
419             # and left the and nodes unsorted,
420             # which is how they are in the current implementation.
421              
422 1833         2599 $or_nodes->[$or_node_id] = $and_node_ids;
423              
424             } ## end OR_NODE: for ( my $or_node_id = 0;; $or_node_id++ )
425              
426 59         279 return $asf;
427              
428             } ## end sub Marpa::R3::ASF2::new
429              
430             sub Marpa::R3::ASF2::DESTROY {
431             # say STDERR "In Marpa::R3::ASF2::DESTROY before test";
432 59     59   3978 my $asf = shift;
433 59         129 my $lua = $asf->[Marpa::R3::Internal_ASF2::L];
434              
435             # If we are destroying the Perl interpreter, then all the Marpa
436             # objects will be destroyed, including Marpa's Lua interpreter.
437             # We do not need to worry about cleaning up the
438             # recognizer is an orderly manner, because the Lua interpreter
439             # containing the recognizer will be destroyed.
440             # In fact, the Lua interpreter may already have been destroyed,
441             # so this test is necessary to avoid a warning message.
442 59 50       162 return if not $lua;
443             # say STDERR "In Marpa::R3::ASF2::DESTROY after test";
444              
445 59         126 my $regix = $asf->[Marpa::R3::Internal_ASF2::REGIX];
446 59         188 $asf->call_by_tag(
447             ('@' . __FILE__ . ':' . __LINE__),
448             <<'END_OF_LUA', '');
449             local asf = ...
450             local regix = asf.regix
451             _M.unregister(_M.registry, regix)
452             END_OF_LUA
453             }
454              
455             sub Marpa::R3::ASF2::glade_is_visited {
456 0     0 0 0 my ( $asf, $glade_id ) = @_;
457 0         0 my $glade = $asf->[Marpa::R3::Internal_ASF2::GLADES]->[$glade_id];
458 0 0       0 return if not $glade;
459 0         0 return $glade->[Marpa::R3::Internal::Glade::VISITED];
460             } ## end sub Marpa::R3::ASF2::glade_is_visited
461              
462             sub Marpa::R3::ASF2::glade_visited_clear {
463 0     0 0 0 my ( $asf, $glade_id ) = @_;
464 0 0       0 my $glade_list =
465             defined $glade_id
466             ? [ $asf->[Marpa::R3::Internal_ASF2::GLADES]->[$glade_id] ]
467             : $asf->[Marpa::R3::Internal_ASF2::GLADES];
468             $_->[Marpa::R3::Internal::Glade::VISITED] = undef
469 0         0 for grep {defined} @{$glade_list};
  0         0  
  0         0  
470 0         0 return;
471             } ## end sub Marpa::R3::ASF2::glade_visited_clear
472              
473             sub nid_sort_ix {
474 942     942   1376 my ( $asf, $nid ) = @_;
475 942         1219 my $slr = $asf->[Marpa::R3::Internal_ASF2::SLR];
476              
477 942 100       1857 if ( $nid >= 0 ) {
478 344         772 my ($result) = $asf->call_by_tag(
479             ('@' . __FILE__ . ':' . __LINE__),
480             <<'END_OF_LUA', 'i', $nid);
481             local asf, nid = ...
482             local slr = asf.slr
483             local irl_id = asf.lmw_b:_or_node_nrl(nid)
484             return slr.slg.g1:_source_irl(irl_id)
485             END_OF_LUA
486 344         620 return $result;
487             }
488              
489 598         986 my $and_node_id = nid_to_and_node($nid);
490              
491 598         1599 my ($result) = $asf->call_by_tag(
492             ('@' . __FILE__ . ':' . __LINE__),
493             <<'END_OF_LUA', 'i', $and_node_id);
494             local asf, and_node_id = ...
495             local slr = asf.slr
496             local token_nsy_id = asf.lmw_b:_and_node_symbol(and_node_id)
497             local token_id = slr.slg.g1:_source_isy(token_nsy_id)
498             -- -2 is reserved for 'end of data'
499             return -token_id - 3
500             END_OF_LUA
501 598         1021 return $result;
502             } ## end sub nid_sort_ix
503              
504             sub Marpa::R3::ASF2::grammar {
505 273     273 0 533 my ($asf) = @_;
506 273         357 my $slr = $asf->[Marpa::R3::Internal_ASF2::SLR];
507 273         363 my $slg = $slr->[Marpa::R3::Internal_R::SLG];
508 273         389 return $slg;
509             } ## end sub Marpa::R3::ASF2::grammar
510              
511             # TODO -- Document this method
512             sub Marpa::R3::ASF2::recognizer {
513 4     4 0 13 my ($asf) = @_;
514 4         6 my $slr = $asf->[Marpa::R3::Internal_ASF2::SLR];
515 4         7 return $slr;
516             }
517              
518             sub nid_rule_id {
519 911     911   1218 my ( $asf, $nid ) = @_;
520 911 100       2754 return if $nid < 0;
521              
522 313         1864 my ($irl_id) = $asf->call_by_tag(
523             ('@' . __FILE__ . ':' . __LINE__),
524             <<'END_OF_LUA', 'i', $nid);
525             local asf, nid = ...
526             local slr = asf.slr
527             local irl_id = asf.lmw_b:_or_node_nrl(nid)
528             local irl_id = slr.slg.g1:_source_irl(irl_id)
529             return irl_id
530             END_OF_LUA
531 313         712 return $irl_id;
532             }
533              
534             sub or_node_es_span {
535 1844     1844   2663 my ( $asf, $choicepoint ) = @_;
536              
537 1844         4138 my ($origin_es, $current_es) = $asf->call_by_tag(
538             ('@' . __FILE__ . ':' . __LINE__),
539             <<'END_OF_LUA', 'i', $choicepoint);
540             local asf, choicepoint = ...
541             local slr = asf.slr
542             local origin_es = asf.lmw_b:_or_node_origin(choicepoint)
543             local current_es = asf.lmw_b:_or_node_set(choicepoint)
544             return origin_es, current_es
545             END_OF_LUA
546              
547 1844         4719 return $origin_es, $current_es - $origin_es;
548             } ## end sub or_node_es_span
549              
550             sub token_es_span {
551 716     716   958 my ( $asf, $and_node_id ) = @_;
552              
553 716         1533 my ($predecessor_id, $parent_or_node_id) = $asf->call_by_tag(
554             ('@' . __FILE__ . ':' . __LINE__),
555             <<'END_OF_LUA',
556             local asf, and_node_id = ...
557             local slr = asf.slr
558             local b = asf.lmw_b
559             return
560             b:_and_node_predecessor(and_node_id),
561             b:_and_node_parent(and_node_id)
562             END_OF_LUA
563             'i', $and_node_id);
564              
565 716 100       1383 if ( defined $predecessor_id ) {
566              
567 253         577 my ($origin_es, $current_es) = $asf->call_by_tag(
568             ('@' . __FILE__ . ':' . __LINE__),
569             <<'END_OF_LUA',
570             local asf, predecessor_id, parent_or_node_id = ...
571             local slr = asf.slr
572             local b = asf.lmw_b
573             return
574             b:_or_node_set(predecessor_id),
575             b:_or_node_set(parent_or_node_id)
576             END_OF_LUA
577             'ii', $predecessor_id, $parent_or_node_id);
578              
579 253         739 return ( $origin_es, $current_es - $origin_es );
580             }
581              
582 463         1026 return or_node_es_span( $asf, $parent_or_node_id );
583             } ## end sub token_es_span
584              
585             sub nid_literal {
586 1814     1814   2467 my ( $asf, $nid ) = @_;
587 1814         2200 my $slr = $asf->[Marpa::R3::Internal_ASF2::SLR];
588 1814 100       3100 if ( $nid <= $NID_LEAF_BASE ) {
589 614         900 my $and_node_id = nid_to_and_node($nid);
590 614         972 my ( $start, $length ) = token_es_span( $asf, $and_node_id );
591 614 100       1890 return q{} if $length == 0;
592 194         637 return $slr->g1_literal( $start, $length );
593             } ## end if ( $nid <= $NID_LEAF_BASE )
594 1200 50       1954 if ( $nid >= 0 ) {
595 1200         1953 return $slr->g1_literal( or_node_es_span( $asf, $nid ) );
596             }
597 0         0 Marpa::R3::exception("No literal for node ID: $nid");
598             }
599              
600             sub nid_span {
601 283     283   496 my ( $asf, $nid ) = @_;
602 283         342 my $slr = $asf->[Marpa::R3::Internal_ASF2::SLR];
603 283 100       575 if ( $nid <= $NID_LEAF_BASE ) {
604 102         153 my $and_node_id = nid_to_and_node($nid);
605 102         174 my ( $start, $length ) = token_es_span( $asf, $and_node_id );
606 102 50       238 return ($start, 0) if $length == 0;
607 0         0 return $start, $length;
608             } ## end if ( $nid <= $NID_LEAF_BASE )
609 181 50       296 if ( $nid >= 0 ) {
610 181         289 return or_node_es_span( $asf, $nid );
611             }
612 0         0 Marpa::R3::exception("No literal for node ID: $nid");
613             }
614              
615             sub nid_token_id {
616 198     198   269 my ( $asf, $nid ) = @_;
617 198 100       457 return if $nid > $NID_LEAF_BASE;
618 130         267 my $and_node_id = nid_to_and_node($nid);
619              
620 130         284 my ($token_id) = $asf->call_by_tag(
621             ('@' . __FILE__ . ':' . __LINE__),
622             <<'END_OF_LUA',
623             local asf, and_node_id = ...
624             local slr = asf.slr
625             local token_nsy_id = asf.lmw_b:_and_node_symbol(and_node_id)
626             local token_id = slr.slg.g1:_source_isy(token_nsy_id)
627             return token_id
628             END_OF_LUA
629             'i', $and_node_id);
630              
631 130         220 return $token_id;
632             }
633              
634             sub nid_symbol_id {
635 198     198   294 my ( $asf, $nid ) = @_;
636 198         353 my $token_id = nid_token_id($asf, $nid);
637 198 100       540 return $token_id if defined $token_id;
638 68 50       139 Marpa::R3::exception("No symbol ID for node ID: $nid") if $nid < 0;
639              
640             # Not a token, so return the LHS of the rule
641 68         184 my ($lhs_id) = $asf->call_by_tag(
642             ('@' . __FILE__ . ':' . __LINE__),
643             <<'END_OF_LUA',
644             local asf, nid = ...
645             local slr = asf.slr
646             local irl_id = asf.lmw_b:_or_node_nrl(nid)
647             local g1g = slr.slg.g1
648             local irl_id = g1g:_source_irl(irl_id)
649             local lhs_id = g1g:rule_lhs(irl_id)
650             return lhs_id
651             END_OF_LUA
652             'i', $nid);
653              
654 68         208 return $lhs_id;
655             }
656              
657             sub nid_symbol_name {
658 0     0   0 my ( $asf, $nid ) = @_;
659 0         0 my $slr = $asf->[Marpa::R3::Internal_ASF2::SLR];
660 0         0 my $slg = $slr->[Marpa::R3::Internal_R::SLG];
661 0         0 my $symbol_id = nid_symbol_id($asf, $nid);
662 0         0 return $slg->g1_symbol_name($symbol_id);
663             }
664              
665             sub nid_token_name {
666 0     0   0 my ( $asf, $nid ) = @_;
667 0         0 my $slr = $asf->[Marpa::R3::Internal_ASF2::SLR];
668 0         0 my $slg = $slr->[Marpa::R3::Internal_R::SLG];
669 0         0 my $token_id = nid_token_id($asf, $nid);
670 0 0       0 return if not defined $token_id;
671 0         0 return $slg->g1_symbol_name($token_id);
672             }
673              
674             # Memoization is heavily used -- it needs to be to keep the worst cases from
675             # going exponential. The need to memoize is the reason for the very heavy use of
676             # hashes. For example, quite often an HOH (hash of hashes) is used where
677             # an HoL (hash of lists) would usually be preferred. But the HOL would leave me
678             # with the problem of having duplicates, which if followed up upon, would make
679             # the algorithm go exponential.
680              
681             # For the "seen" hashes, the intent, in C, is to use a bit vector. Since typically
682             # choicepoints will only use a tiny fraction of the or- and and-node space, I'll create
683             # a per-choicepoint index in the bit vector for each or- and and-node. The index will
684             # per-ASF, and to avoid the overhead of clearing it, it will track, or each node, the
685             # current CP indexing it. It is assumed that the indexes need only remain valid within
686             # the method call that constructs the CPI (choicepoint iterator).
687              
688             sub first_factoring {
689 344     344   523 my ($choicepoint, $nid_of_choicepoint) = @_;
690              
691             # Current NID of current SYMCH
692             # The caller should ensure that we are never called unless the current
693             # NID is for a rule.
694 344 50       628 Marpa::R3::exception(
695             "Internal error: first_factoring() called for negative NID: $nid_of_choicepoint"
696             ) if $nid_of_choicepoint < 0;
697              
698             # Due to skipping, even the top or-node can have no valid choices
699 344         458 my $asf = $choicepoint->[Marpa::R3::Internal::Choicepoint::ASF];
700 344         441 my $or_nodes = $asf->[Marpa::R3::Internal_ASF2::OR_NODES];
701 344 50       420 if ( not scalar @{ $or_nodes->[$nid_of_choicepoint] } ) {
  344         694  
702 0         0 $choicepoint->[Marpa::R3::Internal::Choicepoint::FACTORING_STACK] =
703             undef;
704 0         0 return;
705             }
706              
707             $choicepoint->[Marpa::R3::Internal::Choicepoint::OR_NODE_IN_USE]
708 344         723 ->{$nid_of_choicepoint} = 1;
709 344         621 my $nook = nook_new( $asf, $nid_of_choicepoint );
710 344         636 $choicepoint->[Marpa::R3::Internal::Choicepoint::FACTORING_STACK] =
711             [$nook];
712              
713             # Iterate as long as we cannot finish this stack
714 344         722 while ( not factoring_finish($choicepoint, $nid_of_choicepoint) ) {
715 0 0       0 return if not factoring_iterate($choicepoint);
716             }
717 344         568 return 1;
718              
719             }
720              
721             sub next_factoring {
722 1424     1424   2182 my ($choicepoint, $nid_of_choicepoint) = @_;
723 1424         2028 my $factoring_stack =
724             $choicepoint->[Marpa::R3::Internal::Choicepoint::FACTORING_STACK];
725 1424 50       2309 Marpa::R3::exception(
726             'Attempt to iterate factoring of uninitialized checkpoint')
727             if not $factoring_stack;
728              
729 1424         2476 while ( factoring_iterate($choicepoint) ) {
730 1080 50       1936 return 1 if factoring_finish($choicepoint, $nid_of_choicepoint);
731             }
732              
733             # Found nothing to iterate
734 344         506 return;
735             }
736              
737             sub factoring_iterate {
738 1424     1424   1840 my ($choicepoint) = @_;
739 1424         1691 my $asf = $choicepoint->[Marpa::R3::Internal::Choicepoint::ASF];
740 1424         1870 my $factoring_stack =
741             $choicepoint->[Marpa::R3::Internal::Choicepoint::FACTORING_STACK];
742 1424         1508 FIND_NODE_TO_ITERATE: while (1) {
743 14129 100       13648 if ( not scalar @{$factoring_stack} ) {
  14129         20285  
744 344         437 $choicepoint->[Marpa::R3::Internal::Choicepoint::FACTORING_STACK]
745             = undef;
746 344         731 return;
747             }
748 13785         15401 my $top_nook = $factoring_stack->[-1];
749 13785 100       17623 if ( nook_increment( $asf, $top_nook ) ) {
750 1080         1851 last FIND_NODE_TO_ITERATE; # in C, a "break" will do this
751             }
752              
753             # Could not iterate
754             # "Dirty" the corresponding bits in the parent and pop this nook
755 12705         14218 my $stack_ix_of_parent_nook =
756             $top_nook->[Marpa::R3::Internal::Nook::PARENT];
757 12705 100       16510 if ( $stack_ix_of_parent_nook >= 0 ) {
758 12361         12858 my $parent_nook = $factoring_stack->[$stack_ix_of_parent_nook];
759 12361 100       17226 $parent_nook->[Marpa::R3::Internal::Nook::CAUSE_IS_EXPANDED] = 0
760             if $top_nook->[Marpa::R3::Internal::Nook::IS_CAUSE];
761 12361 100       17627 $parent_nook->[Marpa::R3::Internal::Nook::PREDECESSOR_IS_EXPANDED]
762             = 0
763             if $top_nook->[Marpa::R3::Internal::Nook::IS_PREDECESSOR];
764             } ## end if ( $stack_ix_of_parent_nook >= 0 )
765              
766 12705         13241 my $top_or_node = $top_nook->[Marpa::R3::Internal::Nook::OR_NODE];
767             $choicepoint->[Marpa::R3::Internal::Choicepoint::OR_NODE_IN_USE]
768 12705         17605 ->{$top_or_node} = undef;
769 12705         12201 pop @{$factoring_stack};
  12705         19374  
770             } ## end FIND_NODE_TO_ITERATE: while (1)
771 1080         1914 return 1;
772             } ## end sub factoring_iterate
773              
774             sub factoring_finish {
775 1424     1424   2174 my ($choicepoint, $nid_of_choicepoint) = @_;
776 1424         1846 my $asf = $choicepoint->[Marpa::R3::Internal::Choicepoint::ASF];
777 1424         1545 my $or_nodes = $asf->[Marpa::R3::Internal_ASF2::OR_NODES];
778 1424         1587 my $factoring_stack =
779             $choicepoint->[Marpa::R3::Internal::Choicepoint::FACTORING_STACK];
780              
781 1424         1729 my $nidset_by_id = $asf->[Marpa::R3::Internal_ASF2::NIDSET_BY_ID];
782              
783 1424         1750 my @worklist = ( 0 .. $#{$factoring_stack} );
  1424         2353  
784              
785 1424         2504 DO_WORKLIST: while ( scalar @worklist ) {
786 31362         37546 my $stack_ix_of_work_nook = $worklist[-1];
787 31362         35362 my $work_nook = $factoring_stack->[$stack_ix_of_work_nook];
788 31362         36736 my $work_or_node = $work_nook->[Marpa::R3::Internal::Nook::OR_NODE];
789 31362         32211 my $working_choice =
790             $work_nook->[Marpa::R3::Internal::Nook::FIRST_CHOICE];
791 31362         36112 my $work_and_node_id = $or_nodes->[$work_or_node]->[$working_choice];
792 31362         47733 my $child_or_node;
793             my $child_is_cause;
794 31362         0 my $child_is_predecessor;
795             FIND_CHILD_OR_NODE: {
796              
797 31362 100       29884 if ( !$work_nook->[Marpa::R3::Internal::Nook::CAUSE_IS_EXPANDED] )
  31362         46189  
798             {
799 13778 100       19654 if ( not nook_has_semantic_cause( $asf, $work_nook ) ) {
800 2708         5485 ($child_or_node) = $asf->call_by_tag(
801             ('@' . __FILE__ . ':' . __LINE__),
802             'local asf, work_and_node_id = ...; return asf.lmw_b:_and_node_cause(work_and_node_id)',
803             'i',
804             $work_and_node_id);
805 2708         3535 $child_is_cause = 1;
806 2708         3603 last FIND_CHILD_OR_NODE;
807             } ## end if ( not nook_has_semantic_cause( $asf, $work_nook ))
808             } ## end if ( !$work_nook->[...])
809 28654         34881 $work_nook->[Marpa::R3::Internal::Nook::CAUSE_IS_EXPANDED] = 1;
810 28654 100       40944 if ( !$work_nook
811             ->[Marpa::R3::Internal::Nook::PREDECESSOR_IS_EXPANDED] )
812             {
813 18988         35768 ($child_or_node) = $asf->call_by_tag(
814             ('@' . __FILE__ . ':' . __LINE__),
815             'local asf, work_and_node_id = ...; return asf.lmw_b:_and_node_predecessor(work_and_node_id)',
816             'i',
817             $work_and_node_id);
818 18988 100       29933 if ( defined $child_or_node ) {
819 9653         12293 $child_is_predecessor = 1;
820 9653         13520 last FIND_CHILD_OR_NODE;
821             }
822             } ## end if ( !$work_nook->[...])
823 19001         21866 $work_nook->[Marpa::R3::Internal::Nook::PREDECESSOR_IS_EXPANDED] =
824             1;
825 19001         20099 pop @worklist;
826 19001         34219 next DO_WORKLIST;
827             } ## end FIND_CHILD_OR_NODE:
828              
829             return 0
830             if
831             $choicepoint->[Marpa::R3::Internal::Choicepoint::OR_NODE_IN_USE]
832 12361 50       23985 ->{$child_or_node};
833              
834             return 0
835 12361 50       12843 if not scalar @{ $or_nodes->[$work_or_node] };
  12361         19594  
836              
837 12361         20274 my $new_nook =
838             nook_new( $asf, $child_or_node, $stack_ix_of_work_nook );
839 12361 100       19448 if ($child_is_cause) {
840 2708         4021 $new_nook->[Marpa::R3::Internal::Nook::IS_CAUSE] = 1;
841 2708         3722 $work_nook->[Marpa::R3::Internal::Nook::CAUSE_IS_EXPANDED] = 1;
842             }
843 12361 100       17015 if ($child_is_predecessor) {
844 9653         14340 $new_nook->[Marpa::R3::Internal::Nook::IS_PREDECESSOR] = 1;
845 9653         11816 $work_nook->[Marpa::R3::Internal::Nook::PREDECESSOR_IS_EXPANDED] =
846             1;
847             }
848 12361         12842 push @{$factoring_stack}, $new_nook;
  12361         16955  
849 12361         13842 push @worklist, $#{$factoring_stack};
  12361         23679  
850              
851             } ## end DO_WORKLIST: while ( scalar @worklist )
852              
853 1424         3687 return 1;
854              
855             } ## end sub factoring_finish
856              
857             sub and_nodes_to_cause_nids {
858 11080     11080   15128 my ( $asf, @and_node_ids ) = @_;
859 11080         13653 my %causes = ();
860 11080         14800 for my $and_node_id (@and_node_ids) {
861 11143         19738 my ($cause_nid) = $asf->call_by_tag(
862             ('@' . __FILE__ . ':' . __LINE__),
863             'local asf, and_node_id = ...; return asf.lmw_b:_and_node_cause(and_node_id)',
864             'i',
865             $and_node_id);
866 11143   66     22777 $cause_nid //= and_node_to_nid($and_node_id);
867 11143         21179 $causes{$cause_nid} = 1;
868             }
869 11080         28983 return [ keys %causes ];
870             } ## end sub and_nodes_to_cause_nids
871              
872             sub glade_id_factors {
873 1768     1768   2357 my ($choicepoint) = @_;
874 1768         2142 my $asf = $choicepoint->[Marpa::R3::Internal::Choicepoint::ASF];
875 1768         1976 my $slr = $asf->[Marpa::R3::Internal_ASF2::SLR];
876 1768         1911 my $slg = $slr->[Marpa::R3::Internal_R::SLG];
877 1768         2054 my $or_nodes = $asf->[Marpa::R3::Internal_ASF2::OR_NODES];
878              
879 1768         1885 my @result;
880 1768         2048 my $factoring_stack =
881             $choicepoint->[Marpa::R3::Internal::Choicepoint::FACTORING_STACK];
882 1768 100       3551 return if not $factoring_stack;
883             FACTOR:
884 1424         1747 for (
885             my $factor_ix = 0;
886 20425         32956 $factor_ix <= $#{$factoring_stack};
887             $factor_ix++
888             )
889             {
890 19001         21851 my $nook = $factoring_stack->[$factor_ix];
891 19001 100       25083 next FACTOR if not nook_has_semantic_cause( $asf, $nook );
892 11080         14422 my $or_node = $nook->[Marpa::R3::Internal::Nook::OR_NODE];
893 11080         12697 my $and_nodes = $or_nodes->[$or_node];
894             my $cause_nids = and_nodes_to_cause_nids(
895             $asf,
896 11080         17973 map { $and_nodes->[$_] } (
  11143         21093  
897             $nook->[Marpa::R3::Internal::Nook::FIRST_CHOICE]
898             .. $nook->[Marpa::R3::Internal::Nook::LAST_CHOICE]
899             )
900             );
901 11080         14184 my $base_nidset = Marpa::R3::Nidset->obtain( $asf, @{$cause_nids} );
  11080         20106  
902 11080         16567 my $glade_id = $base_nidset->id();
903              
904 11080         15662 $asf->[Marpa::R3::Internal_ASF2::GLADES]->[$glade_id]
905             ->[Marpa::R3::Internal::Glade::REGISTERED] = 1;
906 11080         21004 push @result, $glade_id;
907             } ## end FACTOR: for ( my $factor_ix = 0; $factor_ix <= $#{...})
908 1424         3843 return \@result;
909             } ## end sub glade_id_factors
910              
911             sub glade_obtain {
912 4084     4084   5115 my ( $asf, $glade_id ) = @_;
913              
914 4084         4796 my $factoring_max = $asf->[Marpa::R3::Internal_ASF2::FACTORING_MAX];
915              
916 4084         4490 my $glades = $asf->[Marpa::R3::Internal_ASF2::GLADES];
917 4084         4695 my $glade = $glades->[$glade_id];
918 4084 50 33     10961 if ( not defined $glade
919             or not $glade->[Marpa::R3::Internal::Glade::REGISTERED] )
920             {
921 0         0 say Data::Dumper::Dumper($glade);
922 0         0 Marpa::R3::exception(
923             "Attempt to use an invalid glade, one whose ID is $glade_id");
924             } ## end if ( not defined $glade or not $glade->[...])
925              
926             # Return the glade if it is already set up
927 4084 100       7468 return $glade if $glade->[Marpa::R3::Internal::Glade::SYMCHES];
928              
929 892         1231 my $base_nidset =
930             $asf->[Marpa::R3::Internal_ASF2::NIDSET_BY_ID]->[$glade_id];
931 892         1118 my $choicepoint;
932             my $choicepoint_powerset;
933             {
934 892         956 my @source_data = ();
  892         1091  
935 892         921 for my $source_nid ( @{ $base_nidset->nids() } ) {
  892         1629  
936 942         1672 my $sort_ix = nid_sort_ix( $asf, $source_nid );
937 942         2144 push @source_data, [ $sort_ix, $source_nid ];
938             }
939 892         1940 my @sorted_source_data = sort { $a->[0] <=> $b->[0] } @source_data;
  52         169  
940 892         1031 my $nid_ix = 0;
941             my ( $sort_ix_of_this_nid, $this_nid ) =
942 892         1057 @{ $sorted_source_data[ $nid_ix++ ] };
  892         1633  
943 892         1165 my @nids_with_current_sort_ix = ();
944 892         953 my $current_sort_ix = $sort_ix_of_this_nid;
945 892         1104 my @symch_ids = ();
946 892         1039 NID: while (1) {
947              
948 1834 100       2921 if ( $sort_ix_of_this_nid != $current_sort_ix ) {
949              
950             # Currently only whole id break logic
951 911         1985 my $nidset_for_sort_ix = Marpa::R3::Nidset->obtain( $asf,
952             @nids_with_current_sort_ix );
953 911         1848 push @symch_ids, $nidset_for_sort_ix->id();
954 911         1184 @nids_with_current_sort_ix = ();
955 911         1120 $current_sort_ix = $sort_ix_of_this_nid;
956             } ## end if ( $sort_ix_of_this_nid != $current_sort_ix )
957 1834 100       3206 last NID if not defined $this_nid;
958 942         1300 push @nids_with_current_sort_ix, $this_nid;
959 942         1216 my $sorted_entry = $sorted_source_data[ $nid_ix++ ];
960 942 100       1406 if ( defined $sorted_entry ) {
961 50         85 ( $sort_ix_of_this_nid, $this_nid ) = @{$sorted_entry};
  50         108  
962 50         100 next NID;
963             }
964 892         1095 $this_nid = undef;
965 892         1065 $sort_ix_of_this_nid = -2;
966             } ## end NID: while (1)
967 892         1715 $choicepoint_powerset = Marpa::R3::Powerset->obtain( $asf, @symch_ids );
968 892         1411 $choicepoint->[Marpa::R3::Internal::Choicepoint::ASF] = $asf;
969 892         1666 $choicepoint->[Marpa::R3::Internal::Choicepoint::FACTORING_STACK] =
970             undef;
971             }
972              
973             # Check if choicepoint already seen?
974 892         1080 my @symches = ();
975 892         1520 my $symch_count = $choicepoint_powerset->count();
976 892         1705 SYMCH: for ( my $symch_ix = 0; $symch_ix < $symch_count; $symch_ix++ ) {
977 911         1181 $choicepoint->[Marpa::R3::Internal::Choicepoint::FACTORING_STACK] =
978             undef;
979 911         1494 my $symch_nidset = $choicepoint_powerset->nidset($asf, $symch_ix);
980 911         1488 my $choicepoint_nid = $symch_nidset->nid(0);
981 911   100     1473 my $g1_symch_rule_id = nid_rule_id($asf, $choicepoint_nid) // -1;
982              
983             # Initial undef indicates no factorings omitted
984 911         1603 my @factorings = ( $g1_symch_rule_id, undef );
985              
986             # For a token
987             # There will not be multiple factorings or nids,
988             # it is assumed, for a token
989 911 100       1499 if ( $g1_symch_rule_id < 0 ) {
990 598         939 my $base_nidset = Marpa::R3::Nidset->obtain( $asf, $choicepoint_nid );
991 598         958 my $glade_id = $base_nidset->id();
992              
993 598         944 $asf->[Marpa::R3::Internal_ASF2::GLADES]->[$glade_id]
994             ->[Marpa::R3::Internal::Glade::REGISTERED] = 1;
995 598         1008 push @factorings, [$glade_id];
996 598         1042 push @symches, \@factorings;
997 598         1455 next SYMCH;
998             } ## end if ( $g1_symch_rule_id < 0 )
999              
1000 313         571 my $symch = $choicepoint_powerset->nidset($asf, $symch_ix);
1001 313         644 my $nid_count = $symch->count();
1002             FACTORINGS_LOOP:
1003 313         657 for ( my $nid_ix = 0; $nid_ix < $nid_count; $nid_ix++ ) {
1004 344         548 $choicepoint_nid = $symch_nidset->nid($nid_ix);
1005 344         796 first_factoring($choicepoint, $choicepoint_nid);
1006 344         632 my $factoring = glade_id_factors($choicepoint);
1007              
1008 344         686 FACTOR: while ( defined $factoring ) {
1009 1424 50       2442 if ( scalar @factorings > $factoring_max ) {
1010              
1011             # update factorings omitted flag
1012 0         0 $factorings[1] = 1;
1013 0         0 last FACTORINGS_LOOP;
1014             }
1015 1424         1827 my @factoring = ();
1016 1424         1508 for (
1017 1424         2768 my $item_ix = $#{$factoring};
1018             $item_ix >= 0;
1019             $item_ix--
1020             )
1021             {
1022 11080         16699 push @factoring, $factoring->[$item_ix];
1023             } ## end for ( my $item_ix = $#{$factoring}; $item_ix >= 0; ...)
1024 1424         3081 push @factorings, \@factoring;
1025 1424         2903 next_factoring($choicepoint, $choicepoint_nid);
1026 1424         2553 $factoring = glade_id_factors($choicepoint);
1027             } ## end FACTOR: while ( defined $factoring )
1028             } ## end FACTORINGS_LOOP: for ( my $nid_ix = 0; $nid_ix < $nid_count; $nid_ix...)
1029 313         848 push @symches, \@factorings;
1030             } ## end SYMCH: for ( my $symch_ix = 0; $symch_ix < $symch_count; ...)
1031              
1032 892         1272 $glade->[Marpa::R3::Internal::Glade::SYMCHES] = \@symches;
1033              
1034 892         1257 $glade->[Marpa::R3::Internal::Glade::ID] = $glade_id;
1035 892         1224 $asf->[Marpa::R3::Internal_ASF2::GLADES]->[$glade_id] = $glade;
1036 892         2098 return $glade;
1037             } ## end sub glade_obtain
1038              
1039             sub Marpa::R3::ASF2::glade_symch_count {
1040 864     864 0 1228 my ( $asf, $glade_id ) = @_;
1041 864         1338 my $glade = glade_obtain( $asf, $glade_id );
1042 864 50       1545 Marpa::R3::exception("No glade found for glade ID $glade_id)") if not defined $glade;
1043 864         956 return scalar @{ $glade->[Marpa::R3::Internal::Glade::SYMCHES] };
  864         1427  
1044             }
1045              
1046             sub Marpa::R3::ASF2::glade_literal {
1047 1814     1814 0 2327 my ( $asf, $glade_id ) = @_;
1048 1814         2393 my $nidset_by_id = $asf->[Marpa::R3::Internal_ASF2::NIDSET_BY_ID];
1049 1814         2030 my $nidset = $nidset_by_id->[$glade_id];
1050 1814 50       2822 Marpa::R3::exception("No glade found for glade ID $glade_id)") if not defined $nidset;
1051 1814         3127 my $nid0 = $nidset->nid(0);
1052 1814         3153 return nid_literal($asf, $nid0);
1053             } ## end sub Marpa::R3::ASF2::glade_literal
1054              
1055             sub Marpa::R3::ASF2::glade_g1_span {
1056 283     283 0 377 my ( $asf, $glade_id ) = @_;
1057 283         347 my $nidset_by_id = $asf->[Marpa::R3::Internal_ASF2::NIDSET_BY_ID];
1058 283         322 my $nidset = $nidset_by_id->[$glade_id];
1059 283 50       447 Marpa::R3::exception("No glade found for glade ID $glade_id)") if not defined $nidset;
1060 283         457 my $nid0 = $nidset->nid(0);
1061 283         573 my ($g1_start, $g1_length) = nid_span($asf, $nid0);
1062 283         470 return $g1_start, $g1_length;
1063             }
1064              
1065             sub Marpa::R3::ASF2::glade_L0_length {
1066 61     61 0 104 my ( $asf, $glade_id ) = @_;
1067 61         119 my ($g1_start, $g1_length) = $asf->glade_g1_span( $glade_id );
1068 61         100 my $slr = $asf->[Marpa::R3::Internal_ASF2::SLR];
1069              
1070 61         171 my ($l0_length) = $slr->call_by_tag(
1071             ('@' . __FILE__ . ':' . __LINE__),
1072             <<'END_OF_LUA', 'ii', $g1_start, $g1_length);
1073             local slr, g1_start, g1_length = ...
1074             return slr:g1_span_l0_length(g1_start, g1_length)
1075             END_OF_LUA
1076 61         113 return $l0_length;
1077             }
1078              
1079             sub Marpa::R3::ASF2::g1_glade_symbol_id {
1080 198     198 0 322 my ( $asf, $glade_id ) = @_;
1081 198         294 my $nidset_by_id = $asf->[Marpa::R3::Internal_ASF2::NIDSET_BY_ID];
1082 198         256 my $nidset = $nidset_by_id->[$glade_id];
1083 198 50       360 Marpa::R3::exception("No glade found for glade ID $glade_id)") if not defined $nidset;
1084 198         368 my $nid0 = $nidset->nid(0);
1085 198         397 return nid_symbol_id($asf, $nid0);
1086             }
1087              
1088             sub Marpa::R3::ASF2::g1_symch_rule_id {
1089 206     206 0 470 my ( $asf, $glade_id, $symch_ix ) = @_;
1090 206         330 my $glade = glade_obtain( $asf, $glade_id );
1091 206         283 my $symches = $glade->[Marpa::R3::Internal::Glade::SYMCHES];
1092 206 50       237 return if $symch_ix > $#{$symches};
  206         377  
1093 206         251 my ($rule_id) = @{ $symches->[$symch_ix] };
  206         315  
1094 206         324 return $rule_id;
1095             }
1096              
1097             sub Marpa::R3::ASF2::symch_factoring_count {
1098 1845     1845 0 2654 my ( $asf, $glade_id, $symch_ix ) = @_;
1099 1845         3034 my $glade = glade_obtain( $asf, $glade_id );
1100 1845 50       2917 Marpa::R3::exception("No glade found for glade ID $glade_id)") if not defined $glade;
1101 1845         2353 my $symches = $glade->[Marpa::R3::Internal::Glade::SYMCHES];
1102 1845 50       2054 return if $symch_ix > $#{$symches};
  1845         3099  
1103 1845         2064 return $#{ $symches->[$symch_ix] } - 1; # length minus 2
  1845         3242  
1104             } ## end sub Marpa::R3::ASF2::symch_factoring_count
1105              
1106             sub Marpa::R3::ASF2::factoring_downglades {
1107 374     374 0 645 my ( $asf, $glade_id, $symch_ix, $factoring_ix ) = @_;
1108 374         546 my $glade = glade_obtain( $asf, $glade_id );
1109 374 50       580 Marpa::R3::exception("No glade found for glade ID $glade_id)") if not defined $glade;
1110 374         456 my $symches = $glade->[Marpa::R3::Internal::Glade::SYMCHES];
1111             Marpa::R3::exception("No symch #$symch_ix exists for glade ID $glade_id")
1112 374 50       417 if $symch_ix > $#{$symches};
  374         605  
1113 374         467 my $symch = $symches->[$symch_ix];
1114 374         381 my ( $rule_id, undef, @factorings ) = @{$symch};
  374         643  
1115 374 50       601 Marpa::R3::exception("No downglades for glade ID $glade_id, symch #$symch_ix: it is a token symch")
1116             if $rule_id < 0;
1117 374 50       588 return if $factoring_ix >= scalar @factorings;
1118 374         440 my $factoring = $factorings[$factoring_ix];
1119 374         569 return $factoring;
1120             }
1121              
1122             sub Marpa::R3::ASF2::factoring_symbol_count {
1123 97     97 0 239 my ( $asf, $glade_id, $symch_ix, $factoring_ix ) = @_;
1124 97         160 my $factoring = $asf->factoring_downglades($glade_id, $symch_ix, $factoring_ix);
1125 97 50       174 return if not defined $factoring;
1126 97         106 return scalar @{$factoring};
  97         140  
1127             } ## end sub Marpa::R3::ASF2::factoring_symbol_count
1128              
1129             sub Marpa::R3::ASF2::factor_downglade {
1130 168     168 0 262 my ( $asf, $glade_id, $symch_ix, $factoring_ix, $symbol_ix ) = @_;
1131 168         273 my $factoring = $asf->factoring_downglades($glade_id, $symch_ix, $factoring_ix);
1132 168 50       265 return if not defined $factoring;
1133 168         241 return $factoring->[$symbol_ix];
1134             } ## end sub Marpa::R3::ASF2::factor_downglade
1135              
1136             sub Marpa::R3::Internal_ASF2::ambiguities {
1137 16     16   104 my ($asf) = @_;
1138 16         69 my $peak = $asf->peak();
1139 16         69 return Marpa::R3::Internal_ASF2::glade_ambiguities( $asf, $peak, [] );
1140             }
1141              
1142             sub Marpa::R3::Internal_ASF2::glade_ambiguities {
1143 44     44   86 my ( $asf, $glade, $seen ) = @_;
1144 44 50       102 return [] if $seen->[$glade]; # empty on revisit
1145 44         77 $seen->[$glade] = 1;
1146 44         102 my $grammar = $asf->grammar();
1147 44         106 my $symch_count = $asf->glade_symch_count($glade);
1148 44 100       105 if ( $symch_count > 1 ) {
1149 5         19 my $literal = $asf->glade_literal($glade);
1150 5         25 my $symbol_id = $asf->g1_glade_symbol_id($glade);
1151 5         27 my $display_form = $grammar->g1_symbol_display_form($symbol_id);
1152 5         29 return [ [ 'symch', $glade, ] ];
1153             } ## end if ( $symch_count > 1 )
1154 39         120 my $g1_rule_id = $asf->g1_symch_rule_id( $glade, 0 );
1155 39 100       106 return [] if $g1_rule_id < 0; # no ambiguities if a token
1156              
1157             # ignore any truncation of the factorings
1158              
1159 34         99 my $factoring_count = $asf->symch_factoring_count( $glade, 0 );
1160 34 100       84 if ( $factoring_count <= 1 ) {
1161 23         72 my $downglades = $asf->factoring_downglades( $glade, 0, 0 );
1162             my @problems =
1163 23         41 map { @{ glade_ambiguities( $asf, $_, $seen ) } } @{$downglades};
  27         38  
  27         184  
  23         52  
1164 23         107 return \@problems;
1165             } ## end if ( $factoring_count <= 1 )
1166 11         29 my @results = ();
1167              
1168 11         38 my $downglades = $asf->factoring_downglades( $glade, 0, 0 );
1169 11         20 my $min_factors = $#{$downglades} + 1;
  11         27  
1170 11         46 my ( $upglade_start, $upglade_length ) = $asf->glade_g1_span($glade);
1171 11         34 my $sync_location = $upglade_start + $upglade_length;
1172              
1173 11         30 my @factors_by_factoring = ($downglades);
1174 11         50 for (
1175             my $factoring_ix = 1;
1176             $factoring_ix < $factoring_count;
1177             $factoring_ix++
1178             )
1179             {
1180 35         67 my $downglades =
1181             $asf->factoring_downglades( $glade, 0, $factoring_ix );
1182 35         46 my $factor_count = $#{$downglades} + 1;
  35         56  
1183 35 100       82 $min_factors =
1184             $min_factors > $factor_count ? $factor_count : $min_factors;
1185              
1186             # Determine a first potential
1187             # "sync location of the factors" from
1188             # the earliest start of the first downglade of any factoring.
1189             # Currently this will be the start of the parent glade, but this
1190             # method will be safe against any future hacks.
1191 35         70 my ($this_sync_location) = $asf->glade_g1_span( $downglades->[0] );
1192 35         96 $sync_location =
1193             List::Util::min( $this_sync_location, $sync_location );
1194              
1195 35         82 push @factors_by_factoring, $downglades;
1196             } ## end for ( my $factoring_ix = 1; $factoring_ix < $factoring_count...)
1197              
1198 11         45 my @factor_ix = (0) x $factoring_count;
1199 11         25 SYNC_PASS: while (1) {
1200              
1201             # Assume synced and unambiguous until we see otherwise.
1202 34         52 my $is_synced = 1;
1203              
1204             # First find a synch'ed set of factors, if we can
1205             FACTORING:
1206 34         76 for (
1207             my $factoring_ix = 0;
1208             $factoring_ix < $factoring_count;
1209             $factoring_ix++
1210             )
1211             {
1212 115         159 my $this_factor_ix = $factor_ix[$factoring_ix];
1213 115         136 my $this_downglade =
1214             $factors_by_factoring[$factoring_ix][$this_factor_ix];
1215 115         174 my ($this_start) = $asf->glade_g1_span($this_downglade);
1216              
1217             # To keep time complexity down we limit the number of times we deal
1218             # with a factoring at a sync location to 3, worst case -- a pass which
1219             # identifies it as a potential sync location, a pass which
1220             # (if possible) brings all the factors to that location, and a
1221             # pass which leaves all factor IX's where they are, and determines
1222             # we have found a sync location. This makes out time O(f*n), where
1223             # f is the factoring count and n is the mininum number of factors.
1224              
1225 115         203 while ( $this_start < $sync_location ) {
1226 32         42 $factor_ix[$factoring_ix]++;
1227 32 100       72 last SYNC_PASS if $factor_ix[$factoring_ix] >= $min_factors;
1228 21         36 $this_start = $asf->glade_g1_span($this_downglade);
1229             } ## end if ( $this_start < $sync_location )
1230 104 100       217 if ( $this_start > $sync_location ) {
1231 16         22 $is_synced = 0;
1232 16         32 $sync_location = $this_start;
1233             }
1234             } ## end FACTORING: for ( my $factoring_ix = 0; $factoring_ix < ...)
1235              
1236 23 100       71 next SYNC_PASS if not $is_synced;
1237              
1238             # If here, every factor starts at the sync location
1239              
1240             SYNCED_RESULT: {
1241              
1242 15         24 my $ambiguous_factors;
  15         25  
1243 15         23 my $first_factor_ix = $factor_ix[0];
1244 15         29 my $first_downglade = $factors_by_factoring[0][$first_factor_ix];
1245              
1246             FACTORING:
1247 15         49 for (
1248             my $factoring_ix = 1;
1249             $factoring_ix < $factoring_count;
1250             $factoring_ix++
1251             )
1252             {
1253 21         32 my $this_factor_ix = $factor_ix[$factoring_ix];
1254 21         32 my $this_downglade =
1255             $factors_by_factoring[$factoring_ix][$this_factor_ix];
1256 21 100       52 if ( $this_downglade != $first_downglade ) {
1257 14         34 $ambiguous_factors = [
1258             $first_factor_ix, $factoring_ix,
1259             $this_factor_ix
1260             ];
1261 14         28 last FACTORING;
1262             } ## end if ( $this_downglade != $first_downglade )
1263              
1264             } ## end FACTORING: for ( my $factoring_ix = 1; $factoring_ix < ...)
1265              
1266             # If here, all the the downglades are identical
1267 15 100       38 if ( not defined $ambiguous_factors ) {
1268             push @results,
1269 1         2 @{ glade_ambiguities( $asf, $first_downglade, $seen ) };
  1         7  
1270 1         2 last SYNCED_RESULT;
1271             }
1272              
1273             # First factoring IX is always zero
1274             push @results,
1275 14         28 [ 'factoring', $glade, 0, @{$ambiguous_factors} ];
  14         47  
1276             } ## end SYNCED_RESULT:
1277              
1278 15         78 $factor_ix[$_]++ for 0 .. $factoring_count;
1279 15 50       71 last SYNC_PASS if List::Util::max(@factor_ix) >= $min_factors;
1280              
1281             } ## end SYNC_PASS: while (1)
1282              
1283 11         62 return \@results;
1284              
1285             } ## end sub Marpa::R3::Internal_ASF2::glade_ambiguities
1286              
1287             # A generic display routine for ambiguities -- complex application will
1288             # want to replace this, using it perhaps as a fallback.
1289             sub Marpa::R3::Internal_ASF2::ambiguities_show {
1290 16     16   169 my ( $asf, $ambiguities ) = @_;
1291 16         53 my $grammar = $asf->grammar();
1292 16         33 my $slr = $asf->[Marpa::R3::Internal_ASF2::SLR];
1293 16         41 my $result = q{};
1294 16         31 AMBIGUITY: for my $ambiguity ( @{$ambiguities} ) {
  16         43  
1295 19         36 my $type = $ambiguity->[0];
1296 19 100       71 if ( $type eq 'symch' ) {
1297              
1298 5         12 my ( undef, $glade ) = @{$ambiguity};
  5         12  
1299 5         17 my $symbol_display_form =
1300             $grammar->g1_symbol_display_form(
1301             $asf->g1_glade_symbol_id($glade) );
1302              
1303 5         25 my $l0_length = $asf->glade_L0_length($glade);
1304 5         15 my ( $g1_start, $g1_length ) = $asf->glade_g1_span($glade);
1305 5         25 my ( $l0_block1, $l0_pos1 ) = $slr->g1_to_block_first($g1_start);
1306 5         27 my ( $l0_block2, $l0_pos2 ) = $slr->g1_to_block_last($g1_start + $g1_length -1);
1307 5         29 my $l0_range = $slr->lc_brief($l0_block1, $l0_pos1, $l0_block2, $l0_pos2);
1308 5         22 my $display_length = List::Util::min( $l0_length, 60 );
1309 5         22 $result
1310             .= qq{Ambiguous symch at Glade=$glade, Symbol=<$symbol_display_form>:\n};
1311 5         22 $result
1312             .= qq{ The ambiguity is at $l0_range\n};
1313 5 50       19 my $literal_label =
1314             $display_length == $l0_length ? 'Text is: ' : 'Text begins: ';
1315              
1316 5         23 my ($escaped_input) = $slr->call_by_tag(
1317             ('@' . __FILE__ . ':' . __LINE__),
1318             <<'END_OF_LUA', 'ii', $g1_start, $display_length);
1319             local slr, g1_start, input_length = ...
1320             return slr:g1_escape(g1_start, input_length)
1321             END_OF_LUA
1322              
1323 5         19 $result
1324             .= q{ }
1325             . $literal_label
1326             . $escaped_input
1327             . qq{\n};
1328              
1329 5         13 my $symch_count = $asf->glade_symch_count($glade);
1330 5         16 my $display_symch_count = List::Util::min( 5, $symch_count );
1331 5 50       33 $result .=
1332             $symch_count == $display_symch_count
1333             ? " There are $symch_count symches\n"
1334             : " There are $symch_count symches -- showing only the first $display_symch_count\n";
1335 5         22 SYMCH_IX: for my $symch_ix ( 0 .. $display_symch_count - 1 ) {
1336 10         25 my $g1_rule_id = $asf->g1_symch_rule_id( $glade, $symch_ix );
1337 10 50       24 if ( $g1_rule_id < 0 ) {
1338 0         0 $result .= " Symch $symch_ix is a token\n";
1339 0         0 next SYMCH_IX;
1340             }
1341 10         41 $result .= " Symch $symch_ix is a rule: "
1342             . $grammar->g1_rule_show($g1_rule_id) . "\n";
1343             } ## end SYMCH_IX: for my $symch_ix ( 0 .. $display_symch_count - 1 )
1344              
1345 5         20 next AMBIGUITY;
1346             } ## end if ( $type eq 'symch' )
1347 14 50       40 if ( $type eq 'factoring' ) {
1348 14         28 my $factoring_ix1 = 0;
1349             my ( undef, $glade, $symch_ix, $factor_ix1, $factoring_ix2,
1350             $factor_ix2 )
1351 14         28 = @{$ambiguity};
  14         55  
1352 14         37 my $first_downglades =
1353             $asf->factoring_downglades( $glade, $symch_ix, 0 );
1354 14         36 my $first_downglade = $first_downglades->[$factor_ix1];
1355             {
1356 14         21 my $these_downglades =
  14         31  
1357             $asf->factoring_downglades( $glade, $symch_ix,
1358             $factoring_ix2 );
1359 14         24 my $this_downglade = $these_downglades->[$factor_ix2];
1360 14         225 my $symbol_display_form =
1361             $grammar->g1_symbol_display_form(
1362             $asf->g1_glade_symbol_id($first_downglade) );
1363              
1364 14         38 my ( $g1_start, $g1_length ) =
1365             $asf->glade_g1_span($first_downglade);
1366 14 100       49 my $g1_last = $g1_length > 0 ? ($g1_start + $g1_length - 1) : $g1_start;
1367              
1368 14         92 my ( $l0_block1, $l0_pos1 ) = $slr->g1_to_block_first($g1_start);
1369 14         53 my ( $l0_block2, $l0_pos2 ) = $slr->g1_to_block_last($g1_last);
1370 14         73 my $l0_range = $slr->lc_brief($l0_block1, $l0_pos1, $l0_block2, $l0_pos2);
1371              
1372 14         50 my $first_length = $asf->glade_L0_length($first_downglade);
1373 14         36 my $this_length = $asf->glade_L0_length($this_downglade);
1374 14         47 my $display_length =
1375             List::Util::min( $first_length, $this_length, 60 );
1376 14         58 $result
1377             .= qq{Length of symbol "$symbol_display_form" at $l0_range is ambiguous\n};
1378              
1379 14 100       42 if ( $display_length > 0 ) {
1380              
1381 2         9 my ($piece) = $slr->call_by_tag(
1382             ('@' . __FILE__ . ':' . __LINE__),
1383             <<'END_OF_LUA', 'ii', $g1_start, $display_length);
1384             local slr, g1_start, input_length = ...
1385             local escaped_input = slr:g1_escape(g1_start, input_length)
1386             return " Choices start with: " .. escaped_input .. "\n"
1387             END_OF_LUA
1388              
1389 2         8 $result .= $piece;
1390              
1391             } ## end if ( $display_length > 0 )
1392              
1393 14         29 my @display_downglade = ( $first_downglade, $this_downglade );
1394             DISPLAY_GLADE:
1395 14         57 for (
1396             my $glade_ix = 0;
1397             $glade_ix <= $#display_downglade;
1398             $glade_ix++
1399             )
1400             {
1401             # Choices may be zero length
1402 28         52 my $choice_number = $glade_ix + 1;
1403 28         43 my $glade_id = $display_downglade[$glade_ix];
1404 28         71 my $l0_length = $asf->glade_L0_length($glade_id);
1405 28 100       68 if ( $l0_length <= 0 ) {
1406 12         36 $result
1407             .= qq{ Choice $choice_number is zero length\n};
1408 12         35 next DISPLAY_GLADE;
1409             }
1410              
1411 16         43 my ( $g1_start, $g1_length ) = $asf->glade_g1_span($glade_id);
1412 16         53 my ( $l0_block, $l0_pos ) = $slr->g1_to_block_last($g1_start + $g1_length -1);
1413 16         48 my $l0_location = $slr->lc_brief($l0_block, $l0_pos);
1414              
1415 16         79 $result
1416             .= qq{ Choice $choice_number, length=$l0_length, ends at $l0_location\n};
1417              
1418 16         55 my ($piece) = $slr->call_by_tag(
1419             ('@' . __FILE__ . ':' . __LINE__),
1420             <<'END_OF_LUA', 'iiii', $choice_number, $g1_start, $g1_length, $l0_length);
1421             local slr, choice_number, g1_start, g1_length, l0_length = ...
1422             local subpieces = {}
1423             local escaped_input
1424             if l0_length > 60 then
1425             escaped_input =
1426             slr:reverse_g1_escape(g1_start + g1_length, 60)
1427             subpieces[#subpieces+1] = string.format(" Choice %d ending: %s\n",
1428             choice_number,
1429             escaped_input)
1430             end
1431             local display_length = math.min(l0_length, 60)
1432             escaped_input = slr:g1_escape(g1_start, display_length)
1433             subpieces[#subpieces+1] = string.format(" Choice %d: %s\n",
1434             choice_number,
1435             escaped_input)
1436             return table.concat(subpieces)
1437             END_OF_LUA
1438              
1439 16         62 $result .= $piece;
1440              
1441             } ## end DISPLAY_GLADE: for ( my $glade_ix = 0; $glade_ix <= ...)
1442 14         51 next AMBIGUITY;
1443             } ## end FACTORING: for ( my $factoring_ix = 1; $factoring_ix < ...)
1444 0         0 next AMBIGUITY;
1445             } ## end if ( $type eq 'factoring' )
1446             $result
1447 0         0 .= qq{Ambiguities of type "$type" not implemented:\n}
1448             . Data::Dumper::dumper($ambiguity);
1449 0         0 next AMBIGUITY;
1450              
1451             } ## end AMBIGUITY: for my $ambiguity ( @{$ambiguities} )
1452 16         102 return $result;
1453             } ## end sub Marpa::R3::Internal_ASF2::ambiguities_show
1454              
1455             # The higher level calls
1456              
1457             sub Marpa::R3::ASF2::traverse {
1458 31     31 0 963 my ( $asf, $per_traverse_object, $method ) = @_;
1459 31 50       131 if ( ref $method ne 'CODE' ) {
1460 0         0 Marpa::R3::exception(
1461             'Argument to $asf->traverse() must be an anonymous subroutine');
1462             }
1463 31 50       96 if ( not ref $per_traverse_object ) {
1464 0         0 Marpa::R3::exception(
1465             'Argument to $asf->traverse() must be a reference');
1466             }
1467 31         105 my $peak = $asf->peak();
1468 31         70 my $peak_glade = glade_obtain( $asf, $peak );
1469 31         96 my $traverser = bless [], "Marpa::R3::Internal_ASF2::Traverse";
1470 31         75 $traverser->[Marpa::R3::Internal_ASF2::Traverse::ASF] = $asf;
1471 31         63 $traverser->[Marpa::R3::Internal_ASF2::Traverse::CODE] = $method;
1472 31         58 $traverser->[Marpa::R3::Internal_ASF2::Traverse::PER_TRAVERSE_OBJECT] = $per_traverse_object;
1473 31         61 $traverser->[Marpa::R3::Internal_ASF2::Traverse::VALUES] = [];
1474 31         69 $traverser->[Marpa::R3::Internal_ASF2::Traverse::GLADE] = $peak_glade;
1475 31         85 $traverser->[Marpa::R3::Internal_ASF2::Traverse::SYMCH_IX] = 0;
1476 31         57 $traverser->[Marpa::R3::Internal_ASF2::Traverse::FACTORING_IX] = 0;
1477 31         97 return $method->( $traverser, $per_traverse_object );
1478             } ## end sub Marpa::R3::ASF2::traverse
1479              
1480             sub Marpa::R3::Internal_ASF2::Traverse::all_choices {
1481 12     12   72 my ( $traverser ) = @_;
1482              
1483 12         27 my @values = Marpa::R3::Internal_ASF2::Traverse::rh_values( $traverser );
1484 12         21 my @results = ( [] );
1485 12         29 for my $rh_ix ( 0 .. @values - 1 ) {
1486 24         28 my @new_results = ();
1487 24         30 for my $old_result (@results) {
1488 26         30 my $child_value = $values[$rh_ix];
1489 26 50       48 $child_value = [ $child_value ] unless ref $child_value eq 'ARRAY';
1490 26         28 for my $new_value ( @{ $child_value } ) {
  26         37  
1491 30         30 push @new_results, [ @{$old_result}, $new_value ];
  30         59  
1492             }
1493             }
1494 24         42 @results = @new_results;
1495             } ## end for my $rh_ix ( 0 .. $length - 1 )
1496              
1497 12         25 return @results;
1498             }
1499              
1500             # TODO -- Document this method
1501             sub Marpa::R3::Internal_ASF2::Traverse::asf {
1502 4     4   11 my ( $traverser ) = @_;
1503 4         8 return $traverser->[Marpa::R3::Internal_ASF2::Traverse::ASF];
1504             }
1505              
1506             sub Marpa::R3::Internal_ASF2::Traverse::literal {
1507 1742     1742   6361 my ( $traverser ) = @_;
1508 1742         2064 my $asf = $traverser->[Marpa::R3::Internal_ASF2::Traverse::ASF];
1509 1742         2113 my $glade = $traverser->[Marpa::R3::Internal_ASF2::Traverse::GLADE];
1510 1742         1932 my $glade_id = $glade->[Marpa::R3::Internal::Glade::ID];
1511 1742         2759 return $asf->glade_literal($glade_id);
1512             }
1513              
1514             # TODO document span() -> g1_span()
1515             sub Marpa::R3::Internal_ASF2::Traverse::g1_span {
1516 4     4   31 my ( $traverser ) = @_;
1517 4         5 my $asf = $traverser->[Marpa::R3::Internal_ASF2::Traverse::ASF];
1518 4         7 my $glade = $traverser->[Marpa::R3::Internal_ASF2::Traverse::GLADE];
1519 4         5 my $glade_id = $glade->[Marpa::R3::Internal::Glade::ID];
1520 4         10 return $asf->glade_g1_span($glade_id);
1521             }
1522              
1523             sub Marpa::R3::Internal_ASF2::Traverse::symbol_id {
1524 103     103   258 my ( $traverser ) = @_;
1525 103         122 my $asf = $traverser->[Marpa::R3::Internal_ASF2::Traverse::ASF];
1526 103         112 my $glade = $traverser->[Marpa::R3::Internal_ASF2::Traverse::GLADE];
1527 103         129 my $glade_id = $glade->[Marpa::R3::Internal::Glade::ID];
1528 103         191 return $asf->g1_glade_symbol_id($glade_id);
1529             }
1530              
1531             sub Marpa::R3::Internal_ASF2::Traverse::rule_id {
1532 1788     1788   7527 my ( $traverser ) = @_;
1533 1788         2079 my $glade = $traverser->[Marpa::R3::Internal_ASF2::Traverse::GLADE];
1534 1788         1914 my $symch_ix =
1535             $traverser->[Marpa::R3::Internal_ASF2::Traverse::SYMCH_IX];
1536 1788         2170 my $symch = $glade->[Marpa::R3::Internal::Glade::SYMCHES]->[$symch_ix];
1537 1788         1796 my ( $rule_id ) = @{$symch};
  1788         2671  
1538 1788 100       3204 return if $rule_id < 0;
1539 1241         1934 return $rule_id;
1540             } ## end sub Marpa::R3::Internal_ASF2::Traverse::rule_id
1541              
1542             sub Marpa::R3::Internal_ASF2::Traverse::rh_length {
1543 1249     1249   3371 my ( $traverser ) = @_;
1544 1249         1548 my $glade = $traverser->[Marpa::R3::Internal_ASF2::Traverse::GLADE];
1545 1249         1414 my $symch_ix =
1546             $traverser->[Marpa::R3::Internal_ASF2::Traverse::SYMCH_IX];
1547 1249         1351 my $symch = $glade->[Marpa::R3::Internal::Glade::SYMCHES]->[$symch_ix];
1548 1249         1278 my ( $rule_id, undef, @factorings ) = @{$symch};
  1249         7657  
1549 1249 50       1970 Marpa::R3::exception(
1550             '$glade->rh_length($rh_ix) called for a token -- that is not allowed')
1551             if $rule_id < 0;
1552 1249         1553 my $factoring_ix =
1553             $traverser->[Marpa::R3::Internal_ASF2::Traverse::FACTORING_IX];
1554 1249         1707 my $factoring = $factorings[$factoring_ix];
1555 1249         1396 return scalar @{$factoring};
  1249         6022  
1556             } ## end sub Marpa::R3::Internal_ASF2::Traverse::rh_length
1557              
1558             sub Marpa::R3::Internal_ASF2::Traverse::rh_value {
1559 10708     10708   21402 my ( $traverser, $rh_ix ) = @_;
1560 10708         11762 my $glade = $traverser->[Marpa::R3::Internal_ASF2::Traverse::GLADE];
1561 10708         10712 my $symch_ix =
1562             $traverser->[Marpa::R3::Internal_ASF2::Traverse::SYMCH_IX];
1563 10708         11365 my $symch = $glade->[Marpa::R3::Internal::Glade::SYMCHES]->[$symch_ix];
1564 10708         10464 my ( $rule_id, undef, @factorings ) = @{$symch};
  10708         47003  
1565 10708 50       15609 Marpa::R3::exception(
1566             '$glade->rh_value($rh_ix) called for a token -- that is not allowed')
1567             if $rule_id < 0;
1568 10708         11713 my $factoring_ix =
1569             $traverser->[Marpa::R3::Internal_ASF2::Traverse::FACTORING_IX];
1570 10708         10732 my $factoring = $factorings[$factoring_ix];
1571 10708 50       10387 return if $rh_ix > $#{$factoring};
  10708         14722  
1572 10708         12584 my $downglade_id = $factoring->[$rh_ix];
1573 10708         11848 my $memoized_value = $traverser->[Marpa::R3::Internal_ASF2::Traverse::VALUES]->[$downglade_id];
1574 10708 100       46557 return $memoized_value if defined $memoized_value;
1575 702         844 my $asf = $traverser->[Marpa::R3::Internal_ASF2::Traverse::ASF];
1576 702         1154 my $downglade = glade_obtain( $asf, $downglade_id );
1577 702         1171 my $blessing = ref $traverser;
1578              
1579             # A shallow clone
1580 702         808 my $child_traverser = bless [ @{$traverser} ], $blessing;
  702         1969  
1581 702         916 $child_traverser->[Marpa::R3::Internal_ASF2::Traverse::GLADE] =
1582             $downglade;
1583 702         831 $child_traverser->[Marpa::R3::Internal_ASF2::Traverse::SYMCH_IX] = 0;
1584 702         755 $child_traverser->[Marpa::R3::Internal_ASF2::Traverse::FACTORING_IX] = 0;
1585 702         780 my $code = $traverser->[Marpa::R3::Internal_ASF2::Traverse::CODE];
1586 702         1821 my $value = $code->(
1587             $child_traverser,
1588             $traverser->[Marpa::R3::Internal_ASF2::Traverse::PER_TRAVERSE_OBJECT]
1589             );
1590 702 50       2663 Marpa::R3::exception(
1591             'The ASF traversing method returned undef -- that is not allowed')
1592             if not defined $value;
1593 702         1138 $traverser->[Marpa::R3::Internal_ASF2::Traverse::VALUES]->[$downglade_id]
1594             = $value;
1595 702         3243 return $value;
1596             } ## end sub Marpa::R3::Internal_ASF2::Traverse::rh_value
1597              
1598             sub Marpa::R3::Internal_ASF2::Traverse::rh_values {
1599 17     17   39 my ( $traverser ) = @_;
1600 17         70 return map { Marpa::R3::Internal_ASF2::Traverse::rh_value( $traverser, $_ ) }
  35         75  
1601             0 .. Marpa::R3::Internal_ASF2::Traverse::rh_length( $traverser ) - 1;
1602             }
1603              
1604             sub Marpa::R3::Internal_ASF2::Traverse::next_factoring {
1605 1721     1721   2158 my ($traverser) = @_;
1606 1721         2185 my $glade = $traverser->[Marpa::R3::Internal_ASF2::Traverse::GLADE];
1607 1721         1949 my $glade_id = $glade->[Marpa::R3::Internal::Glade::ID];
1608 1721         1851 my $asf = $traverser->[Marpa::R3::Internal_ASF2::Traverse::ASF];
1609 1721         1811 my $symch_ix = $traverser->[Marpa::R3::Internal_ASF2::Traverse::SYMCH_IX];
1610 1721         2694 my $last_factoring =
1611             $asf->symch_factoring_count( $glade_id, $symch_ix ) - 1;
1612 1721         2107 my $factoring_ix =
1613             $traverser->[Marpa::R3::Internal_ASF2::Traverse::FACTORING_IX];
1614 1721 100       3557 return if $factoring_ix >= $last_factoring;
1615 1056         1201 $factoring_ix++;
1616 1056         1210 $traverser->[Marpa::R3::Internal_ASF2::Traverse::FACTORING_IX] =
1617             $factoring_ix;
1618 1056         2561 return $factoring_ix;
1619             } ## end sub Marpa::R3::Internal_ASF2::Traverse::next_factoring
1620              
1621             sub Marpa::R3::Internal_ASF2::Traverse::next_symch {
1622 665     665   890 my ($traverser) = @_;
1623 665         751 my $glade = $traverser->[Marpa::R3::Internal_ASF2::Traverse::GLADE];
1624 665         744 my $glade_id = $glade->[Marpa::R3::Internal::Glade::ID];
1625 665         813 my $asf = $traverser->[Marpa::R3::Internal_ASF2::Traverse::ASF];
1626 665         708 my $symch_ix = $traverser->[Marpa::R3::Internal_ASF2::Traverse::SYMCH_IX];
1627 665         1018 my $last_symch = $asf->glade_symch_count( $glade_id ) - 1;
1628 665 100       1867 return if $symch_ix >= $last_symch;
1629 7         12 $symch_ix++;
1630 7         13 $traverser->[Marpa::R3::Internal_ASF2::Traverse::SYMCH_IX] = $symch_ix;
1631 7         11 $traverser->[Marpa::R3::Internal_ASF2::Traverse::FACTORING_IX] = 0;
1632 7         23 return $symch_ix;
1633             } ## end sub Marpa::R3::Internal_ASF2::Traverse::next_symch
1634              
1635             sub Marpa::R3::Internal_ASF2::Traverse::next {
1636 1721     1721   3071 my ($traverser) = @_;
1637 1721   100     2687 return $traverser->next_factoring() // $traverser->next_symch();
1638             }
1639              
1640             # GLADE_SEEN is a local -- this is to silence warnings
1641             our %GLADE_SEEN;
1642              
1643             sub form_choice {
1644 40     40   69 my ( $parent_choice, $sub_choice ) = @_;
1645 40 100       89 return $sub_choice if not defined $parent_choice;
1646 12         29 return join q{.}, $parent_choice, $sub_choice;
1647             }
1648              
1649             sub Marpa::R3::ASF2::dump_glade {
1650 181     181 0 289 my ( $asf, $glade_id, $parent_choice, $item_ix ) = @_;
1651 181 100       351 if ( $GLADE_SEEN{$glade_id} ) {
1652 44         106 return [ [0, $glade_id, "already displayed"] ];
1653             }
1654 137         249 $GLADE_SEEN{$glade_id} = 1;
1655              
1656 137         230 my $grammar = $asf->grammar();
1657 137         169 my @lines = ();
1658 137         156 my $symch_indent = 0;
1659              
1660 137         242 my $symch_count = $asf->glade_symch_count($glade_id);
1661 137         176 my $symch_choice = $parent_choice;
1662 137 100       222 if ( $symch_count > 1 ) {
1663 4   50     11 $item_ix //= 0;
1664 4         26 push @lines,
1665             [ 0, undef, "Symbol #$item_ix "
1666             . $grammar->g1_symbol_display_form($asf->g1_glade_symbol_id($glade_id))
1667             . " has $symch_count symches" ];
1668 4         19 $symch_indent += 2;
1669 4         11 $symch_choice = form_choice( $parent_choice, $item_ix );
1670             } ## end if ( $symch_count > 1 )
1671 137         239 for ( my $symch_ix = 0; $symch_ix < $symch_count; $symch_ix++ ) {
1672 141 100       242 my $current_choice =
1673             $symch_count > 1
1674             ? form_choice( $symch_choice, $symch_ix )
1675             : $symch_choice;
1676 141         152 my $indent = $symch_indent;
1677 141 100       203 if ( $symch_count > 1 ) {
1678 8         20 push @lines, [ $symch_indent , undef, "Symch #$current_choice" ];
1679             }
1680 141         307 my $rule_id = $asf->g1_symch_rule_id( $glade_id, $symch_ix );
1681 141 100       201 if ( $rule_id >= 0 ) {
1682 78         327 push @lines,
1683             [
1684             $symch_indent, $glade_id,
1685             "Rule $rule_id: " . $grammar->g1_rule_show($rule_id)
1686             ];
1687 78         117 for my $line (
1688 78         200 @{ dump_factorings(
1689             $asf, $glade_id, $symch_ix, $current_choice
1690             ) }
1691             )
1692             {
1693 561         637 my ( $line_indent, @rest_of_line ) = @{$line};
  561         824  
1694 561         1197 push @lines, [ $line_indent + $symch_indent + 2, @rest_of_line ];
1695             } ## end for my $line ( dump_factorings( $asf, $glade_id, ...))
1696             } ## end if ( $rule_id >= 0 )
1697             else {
1698 63         118 my $line = dump_terminal( $asf, $glade_id, $current_choice );
1699 63         87 my ( $line_indent, @rest_of_line ) = @{$line};
  63         132  
1700 63         256 push @lines, [ $line_indent + $symch_indent, @rest_of_line ];
1701             } ## end else [ if ( $rule_id >= 0 ) ]
1702             } ## end for ( my $symch_ix = 0; $symch_ix < $symch_count; $symch_ix...)
1703 137         295 return \@lines;
1704             }
1705              
1706             # Show all the factorings of a SYMCH
1707             sub dump_factorings {
1708 78     78   136 my ( $asf, $glade_id, $symch_ix, $parent_choice ) = @_;
1709              
1710 78         95 my @lines;
1711 78         169 my $factoring_count = $asf->symch_factoring_count( $glade_id, $symch_ix );
1712 78         152 for (
1713             my $factoring_ix = 0;
1714             $factoring_ix < $factoring_count;
1715             $factoring_ix++
1716             )
1717             {
1718 97         124 my $indent = 0;
1719 97         108 my $current_choice = $parent_choice;
1720 97 100       143 if ( $factoring_count > 1 ) {
1721 28         40 $indent = 2;
1722 28         51 $current_choice = form_choice( $parent_choice, $factoring_ix );
1723 28         72 push @lines, [ 0, undef, "Factoring #$current_choice" ];
1724             }
1725 97         193 my $symbol_count =
1726             $asf->factoring_symbol_count( $glade_id, $symch_ix,
1727             $factoring_ix );
1728 97         224 SYMBOL: for my $symbol_ix ( 0 .. $symbol_count - 1 ) {
1729 168         330 my $downglade =
1730             $asf->factor_downglade( $glade_id, $symch_ix, $factoring_ix,
1731             $symbol_ix );
1732 168         195 for my $line (
1733 168         361 @{ $asf->dump_glade( $downglade, $current_choice,
1734             $symbol_ix )
1735             }
1736             )
1737             {
1738 533         558 my ( $line_indent, @rest_of_line ) = @{$line};
  533         793  
1739 533         1252 push @lines, [ $line_indent + $indent, @rest_of_line ];
1740              
1741             } ## end for my $line ( @{ $asf->dump_glade( $downglade, ...)})
1742             } ## end SYMBOL: for my $symbol_ix ( 0 .. $symbol_count - 1 )
1743             } ## end for ( my $factoring_ix = 0; $factoring_ix < $factoring_count...)
1744 78         145 return \@lines;
1745             } ## end sub dump_factorings
1746              
1747             sub dump_terminal {
1748 63     63   126 my ( $asf, $glade_id, $symch_ix, $parent_choice ) = @_;
1749              
1750             # There can only be one symbol in a terminal and therefore only one factoring
1751 63         89 my $current_choice = $parent_choice;
1752 63         99 my $literal = $asf->glade_literal($glade_id);
1753 63         145 my $symbol_id = $asf->g1_glade_symbol_id($glade_id);
1754 63         119 my $grammar = $asf->grammar();
1755 63         195 my $display_form = $grammar->g1_symbol_display_form($symbol_id);
1756 63         214 return [0, $glade_id, qq{Symbol $display_form: "$literal"}];
1757             } ## end sub dump_terminal
1758              
1759             sub Marpa::R3::ASF2::dump {
1760 13     13 0 85 my ($asf) = @_;
1761 13         37 my $peak = $asf->peak();
1762 13         26 local %GLADE_SEEN = (); ## no critic (Variables::ProhibitLocalVars)
1763 13         41 my $lines = $asf->dump_glade( $peak );
1764 13         22 my $next_sequenced_id = 1; # one-based
1765 13         29 my %sequenced_id = ();
1766 13   66     22 $sequenced_id{$_} //= $next_sequenced_id++ for grep { defined } map { $_->[1] } @{$lines};
  225         496  
  225         283  
  13         22  
1767 13         37 my $text = q{};
1768 13         34 for my $line ( @{$lines}[ 1 .. $#$lines ] ) {
  13         49  
1769 212         217 my ( $line_indent, $glade_id, $body ) = @{$line};
  212         273  
1770 212         232 $line_indent -= 2;
1771 212         268 $text .= q{ } x $line_indent;
1772 212 100       368 $text .= 'GL' . $sequenced_id{$glade_id} . q{ } if defined $glade_id;
1773 212         295 $text .= "$body\n";
1774             }
1775 13         122 return $text;
1776             } ## end sub show
1777              
1778             sub Marpa::R3::ASF2::show_nidsets {
1779 0     0 0 0 my ($asf) = @_;
1780 0         0 my $text = q{};
1781 0         0 my $nidsets = $asf->[Marpa::R3::Internal_ASF2::NIDSET_BY_ID];
1782 0         0 for my $nidset ( grep {defined} @{$nidsets} ) {
  0         0  
  0         0  
1783 0         0 $text .= $nidset->show() . "\n";
1784             }
1785 0         0 return $text;
1786             } ## end sub Marpa::R3::ASF2::show_nidsets
1787              
1788             sub Marpa::R3::ASF2::show_powersets {
1789 0     0 0 0 my ($asf) = @_;
1790 0         0 my $text = q{};
1791 0         0 my $powersets = $asf->[Marpa::R3::Internal_ASF2::POWERSET_BY_ID];
1792 0         0 for my $powerset ( grep {defined} @{$powersets} ) {
  0         0  
  0         0  
1793 0         0 $text .= $powerset->show() . "\n";
1794             }
1795 0         0 return $text;
1796             } ## end sub Marpa::R3::ASF2::show_powersets
1797              
1798             sub dump_nook {
1799 0     0   0 my ( $asf, $nook ) = @_;
1800 0         0 my $or_nodes = $asf->[Marpa::R3::Internal_ASF2::OR_NODES];
1801 0         0 my $or_node_id = $nook->[Marpa::R3::Internal::Nook::OR_NODE];
1802 0         0 my $and_node_count = scalar @{ $or_nodes->[$or_node_id] };
  0         0  
1803 0         0 my $text = 'Nook ';
1804 0         0 my @text = ();
1805 0 0       0 push @text, $nook->[Marpa::R3::Internal::Nook::IS_CAUSE] ? q{C} : q{-};
1806 0 0       0 push @text,
1807             $nook->[Marpa::R3::Internal::Nook::IS_PREDECESSOR] ? q{P} : q{-};
1808 0 0       0 push @text,
1809             $nook->[Marpa::R3::Internal::Nook::CAUSE_IS_EXPANDED] ? q{C+} : q{--};
1810 0 0       0 push @text,
1811             $nook->[Marpa::R3::Internal::Nook::PREDECESSOR_IS_EXPANDED]
1812             ? q{P+}
1813             : q{--};
1814 0         0 $text .= join q{ }, @text;
1815 0         0 $text
1816             .= ' @'
1817             . $nook->[Marpa::R3::Internal::Nook::FIRST_CHOICE] . q{-}
1818             . $nook->[Marpa::R3::Internal::Nook::LAST_CHOICE]
1819             . qq{ of $and_node_count: };
1820 0         0 $text .= $asf->verbose_or_node($or_node_id);
1821 0         0 return $text;
1822             } ## end sub dump_nook
1823              
1824             # For debugging
1825             sub dump_factoring_stack {
1826 0     0   0 my ( $asf, $stack ) = @_;
1827 0         0 my $text = q{};
1828 0         0 for ( my $stack_ix = 0; $stack_ix <= $#{$stack}; $stack_ix++ ) {
  0         0  
1829              
1830             # Nook already has newline at end
1831 0         0 $text .= "$stack_ix: " . dump_nook( $asf, $stack->[$stack_ix] );
1832             }
1833 0         0 return $text . "\n";
1834             } ## end sub dump_factoring_stack
1835              
1836             # not to be documented
1837             sub Marpa::R3::ASF2::call_by_tag {
1838 96829     96829 0 147667 my ( $asf, $tag, $codestr, $signature, @args ) = @_;
1839 96829         112838 my $lua = $asf->[Marpa::R3::Internal_ASF2::L];
1840 96829         98118 my $regix = $asf->[Marpa::R3::Internal_ASF2::REGIX];
1841              
1842 96829         151579 my @results;
1843             my $eval_error;
1844 96829         0 my $eval_ok;
1845             {
1846 96829         94090 local $@;
  96829         99725  
1847 96829         109242 $eval_ok = eval {
1848 96829         498609 @results =
1849             $lua->call_by_tag( $regix, $tag, $codestr, $signature, @args );
1850 96829         145598 return 1;
1851             };
1852 96829         137125 $eval_error = $@;
1853             }
1854 96829 50       140917 if ( not $eval_ok ) {
1855 0         0 Marpa::R3::exception($eval_error);
1856             }
1857 96829         156472 return @results;
1858             }
1859              
1860             # not to be documented
1861             sub Marpa::R3::ASF2::coro_by_tag {
1862 0     0 0   my ( $asf, $tag, $args, $codestr ) = @_;
1863 0           my $lua = $asf->[Marpa::R3::Internal_ASF2::L];
1864 0           my $regix = $asf->[Marpa::R3::Internal_ASF2::REGIX];
1865 0   0       my $handler = $args->{handlers} // {};
1866 0           my $resume_tag = $tag . '[R]';
1867 0   0       my $signature = $args->{signature} // '';
1868 0   0       my $p_args = $args->{args} // [];
1869              
1870 0           my @results;
1871             my $eval_error;
1872 0           my $eval_ok;
1873             {
1874 0           local $@;
  0            
1875 0           $eval_ok = eval {
1876 0           $lua->call_by_tag( $regix, $tag, $codestr, $signature, @{$p_args} );
  0            
1877 0           my @resume_args = ('');
1878 0           my $signature = 's';
1879 0           CORO_CALL: while (1) {
1880 0           my ( $cmd, $yield_data ) =
1881             $lua->call_by_tag( $regix, $resume_tag,
1882             'local asf, resume_arg = ...; return _M.resume(resume_arg)',
1883             $signature, @resume_args ) ;
1884 0 0         if (not $cmd) {
1885 0           @results = @{$yield_data};
  0            
1886 0           return 1;
1887             }
1888 0           my $handler = $handler->{$cmd};
1889 0 0         Marpa::R3::exception(qq{No coro handler for "$cmd"})
1890             if not $handler;
1891 0   0       $yield_data //= [];
1892 0           my ($handler_cmd, $new_resume_args) = $handler->(@{$yield_data});
  0            
1893 0 0         Marpa::R3::exception(qq{Undefined return command from handler for "$cmd"})
1894             if not defined $handler_cmd;
1895 0 0         if ($handler_cmd eq 'ok') {
1896 0           $signature = 's';
1897 0           @resume_args = ($new_resume_args);
1898 0 0         if (scalar @resume_args < 1) {
1899 0           @resume_args = ('');
1900             }
1901 0           next CORO_CALL;
1902             }
1903 0 0         if ($handler_cmd eq 'sig') {
1904 0           @resume_args = @{$new_resume_args};
  0            
1905 0           $signature = shift @resume_args;
1906 0           next CORO_CALL;
1907             }
1908 0           Marpa::R3::exception(qq{Bad return command ("$handler_cmd") from handler for "$cmd"})
1909             }
1910 0           return 1;
1911             };
1912 0           $eval_error = $@;
1913             }
1914 0 0         if ( not $eval_ok ) {
1915             # if it's an object, just die
1916 0 0         die $eval_error if ref $eval_error;
1917 0           Marpa::R3::exception($eval_error);
1918             }
1919 0           return @results;
1920             }
1921              
1922             # not to be documented
1923             sub Marpa::R3::ASF2::and_node_tag {
1924 0     0 0   my ( $asf, $and_node_id ) = @_;
1925              
1926 0           my ($tag) = $asf->call_by_tag( ( '@' . __FILE__ . ':' . __LINE__ ),
1927             << 'END_OF_LUA', 'i', $and_node_id );
1928             local asf, and_node_id=...
1929             return asf:and_node_tag(and_node_id)
1930             END_OF_LUA
1931              
1932 0           return $tag;
1933             }
1934              
1935             # not to be documented
1936             sub Marpa::R3::ASF2::verbose_or_node {
1937 0     0 0   my ( $asf, $or_node_id ) = @_;
1938 0           my $slr = $asf->[Marpa::R3::Internal_ASF2::SLR];
1939 0           my $slg = $slr->[Marpa::R3::Internal_R::SLG];
1940              
1941 0           my ($text, $nrl_id, $position)
1942             = $asf->call_by_tag(
1943             ('@' . __FILE__ . ':' . __LINE__),
1944             <<'END_OF_LUA', 'i', $or_node_id);
1945             local asf, or_node_id = ...
1946             local slr = asf.slr
1947             local bocage = asf.lmw_b
1948             local origin = bocage:_or_node_origin(or_node_id)
1949             if not origin then return end
1950             local set = bocage:_or_node_set(or_node_id)
1951             local position = bocage:_or_node_position(or_node_id)
1952             local g1r = slr.g1
1953             local origin_earleme = g1r:earleme(origin)
1954             local current_earleme = g1r:earleme(set)
1955             local text = string.format(
1956             'OR-node #%d: R%d:@%d-%d\n',
1957             or_node_id,
1958             position,
1959             origin_earleme,
1960             current_earleme,
1961             )
1962              
1963             END_OF_LUA
1964 0 0         return if not $text;
1965              
1966 0           $text .= ( q{ } x 4 )
1967             . $slg->dotted_nrl_show( $nrl_id, $position ) . "\n";
1968 0           return $text;
1969             }
1970              
1971             # not to be documented
1972             sub Marpa::R3::ASF2::bocage_show {
1973 0     0 0   my ($asf) = @_;
1974              
1975 0           my ($result) = $asf->call_by_tag(
1976             ('@' . __FILE__ . ':' . __LINE__),
1977             <<'END_OF_LUA', '');
1978             local asf = ...
1979             return asf:bocage_show()
1980             END_OF_LUA
1981              
1982 0           return $result;
1983             }
1984              
1985             # not to be documented
1986             sub Marpa::R3::ASF2::or_nodes_show {
1987 0     0 0   my ( $asf ) = @_;
1988              
1989 0           my ($result) = $asf->call_by_tag(
1990             ('@' . __FILE__ . ':' . __LINE__),
1991             <<'END_OF_LUA', '');
1992             local asf = ...
1993             return asf:or_nodes_show()
1994             END_OF_LUA
1995              
1996 0           return $result;
1997             }
1998              
1999             # not to be documented
2000             sub Marpa::R3::ASF2::and_nodes_show {
2001 0     0 0   my ( $asf ) = @_;
2002 0           my ($result) = $asf->call_by_tag(
2003             ('@' . __FILE__ . ':' . __LINE__),
2004             <<'END_OF_LUA', '');
2005             local asf = ...
2006             return asf:and_nodes_show()
2007             END_OF_LUA
2008              
2009 0           return $result;
2010             }
2011              
2012             sub Marpa::R3::ASF2::ambiguity_level {
2013 0     0 0   my ($asf) = @_;
2014              
2015 0           my ($metric) = $asf->call_by_tag(
2016             ('@' . __FILE__ . ':' . __LINE__),
2017             <<'END__OF_LUA', '>*' );
2018             local asf = ...
2019             return asf:ambiguity_level()
2020             END__OF_LUA
2021 0           return $metric;
2022             }
2023              
2024             sub Marpa::R3::ASF2::g1_pos {
2025 0     0 0   my ( $asf ) = @_;
2026 0           my ($g1_pos) = $asf->call_by_tag(
2027             ('@' . __FILE__ . ':' . __LINE__),
2028             <<'END__OF_LUA', '>*' );
2029             local asf = ...
2030             return asf:g1_pos()
2031             END__OF_LUA
2032 0           return $g1_pos;
2033             }
2034              
2035             # not to be documented
2036             sub Marpa::R3::ASF2::regix {
2037 0     0 0   my ( $asf ) = @_;
2038 0           my $regix = $asf->[Marpa::R3::Internal_ASF2::REGIX];
2039 0           return $regix;
2040             }
2041              
2042             1;
2043              
2044             # vim: expandtab shiftwidth=4: