# A fairly complete (but almost certainly buggy) operator precedence parser # method EXPR (%preclim = %LOOSEST, :$stop = &stdstopper) { sub EXPR { my($cls,$Hpreclim,%args)=@_; $Hpreclim ||= $HLOOSEST; $args{stop} ||= \&stdstopper; # my $preclim = %preclim; my $preclim = $Hpreclim->{prec}; # my $inquote is context = 0; local $inquote = 0; # if m:p/ > / { # return; # } #XXX # my $prevop is context; # my %thisop is context; local $prevop; local $Hthisop; # my @termstack; # my @opstack; my @termstack; my @opstack; # push @opstack, %terminator; # (just a sentinel value) # push @termstack, $.expect_term(); push @opstack, $Hterminator; # (just a sentinel value) push @termstack, $cls->expect_term(); # my sub reduce () { my $reduce = sub { #my $op = pop @opstack; my $op = pop @opstack; #given $op { local $_ = $op->{assoc}; # when 'chain' { # my @chain; # push @chain, pop(@termstack); # push @chain, $op; # while @opstack { # last if $op ne @opstack[-1]; # push @chain, pop(@termstack); # push @chain, pop(@opstack); # } # push @chain, pop(@termstack); # $op = reverse @chain; # push @termstack, $op; # } { if($_ eq 'chain') { my @chain; push @chain, pop(@termstack); push @chain, $op; while (@opstack) { last if $op->{prec} ne $opstack[-1]{prec}; push @chain, pop(@termstack); push @chain, pop(@opstack)->{top}; } push @chain, pop(@termstack); $op->{top}{chain} = [reverse @chain]; push @termstack, $op->{top}; } # when 'list' { # my @list; # push @list, pop(@termstack); # while @opstack { # last if $op ne @opstack[-1]; # push @list, pop(@termstack); # pop(@opstack); # } # push @list, pop(@termstack); # $op = reverse @list; # push @termstack, $op; # } elsif($_ eq 'list') { my @list; push @list, pop(@termstack); while @opstack { last if $op->{top}{sym} ne $opstack[-1]{top}{sym}; push @list, pop(@termstack); pop(@opstack); } push @list, pop(@termstack); $op->{top}{list} = [reverse @list]; push @termstack, $op->{top}; } # default { # $op = pop @termstack; # $op = pop @termstack; # push @termstack, $op; # } else { $op->{top}{right} = pop @termstack; $op->{top}{left} = pop @termstack; push @termstack, $op->{top}; } } }; # while not m:p/ > / { #XXX { # %thisop = (); $Hthisop = {}; # my $infix := $.expect_tight_infix($preclim); my $infix := $cls->expect_tight_infix($preclim); # if not defined %thisop { # %thisop = %terminator; # } if(!defined $Hthisop->{prec}) { $Hthisop = $Hterminator; } # my Str $newprec = %thisop; my $newprec = $Hthisop->{prec}; # # Does new infix (or terminator) force any reductions? # while @opstack[-1] lt $newprec { # reduce(); # } # Does new infix (or terminator) force any reductions? while($opstack[-1]{prec} lt $newprec) { $reduce->(); } # # Not much point in reducing the sentinels... # last if $newprec lt $LOOSEST; # Not much point in reducing the sentinels... last if $newprec lt $LOOSEST; # # Equal precedence, so use associativity to decide. # if @opstack[-1] eq $newprec { # given %thisop { # when 'non' { panic(qq["$infix" is not associative]) } # when 'left' { reduce() } # reduce immediately # when 'right' | 'chain' { } # just shift # when 'list' { # if op differs reduce else shift # reduce() if %thisop !eqv @opstack[-1]; # } # default { panic(qq[Unknown associativity "$_" for "$infix"]) } # } # } # Equal precedence, so use associativity to decide. if($opstack[-1]{prec} eq $newprec) { local $_ = $Hthisop->{assoc}; { if($_ eq 'non') { panic(qq["$infix" is not associative]) } elsif($_ eq 'left') { $reduce->() } # reduce immediately elsif($_ eq 'right' || $_ eq 'chain') { } # just shift elsif($_ eq 'list') { # if op differs reduce else shift $reduce->() if $Hthisop=>{top}{sym} ne $opstack[-1]{top}{sym}; } else { panic(qq[Unknown associativity "$_" for "$infix"]) } } } # push @opstack, %thisop; push @opstack, $Hthisop; # if m:p/ > / { # fail("$infix.perl() is missing right term"); # } #XXX # %thisop = (); # push @termstack, $.expect_term(); $Hthisop = {}; push @termstack, $cls->expect_term(); } # reduce() if @termstack > 1; # @termstack == 1 or panic("Internal operator parser error"); # return @termstack[0]; $reduce->() if @termstack > 1; @termstack == 1 or panic("Internal operator parser error"); return $termstack[0]; }