#~/usr/bin/perl package P6RulesInP5::OpTable; use base qw/Class::Accessor/; use strict; use warnings; use constant PGE_OPTABLE_EMPTY => 0; use constant PGE_OPTABLE_TERM => 1; use constant PGE_OPTABLE_POSTFIX => 2; use constant PGE_OPTABLE_CLOSE => 3; use constant PGE_OPTABLE_PREFIX => 4; use constant PGE_OPTABLE_INFIX => 5; use constant PGE_OPTABLE_TERNARY => 6; use constant PGE_OPTABLE_POSTCIRCUMFIX => 7; use constant PGE_OPTABLE_CIRCUMFIX => 8; BEGIN { __PACKAGE__->mk_accessors(qw/ tokenTable termTable operatorTable whitespaceTermTable whitespaceOperatorTable/) } sub new { my $class = shift; my $object = bless {}, $class; $object->tokenTable({}) ; $object->termTable({}); $object->operatorTable({}); $object->whitespaceTermTable({}); $object->whitespaceOperatorTable({}); $object; } sub printHash { my ($hash, $desc) = @_; while ( my ($k,$v) = each %$hash ) { print "$desc: $k => $v\n"; } if ( not scalar keys %$hash ) { print "$desc: empty\n"; } } sub addToken { my ($self, $name, $relativePrecedence, $options, $match) = @_; #print "name: $name - rp: $relativePrecedence - options: $options - match: $match\n" ; my $equivalence = "="; $match = "P6RulesInP5::Match" if ! defined $match; $options = "left" if not $options; if ( $relativePrecedence and $relativePrecedence ne "" ) { if ( substr $relativePrecedence, 0, 1 =~ m/[\=\<\>]/ ) { my $tokenTableKey = substr $relativePrecedence, -1, 0; $equivalence = $self->tokenTable()->{$tokenTableKey}{equiv}; } else { $equivalence = $self->tokenTable()->{$relativePrecedence}{equiv}; } } my $noWhitespace = 0; $noWhitespace = 1 if ((index $options, "nows") >= 0); #print "name: $name - eq: $equivalence - rp: $relativePrecedence - options: $options - match: $match\n" ; my %token = ( name => $name, options => $options, equiv => $equivalence, match => $match, arity => 1 ); my @nameSplit = split /:/, $name; #print "@nameSplit ", length @nameSplit, " ", $#nameSplit, "\n"; my @spacePresent = split / /, $nameSplit[$#nameSplit]; if ( @spacePresent > 1 ) { $token{token2} = $spacePresent[1]; my %tokenClone = %token; $tokenClone{syncat} = PGE_OPTABLE_CLOSE; $self->operatorTable()->{$spacePresent[1]} = %tokenClone; $self->whitespaceOperatorTable()->{$spacePresent[1]} = %tokenClone; } $token{token1} = $nameSplit[$#nameSplit]; $self->tokenTable()->{$name} = \%token; my $syncat = $nameSplit[0]; if ( $syncat eq "infix" ) { $token{syncat} = PGE_OPTABLE_INFIX; $token{arity} = 2; } elsif ( $syncat eq "postfix" ) { $token{syncat} = PGE_OPTABLE_POSTFIX; } elsif ( $syncat eq "circumfix" ) { $token{syncat} = PGE_OPTABLE_CIRCUMFIX; } elsif ( $syncat eq "prefix" ) { $token{syncat} = PGE_OPTABLE_PREFIX; } elsif ( $syncat eq "postcircumfix" ) { $token{syncat} = PGE_OPTABLE_POSTCIRCUMFIX; $token{arity} = 2; } elsif ( $syncat eq "ternary" ) { $token{syncat} = PGE_OPTABLE_TERNARY; $token{arity} = 3; } elsif ( $syncat eq "close" ) { $token{syncat} = PGE_OPTABLE_CLOSE; $token{arity} = 0; } else { $token{syncat} = PGE_OPTABLE_TERM; } } sub parse { my ($self, $matchObj) = @_; my @termStack = []; my @operatorStack = []; my @tokenStack = []; my $termEmpty = $self->termTable()->{""}; my $operatorEmpty = $self->operatorTable()->{""}; ($matchObj, my $target, my $matchObjFrom, my $matchObjPos) = Parser::Rule::Match::newfrom($matchObj, 0); my $position = $matchObjPos; my $lastPosition = length $target; my @bsrStack = []; #XXX my $operator; expect_term: goto null_term if $position >= $lastPosition; my $currentTermTable = $self->whitespaceTermTable(); (substr $target, $position, $lastPosition) =~ m/[^\s]/; my $whitespacePosition = $-[0]; $currentTermTable = $self->termTable() if ($whitespacePosition <= $position); expect_term_1: my $key = $$currentTermTable->lkey($target, $whitespacePosition); my $token = $$currentTermTable{$key}; unless ($token) { push @bsrStack, "bsr_token_match_1"; goto token_match; bsr_token_match_1: goto found_term if $operator; goto null_term if ($key eq ""); } expect_term_empty: if ( $termEmpty ) { $token = $termEmpty; $key = ""; $whitespacePosition = $position; push @bsrStack, "bsr_token_match_2"; goto token_match; bsr_token_match_2: goto found_term if $operator; } null_term: if (@tokenStack) { my $top = $tokenStack[-1]; goto missing_term if ((index $$top["opts"], "nullterm") < 1); ($operator, my $stringTemp, my $tempPos, my $tempPos2) = newfrom( $matchObj, $whitespacePosition, "PGE::Match"); my $temp2 = $whitespacePosition; push @termStack, $operator; goto expect_operator; } else { goto missing_term; } missing_term: push @termStack, newfrom( $matchObj, $whitespacePosition, "PGE::Match" ); goto end; found_term: my $tokenCat = $$token{syncat}; $position = $operator->to(); goto oper_shift if ( $tokenCat == PGE_OPTABLE_PREFIX ); goto oper_shift if ( $tokenCat == PGE_OPTABLE_CIRCUMFIX ); push @termStack, $operator; expect_oper: goto end if ( $position >= $lastPosition ); my $currentOperatorTable = $self->whitespaceOperatorTable(); (substr $target, $position, $lastPosition) =~ m/[^\s]/; $whitespacePosition = $-[0]; $currentOperatorTable = $self->operatorTable() if ( $whitespacePosition <= $position ); expect_oper_1: $key = $$currentOperatorTable->lkey($target, $whitespacePosition); $token = $$currentOperatorTable{$key}; if ( $token ) { push @bsrStack, "bsr_token_match2_1"; goto token_match; bsr_token_match2_1: goto found_oper if $operator; } expect_oper_empty: goto end unless $operatorEmpty; $token = $operatorEmpty; $key = ""; $whitespacePosition = $position; push @bsrStack, "bsr_token_match_3"; goto token_match; bsr_token_match_3: goto end unless $operator; found_oper: $tokenCat = $$token{syncat}; shift_reduce: my $topcat = PGE_OPTABLE_EMPTY; if ( @tokenStack <= 0 ) { goto end if $tokenCat == PGE_OPTABLE_CLOSE; goto oper_shift; } shift_reduce_1: my $top = $tokenStack[-1]; $topcat = $$top{syncat}; goto oper_reduce if ( $topcat == PGE_OPTABLE_POSTFIX ); goto oper_close if ( $topcat == PGE_OPTABLE_CLOSE ); goto oper_shift if ( $topcat == PGE_OPTABLE_POSTCIRCUMFIX ); #$temp1 = $$token{eqiv}; #$temp2 = $$top{eqiv}; goto oper_shift if $$token{eqiv} > $$top{eqiv}; goto shift_reduce_2 if ( $topcat == PGE_OPTABLE_TERNARY ); goto ternary_error if ( $topcat == PGE_OPTABLE_TERNARY ); goto oper_shift; shift_reduce2: #if ( $temp1 >= $temp2 ) if ( $$token{eqiv} > $$top{eqiv} ) { goto oper_shift if ( (index $$top{opts}, "right" ) >=0 ); } oper_reduce: push @bsrStack, "bsr_reduce_1"; goto reduce; bsr_reduce_1: goto shift_reduce; oper_close: goto oper_reduce if $topcat < PGE_OPTABLE_TERNARY; goto end if $key ne $$top{token2}; oper_shift: push @tokenStack, $token; push @operatorStack, $operator; $position = $operator->to(); goto expect_term if ( $tokenCat >= PGE_OPTABLE_PREFIX ); goto expect_oper if ( $tokenCat >= PGE_OPTABLE_POSTFIX ); goto expect_term if ( $tokenCat >= PGE_OPTABLE_TERNARY ); goto expect_oper; reduce: my $temp1 = pop @tokenStack; my $temp2 = $$temp1{syncat}; if ( $temp2 == PGE_OPTABLE_CLOSE ) { $temp1 = pop @tokenStack; $temp2 = pop @operatorStack; } reduce_1: my $arity = $$temp1{arity}; $temp1 = pop @operatorStack; reduce_args: goto reduce_saveterm if $arity < 1; $temp2 = pop @termStack; if ( $temp2 ) { $arity--; $$temp1[$arity] = $temp2; goto reduce_args; } reduce_backtrack: $position = $temp1->from(); goto reduce_end if $arity > 1; $temp1 = $temp2; reduce_save_term: push @termStack, $temp1; reduce_end: goto (pop @bsrStack); token_match: $matchObjPos = $whitespacePosition; my $match = $$token{match}; #TODO == should be isa goto tok_match_sub if ( $match == "Sub" ); ($operator) = newfrom($matchObj, $whitespacePosition, $match); #What does this line #$temp1 = length $key + $whitespacePosition; goto tok_match_end; tok_match_sub: $operator = &$$match($matchObj); tok_match_end: $$operator{type} = $$token{name}; goto (pop @bsrStack); end: if ( @tokenStack >= 1 ) { push @bsrStack, "bsr_reduce_2"; goto reduce; bsr_reduce_2: goto end; } end_1: goto end_2 if @termStack < 1; $temp1 = pop @termStack; goto end_2 unless $temp1; $$matchObj{expr} = $temp1; $matchObjPos = $position; return ( $matchObj ); end_2: $matchObjPos = -1; return ( $matchObj ); ternary_error: $matchObjPos = -1; die "Missing ternary close at offset $whitespacePosition \n"; return ( $matchObj ); } __PACKAGE__;