1# Copyright 2012 Jeffrey Kegler
2# This file is part of Marpa::PP.  Marpa::PP is free software: you can
3# redistribute it and/or modify it under the terms of the GNU Lesser
4# General Public License as published by the Free Software Foundation,
5# either version 3 of the License, or (at your option) any later version.
6#
7# Marpa::PP is distributed in the hope that it will be useful,
8# but WITHOUT ANY WARRANTY; without even the implied warranty of
9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10# Lesser General Public License for more details.
11#
12# You should have received a copy of the GNU Lesser
13# General Public License along with Marpa::PP.  If not, see
14# http://www.gnu.org/licenses/.
15
16package Marpa::PP::Value;
17
18use 5.010;
19use warnings;
20use strict;
21use integer;
22
23use vars qw($VERSION $STRING_VERSION);
24$VERSION        = '0.014000';
25$STRING_VERSION = $VERSION;
26{
27## no critic (BuiltinFunctions::ProhibitStringyEval)
28## no critic (ValuesAndExpressions::RequireConstantVersion)
29    $VERSION = eval $VERSION;
30}
31
32package Marpa::PP::Internal::Value;
33
34use English qw( -no_match_vars );
35
36no warnings qw(qw);    ## no critic (TestingAndDebugging::ProhibitNoWarnings)
37
38BEGIN {
39    my $structure = <<'END_OF_STRUCTURE';
40
41    :package=Marpa::PP::Internal::Or_Node
42
43    ID
44    TAG
45    ITEM
46    RULE_ID
47    POSITION
48    AND_NODE_IDS
49
50    CYCLE { Can this Or node be part of a cycle? }
51
52    INITIAL_RANK_REF
53
54    =LAST_FIELD
55END_OF_STRUCTURE
56    Marpa::PP::offset($structure);
57} ## end BEGIN
58
59BEGIN {
60    my $structure = <<'END_OF_STRUCTURE';
61
62    :package=Marpa::PP::Internal::And_Node
63
64    ID
65    TAG
66    RULE_ID
67    TOKEN_NAME
68    VALUE_REF
69    VALUE_OPS
70
71    { Fields before this (except ID)
72    are used in evaluate() }
73
74    PREDECESSOR_ID
75    CAUSE_ID
76
77    CAUSE_EARLEME
78
79    INITIAL_RANK_REF
80    CONSTANT_RANK_REF
81    TOKEN_RANK_REF
82
83    { These earleme positions will be needed for the callbacks: }
84
85    START_EARLEME
86    END_EARLEME
87
88    POSITION { This is only used for diagnostics, but
89    diagnostics are important. }
90
91    =LAST_FIELD
92
93END_OF_STRUCTURE
94    Marpa::PP::offset($structure);
95} ## end BEGIN
96
97BEGIN {
98    my $structure = <<'END_OF_STRUCTURE';
99
100    :package=Marpa::PP::Internal::Iteration_Node
101
102    OR_NODE { The or-node }
103
104    CHOICES {
105    A list of remaining choices of and-node.
106    The current choice is first in the list.
107    }
108
109    PARENT { Offset of the parent in the iterations stack }
110
111    CAUSE_IX { Offset of the cause child, if any }
112    PREDECESSOR_IX { Offset of the predecessor child, if any }
113    { IX value is -1 if IX needs to be recalculated }
114
115    CHILD_TYPE { Cause or Predecessor }
116
117    RANK { Current rank }
118    CLEAN { Boolean -- true if rank does not need to
119    be recalculated }
120
121END_OF_STRUCTURE
122    Marpa::PP::offset($structure);
123} ## end BEGIN
124
125BEGIN {
126    my $structure = <<'END_OF_STRUCTURE';
127
128    :package=Marpa::PP::Internal::Task
129
130    INITIALIZE
131    POPULATE_OR_NODE
132    POPULATE_DEPTH
133
134    RANK_ALL
135
136    ITERATE
137    FIX_TREE
138    STACK_INODE
139
140END_OF_STRUCTURE
141    Marpa::PP::offset($structure);
142} ## end BEGIN
143
144BEGIN {
145    my $structure = <<'END_OF_STRUCTURE';
146
147    :package=Marpa::PP::Internal::Op
148
149    :{ These are the valuation-time ops }
150    ARGC
151    CALL
152    CONSTANT_RESULT
153    VIRTUAL_HEAD
154    VIRTUAL_KERNEL
155    VIRTUAL_TAIL
156
157END_OF_STRUCTURE
158    Marpa::PP::offset($structure);
159} ## end BEGIN
160
161BEGIN {
162    my $structure = <<'END_OF_STRUCTURE';
163
164    :package=Marpa::PP::Internal::Choice
165
166    { These are the valuation-time ops }
167
168    AND_NODE
169    RANK { *NOT* a rank ref }
170
171END_OF_STRUCTURE
172    Marpa::PP::offset($structure);
173} ## end BEGIN
174
175use constant SKIP => -1;
176
177use warnings;
178
179# The internal parameter is slightly misnamed -- between
180# calls it is the count of the *next* parse
181sub Marpa::PP::Recognizer::parse_count {
182    my ($recce) = @_;
183    return $recce->[Marpa::PP::Internal::Recognizer::PARSE_COUNT] - 1;
184}
185
186sub Marpa::PP::Recognizer::and_node_tag {
187    my ( $recce, $and_node ) = @_;
188    my $or_nodes    = $recce->[Marpa::PP::Internal::Recognizer::OR_NODES];
189    my $grammar     = $recce->[Marpa::PP::Internal::Recognizer::GRAMMAR];
190    my $symbol_hash = $grammar->[Marpa::PP::Internal::Grammar::SYMBOL_HASH];
191    my $recce_c     = $recce->[Marpa::PP::Internal::Recognizer::C];
192    my $origin_earleme =
193        $and_node->[Marpa::PP::Internal::And_Node::START_EARLEME];
194    my $current_earleme =
195        $and_node->[Marpa::PP::Internal::And_Node::END_EARLEME];
196    my $middle_earleme =
197        $and_node->[Marpa::PP::Internal::And_Node::CAUSE_EARLEME];
198    my $position = $and_node->[Marpa::PP::Internal::And_Node::POSITION] + 1;
199    my $rule     = $and_node->[Marpa::PP::Internal::And_Node::RULE_ID];
200
201    my $tag = 'R'    # perltidy, v20090616 adds trailing whitespace here
202        . $rule . q{:}
203        . $position . q{@}
204        . $origin_earleme . q{-}
205        . $current_earleme;
206    my $cause_id = $and_node->[Marpa::PP::Internal::And_Node::CAUSE_ID];
207
208    if ( defined $cause_id ) {
209        my $cause      = $or_nodes->[$cause_id];
210        my $cause_rule = $cause->[Marpa::PP::Internal::Or_Node::RULE_ID];
211        $tag .= 'C' . $cause_rule;
212    }
213    else {
214        my $token_name =
215            $and_node->[Marpa::PP::Internal::And_Node::TOKEN_NAME];
216        my $symbol = $symbol_hash->{$token_name};
217        $tag .= 'S' . $symbol;
218    } ## end else [ if ( defined $cause_id ) ]
219    $tag .= q{@} . $middle_earleme;
220    return $tag;
221} ## end sub Marpa::PP::Recognizer::and_node_tag
222
223sub Marpa::PP::Recognizer::or_node_tag {
224    my ( $recce, $or_node ) = @_;
225    die unless defined $or_node;
226    my $item     = $or_node->[Marpa::PP::Internal::Or_Node::ITEM];
227    my $set      = $item->[Marpa::PP::Internal::Earley_Item::SET];
228    my $origin   = $item->[Marpa::PP::Internal::Earley_Item::ORIGIN];
229    my $rule     = $or_node->[Marpa::PP::Internal::Or_Node::RULE_ID];
230    my $position = $or_node->[Marpa::PP::Internal::Or_Node::POSITION];
231    return 'R' . $rule . q{:} . $position . q{@} . $origin . q{-} . $set;
232} ## end sub Marpa::PP::Recognizer::or_node_tag
233
234sub Marpa::PP::Recognizer::show_and_nodes {
235    my ($recce)   = @_;
236    my $and_nodes = $recce->[Marpa::PP::Internal::Recognizer::AND_NODES];
237    my @data      = ();
238    for my $and_node ( @{$and_nodes} ) {
239        my $desc = $recce->and_node_tag($and_node);
240        my ( $rule, $position, $origin, $dot, $cause_type, $cause, $middle ) =
241            (
242            $desc =~ m{
243	     \A R (\d+) [:] (\d+)
244	     [@] (\d+) [-] (\d+) ([SC]) (\d+)
245	     [@] (\d+) \z
246	     }msx
247            );
248        push @data,
249            [
250            $origin, $dot, $rule, $position, $middle,
251            ( $cause_type eq 'C' ? $cause : -1 ),
252            ( $cause_type eq 'S' ? $cause : -1 ), $desc
253            ];
254    } ## end for my $and_node ( @{$and_nodes} )
255    my @tags = map { $_->[-1] } sort {
256               $a->[0] <=> $b->[0]
257            or $a->[1] <=> $b->[1]
258            or $a->[2] <=> $b->[2]
259            or $a->[3] <=> $b->[3]
260            or $a->[4] <=> $b->[4]
261            or $a->[5] <=> $b->[5]
262            or $a->[6] <=> $b->[6]
263    } @data;
264    my $result = ( join "\n", @tags ) . "\n";
265    return $result;
266} ## end sub Marpa::PP::Recognizer::show_and_nodes
267
268sub Marpa::PP::Recognizer::show_or_nodes {
269    my ($recce)  = @_;
270    my $or_nodes = $recce->[Marpa::PP::Internal::Recognizer::OR_NODES];
271    my @data     = ();
272    for my $or_node ( @{$or_nodes} ) {
273        my $desc = $recce->or_node_tag($or_node);
274        my @elements =
275            ( $desc =~ /\A R (\d+) [:] (\d+) [@] (\d+) [-] (\d+) \z/msx );
276        push @data, [ @elements, $desc ];
277    } ## end for my $or_node ( @{$or_nodes} )
278    my @tags = map { $_->[-1] } sort {
279               $a->[2] <=> $b->[2]
280            or $a->[3] <=> $b->[3]
281            or $a->[0] <=> $b->[0]
282            or $a->[1] <=> $b->[1]
283    } @data;
284    my $result = ( join "\n", @tags ) . "\n";
285    return $result;
286} ## end sub Marpa::PP::Recognizer::show_or_nodes
287
288sub Marpa::PP::brief_iteration_node {
289    my ($iteration_node) = @_;
290
291    my $or_node =
292        $iteration_node->[Marpa::PP::Internal::Iteration_Node::OR_NODE];
293    my $or_node_id   = $or_node->[Marpa::PP::Internal::Or_Node::ID];
294    my $and_node_ids = $or_node->[Marpa::PP::Internal::Or_Node::AND_NODE_IDS];
295    my $text         = "o$or_node_id";
296    DESCRIBE_CHOICES: {
297        if ( not defined $and_node_ids ) {
298            $text .= ' UNPOPULATED';
299            last DESCRIBE_CHOICES;
300        }
301        my $choices =
302            $iteration_node->[Marpa::PP::Internal::Iteration_Node::CHOICES];
303        if ( not defined $choices ) {
304            $text .= ' Choices not initialized';
305            last DESCRIBE_CHOICES;
306        }
307        my $choice = $choices->[0];
308        if ( defined $choice ) {
309            $text
310                .= " [$choice] == a"
311                . $choice->[Marpa::PP::Internal::Choice::AND_NODE]
312                ->[Marpa::PP::Internal::And_Node::ID];
313            last DESCRIBE_CHOICES;
314        } ## end if ( defined $choice )
315        $text .= "o$or_node_id has no choices left";
316    } ## end DESCRIBE_CHOICES:
317    my $parent_ix =
318        $iteration_node->[Marpa::PP::Internal::Iteration_Node::PARENT]
319        // q{-};
320    return "$text; p=$parent_ix";
321} ## end sub Marpa::PP::brief_iteration_node
322
323sub Marpa::PP::show_rank_ref {
324    my ($rank_ref) = @_;
325    return 'undef' if not defined $rank_ref;
326    return 'SKIP'  if $rank_ref == Marpa::PP::Internal::Value::SKIP;
327    return ${$rank_ref};
328} ## end sub Marpa::PP::show_rank_ref
329
330sub Marpa::PP::Recognizer::show_iteration_node {
331    my ( $recce, $iteration_node, $verbose ) = @_;
332
333    my $or_node =
334        $iteration_node->[Marpa::PP::Internal::Iteration_Node::OR_NODE];
335    my $or_node_id  = $or_node->[Marpa::PP::Internal::Or_Node::ID];
336    my $or_node_tag = $or_node->[Marpa::PP::Internal::Or_Node::TAG];
337    my $text        = "o$or_node_id $or_node_tag; ";
338    given (
339        $iteration_node->[Marpa::PP::Internal::Iteration_Node::CHILD_TYPE] )
340    {
341        when (Marpa::PP::Internal::And_Node::CAUSE_ID) {
342            $text .= 'cause '
343        }
344        when (Marpa::PP::Internal::And_Node::PREDECESSOR_ID) {
345            $text .= 'predecessor '
346        }
347        default {
348            $text .= '- '
349        }
350    } ## end given
351
352    $text
353        .= 'pr='
354        . (
355        $iteration_node->[Marpa::PP::Internal::Iteration_Node::PREDECESSOR_IX]
356            // q{-} )
357        . q{;c=}
358        . ( $iteration_node->[Marpa::PP::Internal::Iteration_Node::CAUSE_IX]
359            // q{-} )
360        . q{;p=}
361        . ( $iteration_node->[Marpa::PP::Internal::Iteration_Node::PARENT]
362            // q{-} )
363        . q{; rank=}
364        . ( $iteration_node->[Marpa::PP::Internal::Iteration_Node::RANK]
365            // 'undef' )
366        . (
367        $iteration_node->[Marpa::PP::Internal::Iteration_Node::CLEAN]
368        ? q{}
369        : ' (dirty)'
370        ) . "\n";
371
372    DESCRIBE_CHOICES: {
373        my $and_node_ids =
374            $or_node->[Marpa::PP::Internal::Or_Node::AND_NODE_IDS];
375        if ( not defined $and_node_ids ) {
376            $text .= " UNPOPULATED\n";
377            last DESCRIBE_CHOICES;
378        }
379        my $choices =
380            $iteration_node->[Marpa::PP::Internal::Iteration_Node::CHOICES];
381        if ( not defined $choices ) {
382            $text .= " Choices not initialized\n";
383            last DESCRIBE_CHOICES;
384        }
385        if ( not scalar @{$choices} ) {
386            $text .= " has no choices left\n";
387            last DESCRIBE_CHOICES;
388        }
389        for my $choice_ix ( 0 .. $#{$choices} ) {
390            my $choice = $choices->[$choice_ix];
391            $text .= " o$or_node_id" . '[' . $choice_ix . '] ';
392            my $and_node = $choice->[Marpa::PP::Internal::Choice::AND_NODE];
393            my $and_node_tag =
394                $and_node->[Marpa::PP::Internal::And_Node::TAG];
395            my $and_node_id = $and_node->[Marpa::PP::Internal::And_Node::ID];
396            $text .= " ::= a$and_node_id $and_node_tag";
397            no integer;
398            if ($verbose) {
399                $text .= q{; rank=}
400                    . $choice->[Marpa::PP::Internal::Choice::RANK];
401            }
402            $text .= "\n";
403            last CHOICE if not $verbose;
404        } ## end for my $choice_ix ( 0 .. $#{$choices} )
405    } ## end DESCRIBE_CHOICES:
406    return $text;
407} ## end sub Marpa::PP::Recognizer::show_iteration_node
408
409sub Marpa::PP::Recognizer::show_iteration_stack {
410    my ( $recce, $verbose ) = @_;
411    my $iteration_stack =
412        $recce->[Marpa::PP::Internal::Recognizer::ITERATION_STACK];
413    my $text = q{};
414    for my $ix ( 0 .. $#{$iteration_stack} ) {
415        my $iteration_node = $iteration_stack->[$ix];
416        $text .= "$ix: "
417            . $recce->show_iteration_node( $iteration_node, $verbose );
418    }
419    return $text;
420} ## end sub Marpa::PP::Recognizer::show_iteration_stack
421
422package Marpa::PP::Internal::Recognizer;
423our $DEFAULT_ACTION_VALUE = \undef;
424
425package Marpa::PP::Internal::Value;
426
427sub Marpa::PP::Internal::Recognizer::set_null_values {
428    my ($recce) = @_;
429    my $grammar = $recce->[Marpa::PP::Internal::Recognizer::GRAMMAR];
430    my $trace_values =
431        $recce->[Marpa::PP::Internal::Recognizer::TRACE_VALUES];
432
433    my $rules   = $grammar->[Marpa::PP::Internal::Grammar::RULES];
434    my $symbols = $grammar->[Marpa::PP::Internal::Grammar::SYMBOLS];
435    my $default_null_value =
436        $grammar->[Marpa::PP::Internal::Grammar::DEFAULT_NULL_VALUE];
437
438    my $null_values;
439    $#{$null_values} = $#{$symbols};
440
441    SYMBOL: for my $symbol ( @{$symbols} ) {
442        next SYMBOL if not $symbol->[Marpa::PP::Internal::Symbol::NULLING];
443
444        my $null_value = undef;
445        if ( $symbol->[Marpa::PP::Internal::Symbol::NULL_VALUE] ) {
446            $null_value =
447                ${ $symbol->[Marpa::PP::Internal::Symbol::NULL_VALUE] };
448        }
449        else {
450            $null_value = $default_null_value;
451        }
452        next SYMBOL if not defined $null_value;
453
454        my $symbol_id = $symbol->[Marpa::PP::Internal::Symbol::ID];
455        $null_values->[$symbol_id] = $null_value;
456
457        if ($trace_values) {
458            print {$Marpa::PP::Internal::TRACE_FH}
459                'Setting null value for symbol ',
460                $symbol->[Marpa::PP::Internal::Symbol::NAME],
461                ' to ', Data::Dumper->new( [ \$null_value ] )->Terse(1)->Dump
462                or Marpa::PP::exception('Could not print to trace file');
463        } ## end if ($trace_values)
464
465    } ## end for my $symbol ( @{$symbols} )
466
467    return $null_values;
468
469}    # set_null_values
470
471# Given the grammar and an action name, resolve it to a closure,
472# or return undef
473sub Marpa::PP::Internal::Recognizer::resolve_semantics {
474    my ( $recce, $closure_name ) = @_;
475    my $grammar  = $recce->[Marpa::PP::Internal::Recognizer::GRAMMAR];
476    my $closures = $recce->[Marpa::PP::Internal::Recognizer::CLOSURES];
477    my $trace_actions =
478        $recce->[Marpa::PP::Internal::Recognizer::TRACE_ACTIONS];
479
480    Marpa::PP::exception(q{Trying to resolve 'undef' as closure name})
481        if not defined $closure_name;
482
483    if ( my $closure = $closures->{$closure_name} ) {
484        if ($trace_actions) {
485            print {$Marpa::PP::Internal::TRACE_FH}
486                qq{Resolved "$closure_name" to explicit closure\n}
487                or Marpa::PP::exception('Could not print to trace file');
488        }
489
490        return $closure;
491    } ## end if ( my $closure = $closures->{$closure_name} )
492
493    my $fully_qualified_name;
494    DETERMINE_FULLY_QUALIFIED_NAME: {
495        if ( $closure_name =~ /([:][:])|[']/xms ) {
496            $fully_qualified_name = $closure_name;
497            last DETERMINE_FULLY_QUALIFIED_NAME;
498        }
499        if (defined(
500                my $actions_package =
501                    $grammar->[Marpa::PP::Internal::Grammar::ACTIONS]
502            )
503            )
504        {
505            $fully_qualified_name = $actions_package . q{::} . $closure_name;
506            last DETERMINE_FULLY_QUALIFIED_NAME;
507        } ## end if ( defined( my $actions_package = $grammar->[...]))
508
509        if (defined(
510                my $action_object_class =
511                    $grammar->[Marpa::PP::Internal::Grammar::ACTION_OBJECT]
512            )
513            )
514        {
515            $fully_qualified_name =
516                $action_object_class . q{::} . $closure_name;
517        } ## end if ( defined( my $action_object_class = $grammar->[...]))
518    } ## end DETERMINE_FULLY_QUALIFIED_NAME:
519
520    return if not defined $fully_qualified_name;
521
522    no strict 'refs';
523    my $closure = *{$fully_qualified_name}{'CODE'};
524    use strict 'refs';
525
526    if ($trace_actions) {
527        print {$Marpa::PP::Internal::TRACE_FH}
528            ( $closure ? 'Successful' : 'Failed' )
529            . qq{ resolution of "$closure_name" },
530            'to ', $fully_qualified_name, "\n"
531            or Marpa::PP::exception('Could not print to trace file');
532    } ## end if ($trace_actions)
533
534    return $closure;
535
536} ## end sub Marpa::PP::Internal::Recognizer::resolve_semantics
537
538sub Marpa::PP::Internal::Recognizer::set_actions {
539    my ($recce) = @_;
540    my $grammar = $recce->[Marpa::PP::Internal::Recognizer::GRAMMAR];
541
542    my ( $rules, $default_action, ) = @{$grammar}[
543        Marpa::PP::Internal::Grammar::RULES,
544        Marpa::PP::Internal::Grammar::DEFAULT_ACTION,
545    ];
546
547    my $evaluator_rules = [];
548
549    my $default_action_closure;
550    if ( defined $default_action ) {
551        $default_action_closure =
552            Marpa::PP::Internal::Recognizer::resolve_semantics( $recce,
553            $default_action );
554        Marpa::PP::exception(
555            "Could not resolve default action named '$default_action'")
556            if not $default_action_closure;
557    } ## end if ( defined $default_action )
558
559    RULE: for my $rule ( @{$rules} ) {
560
561        next RULE if not $rule->[Marpa::PP::Internal::Rule::USED];
562
563        my $rule_id = $rule->[Marpa::PP::Internal::Rule::ID];
564        my $ops = $evaluator_rules->[$rule_id] = [];
565
566        my $virtual_rhs = $rule->[Marpa::PP::Internal::Rule::VIRTUAL_RHS];
567        my $virtual_lhs = $rule->[Marpa::PP::Internal::Rule::VIRTUAL_LHS];
568
569        if ($virtual_lhs) {
570            push @{$ops},
571                (
572                $virtual_rhs
573                ? Marpa::PP::Internal::Op::VIRTUAL_KERNEL
574                : Marpa::PP::Internal::Op::VIRTUAL_TAIL
575                ),
576                $rule->[Marpa::PP::Internal::Rule::REAL_SYMBOL_COUNT];
577            next RULE;
578        } ## end if ($virtual_lhs)
579
580        # If we are here the LHS is real, not virtual
581
582        if ($virtual_rhs) {
583            push @{$ops},
584                Marpa::PP::Internal::Op::VIRTUAL_HEAD,
585                $rule->[Marpa::PP::Internal::Rule::REAL_SYMBOL_COUNT];
586        }
587
588        # assignment instead of comparison is deliberate
589        elsif ( my $argc =
590            scalar @{ $rule->[Marpa::PP::Internal::Rule::RHS] } )
591        {
592            push @{$ops}, Marpa::PP::Internal::Op::ARGC, $argc;
593        }
594
595        if ( my $action = $rule->[Marpa::PP::Internal::Rule::ACTION] ) {
596            my $closure =
597                Marpa::PP::Internal::Recognizer::resolve_semantics( $recce,
598                $action );
599
600            Marpa::PP::exception(qq{Could not resolve action name: "$action"})
601                if not defined $closure;
602            push @{$ops}, Marpa::PP::Internal::Op::CALL, $closure;
603            next RULE;
604        } ## end if ( my $action = $rule->[Marpa::PP::Internal::Rule::ACTION...])
605
606        # Try to resolve the LHS as a closure name,
607        # if it is not internal.
608        # If we can't resolve
609        # the LHS as a closure name, it's not
610        # a fatal error.
611        if ( my $action =
612            $rule->[Marpa::PP::Internal::Rule::LHS]
613            ->[Marpa::PP::Internal::Symbol::NAME] )
614        {
615            if ($action !~ /[\]] \z/xms
616                and defined(
617                    my $closure =
618                        Marpa::PP::Internal::Recognizer::resolve_semantics(
619                        $recce, $action
620                        )
621                )
622                )
623            {
624                push @{$ops}, Marpa::PP::Internal::Op::CALL, $closure;
625                next RULE;
626            } ## end if ( $action !~ /[\]] \z/xms and defined( my $closure...)[)
627        } ## end if ( my $action = $rule->[Marpa::PP::Internal::Rule::LHS...])
628
629        if ( defined $default_action_closure ) {
630            push @{$ops}, Marpa::PP::Internal::Op::CALL,
631                $default_action_closure;
632            next RULE;
633        }
634
635        # If there is no default action specified, the fallback
636        # is to return an undef
637        push @{$ops}, Marpa::PP::Internal::Op::CONSTANT_RESULT,
638            $Marpa::PP::Internal::Recognizer::DEFAULT_ACTION_VALUE;
639
640    } ## end for my $rule ( @{$rules} )
641
642    return $evaluator_rules;
643
644}    # set_actions
645
646# Returns false if no parse
647sub do_rank_all {
648    my ( $recce, $depth_by_id ) = @_;
649    my $grammar = $recce->[Marpa::PP::Internal::Recognizer::GRAMMAR];
650    my $symbols = $grammar->[Marpa::PP::Internal::Grammar::SYMBOLS];
651    my $rules   = $grammar->[Marpa::PP::Internal::Grammar::RULES];
652
653    my $cycle_ranking_action =
654        $grammar->[Marpa::PP::Internal::Grammar::CYCLE_RANKING_ACTION];
655    my $cycle_closure;
656    if ( defined $cycle_ranking_action ) {
657        $cycle_closure =
658            Marpa::PP::Internal::Recognizer::resolve_semantics( $recce,
659            $cycle_ranking_action );
660        Marpa::PP::exception(
661            "Could not resolve cycle ranking action named '$cycle_ranking_action'"
662        ) if not $cycle_closure;
663    } ## end if ( defined $cycle_ranking_action )
664
665    # Set up rank closures by symbol
666    my %ranking_closures_by_symbol = ();
667    SYMBOL: for my $symbol ( @{$symbols} ) {
668        my $ranking_action =
669            $symbol->[Marpa::PP::Internal::Symbol::RANKING_ACTION];
670        next SYMBOL if not defined $ranking_action;
671        my $ranking_closure =
672            Marpa::PP::Internal::Recognizer::resolve_semantics( $recce,
673            $ranking_action );
674        my $symbol_name = $symbol->[Marpa::PP::Internal::Symbol::NAME];
675        Marpa::PP::exception(
676            "Could not resolve ranking action for symbol.\n",
677            qq{    Symbol was "$symbol_name".},
678            qq{    Ranking action was "$ranking_action".}
679        ) if not defined $ranking_closure;
680        $ranking_closures_by_symbol{$symbol_name} = $ranking_closure;
681    }    # end for my $symbol ( @{$symbols} )
682
683    # Get closure used in ranking, by rule
684    my @ranking_closures_by_rule = ();
685    RULE: for my $rule ( @{$rules} ) {
686
687        my $ranking_action =
688            $rule->[Marpa::PP::Internal::Rule::RANKING_ACTION];
689        my $ranking_closure;
690        my $cycle_rule = $rule->[Marpa::PP::Internal::Rule::CYCLE];
691
692        Marpa::PP::exception(
693            "Rule which cycles has an explicit ranking action\n",
694            qq{   The ranking action is "$ranking_action"\n},
695            qq{   To solve this problem,\n},
696            qq{   Rewrite the grammar so that this rule does not cycle\n},
697            qq{   Or eliminate its ranking action.\n}
698        ) if $ranking_action and $cycle_rule;
699
700        if ($ranking_action) {
701            $ranking_closure =
702                Marpa::PP::Internal::Recognizer::resolve_semantics( $recce,
703                $ranking_action );
704            Marpa::PP::exception(
705                "Ranking closure '$ranking_action' not found")
706                if not defined $ranking_closure;
707        } ## end if ($ranking_action)
708
709        if ($cycle_rule) {
710            $ranking_closure = $cycle_closure;
711        }
712
713        next RULE if not $ranking_closure;
714
715        # If the RHS is empty ...
716        # Empty rules are never in cycles -- they are either
717        # unused (because of the CHAF rewrite) or the special
718        # null start rule.
719        if ( not scalar @{ $rule->[Marpa::PP::Internal::Rule::RHS] } ) {
720            Marpa::PP::exception(
721                "Ranking closure '$ranking_action' not found")
722                if not defined $ranking_closure;
723
724            $ranking_closures_by_symbol{ $rule
725                    ->[Marpa::PP::Internal::Rule::LHS]
726                    ->[Marpa::PP::Internal::Symbol::NULL_ALIAS]
727                    ->[Marpa::PP::Internal::Symbol::NAME] } =
728                $ranking_closure;
729        } ## end if ( not scalar @{ $rule->[Marpa::PP::Internal::Rule::RHS...]})
730
731        next RULE if not $rule->[Marpa::PP::Internal::Rule::USED];
732
733        $ranking_closures_by_rule[ $rule->[Marpa::PP::Internal::Rule::ID] ] =
734            $ranking_closure;
735
736    } ## end for my $rule ( @{$rules} )
737
738    my $and_nodes = $recce->[Marpa::PP::Internal::Recognizer::AND_NODES];
739    my $or_nodes  = $recce->[Marpa::PP::Internal::Recognizer::OR_NODES];
740
741    my @and_node_worklist = ();
742    AND_NODE: for my $and_node_id ( 0 .. $#{$and_nodes} ) {
743
744        my $and_node = $and_nodes->[$and_node_id];
745        my $rule_id  = $and_node->[Marpa::PP::Internal::And_Node::RULE_ID];
746        my $rule_closure = $ranking_closures_by_rule[$rule_id];
747        my $token_name =
748            $and_node->[Marpa::PP::Internal::And_Node::TOKEN_NAME];
749        my $token_closure;
750        if ($token_name) {
751            $token_closure = $ranking_closures_by_symbol{$token_name};
752        }
753
754        my $token_rank_ref;
755        my $rule_rank_ref;
756
757        # It is a feature of the ranking closures that they are always
758        # called once per instance, even if the result is never used.
759        # This sometimes makes for unnecessary calls,
760        # but it makes these closures predictable enough
761        # to allow their use for side effects.
762        EVALUATION:
763        for my $evaluation_data (
764            [ \$token_rank_ref, $token_closure ],
765            [ \$rule_rank_ref,  $rule_closure ]
766            )
767        {
768            my ( $rank_ref_ref, $closure ) = @{$evaluation_data};
769            next EVALUATION if not defined $closure;
770
771            my @warnings;
772            my $eval_ok;
773            my $rank_ref;
774            DO_EVAL: {
775                local $Marpa::PP::Internal::CONTEXT =
776                    [ 'and-node', $and_node, $recce ];
777                local $SIG{__WARN__} =
778                    sub { push @warnings, [ $_[0], ( caller 0 ) ]; };
779                $eval_ok = eval { $rank_ref = $closure->(); 1; };
780            } ## end DO_EVAL:
781
782            my $fatal_error;
783            CHECK_FOR_ERROR: {
784                if ( not $eval_ok or scalar @warnings ) {
785                    $fatal_error = $EVAL_ERROR // 'Fatal Error';
786                    last CHECK_FOR_ERROR;
787                }
788                if ( defined $rank_ref and not ref $rank_ref ) {
789                    $fatal_error =
790                        "Invalid return value from ranking closure: $rank_ref";
791                }
792            } ## end CHECK_FOR_ERROR:
793
794            if ( defined $fatal_error ) {
795
796                Marpa::PP::Internal::code_problems(
797                    {   fatal_error => $fatal_error,
798                        grammar     => $grammar,
799                        eval_ok     => $eval_ok,
800                        warnings    => \@warnings,
801                        where       => 'ranking and-node '
802                            . $and_node->[Marpa::PP::Internal::And_Node::TAG],
803                    }
804                );
805            } ## end if ( defined $fatal_error )
806
807            ${$rank_ref_ref} = $rank_ref // Marpa::PP::Internal::Value::SKIP;
808
809        } ## end for my $evaluation_data ( [ \$token_rank_ref, $token_closure...])
810
811        # Set the token rank if there is a token.
812        # It is zero if there is no token, or
813        # if there is one with no closure.
814        # Note: token can never cause a cycle, but they
815        # can cause an and-node to be skipped.
816        if ($token_name) {
817            $and_node->[Marpa::PP::Internal::And_Node::TOKEN_RANK_REF] =
818                $token_rank_ref // \0;
819        }
820
821        # See if we can set the rank for this node to a constant.
822        my $constant_rank_ref;
823        SET_CONSTANT_RANK: {
824
825            if ( defined $token_rank_ref && !ref $token_rank_ref ) {
826                $constant_rank_ref = Marpa::PP::Internal::Value::SKIP;
827                last SET_CONSTANT_RANK;
828            }
829
830            # If we have ranking closure for this rule, the rank
831            # is constant:
832            # 0 for a non-final node,
833            # the result of the closure for a final one
834            if ( defined $rule_rank_ref ) {
835                $constant_rank_ref =
836                      $and_node->[Marpa::PP::Internal::And_Node::VALUE_OPS]
837                    ? $rule_rank_ref
838                    : \0;
839                last SET_CONSTANT_RANK;
840            } ## end if ( defined $rule_rank_ref )
841
842            # It there is a token and no predecessor, the rank
843            # of this rule is a constant:
844            # 0 is there was not token symbol closure
845            # the result of that closure if there was one
846            if ( $token_name
847                and not defined
848                $and_node->[Marpa::PP::Internal::And_Node::PREDECESSOR_ID] )
849            {
850                $constant_rank_ref = $token_rank_ref // \0;
851            } ## end if ( $token_name and not defined $and_node->[...])
852
853        } ## end SET_CONSTANT_RANK:
854
855        if ( defined $constant_rank_ref ) {
856            $and_node->[Marpa::PP::Internal::And_Node::INITIAL_RANK_REF] =
857                $and_node->[Marpa::PP::Internal::And_Node::CONSTANT_RANK_REF]
858                = $constant_rank_ref;
859
860            next AND_NODE;
861        } ## end if ( defined $constant_rank_ref )
862
863        # If we are here there is (so far) no constant rank
864        # so we stack this and-node for depth-sensitive evaluation
865        push @and_node_worklist, $and_node_id;
866
867    } ## end for my $and_node_id ( 0 .. $#{$and_nodes} )
868
869    # Now go through the and-nodes that require context to be ranked
870    # This loop assumes that all cycles has been taken care of
871    # with constant ranks
872    AND_NODE: while ( defined( my $and_node_id = pop @and_node_worklist ) ) {
873
874        no integer;
875
876        my $and_node = $and_nodes->[$and_node_id];
877
878        # Go to next if we have already ranked this and-node
879        next AND_NODE
880            if defined
881                $and_node->[Marpa::PP::Internal::And_Node::INITIAL_RANK_REF];
882
883        # The rank calculated so far from the
884        # children
885        my $calculated_rank = 0;
886
887        my $is_cycle = 0;
888        my $is_skip  = 0;
889        OR_NODE:
890        for my $field (
891            Marpa::PP::Internal::And_Node::PREDECESSOR_ID,
892            Marpa::PP::Internal::And_Node::CAUSE_ID,
893            )
894        {
895            my $or_node_id = $and_node->[$field];
896            next OR_NODE if not defined $or_node_id;
897
898            my $or_node = $or_nodes->[$or_node_id];
899            if (defined(
900                    my $or_node_initial_rank_ref =
901                        $or_node
902                        ->[Marpa::PP::Internal::Or_Node::INITIAL_RANK_REF]
903                )
904                )
905            {
906                if ( ref $or_node_initial_rank_ref ) {
907                    $calculated_rank += ${$or_node_initial_rank_ref};
908                    next OR_NODE;
909                }
910
911                # At this point only possible value is skip
912                $and_node->[Marpa::PP::Internal::And_Node::INITIAL_RANK_REF] =
913                    $and_node
914                    ->[Marpa::PP::Internal::And_Node::CONSTANT_RANK_REF] =
915                    Marpa::PP::Internal::Value::SKIP;
916
917                next AND_NODE;
918            } ## end if ( defined( my $or_node_initial_rank_ref = $or_node...))
919            my @ranks              = ();
920            my @unranked_and_nodes = ();
921            CHILD_AND_NODE:
922            for my $child_and_node_id (
923                @{ $or_node->[Marpa::PP::Internal::Or_Node::AND_NODE_IDS] } )
924            {
925                my $rank_ref =
926                    $and_nodes->[$child_and_node_id]
927                    ->[Marpa::PP::Internal::And_Node::INITIAL_RANK_REF];
928                if ( not defined $rank_ref ) {
929                    push @unranked_and_nodes, $child_and_node_id;
930
931                    next CHILD_AND_NODE;
932                }
933
934                # Right now the only defined scalar value for a rank is
935                # Marpa::PP::Internal::Value::SKIP
936                next CHILD_AND_NODE if not ref $rank_ref;
937
938                push @ranks, ${$rank_ref};
939
940            } ## end for my $child_and_node_id ( @{ $or_node->[...]})
941
942            # If we have unranked child and nodes, those have to be
943            # ranked first.  Schedule the work and move on.
944            if ( scalar @unranked_and_nodes ) {
945
946                push @and_node_worklist, $and_node_id, @unranked_and_nodes;
947                next AND_NODE;
948            }
949
950            # If there were no non-skipped and-nodes, the
951            # parent and-node must also be skipped
952            if ( not scalar @ranks ) {
953                $or_node->[Marpa::PP::Internal::Or_Node::INITIAL_RANK_REF] =
954                    $and_node
955                    ->[Marpa::PP::Internal::And_Node::INITIAL_RANK_REF] =
956                    $and_node
957                    ->[Marpa::PP::Internal::And_Node::CONSTANT_RANK_REF] =
958                    Marpa::PP::Internal::Value::SKIP;
959
960                next AND_NODE;
961            } ## end if ( not scalar @ranks )
962
963            my $or_calculated_rank = List::Util::max @ranks;
964            $or_node->[Marpa::PP::Internal::Or_Node::INITIAL_RANK_REF] =
965                \$or_calculated_rank;
966            $calculated_rank += $or_calculated_rank;
967
968        } ## end for my $field ( ...)
969
970        my $token_rank_ref =
971            $and_node->[Marpa::PP::Internal::And_Node::TOKEN_RANK_REF];
972        $calculated_rank += defined $token_rank_ref ? ${$token_rank_ref} : 0;
973        $and_node->[Marpa::PP::Internal::And_Node::INITIAL_RANK_REF] =
974            \$calculated_rank;
975
976    } ## end while ( defined( my $and_node_id = pop @and_node_worklist...))
977
978    return;
979
980} ## end sub do_rank_all
981
982# Does not modify stack
983sub Marpa::PP::Internal::Recognizer::evaluate {
984    my ( $recce, $stack ) = @_;
985    my $grammar      = $recce->[Marpa::PP::Internal::Recognizer::GRAMMAR];
986    my $trace_values = $recce->[Marpa::PP::Internal::Recognizer::TRACE_VALUES]
987        // 0;
988
989    my $rules = $grammar->[Marpa::PP::Internal::Grammar::RULES];
990    my $action_object_class =
991        $grammar->[Marpa::PP::Internal::Grammar::ACTION_OBJECT];
992
993    my $action_object_constructor;
994    if ( defined $action_object_class ) {
995        my $constructor_name = $action_object_class . q{::new};
996        my $closure =
997            Marpa::PP::Internal::Recognizer::resolve_semantics( $recce,
998            $constructor_name );
999        Marpa::PP::exception(
1000            qq{Could not find constructor "$constructor_name"})
1001            if not defined $closure;
1002        $action_object_constructor = $closure;
1003    } ## end if ( defined $action_object_class )
1004
1005    my $action_object;
1006    if ($action_object_constructor) {
1007        my @warnings;
1008        my $eval_ok;
1009        my $fatal_error;
1010        DO_EVAL: {
1011            local $EVAL_ERROR = undef;
1012            local $SIG{__WARN__} = sub {
1013                push @warnings, [ $_[0], ( caller 0 ) ];
1014            };
1015
1016            $eval_ok = eval {
1017                $action_object =
1018                    $action_object_constructor->($action_object_class);
1019                1;
1020            };
1021            $fatal_error = $EVAL_ERROR;
1022        } ## end DO_EVAL:
1023
1024        if ( not $eval_ok or @warnings ) {
1025            Marpa::PP::Internal::code_problems(
1026                {   fatal_error => $fatal_error,
1027                    grammar     => $grammar,
1028                    eval_ok     => $eval_ok,
1029                    warnings    => \@warnings,
1030                    where       => 'constructing action object',
1031                }
1032            );
1033        } ## end if ( not $eval_ok or @warnings )
1034    } ## end if ($action_object_constructor)
1035
1036    $action_object //= {};
1037
1038    my @evaluation_stack   = ();
1039    my @virtual_rule_stack = ();
1040    TREE_NODE: for my $and_node ( reverse @{$stack} ) {
1041
1042        if ( $trace_values >= 3 ) {
1043            for my $i ( reverse 0 .. $#evaluation_stack ) {
1044                printf {$Marpa::PP::Internal::TRACE_FH} 'Stack position %3d:',
1045                    $i
1046                    or Marpa::PP::exception('print to trace handle failed');
1047                print {$Marpa::PP::Internal::TRACE_FH} q{ },
1048                    Data::Dumper->new( [ $evaluation_stack[$i] ] )->Terse(1)
1049                    ->Dump
1050                    or Marpa::PP::exception('print to trace handle failed');
1051            } ## end for my $i ( reverse 0 .. $#evaluation_stack )
1052        } ## end if ( $trace_values >= 3 )
1053
1054        my $value_ref = $and_node->[Marpa::PP::Internal::And_Node::VALUE_REF];
1055
1056        if ( defined $value_ref ) {
1057
1058            push @evaluation_stack, $value_ref;
1059
1060            if ($trace_values) {
1061                my $token_name =
1062                    $and_node->[Marpa::PP::Internal::And_Node::TOKEN_NAME];
1063
1064                print {$Marpa::PP::Internal::TRACE_FH}
1065                    'Pushed value from ',
1066                    $and_node->[Marpa::PP::Internal::And_Node::TAG], ': ',
1067                    ( $token_name ? qq{$token_name = } : q{} ),
1068                    Data::Dumper->new( [$value_ref] )->Terse(1)->Dump
1069                    or Marpa::PP::exception('print to trace handle failed');
1070            } ## end if ($trace_values)
1071
1072        }    # defined $value_ref
1073
1074        my $ops = $and_node->[Marpa::PP::Internal::And_Node::VALUE_OPS];
1075
1076        next TREE_NODE if not defined $ops;
1077
1078        my $current_data = [];
1079        my $op_ix        = 0;
1080        while ( $op_ix < scalar @{$ops} ) {
1081            given ( $ops->[ $op_ix++ ] ) {
1082
1083                when (Marpa::PP::Internal::Op::ARGC) {
1084
1085                    my $argc = $ops->[ $op_ix++ ];
1086
1087                    if ($trace_values) {
1088                        my $rule_id = $and_node
1089                            ->[Marpa::PP::Internal::And_Node::RULE_ID];
1090                        my $rule = $rules->[$rule_id];
1091                        say {$Marpa::PP::Internal::TRACE_FH}
1092                            'Popping ',
1093                            $argc,
1094                            ' values to evaluate ',
1095                            $and_node->[Marpa::PP::Internal::And_Node::TAG],
1096                            ', rule: ', Marpa::PP::brief_rule($rule)
1097                            or Marpa::PP::exception(
1098                            'Could not print to trace file');
1099                    } ## end if ($trace_values)
1100
1101                    $current_data =
1102                        [ map { ${$_} }
1103                            ( splice @evaluation_stack, -$argc ) ];
1104
1105                } ## end when (Marpa::PP::Internal::Op::ARGC)
1106
1107                when (Marpa::PP::Internal::Op::VIRTUAL_HEAD) {
1108                    my $real_symbol_count = $ops->[ $op_ix++ ];
1109
1110                    if ($trace_values) {
1111                        my $rule_id = $and_node
1112                            ->[Marpa::PP::Internal::And_Node::RULE_ID];
1113                        my $rule = $rules->[$rule_id];
1114                        say {$Marpa::PP::Internal::TRACE_FH}
1115                            'Head of Virtual Rule: ',
1116                            $and_node->[Marpa::PP::Internal::And_Node::TAG],
1117                            ', rule: ', Marpa::PP::brief_rule($rule),
1118                            "\n",
1119                            "Incrementing virtual rule by $real_symbol_count symbols\n",
1120                            'Currently ',
1121                            ( scalar @virtual_rule_stack ),
1122                            ' rules; ', $virtual_rule_stack[-1], ' symbols;',
1123                            or Marpa::PP::exception(
1124                            'Could not print to trace file');
1125                    } ## end if ($trace_values)
1126
1127                    $real_symbol_count += pop @virtual_rule_stack;
1128                    $current_data =
1129                        [ map { ${$_} }
1130                            ( splice @evaluation_stack, -$real_symbol_count )
1131                        ];
1132
1133                } ## end when (Marpa::PP::Internal::Op::VIRTUAL_HEAD)
1134
1135                when (Marpa::PP::Internal::Op::VIRTUAL_KERNEL) {
1136                    my $real_symbol_count = $ops->[ $op_ix++ ];
1137                    $virtual_rule_stack[-1] += $real_symbol_count;
1138
1139                    if ($trace_values) {
1140                        my $rule_id = $and_node
1141                            ->[Marpa::PP::Internal::And_Node::RULE_ID];
1142                        my $rule = $rules->[$rule_id];
1143                        say {$Marpa::PP::Internal::TRACE_FH}
1144                            'Virtual Rule: ',
1145                            $and_node->[Marpa::PP::Internal::And_Node::TAG],
1146                            ', rule: ', Marpa::PP::brief_rule($rule),
1147                            "\nAdding $real_symbol_count",
1148                            or Marpa::PP::exception(
1149                            'Could not print to trace file');
1150                    } ## end if ($trace_values)
1151
1152                } ## end when (Marpa::PP::Internal::Op::VIRTUAL_KERNEL)
1153
1154                when (Marpa::PP::Internal::Op::VIRTUAL_TAIL) {
1155                    my $real_symbol_count = $ops->[ $op_ix++ ];
1156
1157                    if ($trace_values) {
1158                        my $rule_id = $and_node
1159                            ->[Marpa::PP::Internal::And_Node::RULE_ID];
1160                        my $rule = $rules->[$rule_id];
1161                        say {$Marpa::PP::Internal::TRACE_FH}
1162                            'New Virtual Rule: ',
1163                            $and_node->[Marpa::PP::Internal::And_Node::TAG],
1164                            ', rule: ', Marpa::PP::brief_rule($rule),
1165                            "\nReal symbol count is $real_symbol_count",
1166                            or Marpa::PP::exception(
1167                            'Could not print to trace file');
1168                    } ## end if ($trace_values)
1169
1170                    push @virtual_rule_stack, $real_symbol_count;
1171
1172                } ## end when (Marpa::PP::Internal::Op::VIRTUAL_TAIL)
1173
1174                when (Marpa::PP::Internal::Op::CONSTANT_RESULT) {
1175                    my $result = $ops->[ $op_ix++ ];
1176                    if ($trace_values) {
1177                        print {$Marpa::PP::Internal::TRACE_FH}
1178                            'Constant result: ',
1179                            'Pushing 1 value on stack: ',
1180                            Data::Dumper->new( [$result] )->Terse(1)->Dump
1181                            or Marpa::PP::exception(
1182                            'Could not print to trace file');
1183                    } ## end if ($trace_values)
1184                    push @evaluation_stack, $result;
1185                } ## end when (Marpa::PP::Internal::Op::CONSTANT_RESULT)
1186
1187                when (Marpa::PP::Internal::Op::CALL) {
1188                    my $closure = $ops->[ $op_ix++ ];
1189                    my $rule_id =
1190                        $and_node->[Marpa::PP::Internal::And_Node::RULE_ID];
1191                    my $rule = $rules->[$rule_id];
1192                    my $original_rule =
1193                        $rule->[Marpa::PP::Internal::Rule::ORIGINAL_RULE];
1194                    if ( $original_rule
1195                        ->[Marpa::PP::Internal::Rule::DISCARD_SEPARATION] )
1196                    {
1197                        $current_data = [
1198                            @{$current_data}[
1199                                grep { not $_ % 2 } 0 .. $#{$current_data}
1200                            ]
1201                        ];
1202                    } ## end if ( $original_rule->[...])
1203                    my $result;
1204
1205                    my @warnings;
1206                    my $eval_ok;
1207                    DO_EVAL: {
1208                        local $SIG{__WARN__} = sub {
1209                            push @warnings, [ $_[0], ( caller 0 ) ];
1210                        };
1211
1212                        $eval_ok = eval {
1213                            $result =
1214                                $closure->( $action_object,
1215                                @{$current_data} );
1216                            1;
1217                        };
1218
1219                    } ## end DO_EVAL:
1220
1221                    if ( not $eval_ok or @warnings ) {
1222                        my $fatal_error = $EVAL_ERROR;
1223                        Marpa::PP::Internal::code_problems(
1224                            {   fatal_error => $fatal_error,
1225                                grammar     => $grammar,
1226                                eval_ok     => $eval_ok,
1227                                warnings    => \@warnings,
1228                                where       => 'computing value',
1229                                long_where  => 'Computing value for rule: '
1230                                    . Marpa::PP::brief_rule($rule),
1231                            }
1232                        );
1233                    } ## end if ( not $eval_ok or @warnings )
1234
1235                    if ($trace_values) {
1236                        print {$Marpa::PP::Internal::TRACE_FH}
1237                            'Calculated and pushed value: ',
1238                            Data::Dumper->new( [$result] )->Terse(1)->Dump
1239                            or Marpa::PP::exception(
1240                            'print to trace handle failed');
1241                    } ## end if ($trace_values)
1242
1243                    push @evaluation_stack, \$result;
1244
1245                } ## end when (Marpa::PP::Internal::Op::CALL)
1246
1247                default {
1248                    Marpa::PP::exception("Unknown evaluator Op: $_");
1249                }
1250
1251            } ## end given
1252        } ## end while ( $op_ix < scalar @{$ops} )
1253
1254    }    # TREE_NODE
1255
1256    return pop @evaluation_stack;
1257} ## end sub Marpa::PP::Internal::Recognizer::evaluate
1258
1259# null parse is special case
1260sub Marpa::PP::Internal::Recognizer::do_null_parse {
1261    my ( $recce, $start_rule ) = @_;
1262
1263    my $start_symbol = $start_rule->[Marpa::PP::Internal::Rule::LHS];
1264
1265    # Cannot increment the null parse
1266    return if $recce->[Marpa::PP::Internal::Recognizer::PARSE_COUNT]++;
1267    my $null_values = $recce->[Marpa::PP::Internal::Recognizer::NULL_VALUES];
1268    my $evaluator_rules =
1269        $recce->[Marpa::PP::Internal::Recognizer::EVALUATOR_RULES];
1270
1271    my $start_symbol_id = $start_symbol->[Marpa::PP::Internal::Symbol::ID];
1272    my $start_rule_id   = $start_rule->[Marpa::PP::Internal::Rule::ID];
1273
1274    my $and_node = [];
1275    $#{$and_node} = Marpa::PP::Internal::And_Node::LAST_FIELD;
1276    $and_node->[Marpa::PP::Internal::And_Node::VALUE_REF] =
1277        \( $null_values->[$start_symbol_id] );
1278    $and_node->[Marpa::PP::Internal::And_Node::RULE_ID] =
1279        $start_rule->[Marpa::PP::Internal::Rule::ID];
1280    $and_node->[Marpa::PP::Internal::And_Node::VALUE_OPS] =
1281        $evaluator_rules->[$start_rule_id];
1282
1283    $and_node->[Marpa::PP::Internal::And_Node::POSITION]      = 0;
1284    $and_node->[Marpa::PP::Internal::And_Node::START_EARLEME] = 0;
1285    $and_node->[Marpa::PP::Internal::And_Node::CAUSE_EARLEME] = 0;
1286    $and_node->[Marpa::PP::Internal::And_Node::END_EARLEME]   = 0;
1287    $and_node->[Marpa::PP::Internal::And_Node::ID]            = 0;
1288    my $symbol_name = $start_symbol->[Marpa::PP::Internal::Symbol::NAME];
1289    $and_node->[Marpa::PP::Internal::And_Node::TOKEN_NAME] = $symbol_name;
1290    $and_node->[Marpa::PP::Internal::And_Node::TAG] =
1291        Marpa::PP::Recognizer::and_node_tag( $recce, $and_node );
1292
1293    $recce->[Marpa::PP::Internal::Recognizer::AND_NODES]->[0] = $and_node;
1294
1295    return Marpa::PP::Internal::Recognizer::evaluate( $recce, [$and_node] );
1296
1297} ## end sub Marpa::PP::Internal::Recognizer::do_null_parse
1298
1299# Returns false if no parse
1300sub Marpa::PP::Recognizer::value {
1301    my ( $recce, @arg_hashes ) = @_;
1302
1303    my $parse_set_arg = $recce->[Marpa::PP::Internal::Recognizer::END];
1304
1305    my $trace_tasks = $recce->[Marpa::PP::Internal::Recognizer::TRACE_TASKS];
1306    local $Marpa::PP::Internal::TRACE_FH =
1307        $recce->[Marpa::PP::Internal::Recognizer::TRACE_FILE_HANDLE];
1308
1309    my $and_nodes = $recce->[Marpa::PP::Internal::Recognizer::AND_NODES];
1310    my $or_nodes  = $recce->[Marpa::PP::Internal::Recognizer::OR_NODES];
1311    my $ranking_method =
1312        $recce->[Marpa::PP::Internal::Recognizer::RANKING_METHOD];
1313
1314    if ( $recce->[Marpa::PP::Internal::Recognizer::SINGLE_PARSE_MODE] ) {
1315        Marpa::PP::exception(
1316            qq{Arguments were passed directly to value() in a previous call\n},
1317            qq{Only one call to value() is allowed per recognizer when arguments are passed directly\n},
1318            qq{This is the second call to value()\n}
1319        );
1320    } ## end if ( $recce->[Marpa::PP::Internal::Recognizer::SINGLE_PARSE_MODE...])
1321
1322    my $parse_count = $recce->[Marpa::PP::Internal::Recognizer::PARSE_COUNT];
1323    my $max_parses  = $recce->[Marpa::PP::Internal::Recognizer::MAX_PARSES];
1324    if ( $max_parses and $parse_count > $max_parses ) {
1325        Marpa::PP::exception("Maximum parse count ($max_parses) exceeded");
1326    }
1327
1328    for my $arg_hash (@arg_hashes) {
1329
1330        if ( exists $arg_hash->{end} ) {
1331            if ($parse_count) {
1332                Marpa::PP::exception(
1333                    q{Cannot change "end" after first parse result});
1334            }
1335            $recce->[Marpa::PP::Internal::Recognizer::SINGLE_PARSE_MODE] = 1;
1336            $parse_set_arg = $arg_hash->{end};
1337            delete $arg_hash->{end};
1338        } ## end if ( exists $arg_hash->{end} )
1339
1340        if ( exists $arg_hash->{closures} ) {
1341            if ($parse_count) {
1342                Marpa::PP::exception(
1343                    q{Cannot change "closures" after first parse result});
1344            }
1345            $recce->[Marpa::PP::Internal::Recognizer::SINGLE_PARSE_MODE] = 1;
1346            my $closures = $arg_hash->{closures};
1347            while ( my ( $action, $closure ) = each %{$closures} ) {
1348                Marpa::PP::exception(qq{Bad closure for action "$action"})
1349                    if ref $closure ne 'CODE';
1350            }
1351            $recce->[Marpa::PP::Internal::Recognizer::CLOSURES] = $closures;
1352            delete $arg_hash->{closures};
1353        } ## end if ( exists $arg_hash->{closures} )
1354
1355        if ( exists $arg_hash->{trace_actions} ) {
1356            $recce->[Marpa::PP::Internal::Recognizer::SINGLE_PARSE_MODE] = 1;
1357            $recce->[Marpa::PP::Internal::Recognizer::TRACE_ACTIONS] =
1358                $arg_hash->{trace_actions};
1359            delete $arg_hash->{trace_actions};
1360        } ## end if ( exists $arg_hash->{trace_actions} )
1361
1362        if ( exists $arg_hash->{trace_values} ) {
1363            $recce->[Marpa::PP::Internal::Recognizer::SINGLE_PARSE_MODE] = 1;
1364            $recce->[Marpa::PP::Internal::Recognizer::TRACE_VALUES] =
1365                $arg_hash->{trace_values};
1366            delete $arg_hash->{trace_values};
1367        } ## end if ( exists $arg_hash->{trace_values} )
1368
1369        # A typo made its way into the documentation, so now it's a
1370        # synonym.
1371        for my $trace_fh_alias (qw(trace_fh trace_file_handle)) {
1372            if ( exists $arg_hash->{$trace_fh_alias} ) {
1373                $recce->[Marpa::PP::Internal::Recognizer::TRACE_FILE_HANDLE] =
1374                    $Marpa::PP::Internal::TRACE_FH =
1375                    $arg_hash->{$trace_fh_alias};
1376                delete $arg_hash->{$trace_fh_alias};
1377            } ## end if ( exists $arg_hash->{$trace_fh_alias} )
1378        } ## end for my $trace_fh_alias (qw(trace_fh trace_file_handle))
1379
1380        my @unknown_arg_names = keys %{$arg_hash};
1381        Marpa::PP::exception(
1382            'Unknown named argument(s) to Marpa::PP::Recognizer::value: ',
1383            ( join q{ }, @unknown_arg_names ) )
1384            if @unknown_arg_names;
1385
1386    } ## end for my $arg_hash (@arg_hashes)
1387
1388    my $grammar     = $recce->[Marpa::PP::Internal::Recognizer::GRAMMAR];
1389    my $earley_sets = $recce->[Marpa::PP::Internal::Recognizer::EARLEY_SETS];
1390
1391    my $furthest_earleme =
1392        $recce->[Marpa::PP::Internal::Recognizer::FURTHEST_EARLEME];
1393    my $last_completed_earleme =
1394        $recce->[Marpa::PP::Internal::Recognizer::LAST_COMPLETED_EARLEME];
1395    Marpa::PP::exception(
1396        "Attempt to evaluate incompletely recognized parse:\n",
1397        "  Last token ends at location $furthest_earleme\n",
1398        "  Recognition done only as far as location $last_completed_earleme\n"
1399    ) if $furthest_earleme > $last_completed_earleme;
1400
1401    my $rules   = $grammar->[Marpa::PP::Internal::Grammar::RULES];
1402    my $symbols = $grammar->[Marpa::PP::Internal::Grammar::SYMBOLS];
1403
1404    my $current_parse_set = $parse_set_arg
1405        // $recce->[Marpa::PP::Internal::Recognizer::FURTHEST_EARLEME];
1406
1407    # Look for the start item and start rule
1408    my $earley_set = $earley_sets->[$current_parse_set];
1409
1410    # Perhaps this call should be moved.
1411    # The null values are currently a function of the grammar,
1412    # and should be constant for the life of a recognizer.
1413    my $null_values =
1414        $recce->[Marpa::PP::Internal::Recognizer::NULL_VALUES] //=
1415        Marpa::PP::Internal::Recognizer::set_null_values($recce);
1416
1417    my @task_list;
1418    my $start_item;
1419    my $start_rule;
1420    if ($parse_count) {
1421        @task_list = ( [Marpa::PP::Internal::Task::ITERATE] );
1422    }
1423    else {
1424        my $start_state;
1425
1426        EARLEY_ITEM:
1427        for my $item (
1428            @{ $earley_set->[Marpa::PP::Internal::Earley_Set::ITEMS] } )
1429        {
1430            $start_state = $item->[Marpa::PP::Internal::Earley_Item::STATE];
1431            $start_rule =
1432                $start_state->[Marpa::PP::Internal::AHFA::START_RULE];
1433            next EARLEY_ITEM if not $start_rule;
1434            $start_item = $item;
1435            last EARLEY_ITEM;
1436        } ## end for my $item ( @{ $earley_set->[...]})
1437
1438        return if not $start_rule;
1439
1440        $recce->[Marpa::PP::Internal::Recognizer::EVALUATOR_RULES] =
1441            Marpa::PP::Internal::Recognizer::set_actions($recce);
1442
1443        return Marpa::PP::Internal::Recognizer::do_null_parse( $recce,
1444            $start_rule )
1445            if $start_rule->[Marpa::PP::Internal::Rule::LHS]
1446                ->[Marpa::PP::Internal::Symbol::NULLING];
1447
1448        @task_list = ();
1449        push @task_list, [Marpa::PP::Internal::Task::INITIALIZE];
1450    } ## end else [ if ($parse_count) ]
1451
1452    $recce->[Marpa::PP::Internal::Recognizer::PARSE_COUNT]++;
1453
1454    my $evaluator_rules =
1455        $recce->[Marpa::PP::Internal::Recognizer::EVALUATOR_RULES];
1456    my $iteration_stack =
1457        $recce->[Marpa::PP::Internal::Recognizer::ITERATION_STACK];
1458
1459    my $iteration_node_worklist;
1460    my @and_node_in_use = ();
1461    for my $iteration_node ( @{$iteration_stack} ) {
1462        my $choices =
1463            $iteration_node->[Marpa::PP::Internal::Iteration_Node::CHOICES];
1464        my $choice      = $choices->[0];
1465        my $and_node    = $choice->[Marpa::PP::Internal::Choice::AND_NODE];
1466        my $and_node_id = $and_node->[Marpa::PP::Internal::And_Node::ID];
1467        $and_node_in_use[$and_node_id] = 1;
1468    } ## end for my $iteration_node ( @{$iteration_stack} )
1469
1470    TASK: while ( my $task = pop @task_list ) {
1471
1472        my ( $task_type, @task_data ) = @{$task};
1473
1474        # Create the unpopulated top or-node
1475        if ( $task_type == Marpa::PP::Internal::Task::INITIALIZE ) {
1476
1477            if ($trace_tasks) {
1478                print {$Marpa::PP::Internal::TRACE_FH}
1479                    'Task: INITIALIZE; ',
1480                    ( scalar @task_list ), " tasks pending\n"
1481                    or Marpa::PP::exception('print to trace handle failed');
1482            } ## end if ($trace_tasks)
1483
1484            my $start_rule_id = $start_rule->[Marpa::PP::Internal::Rule::ID];
1485
1486            my $start_or_node = [];
1487            $start_or_node->[Marpa::PP::Internal::Or_Node::ID] = 0;
1488            $start_or_node->[Marpa::PP::Internal::Or_Node::ITEM] =
1489                $start_item;
1490            $start_or_node->[Marpa::PP::Internal::Or_Node::RULE_ID] =
1491                $start_rule_id;
1492
1493            # Start or-node cannot cycle
1494            $start_or_node->[Marpa::PP::Internal::Or_Node::CYCLE] = 0;
1495            $start_or_node->[Marpa::PP::Internal::Or_Node::POSITION] =
1496                scalar @{ $start_rule->[Marpa::PP::Internal::Rule::RHS] };
1497            {
1498                my $start_or_node_tag =
1499                    $start_or_node->[Marpa::PP::Internal::Or_Node::TAG] =
1500                    Marpa::PP::Recognizer::or_node_tag( $recce,
1501                    $start_or_node );
1502                $recce->[Marpa::PP::Internal::Recognizer::OR_NODE_HASH]
1503                    ->{$start_or_node_tag} = $start_or_node;
1504            }
1505
1506            # Zero out the evaluation
1507            $#{$and_nodes}       = -1;
1508            $#{$or_nodes}        = -1;
1509            $#{$iteration_stack} = -1;
1510            $#and_node_in_use    = -1;
1511
1512            # Populate the start or-node
1513            $or_nodes->[0] = $start_or_node;
1514
1515            my $start_iteration_node = [];
1516            $start_iteration_node
1517                ->[Marpa::PP::Internal::Iteration_Node::OR_NODE] =
1518                $start_or_node;
1519
1520            @task_list = ();
1521            push @task_list, [Marpa::PP::Internal::Task::FIX_TREE],
1522                [
1523                Marpa::PP::Internal::Task::STACK_INODE,
1524                $start_iteration_node
1525                ];
1526
1527            if ( $ranking_method eq 'constant' ) {
1528                push @task_list, [Marpa::PP::Internal::Task::RANK_ALL],;
1529            }
1530
1531            push @task_list,
1532                [
1533                Marpa::PP::Internal::Task::POPULATE_DEPTH, 0,
1534                [$start_or_node]
1535                ],
1536                [
1537                Marpa::PP::Internal::Task::POPULATE_OR_NODE,
1538                $start_or_node
1539                ];
1540
1541            next TASK;
1542
1543        } ## end if ( $task_type == Marpa::PP::Internal::Task::INITIALIZE)
1544
1545        # Special processing for the top iteration node
1546        if ( $task_type == Marpa::PP::Internal::Task::ITERATE ) {
1547
1548            if ($trace_tasks) {
1549                print {$Marpa::PP::Internal::TRACE_FH}
1550                    'Task: ITERATE; ',
1551                    ( scalar @task_list ), " tasks pending\n"
1552                    or Marpa::PP::exception('print to trace handle failed');
1553            } ## end if ($trace_tasks)
1554
1555            $iteration_node_worklist = undef;
1556
1557            # In this pass, we go up the iteration stack,
1558            # looking a node which we can iterate.
1559            my $iteration_node;
1560            ITERATION_NODE:
1561            while ( $iteration_node = pop @{$iteration_stack} ) {
1562
1563                my $choices = $iteration_node
1564                    ->[Marpa::PP::Internal::Iteration_Node::CHOICES];
1565
1566                # Eliminate the current choice
1567                my $choice = $choices->[0];
1568                my $and_node =
1569                    $choice->[Marpa::PP::Internal::Choice::AND_NODE];
1570                my $and_node_id =
1571                    $and_node->[Marpa::PP::Internal::And_Node::ID];
1572                $and_node_in_use[$and_node_id] = undef;
1573                shift @{$choices};
1574
1575                # Throw away choices until we find one that does not cycle
1576                CHOICE: while ( scalar @{$choices} ) {
1577                    $choice = $choices->[0];
1578                    $and_node =
1579                        $choice->[Marpa::PP::Internal::Choice::AND_NODE];
1580                    $and_node_id =
1581                        $and_node->[Marpa::PP::Internal::And_Node::ID];
1582                    last CHOICE if not $and_node_in_use[$and_node_id];
1583                    shift @{$choices};
1584                } ## end while ( scalar @{$choices} )
1585
1586                # Climb the parent links, marking the ranks
1587                # of the nodes "dirty", until we hit one this is
1588                # already dirty
1589                my $direct_parent = $iteration_node
1590                    ->[Marpa::PP::Internal::Iteration_Node::PARENT];
1591                PARENT:
1592                for ( my $parent = $direct_parent; defined $parent; ) {
1593                    my $parent_node = $iteration_stack->[$parent];
1594                    last PARENT
1595                        if not $parent_node
1596                            ->[Marpa::PP::Internal::Iteration_Node::CLEAN];
1597                    $parent_node->[Marpa::PP::Internal::Iteration_Node::CLEAN]
1598                        = 0;
1599                    $parent = $parent_node
1600                        ->[Marpa::PP::Internal::Iteration_Node::PARENT];
1601                } ## end for ( my $parent = $direct_parent; defined $parent; )
1602
1603                # This or-node is already populated,
1604                # or it would not have been put
1605                # onto the iteration stack
1606                $choices = $iteration_node
1607                    ->[Marpa::PP::Internal::Iteration_Node::CHOICES];
1608
1609                if ( not scalar @{$choices} ) {
1610
1611                    # For the node just popped off the stack
1612                    # unset the pointer to it in its parent
1613                    if ( defined $direct_parent ) {
1614
1615                        #<<< cycles on perltidy version 20090616
1616                        my $child_type = $iteration_node->[
1617                            Marpa::PP::Internal::Iteration_Node::CHILD_TYPE ];
1618                        #>>>
1619                        #
1620                        $iteration_stack->[$direct_parent]->[
1621                            $child_type
1622                            == Marpa::PP::Internal::And_Node::PREDECESSOR_ID
1623                            ? Marpa::PP::Internal::Iteration_Node::PREDECESSOR_IX
1624                            : Marpa::PP::Internal::Iteration_Node::CAUSE_IX
1625                            ]
1626                            = undef;
1627                    } ## end if ( defined $direct_parent )
1628                    next ITERATION_NODE;
1629                } ## end if ( not scalar @{$choices} )
1630
1631                # Dirty the iteration node and put it back
1632                # on the stack
1633                $iteration_node
1634                    ->[Marpa::PP::Internal::Iteration_Node::PREDECESSOR_IX] =
1635                    undef;
1636                $iteration_node
1637                    ->[Marpa::PP::Internal::Iteration_Node::CAUSE_IX] = undef;
1638                $iteration_node->[Marpa::PP::Internal::Iteration_Node::CLEAN]
1639                    = 0;
1640                push @{$iteration_stack}, $iteration_node;
1641
1642                $choice   = $choices->[0];
1643                $and_node = $choice->[Marpa::PP::Internal::Choice::AND_NODE];
1644                $and_node_id = $and_node->[Marpa::PP::Internal::And_Node::ID];
1645                $and_node_in_use[$and_node_id] = 1;
1646
1647                last ITERATION_NODE;
1648
1649            } ## end while ( $iteration_node = pop @{$iteration_stack} )
1650
1651            # If we hit the top of the stack without finding any node
1652            # to iterate, that is it for parsing.
1653            return if not defined $iteration_node;
1654
1655            push @task_list, [Marpa::PP::Internal::Task::FIX_TREE];
1656
1657            next TASK;
1658
1659        } ## end if ( $task_type == Marpa::PP::Internal::Task::ITERATE)
1660
1661        # This task is set up to rerun itself until explicitly exited
1662        FIX_TREE_LOOP:
1663        while ( $task_type == Marpa::PP::Internal::Task::FIX_TREE ) {
1664
1665            # If the work list is undefined, initialize it to the entire stack
1666            $iteration_node_worklist //= [ 0 .. $#{$iteration_stack} ];
1667            next TASK if not scalar @{$iteration_node_worklist};
1668            my $working_node_ix = $iteration_node_worklist->[-1];
1669
1670            if ($trace_tasks) {
1671                print {$Marpa::PP::Internal::TRACE_FH}
1672                    q{Task: FIX_TREE; },
1673                    ( scalar @{$iteration_node_worklist} ),
1674                    " current iteration node #$working_node_ix; ",
1675                    ( scalar @task_list ), " tasks pending\n"
1676                    or Marpa::PP::exception('print to trace handle failed');
1677            } ## end if ($trace_tasks)
1678
1679            # We are done fixing the tree is the worklist is empty
1680
1681            my $working_node = $iteration_stack->[$working_node_ix];
1682            my $choices =
1683                $working_node->[Marpa::PP::Internal::Iteration_Node::CHOICES];
1684            my $choice = $choices->[0];
1685            my $working_and_node =
1686                $choice->[Marpa::PP::Internal::Choice::AND_NODE];
1687
1688            FIELD:
1689            for my $field ( Marpa::PP::Internal::Iteration_Node::CAUSE_IX,
1690                Marpa::PP::Internal::Iteration_Node::PREDECESSOR_IX
1691                )
1692            {
1693                my $ix = $working_node->[$field];
1694                next FIELD if defined $ix;
1695                my $and_node_field =
1696                    $field
1697                    == Marpa::PP::Internal::Iteration_Node::PREDECESSOR_IX
1698                    ? Marpa::PP::Internal::And_Node::PREDECESSOR_ID
1699                    : Marpa::PP::Internal::And_Node::CAUSE_ID;
1700
1701                my $or_node_id = $working_and_node->[$and_node_field];
1702                if ( not defined $or_node_id ) {
1703                    $working_node->[$field] = -999_999_999;
1704                    next FIELD;
1705                }
1706
1707                my $new_iteration_node = [];
1708                $new_iteration_node
1709                    ->[Marpa::PP::Internal::Iteration_Node::OR_NODE] =
1710                    $or_nodes->[$or_node_id];
1711                $new_iteration_node
1712                    ->[Marpa::PP::Internal::Iteration_Node::PARENT] =
1713                    $working_node_ix;
1714                $new_iteration_node
1715                    ->[Marpa::PP::Internal::Iteration_Node::CHILD_TYPE] =
1716                    $and_node_field;
1717
1718                # Restack the current task, adding a task to create
1719                # the child iteration node
1720                push @task_list, $task,
1721                    [
1722                    Marpa::PP::Internal::Task::STACK_INODE,
1723                    $new_iteration_node
1724                    ];
1725                next TASK;
1726            } ## end for my $field ( ...)
1727
1728            $working_node->[Marpa::PP::Internal::Iteration_Node::CLEAN] = 1;
1729            pop @{$iteration_node_worklist};
1730            next FIX_TREE_LOOP;
1731
1732        } ## end while ( $task_type == Marpa::PP::Internal::Task::FIX_TREE)
1733
1734        if ( $task_type == Marpa::PP::Internal::Task::POPULATE_OR_NODE ) {
1735
1736            my $work_or_node = $task_data[0];
1737
1738            if ($trace_tasks) {
1739                print {$Marpa::PP::Internal::TRACE_FH}
1740                    'Task: POPULATE_OR_NODE o',
1741                    $work_or_node->[Marpa::PP::Internal::Or_Node::ID],
1742                    q{; }, ( scalar @task_list ), " tasks pending\n"
1743                    or Marpa::PP::exception('print to trace handle failed');
1744            } ## end if ($trace_tasks)
1745
1746            my $work_node_name =
1747                $work_or_node->[Marpa::PP::Internal::Or_Node::TAG];
1748
1749            # SET Should be the same for all items
1750            my $or_node_item =
1751                $work_or_node->[Marpa::PP::Internal::Or_Node::ITEM];
1752
1753            my $work_set =
1754                $or_node_item->[Marpa::PP::Internal::Earley_Item::SET];
1755            my $work_node_origin =
1756                $or_node_item->[Marpa::PP::Internal::Earley_Item::ORIGIN];
1757
1758            my $work_rule_id =
1759                $work_or_node->[Marpa::PP::Internal::Or_Node::RULE_ID];
1760            my $work_rule = $rules->[$work_rule_id];
1761            my $work_position =
1762                $work_or_node->[Marpa::PP::Internal::Or_Node::POSITION] - 1;
1763            my $work_symbol =
1764                $work_rule->[Marpa::PP::Internal::Rule::RHS]
1765                ->[$work_position];
1766            my $work_symbol_name =
1767                $work_symbol->[Marpa::PP::Internal::Symbol::NAME];
1768
1769            {
1770
1771                my $item           = $or_node_item;
1772                my $or_sapling_set = $work_set;
1773
1774                my $leo_links =
1775                    defined
1776                    $item->[Marpa::PP::Internal::Earley_Item::IS_LEO_EXPANDED]
1777                    ? []
1778                    : $item->[Marpa::PP::Internal::Earley_Item::LEO_LINKS];
1779                $leo_links //= [];
1780
1781                # If this is a Leo completion, translate the Leo links
1782                for my $leo_link ( @{$leo_links} ) {
1783
1784                    my ( $leo_item, $cause ) = @{$leo_link};
1785
1786                    my $next_leo_item = $leo_item
1787                        ->[Marpa::PP::Internal::Leo_Item::PREDECESSOR];
1788                    my $leo_symbol_name = $leo_item
1789                        ->[Marpa::PP::Internal::Leo_Item::LEO_POSTDOT_SYMBOL];
1790                    my $leo_base_item =
1791                        $leo_item->[Marpa::PP::Internal::Leo_Item::BASE];
1792
1793                    my $next_links = [ [ $leo_base_item, $cause, ] ];
1794
1795                    LEO_ITEM: for ( ;; ) {
1796
1797                        if ( not $next_leo_item ) {
1798
1799                            # die join " ", __FILE__, __LINE__, "next link cnt", (scalar @{$next_links})
1800                            # if scalar @{$next_links} != 1;
1801
1802                            #<<< perltidy cycles as of version 20090616
1803                            push @{ $item
1804                                    ->[Marpa::PP::Internal::Earley_Item::LINKS
1805                                    ] },
1806                                @{$next_links};
1807                            #<<<
1808
1809			    # Now that the Leo links are translated, mark the
1810			    # Earley item accordingly
1811			    $item->[Marpa::PP::Internal::Earley_Item::IS_LEO_EXPANDED] = 1;
1812
1813                            last LEO_ITEM;
1814
1815                        } ## end if ( not $next_leo_item )
1816
1817			my ( undef, $base_to_state ) =
1818			    @{ $leo_base_item
1819				->[ Marpa::PP::Internal::Earley_Item::STATE ]
1820				->[Marpa::PP::Internal::AHFA::TRANSITION]
1821				->{$leo_symbol_name} };
1822                        my $origin = $next_leo_item
1823                            ->[Marpa::PP::Internal::Leo_Item::SET];
1824
1825                        my $name = sprintf
1826                            'S%d@%d-%d',
1827                            $base_to_state->[Marpa::PP::Internal::AHFA::ID],
1828                            $origin,
1829                            $or_sapling_set;
1830			my $hash_key = join q{:},
1831                            $base_to_state->[Marpa::PP::Internal::AHFA::ID],
1832			    $origin;
1833			my $earley_hash =
1834			    $earley_sets->[$or_sapling_set]
1835			    ->[Marpa::PP::Internal::Earley_Set::HASH];
1836
1837                        my $target_item = $earley_hash->{$hash_key};
1838                        if ( not defined $target_item ) {
1839                            $target_item = [];
1840                            $target_item
1841                                ->[Marpa::PP::Internal::Earley_Item::ID] =
1842                                $recce->[
1843                                Marpa::PP::Internal::Recognizer::NEXT_EARLEY_ITEM_ID
1844                                ]++;
1845                            $target_item
1846                                ->[Marpa::PP::Internal::Earley_Item::ORIGIN] =
1847                                $origin;
1848                            $target_item
1849                                ->[Marpa::PP::Internal::Earley_Item::STATE] =
1850                                $base_to_state;
1851                            $target_item
1852                                ->[Marpa::PP::Internal::Earley_Item::LINKS] =
1853                                [];
1854                            $target_item
1855                                ->[Marpa::PP::Internal::Earley_Item::SET] =
1856                                $or_sapling_set;
1857                            $earley_hash->{$hash_key} = $target_item;
1858                            push @{ $earley_sets->[$or_sapling_set]
1859                                    ->[Marpa::PP::Internal::Earley_Set::ITEMS]
1860                                }, $target_item;
1861                        } ## end if ( not defined $target_item )
1862
1863                        push @{ $target_item
1864                                ->[Marpa::PP::Internal::Earley_Item::LINKS] },
1865                            @{$next_links};
1866
1867                        $leo_item      = $next_leo_item;
1868                        $next_leo_item = $leo_item
1869                            ->[Marpa::PP::Internal::Leo_Item::PREDECESSOR];
1870                        $leo_base_item =
1871                            $leo_item->[Marpa::PP::Internal::Leo_Item::BASE];
1872			$leo_symbol_name =
1873			    $leo_item->[Marpa::PP::Internal::Leo_Item::LEO_POSTDOT_SYMBOL];
1874
1875                        $next_links = [ [ $leo_base_item, $target_item, $leo_symbol_name ] ];
1876
1877                    } ## end for ( ;; )
1878                } ## end for my $leo_link ( @{$leo_links} )
1879
1880            }
1881
1882            my @link_worklist;
1883
1884            CREATE_LINK_WORKLIST: {
1885
1886                # Several Earley items may be the source of the same or-node,
1887                # but the or-node only keeps track of one.  This is sufficient,
1888                # because the Earley item is tracked by the or-node only for its
1889                # links, and the links for every Earley item which is the source
1890                # of the same or-node must be the same.  There's more about this
1891		# in the libmarpa docs.
1892
1893                # link worklist item is $predecessor, $cause, $token_name, $value_ref
1894
1895                # All predecessors apply to a
1896                # nulling work symbol.
1897
1898                if ( $work_symbol->[Marpa::PP::Internal::Symbol::NULLING] ) {
1899                    my $nulling_symbol_id =
1900                        $work_symbol->[Marpa::PP::Internal::Symbol::ID];
1901                    my $value_ref = \$null_values->[$nulling_symbol_id];
1902                    @link_worklist =
1903                        [ $or_node_item, undef, $work_symbol_name, $value_ref ];
1904                    last CREATE_LINK_WORKLIST;
1905                } ## end if ( $work_symbol->[...])
1906
1907                # Collect links for or node items
1908                # into link work items
1909                @link_worklist =
1910                    @{ $or_node_item->[Marpa::PP::Internal::Earley_Item::LINKS] };
1911
1912            } ## end CREATE_LINK_WORKLIST:
1913
1914            # The and node data is put into the hash, only to be taken out immediately,
1915            # but in the process the very important step of eliminating duplicates
1916            # is accomplished.
1917            my %and_node_data = ();
1918
1919            LINK_WORK_ITEM: for my $link_work_item (@link_worklist) {
1920
1921		my ( $predecessor, $cause, $symbol_name, $value_ref ) =
1922                    @{$link_work_item};
1923
1924		# next LINK_WORK_ITEM if $symbol_name ne $work_symbol_name;
1925
1926                my $cause_earleme = $work_node_origin;
1927                my $predecessor_id;
1928		my $predecessor_name;
1929
1930                if ( $work_position > 0 ) {
1931
1932                    $cause_earleme =
1933                        $predecessor->[Marpa::PP::Internal::Earley_Item::SET];
1934
1935                    $predecessor_name =
1936                        "R$work_rule_id:$work_position" . q{@}
1937                        . $predecessor
1938                        ->[Marpa::PP::Internal::Earley_Item::ORIGIN] . q{-}
1939                        . $cause_earleme;
1940
1941                    FIND_PREDECESSOR: {
1942                        my $predecessor_or_node =
1943                            $recce
1944                            ->[Marpa::PP::Internal::Recognizer::OR_NODE_HASH]
1945                            ->{$predecessor_name};
1946                        if ($predecessor_or_node) {
1947                            $predecessor_id = $predecessor_or_node
1948                                ->[Marpa::PP::Internal::Or_Node::ID];
1949
1950                            last FIND_PREDECESSOR;
1951
1952                        } ## end if ($predecessor_or_node)
1953
1954                        $predecessor_or_node = [];
1955                        $predecessor_or_node
1956                            ->[Marpa::PP::Internal::Or_Node::TAG] =
1957                            $predecessor_name;
1958                        $recce
1959                            ->[Marpa::PP::Internal::Recognizer::OR_NODE_HASH]
1960                            ->{$predecessor_name} = $predecessor_or_node;
1961                        $predecessor_or_node
1962                            ->[Marpa::PP::Internal::Or_Node::RULE_ID] =
1963                            $work_rule_id;
1964
1965                        # nulling nodes are never part of cycles
1966                        # thanks to the CHAF rewrite
1967                        $predecessor_or_node
1968                            ->[Marpa::PP::Internal::Or_Node::CYCLE] =
1969                            $work_rule
1970                            ->[Marpa::PP::Internal::Rule::VIRTUAL_CYCLE]
1971                            && $cause_earleme != $work_node_origin;
1972                        $predecessor_or_node
1973                            ->[Marpa::PP::Internal::Or_Node::POSITION] =
1974                            $work_position;
1975                        $predecessor_or_node
1976                            ->[Marpa::PP::Internal::Or_Node::ITEM] =
1977                                $predecessor;
1978                        $predecessor_id =
1979                            ( push @{$or_nodes}, $predecessor_or_node ) - 1;
1980
1981                        Marpa::PP::exception(
1982                            "Too many or-nodes for evaluator: $predecessor_id"
1983                            )
1984                            if $predecessor_id
1985                                & ~(Marpa::PP::Internal::N_FORMAT_MAX);
1986                        $predecessor_or_node
1987                            ->[Marpa::PP::Internal::Or_Node::ID] =
1988                            $predecessor_id;
1989
1990                    } ## end FIND_PREDECESSOR:
1991
1992                } ## end if ( $work_position > 0 )
1993
1994                my $cause_id;
1995
1996                if ( defined $cause ) {
1997
1998                    my $cause_symbol_id =
1999                        $work_symbol->[Marpa::PP::Internal::Symbol::ID];
2000
2001                    my $state =
2002                        $cause->[Marpa::PP::Internal::Earley_Item::STATE];
2003
2004                    for my $cause_rule (
2005                        @{  $state
2006                                ->[Marpa::PP::Internal::AHFA::COMPLETE_RULES]
2007                                ->[$cause_symbol_id]
2008                        }
2009                        )
2010                    {
2011
2012                        my $cause_rule_id =
2013                            $cause_rule->[Marpa::PP::Internal::Rule::ID];
2014
2015                        my $cause_name =
2016                            "R$cause_rule_id:"
2017			    .  (scalar @{ $cause_rule->[Marpa::PP::Internal::Rule::RHS] })
2018			    . q{@}
2019                            . $cause->[Marpa::PP::Internal::Earley_Item::ORIGIN]
2020                            . q{-}
2021                            . $cause->[Marpa::PP::Internal::Earley_Item::SET];
2022
2023                        FIND_CAUSE: {
2024                            my $cause_or_node =
2025                                $recce->[
2026                                Marpa::PP::Internal::Recognizer::OR_NODE_HASH]
2027                                ->{$cause_name};
2028                            if ($cause_or_node) {
2029                                $cause_id = $cause_or_node
2030                                    ->[Marpa::PP::Internal::Or_Node::ID];
2031                                last FIND_CAUSE;
2032                            } ## end if ($cause_or_node)
2033
2034                            $cause_or_node = [];
2035                            $cause_or_node
2036                                ->[Marpa::PP::Internal::Or_Node::TAG] =
2037                                $cause_name;
2038                            $recce->[
2039                                Marpa::PP::Internal::Recognizer::OR_NODE_HASH]
2040                                ->{$cause_name} = $cause_or_node;
2041                            $cause_or_node
2042                                ->[Marpa::PP::Internal::Or_Node::RULE_ID] =
2043                                $cause_rule_id;
2044
2045                            # nulling nodes are never part of cycles
2046                            # thanks to the CHAF rewrite
2047                            $cause_or_node
2048                                ->[Marpa::PP::Internal::Or_Node::CYCLE] =
2049                                $cause_rule
2050                                ->[Marpa::PP::Internal::Rule::VIRTUAL_CYCLE]
2051                                && $cause_earleme != $work_set;
2052                            $cause_or_node
2053                                ->[Marpa::PP::Internal::Or_Node::POSITION] =
2054                                scalar @{ $cause_rule
2055                                    ->[Marpa::PP::Internal::Rule::RHS] };
2056                            $cause_or_node ->[Marpa::PP::Internal::Or_Node::ITEM] =
2057                                    $cause;
2058                            $cause_id =
2059                                ( push @{$or_nodes}, $cause_or_node ) - 1;
2060
2061                            Marpa::PP::exception(
2062                                "Too many or-nodes for evaluator: $cause_id")
2063                                if $cause_id
2064                                    & ~(Marpa::PP::Internal::N_FORMAT_MAX);
2065                            $cause_or_node->[Marpa::PP::Internal::Or_Node::ID]
2066                                = $cause_id;
2067
2068                        } ## end FIND_CAUSE:
2069
2070                        my $and_node = [];
2071                        #<<< cycles in perltidy as of 5 Jul 2010
2072                        $and_node
2073                            ->[Marpa::PP::Internal::And_Node::PREDECESSOR_ID
2074                            ] = $predecessor_id;
2075                        #>>>
2076                        $and_node
2077                            ->[Marpa::PP::Internal::And_Node::CAUSE_EARLEME] =
2078                            $cause_earleme;
2079                        $and_node->[Marpa::PP::Internal::And_Node::CAUSE_ID] =
2080                            $cause_id;
2081
2082                        $and_node_data{
2083                            join q{:},
2084                            ( $predecessor_id // q{} ),
2085                            $cause_id
2086                            }
2087                            = $and_node;
2088
2089                    } ## end for
2090
2091                    next LINK_WORK_ITEM;
2092
2093                }    # if cause
2094
2095                my $and_node = [];
2096                $and_node->[Marpa::PP::Internal::And_Node::PREDECESSOR_ID] =
2097                    $predecessor_id;
2098                $and_node->[Marpa::PP::Internal::And_Node::CAUSE_EARLEME] =
2099                    $cause_earleme;
2100                $and_node->[Marpa::PP::Internal::And_Node::TOKEN_NAME] =
2101                    $symbol_name;
2102                $and_node->[Marpa::PP::Internal::And_Node::VALUE_REF] =
2103                    $value_ref;
2104
2105                $and_node_data{
2106                    join q{:}, ( $predecessor_id // q{} ),
2107                    q{}, $symbol_name
2108                    }
2109                    = $and_node;
2110
2111            } ## end for
2112
2113            my @child_and_nodes =
2114                map { $and_node_data{$_} } sort keys %and_node_data;
2115
2116            for my $and_node (@child_and_nodes) {
2117
2118                $and_node->[Marpa::PP::Internal::And_Node::RULE_ID] =
2119                    $work_rule_id;
2120
2121                $and_node->[Marpa::PP::Internal::And_Node::VALUE_OPS] =
2122                    $work_position
2123                    == $#{ $work_rule->[Marpa::PP::Internal::Rule::RHS] }
2124                    ? $evaluator_rules
2125                    ->[ $work_rule->[Marpa::PP::Internal::Rule::ID] ]
2126                    : undef;
2127
2128                $and_node->[Marpa::PP::Internal::And_Node::POSITION] =
2129                    $work_position;
2130                $and_node->[Marpa::PP::Internal::And_Node::START_EARLEME] =
2131                    $work_node_origin;
2132                $and_node->[Marpa::PP::Internal::And_Node::END_EARLEME] =
2133                    $work_set;
2134                my $id = ( push @{$and_nodes}, $and_node ) - 1;
2135                Marpa::PP::exception("Too many and-nodes for evaluator: $id")
2136                    if $id & ~(Marpa::PP::Internal::N_FORMAT_MAX);
2137                $and_node->[Marpa::PP::Internal::And_Node::ID] = $id;
2138                $and_node->[Marpa::PP::Internal::And_Node::TAG] =
2139                    Marpa::PP::Recognizer::and_node_tag( $recce, $and_node );
2140
2141            } ## end for my $and_node (@child_and_nodes)
2142
2143            # Populate the or-node, now that we have ID's for all the and-nodes
2144            $work_or_node->[Marpa::PP::Internal::Or_Node::AND_NODE_IDS] =
2145                [ map { $_->[Marpa::PP::Internal::And_Node::ID] }
2146                    @child_and_nodes ];
2147
2148            next TASK;
2149        } ## end if ( $task_type == ...)
2150
2151        if ( $task_type == Marpa::PP::Internal::Task::STACK_INODE ) {
2152
2153            my $work_iteration_node = $task_data[0];
2154            my $or_node             = $work_iteration_node
2155                ->[Marpa::PP::Internal::Iteration_Node::OR_NODE];
2156
2157            if ($trace_tasks) {
2158                print {$Marpa::PP::Internal::TRACE_FH}
2159                    'Task: STACK_INODE o',
2160                    $or_node->[Marpa::PP::Internal::Or_Node::ID],
2161                    q{; }, ( scalar @task_list ), " tasks pending\n"
2162                    or Marpa::PP::exception('print to trace handle failed');
2163            } ## end if ($trace_tasks)
2164
2165            my $and_node_ids =
2166                $or_node->[Marpa::PP::Internal::Or_Node::AND_NODE_IDS];
2167
2168            # If the or-node is not populated,
2169            # restack this task, and stack a task to populate the
2170            # or-node on top of it.
2171            if ( not defined $and_node_ids ) {
2172                push @task_list, $task,
2173                    [ Marpa::PP::Internal::Task::POPULATE_OR_NODE, $or_node ];
2174                next TASK;
2175            }
2176
2177            my $choices = $work_iteration_node
2178                ->[Marpa::PP::Internal::Iteration_Node::CHOICES];
2179
2180            # At this point we know the iteration node is populated, so if we don't
2181            # have the choices list initialized, we can do so now.
2182            if ( not defined $choices ) {
2183
2184                if ( $ranking_method eq 'constant' ) {
2185                    no integer;
2186                    my @choices = ();
2187                    AND_NODE: for my $and_node_id ( @{$and_node_ids} ) {
2188                        my $and_node   = $and_nodes->[$and_node_id];
2189                        my $new_choice = [];
2190                        $new_choice->[Marpa::PP::Internal::Choice::AND_NODE] =
2191                            $and_node;
2192
2193                        #<<< cycles on perltidy 20090616
2194                        my $rank_ref = $and_node->[
2195                            Marpa::PP::Internal::And_Node::INITIAL_RANK_REF ];
2196                        #>>>
2197                        die "Undefined rank for a$and_node_id"
2198                            if not defined $rank_ref;
2199                        next AND_NODE if not ref $rank_ref;
2200                        $new_choice->[Marpa::PP::Internal::Choice::RANK] =
2201                            ${$rank_ref};
2202                        push @choices, $new_choice;
2203                    } ## end for my $and_node_id ( @{$and_node_ids} )
2204                    ## no critic (BuiltinFunctions::ProhibitReverseSortBlock)
2205                    $choices = [
2206                        sort {
2207                            $b->[Marpa::PP::Internal::Choice::RANK]
2208                                <=> $a->[Marpa::PP::Internal::Choice::RANK]
2209                            } @choices
2210                    ];
2211                } ## end if ( $ranking_method eq 'constant' )
2212                else {
2213                    $choices =
2214                        [ map { [ $and_nodes->[$_], 0 ] } @{$and_node_ids} ];
2215                }
2216                $work_iteration_node
2217                    ->[Marpa::PP::Internal::Iteration_Node::CHOICES] =
2218                    $choices;
2219
2220            } ## end if ( not defined $choices )
2221
2222            # Due to skipping, even an initialized set of choices
2223            # may be empty.  If it is, throw away the stack and iterate.
2224            if ( not scalar @{$choices} ) {
2225
2226                @task_list = ( [Marpa::PP::Internal::Task::ITERATE] );
2227                next TASK;
2228            }
2229
2230            # Make our choice and set RANK
2231            my $choice = $choices->[0];
2232
2233            # Rank is left until later to be initialized
2234
2235            my $and_node = $choice->[Marpa::PP::Internal::Choice::AND_NODE];
2236            my $and_node_id = $and_node->[Marpa::PP::Internal::And_Node::ID];
2237            my $next_iteration_stack_ix = scalar @{$iteration_stack};
2238
2239            # Check if we are about to cycle.
2240            if ( $and_node_in_use[$and_node_id] ) {
2241
2242                # If there is another choice, increment choice and restack
2243                # this task ...
2244                #
2245                # This iteration node is not yet on the stack, so we
2246                # don't need to do anything with the pointers.
2247                if ( scalar @{$choices} > 1 ) {
2248                    shift @{$choices};
2249                    push @task_list, $task;
2250                    next TASK;
2251                }
2252
2253                # Otherwise, throw away all pending tasks and
2254                # iterate
2255                @task_list = ( [Marpa::PP::Internal::Task::ITERATE] );
2256                next TASK;
2257            } ## end if ( $and_node_in_use[$and_node_id] )
2258            $and_node_in_use[$and_node_id] = 1;
2259
2260            # Tell the parent that the new iteration node is its child.
2261            if (defined(
2262                    my $child_type =
2263                        $work_iteration_node
2264                        ->[Marpa::PP::Internal::Iteration_Node::CHILD_TYPE]
2265                )
2266                )
2267            {
2268                my $parent_ix = $work_iteration_node
2269                    ->[Marpa::PP::Internal::Iteration_Node::PARENT];
2270                $iteration_stack->[$parent_ix]->[
2271                    $child_type
2272                    == Marpa::PP::Internal::And_Node::PREDECESSOR_ID
2273                    ? Marpa::PP::Internal::Iteration_Node::PREDECESSOR_IX
2274                    : Marpa::PP::Internal::Iteration_Node::CAUSE_IX
2275                    ]
2276                    = scalar @{$iteration_stack};
2277            } ## end if ( defined( my $child_type = $work_iteration_node->...))
2278
2279            # If we are keeping an iteration node worklist,
2280            # add this node to it.
2281            defined $iteration_node_worklist
2282                and push @{$iteration_node_worklist},
2283                scalar @{$iteration_stack};
2284
2285            push @{$iteration_stack}, $work_iteration_node;
2286
2287            next TASK;
2288
2289        } ## end if ( $task_type == Marpa::PP::Internal::Task::STACK_INODE)
2290
2291        if ( $task_type == Marpa::PP::Internal::Task::RANK_ALL ) {
2292
2293            if ($trace_tasks) {
2294                print {$Marpa::PP::Internal::TRACE_FH} 'Task: RANK_ALL; ',
2295                    ( scalar @task_list ), " tasks pending\n"
2296                    or Marpa::PP::exception('print to trace handle failed');
2297            }
2298
2299            do_rank_all($recce);
2300
2301            next TASK;
2302        } ## end if ( $task_type == Marpa::PP::Internal::Task::RANK_ALL)
2303
2304        # This task is for pre-populating the entire and-node and or-node
2305        # space one "depth level" at a time.  It is used when ranking is
2306        # being done, because to rank you need to make a pre-pass through
2307        # the entire and-node and or-node space.
2308        #
2309        # As a side effect, depths are calculated for all the and-nodes.
2310        if ( $task_type == Marpa::PP::Internal::Task::POPULATE_DEPTH ) {
2311            my ( $depth, $or_node_list ) = @task_data;
2312
2313            if ($trace_tasks) {
2314                print {$Marpa::PP::Internal::TRACE_FH}
2315                    'Task: POPULATE_DEPTH; ',
2316                    ( scalar @task_list ), " tasks pending\n"
2317                    or Marpa::PP::exception('print to trace handle failed');
2318            } ## end if ($trace_tasks)
2319
2320            # We can assume all or-nodes in the list are populated
2321
2322            my %or_nodes_at_next_depth = ();
2323
2324            # Assign a depth to all the and-node children which
2325            # do not already have one assigned.
2326            for my $and_node_id (
2327                map { @{ $_->[Marpa::PP::Internal::Or_Node::AND_NODE_IDS] } }
2328                @{$or_node_list} )
2329            {
2330                my $and_node = $and_nodes->[$and_node_id];
2331                FIELD:
2332                for my $field (
2333                    Marpa::PP::Internal::And_Node::PREDECESSOR_ID,
2334                    Marpa::PP::Internal::And_Node::CAUSE_ID
2335                    )
2336                {
2337                    my $child_or_node_id = $and_node->[$field];
2338                    next FIELD if not defined $child_or_node_id;
2339
2340                    my $next_depth_or_node = $or_nodes->[$child_or_node_id];
2341
2342                    # Push onto list only if child or-node
2343                    # is not already populated
2344                    $next_depth_or_node
2345                        ->[Marpa::PP::Internal::Or_Node::AND_NODE_IDS]
2346                        or $or_nodes_at_next_depth{$next_depth_or_node} =
2347                        $next_depth_or_node;
2348
2349                } ## end for my $field ( ...)
2350
2351            } ## end for my $and_node_id ( map { @{ $_->[...]}})
2352
2353            # No or-nodes at next depth?
2354            # Great, we are done!
2355            my @or_nodes_at_next_depth =
2356                map { $or_nodes_at_next_depth{$_} }
2357                sort keys %or_nodes_at_next_depth;
2358            next TASK if not scalar @or_nodes_at_next_depth;
2359
2360            push @task_list,
2361                [
2362                Marpa::PP::Internal::Task::POPULATE_DEPTH, $depth + 1,
2363                \@or_nodes_at_next_depth
2364                ],
2365                map { [ Marpa::PP::Internal::Task::POPULATE_OR_NODE, $_ ] }
2366                @or_nodes_at_next_depth;
2367
2368            next TASK;
2369
2370        } ## end if ( $task_type == Marpa::PP::Internal::Task::POPULATE_DEPTH)
2371
2372        Marpa::PP::internal_error(
2373            "Internal error: Unknown task type: $task_type");
2374
2375    } ## end while ( my $task = pop @task_list )
2376
2377    my @stack = map {
2378        $_->[Marpa::PP::Internal::Iteration_Node::CHOICES]->[0]
2379            ->[Marpa::PP::Internal::Choice::AND_NODE]
2380    } @{$iteration_stack};
2381
2382    return Marpa::PP::Internal::Recognizer::evaluate( $recce, \@stack );
2383
2384} ## end sub Marpa::PP::Recognizer::value
2385
23861;
2387