The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl
# Marpa::R3 is Copyright (C) 2016, Jeffrey Kegler.
#
# This module is free software; you can redistribute it and/or modify it
# under the same terms as Perl 5.10.1. For more details, see the full text
# of the licenses in the directory LICENSES.
#
# This program is distributed in the hope that it will be
# useful, but it is provided “as is” and without any express
# or implied warranties. For details, see the full text of
# of the licenses in the directory LICENSES.

# Example of use of discard events

use 5.010001;
use strict;
use warnings;
use Test::More tests => 1;
use English qw( -no_match_vars );
use Scalar::Util;
use POSIX qw(setlocale LC_ALL);

POSIX::setlocale(LC_ALL, "C");

use lib 'inc';
use Marpa::R3::Test;

## no critic (ErrorHandling::RequireCarping);

# Marpa::R3::Display
# name: SLIF discard event synopsis

use Marpa::R3;

my $grammar = Marpa::R3::Scanless::G->new(
    {
        source        => \(<<'END_OF_SOURCE'),
:default ::= action => [g1start, g1length, values]

Script ::= Expression+ separator => comma action => do_expression
comma ~ [,]
Expression ::= Subexpression action => [g1start,g1length,value]
Subexpression ::=
    Number action => do_number
    | ('(') Subexpression (')') assoc => group action => do_paren
   || Subexpression ('**') Subexpression assoc => right action => do_power
   || Subexpression ('*') Subexpression  action => do_multiply
    | Subexpression ('/') Subexpression  action => do_divide
   || Subexpression ('+') Subexpression  action => do_add
    | Subexpression ('-') Subexpression  action => do_subtract

Number ~ [\d]+
:discard ~ whitespace event => ws
whitespace ~ [\s]+
# allow comments
:discard ~ <hash comment> event => comment
<hash comment> ~ <terminated hash comment> | <unterminated
   final hash comment>
<terminated hash comment> ~ '#' <hash comment body> <vertical space char>
<unterminated final hash comment> ~ '#' <hash comment body>
<hash comment body> ~ <hash comment char>*
<vertical space char> ~ [\x{A}\x{B}\x{C}\x{D}\x{2028}\x{2029}]
<hash comment char> ~ [^\x{A}\x{B}\x{C}\x{D}\x{2028}\x{2029}]
END_OF_SOURCE
    }
);

# Marpa::R3::Display::End

my $input = <<'EOI';
42*2+7/3, 42*(2+7)/3, 2**7-3, 2**(7-3),
# Hardy-Ramanujan number
1729, 1**3+12**3, 9**3+10**3,
# Next highest taxicab number
# note: weird spacing is deliberate
87539319, 167**3+ 436**3,228**3 + 423**3,255**3+414**3
EOI

my $output_re =
            qr/\A 86[.]3\d+ \s+ 126 \s+ 125 \s+ 16 \s+ 1729 \s+ 1729 \s+ 1729 .*\z/xms;


    my $length = length $input;
    my $recce = Marpa::R3::Scanless::R->new( { grammar => $grammar,
    semantics_package => 'My_Nodes',
    } );

    my $pos = $recce->read(\$input);

    my @events = ();
    READ: while (1) {

        my @actual_events = ();

        EVENT:
        for my $event ( @{ $recce->events() } ) {
            my ( $name, @other_stuff ) = @{$event};
            # say STDERR 'Event received!!! -- ', Data::Dumper::Dumper($event);
            push @events, $event;
        }

        last READ if $pos >= $length;
        $pos = $recce->resume($pos);
    } ## end READ: while (1)

    my $value_ref = $recce->value();
    if ( not defined $value_ref ) {
        die "No parse was found, after reading the entire input\n";
    }

    my $event_ix = 0;
    my $result = '';
    for my $expression (@{${$value_ref}}) {
        my ($g1start, $g1length, $value) = @{$expression};
        my $g1end = $g1start+$g1length-1;
        $result .= qq{expression: "} . $recce->g1_literal( $g1start, $g1length-1 ) .
            qq{" = } . round_value($value);
        $result .= "\n";
        EVENT: while ($event_ix <= $#events) {
            my $event = $events[$event_ix];
            my $g1loc = $event->[3];
            last EVENT if $g1loc >= $g1end;
            my $type = $g1loc == $g1start ? 'preceding' : 'internal';
            $result .= join q{ }, $type, display_event($recce, @{$event});
            $result .= "\n";
            $event_ix++;
        }
        $result .= "\n";
    }

    EVENT: while ( $event_ix <= $#events ) {
        my $event = $events[$event_ix];
        $result .= join q{ }, 'trailing', display_event($recce, @{$event});
        $result .= "\n";
        $event_ix++;
    } ## end EVENT: while ( $event_ix <= $#events )


# round value down, for testing on platforms
# with various float precisions
sub round_value {
    my ( $value ) = @_;
    return (int $value*100)/100;
}

sub display_event {
    my ( $recce, $event_name, $start, $end ) = @_;
    if ($event_name eq 'ws') {
       return "ws of length " . ($end-$start);
    }
    my $literal = $recce->literal($start, ($end-$start));
    $literal =~ s/\n/\\n/xmsg;
    return qq{$event_name: "$literal"};
}

my $expected_result = <<'END_OF_RESULT';
expression: "42*2+7/3" = 86.33

expression: "42*(2+7)/3" = 126
preceding ws of length 1

expression: "2**7-3" = 125
preceding ws of length 1

expression: "2**(7-3)" = 16
preceding ws of length 1

expression: "1729" = 1729
preceding ws of length 1
preceding comment: "# Hardy-Ramanujan number\n"

expression: "1**3+12**3" = 1729
preceding ws of length 1

expression: "9**3+10**3" = 1729
preceding ws of length 1

expression: "87539319" = 87539319
preceding ws of length 1
preceding comment: "# Next highest taxicab number\n"
preceding comment: "# note: weird spacing is deliberate\n"

expression: "167**3+ 436**3" = 87539319
preceding ws of length 1
internal ws of length 1

expression: "228**3 + 423**3" = 87539319
internal ws of length 1
internal ws of length 1

expression: "255**3+414**3" = 87539319

trailing ws of length 1
END_OF_RESULT

Marpa::R3::Test::is($result, $expected_result, "interweave of events and parse tree");

package My_Nodes;

sub My_Nodes::do_expression {
    my ($parse, $v) = @_;
    my @values = @{$v};
    return \@values;
    # say STDERR "pushing value: ", Data::Dumper::Dumper(\@_);
}

sub My_Nodes::do_number {
    my ($parse, $v) = @_;
    my ($number) = @{$v};
    return $number+0;
}

sub My_Nodes::do_paren  {
    my ($parse, $v) = @_;
    my ($expr) = @{$v};
    return $expr;
}

sub My_Nodes::do_add {
    my ($parse, $v) = @_;
    my ($right, $left) = @{$v};
    return $right + $left;
}

sub My_Nodes::do_subtract {
    my ($parse, $v) = @_;
    my ($right, $left) = @{$v};
    return $right - $left;
}

sub My_Nodes::do_multiply {
    my ($parse, $v) = @_;
    my ($right, $left) = @{$v};
    return $right * $left;
}

sub My_Nodes::do_divide {
    my ($parse, $v) = @_;
    my ($right, $left) = @{$v};
    return $right / $left;
}

sub My_Nodes::do_power {
    my ($parse, $v) = @_;
    my ($right, $left) = @{$v};
    return $right ** $left;
}

# vim: expandtab shiftwidth=4: