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