#!/usr/bin/perl
#Copyright 2007-9 Arthur S Goldstein
use Test::More tests => 13;
use Carp;
BEGIN { use_ok('Parse::Stallion') };

my %calculator_rules = (
 start_expression => A(
   'expression', 'end_of_string',
   E(sub {return $_[0]->{expression}})
  )
,
 expression => A( L(PF(sub{return 1})),
   'expression', 'plus_or_minus', 'number'
   )
,
end_of_string => L(
  qr/\z/
 ),
,
number => L(
  qr/\s*[+-]?(\d+(\.\d*)?|\.\d+)\s*/,
  E(sub{
   return $_[0];
  })
 ),
,
plus_or_minus => O(
  'plus', 'minus'
 ),
,
plus => L(
  qr/\s*\+\s*/
 ),
,
minus => L(
  qr/\s*\-\s*/
 ),
);

my $calculator_parser = new Parse::Stallion(
  \%calculator_rules,
  {start_rule => 'start_expression'});

eval { my $result = $calculator_parser->parse_and_evaluate("7+4"); };
like ($@, qr/^expression duplicated at position /,'invalid grammar 1');

my %empty_rules = (
 start_expression => A(
   'expression', 'end_of_string',
   E(sub {return $_[0]->{expression}}),
  )
,
 expression => A( L(PF(sub{return 1})),
   'empty', 'expression', 'number'
   )
,
end_of_string => L(
  qr/\z/
 ),
,
number => L(
  qr/\s*[+-]?(\d+(\.\d*)?|\.\d+)\s*/,
  E(sub{
   return $_[0];
  })
 )
,
empty => L(
  qr/^/
 )
,
);

my @pt;
my $empty_parser = new Parse::Stallion(
  \%empty_rules,
  {start_rule => 'start_expression'
  });

eval { my $result = $empty_parser->parse_and_evaluate( "7+4",
  {parse_trace => \@pt,
   max_steps => 100} ); };
like ($@, qr/^expression duplicated at position/,'invalid grammar 2');
#use Data::Dumper; print STDERR "pt is ".Dumper(\@pt)."\n";

my %zempty_rules = (
 start_expression => A(
   'expression', 'end_of_string',
   E(sub {return $_[0]->{expression}}),
  )
,
 expression => A(
   'nothing', 'expression', 'number'
   )
,
end_of_string => L(
  qr/\z/
 ),
,
number => L(
  qr/\s*[+-]?(\d+(\.\d*)?|\.\d+)\s*/,
  E(sub{
   return $_[0];
  })
 )
,
nothing => A('empty'),
empty => L(
  qr/^/
 )
,
);

my $zempty_parser = eval {new Parse::Stallion(
  \%zempty_rules,
  {start_rule => 'start_expression'
  })};
like ($@, qr/^Left recursion in grammar/,'invalid grammar 3');

my %third_calculator_rules = (
 start_expression => A(
   'expression', 'end_of_string',
   E(sub {return $_[0]->{expression}}),
  )
,
 expression => O(
 A('number', 'plus_or_minus', 'expression'),
'number',
   )
,
end_of_string => L(
  qr/\z/
 )
,
number => L(
  qr/\s*[+-]?(\d+(\.\d*)?|\.\d+)\s*/,
  E(sub{
   return $_[0];
  })
 )
,
plus_or_minus => O(
  'plus', 'minus'
 )
,
plus => L(
  qr/\s*\+\s*/
 )
,
minus => L(
  qr/\s*\-\s*/
 )
);

my $third_calculator_parser = new Parse::Stallion(
  \%third_calculator_rules,
  {start_rule => 'start_expression'});

#print STDERR "third calc\n";

$result = {};
my $x;
$x = $third_calculator_parser->parse_and_evaluate("7", {parse_info=>$result});
is ($result->{parse_succeeded}, 1, 'third calculator number');

$result = {};
$x = $third_calculator_parser->parse_and_evaluate("7+4", {parse_info=>$result});
is ($result->{parse_succeeded}, 1, 'third calculator number plus number');

my %bad_rule_set = (
 start_expression => A(
   'expression',
   E(sub {return $_[0]->{expression}})
  )
);

my $bad_parser = eval {new Parse::Stallion(
  \%bad_rule_set,
  { start_rule => 'start_expression'
})};

like ($@, qr/^Missing rules: Rule start_expression missing /,'missing rule');

my %bad_rule_set_2 = (
 start_expression => A(
   'expression'
  ),
 expression =>
   qr/AA/
  ,
 junk_rule =>
   qr/BB/
);

my $bad_parser_2 = eval {new Parse::Stallion(
  \%bad_rule_set_2,
   { start_rule => 'start_expression' })};

like ($@, qr/^Unreachable rules: No path to rule junk_rule/,'unreachable rule');

my %bad_rule_set_3 = (
 start__XZ__expression => A(
   'expression',
   E(sub {return $_[0]->{expression}})
  )
);

$bad_parser = eval {new Parse::Stallion( \%bad_rule_set_3)};

like ($@, qr/contains separator/,'separator named rule');

   my $one_grammar = {start => L(qr/1/)};
   my $parser = new Parse::Stallion($one_grammar);
   my $partial_parser = new Parse::Stallion($one_grammar, 
     {need_not_match_whole_string => 1});
   $one_results = $parser->parse_and_evaluate('12');  # does not parse
   $onep_results = $partial_parser->parse_and_evaluate('12');  # parses

is($one_results, undef, "no partial match");
is($onep_results, '1', "partial match");

$mult_grammar = {
  start => A(M('empty'), qr/1/),
  empty => qr/.*/};
my $mult_parser = new Parse::Stallion($mult_grammar);
$m_result = $mult_parser->parse_and_evaluate('1');
$m_result2 = $mult_parser->parse_and_evaluate('11');

is($m_result, '1', "multiple empty");
is($m_result2, undef, "multiple empty 2");

print "\nAll done\n";


