#!/usr/bin/perl
#Copyright 2007-10 Arthur S Goldstein
use Test::More tests => 31;
BEGIN { use_ok('Parse::Stallion') };
#use Data::Dumper;
my %parsing_rules = (
start_expression => A(
'parse_expression', L(qr/\z/)
),
parse_expression => O(
'same_sized_lists','list_div_four', 'odd_leaf',
'and_part_or_test', 'or_test', 'multi_test'),
list_div_four => A(
'string_list',
E(sub {
if (@{$_[0]->{string_list}} % 4 != 0) {
return (undef, 1);
}
return $_[0]->{string_list};
})
),
same_sized_lists => A(
{list_one=>'string_list'}, 'middle', {list_two=>'string_list'},
E(sub {
if (scalar(@{$_[0]->{list_one}})
!= scalar(@{$_[0]->{list_two}})) {
return (undef, 1);
}
})
),
middle => L(
qr/\s+middle\s+/
),
string_value => L(qr/\w+/),
string_list => A(
'string_value', M(A('comma','string_value')),
E(sub {
return $_[0]->{string_value}})
),
comma => L(qr/\,/),
odd_leaf => L(qr/\w+/,E(sub{
$_ = shift;
if ($_ ne 'odd') {return (0,1)} return $_})),
and_part_or_test => A('theand', 'abc','dbf'),
or_test => O('abc','dbf',
E(sub {
#print STDERR "or_test\n";
#print STDERR Dumper(\@_);
my $or_in = shift;
($_) = values %$or_in;
#print STDERR "looking at ".$_."\n";
if (/bbb/) {return (0,1)} return $_})
),
abc => L(qr/a+b*c+/),
dbf => L(qr/d+b*f+/),
theand => L(qr/theand/),
multi_test => M('comma',2,0,
E(sub {
#use Data::Dumper;print STDERR "mt has ".Dumper(\@_);
if ($#{$_[0]->{comma}} != 4) {
return (0,1);
}
return (\@_,0);
})
),
);
my $pe_parser = new Parse::Stallion(
\%parsing_rules,
{
do_evaluation_in_parsing => 1,
start_rule => 'start_expression',
});
my $result;
my $x;
$x =
eval{$pe_parser->parse_and_evaluate("abc middle def", {parse_info=>$result={}})};
is ($result->{parse_succeeded},1, 'simple middle parse');
$x =
$pe_parser->parse_and_evaluate("a,bc middle de,f", {parse_info=>$result={}});
is ($result->{parse_succeeded},1, 'two list middle parse');
$x =
$pe_parser->parse_and_evaluate("a,bc middle def", {parse_info=>$result={}});
is ($result->{parse_succeeded},0, 'illegal middle parse');
#print STDERR "illmp ".$pe_parser->{parse_succeeded}."\n";
$x =
$pe_parser->parse_and_evaluate("a,bc,de,f", {parse_info=>$result={}});
is ($result->{parse_succeeded},1, 'legal list div 4');
$x =
eval{$pe_parser->parse_and_evaluate("a,bc,de,f,g", {parse_info=>$result={}})};
is ($result->{parse_succeeded},0, 'illegal list div 4');
$x = eval{$pe_parser->parse_and_evaluate("odd", {parse_info=>$result={}})};
is ($result->{parse_succeeded},1, 'odd leaf');
$x = eval{$pe_parser->parse_and_evaluate("even", {parse_info=>$result={}})};
is ($result->{parse_succeeded},0, 'even leaf');
$x = eval{$pe_parser->parse_and_evaluate("theandabcdbff", {parse_info=>$result={}})};
is ($result->{parse_succeeded},1, 'or and part');
$x = eval{$pe_parser->parse_and_evaluate("theandabcdbfg", {parse_info=>$result={}})};
is ($result->{parse_succeeded},0, 'or fail and part');
$x = eval{$pe_parser->parse_and_evaluate("abbc", {parse_info=>$result={}})};
is ($result->{parse_succeeded},1, 'or evaluation test');
$x = eval{$pe_parser->parse_and_evaluate("abbbc", {parse_info=>$result={}})};
is ($result->{parse_succeeded},0, 'or fail evaluation test');
#print STDERR "dbforzero\n";
$x = eval{$pe_parser->parse_and_evaluate("dbbf", {parse_info=>$result={}})};
is ($result->{parse_succeeded},1, 'dbf or evaluation test');
#print STDERR "dbfor\n";
$x = eval{$pe_parser->parse_and_evaluate("dbbbbf", {parse_info=>$result={}})};
is ($result->{parse_succeeded},0, 'dbf or fail evaluation test');
$x = eval{$pe_parser->parse_and_evaluate(",,,,,", {parse_info=>$result={}})};
is ($result->{parse_succeeded},1, 'multi comma test');
$x = $pe_parser->parse_and_evaluate(",,,,", {parse_info=>$result={}});
is ($result->{parse_succeeded},0, 'multi comma test parse succeed');
my $eval_pe_parser = new Parse::Stallion(
\%parsing_rules,
{
do_evaluation_in_parsing => 1,
start_rule => 'start_expression',
});
my @pt;
$x = $pe_parser->parse_and_evaluate("a,bc middle de,f", {parse_info=>$result={},
parse_trace => \@pt});
my @trace;
foreach my $tr (@pt) {
push @trace, $tr->{rule_name}, $tr->{position};
}
#use Data::Dumper;print STDERR "pt is ".Dumper($result->{parse_trace})."\n";
#use Data::Dumper;print STDERR "trace is ".Dumper(\@trace)."\n";
is_deeply
(\@trace,
[
'start_expression',
0,
'parse_expression',
0,
'same_sized_lists',
0,
'string_list',
0,
'string_list',
1,
'string_list__XZ__1',
1,
'string_list__XZ__2',
1,
'string_list__XZ__2',
2,
'string_list__XZ__2',
4,
'string_list__XZ__1',
4,
'string_list__XZ__2',
4,
'string_list__XZ__2',
4,
'string_list__XZ__1',
4,
'string_list',
4,
'same_sized_lists',
4,
'same_sized_lists',
12,
'string_list',
12,
'string_list',
14,
'string_list__XZ__1',
14,
'string_list__XZ__2',
14,
'string_list__XZ__2',
15,
'string_list__XZ__2',
16,
'string_list__XZ__1',
16,
'string_list__XZ__2',
16,
'string_list__XZ__2',
16,
'string_list__XZ__1',
16,
'string_list',
16,
'same_sized_lists',
16,
'parse_expression',
16,
'start_expression',
16,
'start_expression',
16
]
,'trace test');
my %multi_test_rules = (
start_expression => A(
'parse_expression', 'chars', L(qr/\z/))
,
parse_expression => M(
'somerepeat',
E(sub {return (undef, 1)})
),
somerepeat => L(
qr/./s
),
chars => L(
qr/.*/s
),
);
my $multi_test_parser = new Parse::Stallion(
\%multi_test_rules,
{
do_evaluation_in_parsing => 1,
start_rule => 'start_expression',
});
$x = $multi_test_parser->parse_and_evaluate("a,bc middle de,f", {parse_info=>$result={}});
#use Data::Dumper; print STDERR Dumper($result)."\n";
is ($result->{parse_succeeded}, 0, 'Always fail multiple rule');
# $aa_parser = new Parse::Stallion({
# rules_to_set_up_hash => {s => qr/aa/},
# start_rule => 's',
# end_of_parse_allowed => sub {return 1},
# });
#
# my ($results, $info) = $aa_parser->parse_and_evaluate('aab', {parse_info=>$result={}});
#
#is ($info->{unparsed}, 'b', 'aa parser b');
#
# $x = 'aabb';
# my $y = $aa_parser->parse_and_evaluate($x);
# is ($x, 'aabb', 'no change aa parser');
# is ($y, 'aa', 'no change y aa parser');
# $y = $aa_parser->parse_and_evaluate(\$x);
# is ($x, 'bb', 'change aa parser');
# is ($y, 'aa', 'change y aa parser');
# $x = 'aabb';
# $y = $aa_parser->parse_and_evaluate(\$x);
# is ($x, 'bb', 'change 2 aa parser');
# is ($y, 'aa', 'change 2 y aa parser');
our $u = '';
my %qr_test_rules = (
start_expression => A(
qr/aa/, {y=>qr/ab/}, qr/\z/,
E(sub {$u = $_[0]->{y}})
),
);
my $qr_test_parser = new Parse::Stallion(
\%qr_test_rules,
{ start_rule => 'start_expression',
});
$x
= $qr_test_parser->parse_and_evaluate("a,bc middle de,f", {parse_info=>$result={}});
is ($result->{parse_succeeded}, 0, 'Fail qr rule');
$x
= $qr_test_parser->parse_and_evaluate("aaab", {parse_info=>$result={}});
is ($result->{parse_succeeded}, 1, 'Succeed qr rule');
is ($u, 'ab', 'ab matched and aliased');
my %x_test_rules = (
start_expression => A('char', qr/.\z/)
,
char => L(qr/./, E(sub {my ($leaf, $parameters) = @_;
my $object_ref = $parameters->{parse_this_ref};
my $position = $parameters->{current_position};
pos $$object_ref = $position;
if (!($$object_ref =~ /\GX\z/g)) {
return (undef, 1)}
return;})),
);
my $x_test_parser = new Parse::Stallion(
\%x_test_rules,
{
do_evaluation_in_parsing => 1
});
$x
= $x_test_parser->parse_and_evaluate("aX", {parse_info=>$result={}});
is ($result->{parse_succeeded}, 1, 'look ahead on x');
$x
= $x_test_parser->parse_and_evaluate("aY", {parse_info=>$result={}});
is ($result->{parse_succeeded}, 0, 'look ahead on x not to parse');
#my %bad_and = (
# start => AND(qr/a/, PF(sub {return (1, undef, 0)}))
#);
#eval {my $bad_and_parser = new Parse::Stallion(\%bad_and);};
#like ($@, qr/Parse forward in rule/, 'parse forward not in leaf');
my %two_pf_and = (
start => AND(qr/a/,
L(PF(sub {my $parameters = shift;
my $current_position = $parameters->{current_position};
$parameters->{parent_node}->{x} = 2;
return 1, undef, 0;
})),
{f => L(PF(sub {my $parameters = shift;
my $current_position = $parameters->{current_position};
return 1, $parameters->{parent_node}->{x}+1, 0;
}))},
E(sub {return $_[0]->{f}}),
),
);
my $two_pf_and_parser = new Parse::Stallion(\%two_pf_and);
$result = $two_pf_and_parser->parse_and_evaluate('a');
is ($result, 3, 'Two pf and');
our $latest_node_hash;
our $latest_parse_hash;
sub increment_hashes {
#use Data::Dumper;print STDERR "ihp ".Dumper(\@_)."\n";
my $parameters = shift;
my $current_position = $parameters->{current_position};
$latest_node_hash = ++${$parameters->{__current_node_ref}}->{x};
$latest_parse_hash = ++$parameters->{x};
return 1, undef, 0;
}
my %check_hashes = (
start => A('other', 'deeper', qr/a/, L(PF(\&increment_hashes)),
L(PF(\&increment_hashes))),
other => L(PF(\&increment_hashes)),
deeper => A('other')
);
my $check_hashes_parser = new Parse::Stallion(\%check_hashes);
$result = $check_hashes_parser->parse_and_evaluate('a');
is ($latest_node_hash, 3, 'check hashes node');
is ($latest_parse_hash, 4, 'check hashes parse');
my %bad_leaf = (
start => L(qr/a/, PF(sub {return (1, undef, 0)}),
PF(sub {return 1}))
);
eval {my $bad_leaf_parser = new Parse::Stallion(\%bad_leaf);};
like ($@, qr/Rule start has more than one/, '2 parse forwards in leaf');
our $stored_parameters;
our $stored_node;
my %eval_arg_rules = (
start => A(qr/./, qr/./, E(
sub {
$stored_parameters = \@_;
$stored_node = $stored_parameters->[1]->{current_node};
}
))
);
my $eval_arg_parser = new Parse::Stallion(\%eval_arg_rules);
$result = $eval_arg_parser->parse_and_evaluate('ab');
$stored_parameters->[1]->{current_node} = $stored_node;
#use Data::Dumper;print Dumper($stored_parameters)." st\n";
my $check_parameters = [$stored_parameters->[0]];
$check_parameters->[1]->{parameters} =
$stored_parameters->[1]->{current_node}->{__parameters};
$check_parameters->[1]->{parse_this_ref} =
$stored_parameters->[1]->{parse_this_ref};
is_deeply($check_parameters,
[
{
'' => [
'a',
'b'
]
},
{
'parameters' => {'' => ['a','b']},
'parse_this_ref' => \'ab'
}
]
, 'params to eval');
my %evals_arg_rules = (
start => A(qr/./, qr/./, E(
sub {
$stored_parameters = \@_;
$stored_node = $stored_parameters->[1]->{current_node};
#use Data::Dumper;print STDERR Dumper(\@_)."\n";
}
), USE_STRING_MATCH)
);
my $evals_arg_parser = new Parse::Stallion(\%evals_arg_rules);
$result = $evals_arg_parser->parse_and_evaluate('ab');
$stored_parameters->[1]->{current_node} = $stored_node;
$check_parameters = [$stored_parameters->[0]];
$check_parameters->[1]->{parameters} =
$stored_parameters->[1]->{current_node}->{__parameters};
$check_parameters->[1]->{parse_this_ref} =
$stored_parameters->[1]->{parse_this_ref};
is_deeply(
$check_parameters,
[
'ab',
{
'parameters' => {
'' => [
'a',
'b'
]
},
'parse_this_ref' => \'ab',
}
]
, 'params to evals');
our $pb_stored_parameters;
our $pf_stored_parameters;
my %pf_arg_rules = (
start => A(qr/./,
L(PF(
sub {
$_[0]->{parent_node}->{xx} = 1;
return (1, 'nn', 0);
}
)),
L(PF(
sub {
#use Data::Dumper;print STDERR Dumper(\@_)." pf \n";
return (1, 'mmm', 0);
}
),
PB(
sub {
#use Data::Dumper;print STDERR Dumper(\@_)." pb \n";
return;
}
)),
L(PF(
sub {
#use Data::Dumper;print STDERR Dumper(\@_)." pf2 \n";
return (1, ['www'], 0);
}
),
PB(
sub {
#use Data::Dumper;print STDERR Dumper(\@_)." pb2 \n";
return;
}
)),
L(PF(
sub {
$pf_stored_parameters = \@_;
delete $pf_stored_parameters->[0]->{parser};
delete $pf_stored_parameters->[0]->{parent_node};
delete $pf_stored_parameters->[0]->{current_node};
delete $pf_stored_parameters->[0]->{__blocked};
delete $pf_stored_parameters->[0]->{parse_stallion};
delete $pf_stored_parameters->[0]->{__moving_forward_ref};
delete $pf_stored_parameters->[0]->{__tree};
delete $pf_stored_parameters->[0]->{__message_ref};
delete $pf_stored_parameters->[0]->{__steps_ref};
delete $pf_stored_parameters->[0]->{__current_position_ref};
delete $pf_stored_parameters->[0]->{__moving_down_ref};
delete $pf_stored_parameters->[0]->{__current_node_ref};
delete $pf_stored_parameters->[0]->{__current_node};
delete $pf_stored_parameters->[0]->{__current_node_name_ref};
delete $pf_stored_parameters->[0]->{__continue_forward_ref};
delete $pf_stored_parameters->[0]->{__tree_size_ref};
delete $pf_stored_parameters->[0]->{__current_rule_ref};
delete $pf_stored_parameters->[0]->{__position_tree_size};
delete $pf_stored_parameters->[0]->{__parse_trace_routine};
delete $pf_stored_parameters->[0]->{__bottom_up};
delete $pf_stored_parameters->[0]->{__initial_position};
delete $pf_stored_parameters->[0]->{__bottom_up_left_to_right};
delete $pf_stored_parameters->[0]->{__match_length};
delete $pf_stored_parameters->[0]->{__parse_this_length};
#use Data::Dumper;print STDERR Dumper(\@_)." pf3 \n";
is_deeply($pf_stored_parameters,
[
{
'rule_info' => undef,
'rule_name' => 'start__XZ__5',
'current_position' => 1,
'parse_this_ref' => \'ab'
}
]
, 'parse forward parameters with eval');
return (1, 'uuu', 0);
}
),
PB(
sub {
$pb_stored_parameters = \@_;
delete $pb_stored_parameters->[0]->{parser};
delete $pb_stored_parameters->[0]->{parent_node};
delete $pb_stored_parameters->[0]->{current_node};
delete $pf_stored_parameters->[0]->{__blocked};
delete $pf_stored_parameters->[0]->{parse_stallion};
delete $pf_stored_parameters->[0]->{__moving_forward_ref};
delete $pf_stored_parameters->[0]->{__tree};
delete $pf_stored_parameters->[0]->{__message_ref};
delete $pf_stored_parameters->[0]->{__steps_ref};
delete $pf_stored_parameters->[0]->{__current_position_ref};
delete $pf_stored_parameters->[0]->{__moving_down_ref};
delete $pf_stored_parameters->[0]->{__current_node_ref};
delete $pf_stored_parameters->[0]->{__current_node};
delete $pf_stored_parameters->[0]->{__current_node_name_ref};
delete $pf_stored_parameters->[0]->{__continue_forward_ref};
delete $pf_stored_parameters->[0]->{__tree_size_ref};
delete $pf_stored_parameters->[0]->{__current_rule_ref};
delete $pf_stored_parameters->[0]->{__position_tree_size};
delete $pf_stored_parameters->[0]->{__parse_trace_routine};
delete $pf_stored_parameters->[0]->{__bottom_up};
#use Data::Dumper;print STDERR Dumper(\@_)." pb3 \n";
is_deeply($pb_stored_parameters,
[
{
'rule_info' => undef,
'parse_this_ref' => \'ab',
'rule_name' => 'start__XZ__5',
'parse_match' => 'uuu',
'current_position' => 1,
}
]
, 'parse backtrack parameters with eval');
return;
}
)),
qr/x/)
);
my $pf_arg_parser = new Parse::Stallion(\%pf_arg_rules,
{do_evaluation_in_parsing => 1});
$result = $pf_arg_parser->parse_and_evaluate('ab');
#5 pf with eval in parsing (vs without #5b)
print "\nAll done\n";