Skip to content
This repository
Fetching contributors…

Cannot retrieve contributors at this time

file 811 lines (695 sloc) 26.371 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
use QRegex;

grammar HLL::Grammar {
    my $brackets := "<>[]()\{}\xab\xbb\x[0f3a]\x[0f3b]\x[0f3c]\x[0f3d]\x[169b]\x[169c]\x[2045]\x[2046]\x[207d]\x[207e]\x[208d]\x[208e]\x[2329]\x[232a]\x[2768]\x[2769]\x[276a]\x[276b]\x[276c]\x[276d]\x[276e]\x[276f]\x[2770]\x[2771]\x[2772]\x[2773]\x[2774]\x[2775]\x[27c5]\x[27c6]\x[27e6]\x[27e7]\x[27e8]\x[27e9]\x[27ea]\x[27eb]\x[2983]\x[2984]\x[2985]\x[2986]\x[2987]\x[2988]\x[2989]\x[298a]\x[298b]\x[298c]\x[298d]\x[298e]\x[298f]\x[2990]\x[2991]\x[2992]\x[2993]\x[2994]\x[2995]\x[2996]\x[2997]\x[2998]\x[29d8]\x[29d9]\x[29da]\x[29db]\x[29fc]\x[29fd]\x[3008]\x[3009]\x[300a]\x[300b]\x[300c]\x[300d]\x[300e]\x[300f]\x[3010]\x[3011]\x[3014]\x[3015]\x[3016]\x[3017]\x[3018]\x[3019]\x[301a]\x[301b]\x[301d]\x[301e]\x[fd3e]\x[fd3f]\x[fe17]\x[fe18]\x[fe35]\x[fe36]\x[fe37]\x[fe38]\x[fe39]\x[fe3a]\x[fe3b]\x[fe3c]\x[fe3d]\x[fe3e]\x[fe3f]\x[fe40]\x[fe41]\x[fe42]\x[fe43]\x[fe44]\x[fe47]\x[fe48]\x[fe59]\x[fe5a]\x[fe5b]\x[fe5c]\x[fe5d]\x[fe5e]\x[ff08]\x[ff09]\x[ff3b]\x[ff3d]\x[ff5b]\x[ff5d]\x[ff5f]\x[ff60]\x[ff62]\x[ff63]";
    my $cursor_class := NQPCursor;

    method throw_unrecog_backslash_seq ($sequence) {
        self.panic("Unrecognized backslash sequence: '\\" ~ $sequence ~ "'");
    }

    token termish {
        <prefixish>*
        <term>
        <postfixish>*
    }

    proto token term { <...> }
    proto token infix { <...> }
    proto token prefix { <...> }
    proto token postfix { <...> }
    proto token circumfix { <...> }
    proto token postcircumfix { <...> }

    token term:sym<circumfix> { <circumfix> }

    token infixish { <OPER=infix> }
    token prefixish { <OPER=prefix> <.ws> }
    token postfixish {
        | <OPER=postfix>
        | <OPER=postcircumfix>
    }

    token nullterm { <?> }
    token nullterm_alt { <term=.nullterm> }

    # Return <termish> if it matches, <nullterm_alt> otherwise.
    method nulltermish() { self.termish || self.nullterm_alt }

    token quote_delimited {
        <starter> <quote_atom>* <stopper>
    }

    token quote_atom {
        <!stopper>
        [
        | <quote_escape>
        | [ <-quote_escape-stopper-starter> ]+
        | <starter> <quote_atom>* <stopper>
        ]
    }

    token decint { [\d+]+ % '_' }
token decints { [<.ws><decint><.ws>]+ % ',' }

token hexint { [<[ 0..9 a..f A..F ]>+]+ % '_' }
token hexints { [<.ws><hexint><.ws>]+ % ',' }

token octint { [<[ 0..7 ]>+]+ % '_' }
token octints { [<.ws><octint><.ws>]+ % ',' }

token binint { [<[ 0..1 ]>+]+ % '_' }
token binints { [<.ws><binint><.ws>]+ % ',' }

token integer {
[
| 0 [ b <VALUE=binint>
| o <VALUE=octint>
| x <VALUE=hexint>
| d <VALUE=decint>
]
| <VALUE=decint>
]
}

token dec_number {
| $<coeff>=[ '.' \d+ ] <escale>?
| $<coeff>=[ \d+ '.' \d+ ] <escale>?
| $<coeff>=[ \d+ ] <escale>
}

token escale { <[Ee]> <[+\-]>? \d+ }

proto token quote_escape { <...> }
token quote_escape:sym<backslash> { \\ \\ <?quotemod_check('q')> }
token quote_escape:sym<stopper> { \\ <?quotemod_check('q')> <stopper> }

token quote_escape:sym<bs> { \\ b <?quotemod_check('b')> }
token quote_escape:sym<nl> { \\ n <?quotemod_check('b')> }
token quote_escape:sym<cr> { \\ r <?quotemod_check('b')> }
token quote_escape:sym<tab> { \\ t <?quotemod_check('b')> }
token quote_escape:sym<ff> { \\ f <?quotemod_check('b')> }
token quote_escape:sym<esc> { \\ e <?quotemod_check('b')> }
token quote_escape:sym<hex> {
\\ x <?quotemod_check('b')>
[ <hexint> | '[' <hexints> ']' ]
}
token quote_escape:sym<oct> {
\\ o <?quotemod_check('b')>
[ <octint> | '[' <octints> ']' ]
}
token quote_escape:sym<chr> { \\ c <?quotemod_check('b')> <charspec> }
token quote_escape:sym<0> { \\ <sym> <?quotemod_check('b')> }
token quote_escape:sym<misc> {
{} \\
[
|| <?quotemod_check('b')>
[
| $<textqq>=(\W)
| (\w) { self.throw_unrecog_backslash_seq: $/[0].Str }
]
|| $<textq>=[.]
]
}

token charname {
|| <integer>
|| <[a..z A..Z]> <-[ \] , # ]>*? <[a..z A..Z ) ]>
<?before \s* <[ \] , # ]> >
}
token charnames { [<.ws><charname><.ws>]+ % ',' }
token charspec {
[
| '[' <charnames> ']'
| \d+ [ _ \d+]*
| <[ ?..Z ]>
| <.panic: 'Unrecognized \\c character'>
]
}

# XXX Everything that follows is a "cheat" because it's still partially in
    # PIR. They used to live in a separate file, but in the 6model transition got
    # moved here, since it was the easier way.

=begin

=item O(spec [, save])

This subrule attaches operator precedence information to
a match object (such as an operator token). A typical
invocation for the subrule might be:

    token infix:sym<+> { <sym> <O( q{ %additive, :pirop<add> } )> }

This says to add all of the attribute of the C<%additive> hash
(described below) and a C<pirop> entry into the match object
returned by the C<< infix:sym<+> >> token (as the C<O> named
capture). Note that this is a alphabetic 'O", not a digit zero.

Currently the C<O> subrule accepts a string argument describing
the hash to be stored. (Note the C< q{ ... } > above. Eventually
it may be possible to omit the 'q' such that an actual (constant)
hash constructor is passed as an argument to C<O>.

The hash built via the string argument to C<O> is cached, so that
subsequent parses of the same token re-use the hash built from
previous parses of the token, rather than building a new hash
on each invocation.

The C<save> argument is used to build "hash" aggregates that can
be referred to by subsequent calls to C<O>. For example,

NQP::Grammar.O(':prec<t=>, :assoc<left>', '%additive' );

specifies the values to be associated with later references to
"%additive". Eventually it will likely be possible to use true
hashes from a package namespace, but this works for now.

Currently the only pairs recognized have the form C< :pair >,
C< :!pair >, and C<< :pair<strval> >>.

=end
method O(str $spec, $save?) {
Q:PIR {
.local pmc self, cur_class
.local string spec, save
.local int has_save
self = find_lex 'self'
cur_class = find_lex '$cursor_class'
spec = find_lex '$spec'
has_save = 0
$P0 = find_lex '$save'
unless $P0 goto no_save
save = $P0
has_save = 1
no_save:

# First, get the hash cache. Right now we have one
# cache for all grammars; eventually we may need a way to
# separate them out by cursor type.
.local pmc ohash
ohash = get_global '%!ohash'
unless null ohash goto have_ohash
ohash = new ['Hash']
set_global '%!ohash', ohash
have_ohash:

# See if we've already created a Hash for the current
            # specification string -- if so, use that.
            .local pmc hash
            hash = ohash[spec]
            unless null hash goto hash_done

            # Otherwise, we need to build a new one.
            hash = new ['Hash']
            .local int pos, eos
            pos = 0
            eos = length spec
          spec_loop:
            pos = find_not_cclass .CCLASS_WHITESPACE, spec, pos, eos
            if pos >= eos goto spec_done
            $S0 = substr spec, pos, 1
            if $S0 == ',' goto spec_comma
            if $S0 == ':' goto spec_pair

            # If whatever we found doesn't start with a colon, treat it
            # as a lookup of a previously saved hash to be merged in.
            .local string lookup
            .local int lpos
            # Find the first whitespace or comma
            lpos = find_cclass .CCLASS_WHITESPACE, spec, pos, eos
            $I0 = index spec, ',', pos
            if $I0 < 0 goto have_lookup_lpos
            if $I0 >= lpos goto have_lookup_lpos
            lpos = $I0
          have_lookup_lpos:
            $I0 = lpos - pos
            lookup = substr spec, pos, $I0
            .local pmc lhash, lhash_it
            lhash = ohash[lookup]
            if null lhash goto err_lookup
            lhash_it = iter lhash
          lhash_loop:
            unless lhash_it goto lhash_done
            $S0 = shift lhash_it
            $P0 = lhash[$S0]
            hash[$S0] = $P0
            goto lhash_loop
          lhash_done:
            pos = lpos
            goto spec_loop

            # We just ignore commas between elements for now.
          spec_comma:
            inc pos
            goto spec_loop

            # If we see a colon, then we want to parse whatever
            # comes next like a pair.
          spec_pair:
            # eat colon
            inc pos
            .local string name
            .local pmc value
            value = new ['Boolean']

            # If the pair is of the form :!name, then reverse the value
            # and skip the colon.
            $S0 = substr spec, pos, 1
            $I0 = iseq $S0, '!'
            pos += $I0
            $I0 = not $I0
            value = $I0

            # Get the name of the pair.
            lpos = find_not_cclass .CCLASS_WORD, spec, pos, eos
            $I0 = lpos - pos
            name = substr spec, pos, $I0
            pos = lpos

            # Look for a <...> that follows.
            $S0 = substr spec, pos, 1
            unless $S0 == '<' goto have_value
            inc pos
            lpos = index spec, '>', pos
            $I0 = lpos - pos
            $S0 = substr spec, pos, $I0
            value = box $S0
            pos = lpos + 1
          have_value:
            # Done processing the pair, store it in the hash.
            hash[name] = value
            goto spec_loop
          spec_done:
            # Done processing the spec string, cache the hash for later.
            ohash[spec] = hash
          hash_done:

            # If we've been called as a subrule, then build a pass-cursor
            # to indicate success and set the hash as the subrule's match object.
            if has_save goto save_hash
            ($P0, $S0, $I0) = self.'!cursor_start'()
            $P0.'!cursor_pass'($I0, '')
            setattribute $P0, cur_class, '$!match', hash
            .return ($P0)

            # save the hash under a new entry
          save_hash:
            ohash[save] = hash
            .return (self)

          err_lookup:
            self.'panic'('Unknown operator precedence specification "', lookup, '"')
        };
    }


=begin

=item panic([args :slurpy])

Throw an exception at the current cursor location. If the message
doesn't end with a newline, also output the line number and offset
of the match.

=end

method panic(*@args) {
my $pos := self.pos();
my $target := self.target();
@args.push(' at line ');
@args.push(HLL::Compiler.lineof($target, $pos) + 1);
@args.push(', near "');
@args.push(pir::escape__SS(nqp::substr($target, $pos, 10)));
@args.push('"');
nqp::die(nqp::join('', @args))
}
method FAILGOAL($goal, $dba?) {
unless $dba {
$dba := ~nqp::callercode();
}
self.panic("Unable to parse expression in $dba; couldn't find final $goal");
}


=begin

=item peek_delimiters(target, pos)

Return the start/stop delimiter pair based on peeking at C<target>
position C<pos>.

=end

method peek_delimiters(str $target, int $pos) {
# peek at the next character
my str $start := nqp::substr($target, $pos, 1);
# colon, word and whitespace characters aren't valid delimiters
if $start eq ':' {
self.panic('Colons may not be used to delimit quoting constructs');
}
if nqp::iscclass(pir::const::CCLASS_WORD, $start, 0) {
self.panic('Alphanumeric character is not allowed as a delimiter');
}
if nqp::iscclass(pir::const::CCLASS_WHITESPACE, $start, 0) {
self.panic('Whitespace character is not allowed as a delimiter');
}

# assume stop delim is same as start, for the moment
my str $stop := $start;
my int $brac := nqp::index($brackets, $start);
if $brac >= 0 {
# if it's a closing bracket, that's an error also
if $brac % 2 {
self.panic('Use of a closing delimiter for an opener is reserved');
}

# it's an opener, so get the closing bracket
$stop := nqp::substr($brackets, $brac + 1, 1);

# see if the opening bracket is repeated
my int $len := 1;
while nqp::substr($target, ++$pos, 1) eq $start {
$len++;
}
if $len > 1 {
$start := nqp::x($start, $len);
$stop := nqp::x($stop, $len);
}
}
[$start, $stop]
}

my $TRUE := 1;
token quote_EXPR(*@args) {
:my %*QUOTEMOD;
:my $*QUOTE_START;
:my $*QUOTE_STOP;
{
for @args -> $mod {
$mod := nqp::substr($mod, 1);
%*QUOTEMOD{$mod} := $TRUE;
if $mod eq 'qq' {
%*QUOTEMOD{'s'} := $TRUE;
%*QUOTEMOD{'a'} := $TRUE;
%*QUOTEMOD{'h'} := $TRUE;
%*QUOTEMOD{'f'} := $TRUE;
%*QUOTEMOD{'c'} := $TRUE;
%*QUOTEMOD{'b'} := $TRUE;
}
elsif $mod eq 'b' {
%*QUOTEMOD{'q'} := $TRUE;
}
}

my @delims := self.peek_delimiters(self.target, self.pos);
$*QUOTE_START := @delims[0];
$*QUOTE_STOP := @delims[1];
}
<quote_delimited>
}

token quotemod_check(str $mod) {
<?{ %*QUOTEMOD{$mod} }>
}

method starter() {
Q:PIR {
.local pmc self, cur
.local string target, start
.local int pos
self = find_lex 'self'

(cur, target, pos) = self.'!cursor_start'()

$P0 = find_dynamic_lex '$*QUOTE_START'
if null $P0 goto fail
start = $P0

$I0 = length start
$S0 = substr target, pos, $I0
unless $S0 == start goto fail
pos += $I0
cur.'!cursor_pass'(pos, 'starter')
fail:
.return (cur)
};
}

method stopper() {
Q:PIR {
.local pmc self, cur
.local string target, stop
.local int pos
self = find_lex 'self'

(cur, target, pos) = self.'!cursor_start'()

$P0 = find_dynamic_lex '$*QUOTE_STOP'
if null $P0 goto fail
stop = $P0

$I0 = length stop
$S0 = substr target, pos, $I0
unless $S0 == stop goto fail
pos += $I0
cur.'!cursor_pass'(pos, 'stopper')
fail:
.return (cur)
};
}

our method split_words(str $words) {
my @result;
my int $pos := 0;
my int $eos := nqp::chars($words);
my int $ws;
while ($pos := nqp::findnotcclass(pir::const::CCLASS_WHITESPACE, $words, $pos, $eos)) < $eos {
$ws := nqp::findcclass(pir::const::CCLASS_WHITESPACE, $words, $pos, $eos);
nqp::push(@result, nqp::substr($words, $pos, $ws - $pos));
$pos := $ws;
}
@result
}

=begin

=item EXPR(...)

An operator precedence parser.

=end

method EXPR(str $preclim = '', int :$noinfix = 0) {
Q:PIR {
.local pmc self, cur_class
self = find_lex 'self'
cur_class = find_lex '$cursor_class'

.local string preclim
.local int noinfix
preclim = find_lex '$preclim'
noinfix = find_lex '$noinfix'
.local pmc here
.local string tgt
.local int pos
(here, tgt, pos) = self.'!cursor_start'()

.local string termishrx
termishrx = 'termish'

.local pmc opstack, termstack
opstack = new ['ResizablePMCArray']
.lex '@opstack', opstack
termstack = new ['ResizablePMCArray']
.lex '@termstack', termstack

term_loop:
.local pmc termcur
repr_bind_attr_int here, cur_class, "$!pos", pos
termcur = here.termishrx()
pos = repr_get_attr_int termcur, cur_class, "$!pos"
repr_bind_attr_int here, cur_class, "$!pos", pos
if pos < 0 goto fail
.local pmc termish
termish = termcur.'MATCH'()

# interleave any prefix/postfix we might have found
.local pmc termOPER, prefixish, postfixish
termOPER = termish
termOPER_loop:
$I0 = exists termOPER['OPER']
unless $I0 goto termOPER_done
termOPER = termOPER['OPER']
goto termOPER_loop
termOPER_done:
prefixish = termOPER['prefixish']
postfixish = termOPER['postfixish']
if null prefixish goto prefix_done

prepostfix_loop:
unless prefixish goto prepostfix_done
unless postfixish goto prepostfix_done
.local pmc preO, postO
.local string preprec, postprec
$P0 = prefixish[0]
$P0 = $P0['OPER']
preO = $P0['O']
preprec = preO['prec']
$P0 = postfixish[-1]
$P0 = $P0['OPER']
postO = $P0['O']
postprec = postO['prec']
if postprec < preprec goto post_shift
if postprec > preprec goto pre_shift
$S0 = postO['uassoc']
if $S0 == 'right' goto pre_shift
post_shift:
$P0 = pop postfixish
push opstack, $P0
goto prepostfix_loop
pre_shift:
$P0 = shift prefixish
push opstack, $P0
goto prepostfix_loop
prepostfix_done:

prefix_loop:
unless prefixish goto prefix_done
$P0 = shift prefixish
push opstack, $P0
goto prefix_loop
prefix_done:
delete termish['prefixish']

postfix_loop:
if null postfixish goto postfix_done
unless postfixish goto postfix_done
$P0 = pop postfixish
push opstack, $P0
goto postfix_loop
postfix_done:
delete termish['postfixish']

$P0 = termish['term']
push termstack, $P0
if noinfix goto term_done

next_infix:
# Now see if we can fetch an infix operator
.local pmc wscur, infixcur, infix
# First, we need ws to match.
repr_bind_attr_int here, cur_class, "$!pos", pos
wscur = here.'ws'()
pos = repr_get_attr_int wscur, cur_class, '$!pos'
if pos < 0 goto term_done
repr_bind_attr_int here, cur_class, "$!pos", pos
# Next, try the infix itself.
infixcur = here.'infixish'()
pos = repr_get_attr_int infixcur, cur_class, '$!pos'
if pos < 0 goto term_done
infix = infixcur.'MATCH'()

# We got an infix.
.local pmc inO
$P0 = infix['OPER']
inO = $P0['O']
termishrx = inO['nextterm']
if termishrx goto have_termishrx
nonextterm:
termishrx = 'termish'
have_termishrx:

.local string inprec, inassoc, opprec, infake
inprec = inO['prec']
unless inprec goto err_inprec
if inprec < preclim goto term_done

$P0 = inO['sub']
if null $P0 goto subprec_done
inO['prec'] = $P0
subprec_done:

infake = inO['fake']

reduce_loop:
unless opstack goto reduce_gt_done
$P0 = opstack[-1]
$P0 = $P0['OPER']
$P0 = $P0['O']
opprec = $P0['prec']
unless opprec > inprec goto reduce_gt_done
self.'EXPR_reduce'(termstack, opstack)
goto reduce_loop
reduce_gt_done:

unless infake goto fake_done
push opstack, infix
self.'EXPR_reduce'(termstack, opstack)
goto next_infix # not really an infix, so keep trying
fake_done:
unless opprec == inprec goto reduce_done
# equal precedence, use associativity to decide
inassoc = inO['assoc']
unless inassoc == 'left' goto reduce_done
# left associative, reduce immediately
self.'EXPR_reduce'(termstack, opstack)
reduce_done:

push opstack, infix # The Shift
repr_bind_attr_int here, cur_class, "$!pos", pos
wscur = here.'ws'()
pos = repr_get_attr_int wscur, cur_class, '$!pos'
repr_bind_attr_int here, cur_class, "$!pos", pos
if pos < 0 goto fail
goto term_loop
term_done:

opstack_loop:
unless opstack goto opstack_done
self.'EXPR_reduce'(termstack, opstack)
goto opstack_loop
opstack_done:

expr_done:
.local pmc term
term = pop termstack
pos = here.'pos'()
here = self.'!cursor_start'()
here.'!cursor_pass'(pos)
repr_bind_attr_int here, cur_class, '$!pos', pos
setattribute here, cur_class, '$!match', term
here.'!reduce'('EXPR')
goto done

fail:
done:
.return (here)

err_internal:
$I0 = termstack
here.'panic'('Internal operator parser error, @termstack == ', $I0)
err_inprec:
infixcur.'panic'('Missing infixish operator precedence')
};
}

method EXPR_reduce($termstack, $opstack) {
Q:PIR {
.local pmc self, termstack, opstack
self = find_lex 'self'
termstack = find_lex '$termstack'
opstack = find_lex '$opstack'

.local pmc op, opOPER, opO
.local string opassoc
op = pop opstack
# Give it a fresh capture list, since we'll have assumed it has
# no positional captures and not taken them.
.local pmc cap_class
cap_class = find_lex 'NQPCapture'
$P0 = new ['ResizablePMCArray']
setattribute op, cap_class, '@!array', $P0
opOPER = op['OPER']
opO = opOPER['O']
$P0 = opO['assoc']
opassoc = $P0
if opassoc == 'unary' goto op_unary
if opassoc == 'list' goto op_list
op_infix:
.local pmc right, left
right = pop termstack
left = pop termstack
op[0] = left
op[1] = right
$P0 = opO['reducecheck']
if null $P0 goto op_infix_1
$S0 = $P0
self.$S0(op)
op_infix_1:
self.'!reduce_with_match'('EXPR', 'INFIX', op)
goto done

op_unary:
.local pmc arg, afrom, ofrom
arg = pop termstack
op[0] = arg
afrom = arg.'from'()
ofrom = op.'from'()
if afrom < ofrom goto op_postfix
op_prefix:
self.'!reduce_with_match'('EXPR', 'PREFIX', op)
goto done
op_postfix:
self.'!reduce_with_match'('EXPR', 'POSTFIX', op)
goto done

op_list:
.local string sym
sym = ''
$P0 = opOPER['sym']
if null $P0 goto op_list_1
sym = $P0
op_list_1:
arg = pop termstack
unshift op, arg
op_sym_loop:
unless opstack goto op_sym_done
$S0 = ''
$P0 = opstack[-1]
$P0 = $P0['OPER']
$P0 = $P0['sym']
if null $P0 goto op_sym_1
$S0 = $P0
op_sym_1:
if sym != $S0 goto op_sym_done
arg = pop termstack
unshift op, arg
$P0 = pop opstack
goto op_sym_loop
op_sym_done:
arg = pop termstack
unshift op, arg
self.'!reduce_with_match'('EXPR', 'LIST', op)
goto done

done:
push termstack, op
};
}

method ternary($match) {
$match[2] := $match[1];
$match[1] := $match{'infix'}{'EXPR'};
}

method MARKER(str $markname) {
my %markhash := Q:PIR {
%r = get_global '%!MARKHASH'
unless null %r goto have_markhash
%r = new ['Hash']
set_global '%!MARKHASH', %r
have_markhash:
};
my $cur := self."!cursor_start"();
$cur."!cursor_pass"(self.pos());
%markhash{$markname} := $cur;
}
method MARKED(str $markname) {
my %markhash := Q:PIR {
%r = get_global '%!MARKHASH'
unless null %r goto have_markhash
%r = new ['Hash']
set_global '%!MARKHASH', %r
have_markhash:
};
my $cur := %markhash{$markname};
unless nqp::istype($cur, NQPCursor) && $cur.pos() == self.pos() {
$cur := self."!cursor_start"();
}
$cur
}

method LANG($lang, $regex, *@args) {
my $lang_cursor := %*LANG{$lang}.'!cursor_init'(self.orig(), :p(self.pos()), :target(self.target()), :shared(self.'!shared'()));
if self.HOW.traced(self) {
$lang_cursor.HOW.trace-on($lang_cursor, self.HOW.trace_depth(self));
}
my $*ACTIONS := %*LANG{$lang ~ '-actions'};
$lang_cursor."$regex"(|@args);
    }
}
Something went wrong with that request. Please try again.