#!perl # Copyright 2012 Jeffrey Kegler # This file is part of Marpa::XS. Marpa::XS is free software: you can # redistribute it and/or modify it under the terms of the GNU Lesser # General Public License as published by the Free Software Foundation, # either version 3 of the License, or (at your option) any later version. # # Marpa::XS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser # General Public License along with Marpa::XS. If not, see # http://www.gnu.org/licenses/. # the example grammar in Aycock/Horspool "Practical Earley Parsing", # _The Computer Journal_, Vol. 45, No. 6, pp. 620-630, # in its "NNF" form use 5.010; use strict; use warnings; use Test::More tests => 31; use lib 'tool/lib'; use Marpa::XS::Test; BEGIN { Test::More::use_ok('Marpa::XS'); } ## no critic (Subroutines::RequireArgUnpacking) sub default_action { shift; my $v_count = scalar @_; return q{} if $v_count <= 0; return $_[0] if $v_count == 1; return '(' . ( join q{;}, @_ ) . ')'; } ## end sub default_action ## use critic my $grammar = Marpa::XS::Grammar->new( { start => 'S', strip => 0, rules => [ [ 'S', [qw/A A A A/] ], [ 'A', [qw/a/] ], [ 'A', [qw/E/] ], ['E'], ], default_null_value => q{}, default_action => 'main::default_action', } ); $grammar->set( { terminals => ['a'], } ); $grammar->precompute(); Marpa::XS::Test::is( $grammar->show_rules, <<'EOS', 'Aycock/Horspool Rules' ); 0: S -> A A A A /* !used */ 1: A -> a 2: A -> E /* !used */ 3: E -> /* empty !used */ 4: S -> A S[R0:1] /* vrhs real=1 */ 5: S -> A A[] A[] A[] 6: S -> A[] S[R0:1] /* vrhs real=1 */ 7: S[R0:1] -> A S[R0:2] /* vlhs vrhs real=1 */ 8: S[R0:1] -> A A[] A[] /* vlhs real=3 */ 9: S[R0:1] -> A[] S[R0:2] /* vlhs vrhs real=1 */ 10: S[R0:2] -> A A /* vlhs real=2 */ 11: S[R0:2] -> A A[] /* vlhs real=2 */ 12: S[R0:2] -> A[] A /* vlhs real=2 */ 13: S['] -> S /* vlhs real=1 */ 14: S['][] -> /* empty vlhs real=1 */ EOS Marpa::XS::Test::is( $grammar->show_symbols, <<'EOS', 'Aycock/Horspool Symbols' ); 0: S, lhs=[0 4 5 6] rhs=[13] 1: A, lhs=[1 2] rhs=[0 4 5 7 8 10 11 12] 2: a, lhs=[] rhs=[1] terminal 3: E, lhs=[3] rhs=[2] nullable nulling 4: S[], lhs=[] rhs=[] nullable nulling 5: A[], lhs=[] rhs=[5 6 8 9 11 12] nullable nulling 6: S[R0:1], lhs=[7 8 9] rhs=[4 6] 7: S[R0:2], lhs=[10 11 12] rhs=[7 9] 8: S['], lhs=[13] rhs=[] 9: S['][], lhs=[14] rhs=[] nullable nulling EOS Marpa::XS::Test::is( $grammar->show_nullable_symbols, q{A[] E S['][] S[]}, 'Aycock/Horspool Nullable Symbols' ); Marpa::XS::Test::is( $grammar->show_nulling_symbols, q{A[] E S['][] S[]}, 'Aycock/Horspool Nulling Symbols' ); Marpa::XS::Test::is( $grammar->show_productive_symbols, q{A A[] E S S['] S['][] S[R0:1] S[R0:2] S[] a}, 'Aycock/Horspool Productive Symbols' ); Marpa::XS::Test::is( $grammar->show_accessible_symbols, q{A A[] E S S['] S['][] S[R0:1] S[R0:2] S[] a}, 'Aycock/Horspool Accessible Symbols' ); if ( defined $Marpa::XS::VERSION ) { Marpa::XS::Test::is( $grammar->show_AHFA_items(), <<'EOS', 'Aycock/Horspool AHFA Items' ); AHFA item 0: sort = 9; postdot = "a" A -> . a AHFA item 1: sort = 14; completion A -> a . AHFA item 2: sort = 1; postdot = "A" S -> . A S[R0:1] AHFA item 3: sort = 10; postdot = "S[R0:1]" S -> A . S[R0:1] AHFA item 4: sort = 15; completion S -> A S[R0:1] . AHFA item 5: sort = 2; postdot = "A" S -> . A A[] A[] A[] AHFA item 6: sort = 16; completion S -> A A[] A[] A[] . AHFA item 7: sort = 11; postdot = "S[R0:1]" S -> A[] . S[R0:1] AHFA item 8: sort = 17; completion S -> A[] S[R0:1] . AHFA item 9: sort = 3; postdot = "A" S[R0:1] -> . A S[R0:2] AHFA item 10: sort = 12; postdot = "S[R0:2]" S[R0:1] -> A . S[R0:2] AHFA item 11: sort = 18; completion S[R0:1] -> A S[R0:2] . AHFA item 12: sort = 4; postdot = "A" S[R0:1] -> . A A[] A[] AHFA item 13: sort = 19; completion S[R0:1] -> A A[] A[] . AHFA item 14: sort = 13; postdot = "S[R0:2]" S[R0:1] -> A[] . S[R0:2] AHFA item 15: sort = 20; completion S[R0:1] -> A[] S[R0:2] . AHFA item 16: sort = 5; postdot = "A" S[R0:2] -> . A A AHFA item 17: sort = 6; postdot = "A" S[R0:2] -> A . A AHFA item 18: sort = 21; completion S[R0:2] -> A A . AHFA item 19: sort = 7; postdot = "A" S[R0:2] -> . A A[] AHFA item 20: sort = 22; completion S[R0:2] -> A A[] . AHFA item 21: sort = 8; postdot = "A" S[R0:2] -> A[] . A AHFA item 22: sort = 23; completion S[R0:2] -> A[] A . AHFA item 23: sort = 0; postdot = "S" S['] -> . S AHFA item 24: sort = 24; completion S['] -> S . AHFA item 25: sort = 25; completion S['][] -> . EOS } ## end if ( defined $Marpa::XS::VERSION ) if ( defined $Marpa::PP::VERSION ) { Marpa::XS::Test::is( $grammar->show_NFA, <<'EOS', 'Aycock/Horspool NFA' ); S0: /* empty */ empty => S33 S35 S1: A -> . a => S2 S2: A -> a . S3: S -> . A S[R0:1] empty => S1 => S4 S4: S -> A . S[R0:1] empty => S14 S17 S21 => S5 S5: S -> A S[R0:1] . S6: S -> . A A[] A[] A[] empty => S1 => S7 S7: S -> A . A[] A[] A[] at_nulling => S8 S8: S -> A A[] . A[] A[] at_nulling => S9 S9: S -> A A[] A[] . A[] at_nulling => S10 S10: S -> A A[] A[] A[] . S11: S -> . A[] S[R0:1] at_nulling => S12 S12: S -> A[] . S[R0:1] empty => S14 S17 S21 => S13 S13: S -> A[] S[R0:1] . S14: S[R0:1] -> . A S[R0:2] empty => S1 => S15 S15: S[R0:1] -> A . S[R0:2] empty => S24 S27 S30 => S16 S16: S[R0:1] -> A S[R0:2] . S17: S[R0:1] -> . A A[] A[] empty => S1 => S18 S18: S[R0:1] -> A . A[] A[] at_nulling => S19 S19: S[R0:1] -> A A[] . A[] at_nulling => S20 S20: S[R0:1] -> A A[] A[] . S21: S[R0:1] -> . A[] S[R0:2] at_nulling => S22 S22: S[R0:1] -> A[] . S[R0:2] empty => S24 S27 S30 => S23 S23: S[R0:1] -> A[] S[R0:2] . S24: S[R0:2] -> . A A empty => S1 => S25 S25: S[R0:2] -> A . A empty => S1 => S26 S26: S[R0:2] -> A A . S27: S[R0:2] -> . A A[] empty => S1 => S28 S28: S[R0:2] -> A . A[] at_nulling => S29 S29: S[R0:2] -> A A[] . S30: S[R0:2] -> . A[] A at_nulling => S31 S31: S[R0:2] -> A[] . A empty => S1 => S32 S32: S[R0:2] -> A[] A . S33: S['] -> . S empty => S3 S6 S11 => S34 S34: S['] -> S . S35: S['][] -> . EOS } ## end if ( defined $Marpa::PP::VERSION ) Marpa::XS::Test::is( $grammar->show_AHFA, <<'EOS', 'Aycock/Horspool AHFA' ); * S0: S['] -> . S S['][] -> . => S2; leo(S[']) * S1: predict A -> . a S -> . A S[R0:1] S -> . A A[] A[] A[] S -> A[] . S[R0:1] S[R0:1] -> . A S[R0:2] S[R0:1] -> . A A[] A[] S[R0:1] -> A[] . S[R0:2] S[R0:2] -> . A A S[R0:2] -> . A A[] S[R0:2] -> A[] . A => S3; S4 => S6; leo(S) => S7; leo(S[R0:1]) => S5 * S2: leo-c S['] -> S . * S3: S -> A . S[R0:1] S -> A A[] A[] A[] . S[R0:1] -> A . S[R0:2] S[R0:1] -> A A[] A[] . S[R0:2] -> A . A S[R0:2] -> A A[] . S[R0:2] -> A[] A . => S8; leo(S[R0:2]) => S9; leo(S) => S10; leo(S[R0:1]) * S4: predict A -> . a S[R0:1] -> . A S[R0:2] S[R0:1] -> . A A[] A[] S[R0:1] -> A[] . S[R0:2] S[R0:2] -> . A A S[R0:2] -> . A A[] S[R0:2] -> A[] . A => S11; S12 => S7; leo(S[R0:1]) => S5 * S5: A -> a . * S6: leo-c S -> A[] S[R0:1] . * S7: leo-c S[R0:1] -> A[] S[R0:2] . * S8: leo-c S[R0:2] -> A A . * S9: leo-c S -> A S[R0:1] . * S10: leo-c S[R0:1] -> A S[R0:2] . * S11: S[R0:1] -> A . S[R0:2] S[R0:1] -> A A[] A[] . S[R0:2] -> A . A S[R0:2] -> A A[] . S[R0:2] -> A[] A . => S8; leo(S[R0:2]) => S10; leo(S[R0:1]) * S12: predict A -> . a S[R0:2] -> . A A S[R0:2] -> . A A[] S[R0:2] -> A[] . A => S13; S14 => S5 * S13: S[R0:2] -> A . A S[R0:2] -> A A[] . S[R0:2] -> A[] A . => S8; leo(S[R0:2]) * S14: predict A -> . a => S5 EOS my $recce = Marpa::XS::Recognizer->new( { grammar => $grammar } ); my @set = ( <<'END_OF_SET0', <<'END_OF_SET1', <<'END_OF_SET2', <<'END_OF_SET3', <<'END_OF_SET4', ); Earley Set 0 S0@0-0 S1@0-0 END_OF_SET0 Earley Set 1 S2@0-1 [p=S0@0-0; c=S3@0-1] [p=S0@0-0; c=S6@0-1] S3@0-1 [p=S1@0-0; c=S5@0-1] S5@0-1 [p=S1@0-0; s=a; t=\'a'] S6@0-1 [p=S1@0-0; c=S3@0-1] [p=S1@0-0; c=S7@0-1] S7@0-1 [p=S1@0-0; c=S3@0-1] S4@1-1 L6@1 ["S[R0:1]"; S3@0-1] END_OF_SET1 Earley Set 2 S2@0-2 [p=S0@0-0; c=S6@0-2] [p=S0@0-0; c=S9@0-2] S6@0-2 [p=S1@0-0; c=S7@0-2] [p=S1@0-0; c=S10@0-2] S7@0-2 [p=S1@0-0; c=S8@0-2] S8@0-2 [p=S3@0-1; c=S5@1-2] S9@0-2 [l=L6@1; c=S7@1-2] [l=L6@1; c=S11@1-2] S10@0-2 [p=S3@0-1; c=S11@1-2] S5@1-2 [p=S4@1-1; s=a; t=\'a'] S7@1-2 [p=S4@1-1; c=S11@1-2] S11@1-2 [p=S4@1-1; c=S5@1-2] S12@2-2 L7@2 ["S[R0:2]"; L6@1; S11@1-2] END_OF_SET2 Earley Set 3 S2@0-3 [p=S0@0-0; c=S6@0-3] [p=S0@0-0; c=S9@0-3] S6@0-3 [p=S1@0-0; c=S10@0-3] S9@0-3 [l=L6@1; c=S7@1-3] [l=L7@2; c=S13@2-3] S10@0-3 [p=S3@0-1; c=S8@1-3] S7@1-3 [p=S4@1-1; c=S8@1-3] S8@1-3 [p=S11@1-2; c=S5@2-3] S5@2-3 [p=S12@2-2; s=a; t=\'a'] S13@2-3 [p=S12@2-2; c=S5@2-3] S14@3-3 L1@3 ["A"; L7@2; S13@2-3] END_OF_SET3 Earley Set 4 S2@0-4 [p=S0@0-0; c=S9@0-4] S9@0-4 [l=L1@3; c=S5@3-4] S5@3-4 [p=S14@3-3; s=a; t=\'a'] END_OF_SET4 my $input_length = 4; EARLEME: for my $earleme ( 0 .. $input_length + 1 ) { my $furthest = my $last_completed = List::Util::min( $earleme, $input_length ); Marpa::XS::Test::is( $recce->show_earley_sets(1), "Last Completed: $last_completed; Furthest: $furthest\n" . ( join q{}, @set[ 0 .. $furthest ] ), "Aycock/Horspool Parse Status at earleme $earleme" ); next EARLEME if $earleme == $input_length; last EARLEME if $earleme > $input_length; $recce->read( 'a', 'a' ); } ## end for my $earleme ( 0 .. $input_length + 1 ) my @expected = map { +{ map { ( $_ => 1 ) } @{$_} } } [q{}], [qw( (a;;;) (;a;;) (;;a;) (;;;a) )], [qw( (a;a;;) (a;;a;) (a;;;a) (;a;a;) (;a;;a) (;;a;a) )], [qw( (a;a;a;) (a;a;;a) (a;;a;a) (;a;a;a) )], ['(a;a;a;a)']; $recce->set( { max_parses => 20 } ); for my $i ( 0 .. $input_length ) { $recce->reset_evaluation(); $recce->set( { end => $i } ); my $expected = $expected[$i]; while ( my $value_ref = $recce->value() ) { my $value = $value_ref ? ${$value_ref} : 'No parse'; if ( defined $expected->{$value} ) { delete $expected->{$value}; Test::More::pass(qq{Expected result for length=$i, "$value"}); } else { Test::More::fail(qq{Unexpected result for length=$i, "$value"}); } } ## end while ( my $value_ref = $recce->value() ) for my $value ( keys %{$expected} ) { Test::More::fail(qq{Missing result for length=$i, "$value"}); } } ## end for my $i ( 0 .. $input_length ) 1; # In case used as "do" file # Local Variables: # mode: cperl # cperl-indent-level: 4 # fill-column: 100 # End: # vim: expandtab shiftwidth=4: