<commit_before>#!/usr/bin/perl
# Copyright 2015 <NAME>

use 5.010;
use strict;
use warnings;

use Test::More;

use Marpa::R2;

use_ok 'MarpaX::AST';

my $dumper = \&MarpaX::AST::dumper;

#
# Unambiguous grammar to parse arithmetic expressions
#
my $g = Marpa::R2::Scanless::G->new( { source => \(<<'END_OF_SOURCE'),

:default ::= action => [ name, value ]
lexeme default = action => [ name, value ] latm => 1

    Expr ::=
          Number
        | ('(') Expr (')')  assoc => group
       || Expr '**' Expr    assoc => right
       || Expr '*' Expr     # left associativity by default
        | Expr '/' Expr
       || Expr '+' Expr
        | Expr '-' Expr

    Number ~ [\d]+

:discard ~ whitespace
whitespace ~ [\s]+

END_OF_SOURCE
} );

my @inputs = qw{ 4+5*6+8 (4+5)*(6+8) (4+5)*(6+8)**2/5 };

for my $input (@inputs){
    my $ast = MarpaX::AST->new( ${ $g->parse( \$input ) } );
    is ast_eval_AoA($ast), eval $input, "$input, ast_eval_AoA()";
}

# todo: find a place for this -- node_print for distill? also use ->text()
=pod
$ast->walk({
    visit => sub {
        my ($ast, $context) = @_;
        my ($node_id, @children) = @$ast;
        say ' ' x $context->{depth}, $node_id, $ast->is_literal ? ": '" . $ast->text . "'" : '';
    }
});
=cut

#
# evaluate ast as array of arrays with 0th element being the node id
#
sub ast_eval_AoA{
    my ($ast) = @_;
    if (@$ast == 4){
        my ($e1, $op, $e2) = @$ast[1..3];
        if    ($op eq '+'){ ast_eval_AoA($e1) + ast_eval_AoA($e2) }
        elsif ($op eq '-'){ ast_eval_AoA($e1) - ast_eval_AoA($e2) }
        elsif ($op eq '*'){ ast_eval_AoA($e1) * ast_eval_AoA($e2) }
        elsif ($op eq '/'){ ast_eval_AoA($e1) / ast_eval_AoA($e2) }
        elsif ($op eq '**'){ ast_eval_AoA($e1) ** ast_eval_AoA($e2) }
    }
    else{
        return $ast->[1]->[1];
    }
}

is ast_eval_AoA($ast), eval $input, "$input, ast_eval_AoA()";

#
# evaluate ast using MarpaX::AST methods
#
sub ast_eval{
    my ($ast) = @_;
    if ($ast->id eq 'Expr' and $ast->first_child->id eq 'Number'){
        return $ast->first_child->first_child;
    }
    else {
        my ($e1, $op, $e2) = @{ $ast->children() };
        if    ($op eq '+'){ ast_eval($e1) + ast_eval($e2) }
        elsif ($op eq '-'){ ast_eval($e1) - ast_eval($e2) }
        elsif ($op eq '*'){ ast_eval($e1) * ast_eval($e2) }
        elsif ($op eq '/'){ ast_eval($e1) / ast_eval($e2) }
        elsif ($op eq '**'){ ast_eval($e1) ** ast_eval($e2) }
    }
    else{
        die "Unknown node: " . $ast->sprint;
    }
}

for my $input (@inputs){
    my $ast = MarpaX::AST->new( ${ $g->parse( \$input ) } );
    is ast_eval($ast), eval $input, "$input, ast_eval()";
}

#
# evaluate ast using Visitor pattern ($v is for visitor)
#
use_ok 'MarpaX::AST::Visitor';

package My::Visitor;

use parent 'MarpaX::AST::Visitor';

sub visit_num{
    my ($v, $ast) = @_;
    $ast->first_child->text
}

sub visit_add{
    my ($v, $ast) = @_;
    $v->visit( $ast->first_child ) + $v->visit( $ast->last_child )
}

sub visit_sub{
    my ($v, $ast) = @_;
    $v->visit( $ast->first_child ) - $v->visit( $ast->last_child )
}

sub visit_mul{
    my ($v, $ast) = @_;
    $v->visit( $ast->first_child ) * $v->visit( $ast->last_child )
}

sub visit_div{
    my ($v, $ast) = @_;
    $v->visit( $ast->first_child ) / $v->visit( $ast->last_child )
}

sub visit_pow{
    my ($v, $ast) = @_;
    $v->visit( $ast->first_child ) ** $v->visit( $ast->last_child )
}

sub visit_par{
    my ($v, $ast) = @_;
    $v->visit( $ast->first_child )
}

package main;

$g = Marpa::R2::Scanless::G->new( { source => \(<<'END_OF_SOURCE'),

:default ::= action => [ name, value ]
lexeme default = action => [ name, value ] latm => 1

    # as we dispatch based on node id -- methods are defined as visit_$node_id()
    # or interpreter's packages, we need to add name's, but may omit all literals
    Expr ::=
          Number                              name => 'num'
        | ('(') Expr (')')  assoc => group    name => 'par'
       || Expr ('**') Expr  assoc => right    name => 'pow'
       || Expr ('*') Expr                     name => 'mul'
        | Expr ('/') Expr                     name => 'div'
       || Expr ('+') Expr                     name => 'add'
        | Expr ('-') Expr                     name => 'sub'

    Number ~ [\d]+

:discard ~ whitespace
whitespace ~ [\s]+

END_OF_SOURCE
} );

my @inputs = qw{ 4+5*6+8 (4+5)*(6+8) (4+5)*(6+8)**12/5 };

for my $inp (@inputs){
    $ast = MarpaX::AST->new( ${ $g->parse( \$inp ) } );
#    warn $ast->sprint;
    my $v = My::Visitor->new();
    is $v->visit($ast), eval $inp, "$inp, Visitor pattern";
}

#
# evaluate ast using Interpreter pattern (context-free)
#
use_ok 'MarpaX::AST::Interpreter';

sub add::cmpt {
    my (undef, $interp, $ast) = @_;
    $interp->cmpt($ast->first_child) + $interp->cmpt($ast->last_child)
}

sub sub::cmpt { $_[1]->cmpt($_[2]->first_child) - $_[1]->cmpt($_[2]->last_child) }
sub mul::cmpt { $_[1]->cmpt($_[2]->first_child) * $_[1]->cmpt($_[2]->last_child) }
sub div::cmpt { $_[1]->cmpt($_[2]->first_child) / $_[1]->cmpt($_[2]->last_child) }
sub pow::cmpt { $_[1]->cmpt($_[2]->first_child) ** $_[1]->cmpt($_[2]->last_child) }

sub num::cmpt { $_[2]->first_child->text }
sub par::cmpt { $_[1]->cmpt($_[2]->first_child) }

package main;

for my $input (@inputs){
    my $ast = MarpaX::AST->new( ${ $g->parse( \$input ) } );
#    warn $ast->sprint;

    # create interpreter with default options:
    # node_id packages in main::*, context initialized to { }
    my $i = MarpaX::AST::Interpreter->new();

#    warn MarpaX::AST::dumper( $i->ast );
    is $i->cmpt($ast), # context-free interpreting :)
        eval $inp, "$inp, Interpreter pattern (context-free)";
}

#
# evaluate ast using Interpreter pattern (in context)
#
$g = Marpa::R2::Scanless::G->new( { source => \(<<'END_OF_SOURCE'),

:default ::= action => [ name, value ]
lexeme default = action => [ name, value ] latm => 1

    Expr ::=
          Var                                 name => 'var'
       || Number                              name => 'num'
       || ('(') Expr (')')  assoc => group    name => 'par'
       || Expr ('**') Expr  assoc => right    name => 'pow'
       || Expr ('*') Expr                     name => 'mul'
        | Expr ('/') Expr                     name => 'div'
       || Expr ('+') Expr                     name => 'add'
        | Expr ('-') Expr                     name => 'sub'

    Number ~ [\d]+
    Var    ~ [a-z]+

:discard ~ whitespace
whitespace ~ [\s]+

END_OF_SOURCE
} );

package My::Expr::add;

sub cmpt {
    my (undef, $interp, $ast) = @_;
    $interp->cmpt($ast->first_child) + $interp->cmpt($ast->last_child)
}

sub My::Expr::sub::cmpt { $_[1]->cmpt($_[2]->first_child) - $_[1]->cmpt($_[2]->last_child) }
sub My::Expr::mul::cmpt { $_[1]->cmpt($_[2]->first_child) * $_[1]->cmpt($_[2]->last_child) }
sub My::Expr::div::cmpt { $_[1]->cmpt($_[2]->first_child) / $_[1]->cmpt($_[2]->last_child) }
sub My::Expr::pow::cmpt { $_[1]->cmpt($_[2]->first_child) ** $_[1]->cmpt($_[2]->last_child) }

sub My::Expr::num::cmpt { $_[2]->first_child->text }
sub My::Expr::par::cmpt { $_[1]->cmpt($_[2]->first_child) }

sub My::Expr::var::cmpt{ $_[1]->ctx->{ $_[2]->first_child->text } }

package main;

@inputs = qw{   x-1+y*z    (x+y)*(a+b)    (a+b)*(x+y)**12/5   };
my $context = { a => 42, b => 84, x => 1, y => 2, z => 3 };

for my $inp (@inputs){
    $ast = MarpaX::AST->new( ${ $g->parse( \$inp ) } );
#    warn $ast->sprint;

    my $i = MarpaX::AST::Interpreter->new( {
        namespace => 'My::Expr',
        context => $context,
    } );

    my $subst = $inp;
    $subst =~ s/$_/$context->{$_}/g for keys %{ $context };

    is $i->cmpt($ast), # contextual interpreting
        eval $subst, "$inp, Interpreter pattern (in context)";
}

done_testing();
<commit_msg>fix recursive functions<commit_after>#!/usr/bin/perl
# Copyright 2015 <NAME>

use 5.010;
use strict;
use warnings;

use Test::More;

use Marpa::R2;

use_ok 'MarpaX::AST';

my $dumper = \&MarpaX::AST::dumper;

#
# Unambiguous grammar to parse arithmetic expressions
#
my $g = Marpa::R2::Scanless::G->new( { source => \(<<'END_OF_SOURCE'),

:default ::= action => [ name, value ]
lexeme default = action => [ name, value ] latm => 1

    Expr ::=
          Number
        | ('(') Expr (')')  assoc => group
       || Expr '**' Expr    assoc => right
       || Expr '*' Expr     # left associativity by default
        | Expr '/' Expr
       || Expr '+' Expr
        | Expr '-' Expr

    Number ~ [\d]+

:discard ~ whitespace
whitespace ~ [\s]+

END_OF_SOURCE
} );

my @inputs = qw{ 4+5*6+8 (4+5)*(6+8) (4+5)*(6+8)**2/5 };

for my $input (@inputs){
    my $ast = MarpaX::AST->new( ${ $g->parse( \$input ) } );
    is ast_eval_AoA($ast), eval $input, "$input, ast_eval_AoA()";
}

# todo: find a place for this -- node_print for distill? also use ->text()
=pod
$ast->walk({
    visit => sub {
        my ($ast, $context) = @_;
        my ($node_id, @children) = @$ast;
        say ' ' x $context->{depth}, $node_id, $ast->is_literal ? ": '" . $ast->text . "'" : '';
    }
});
=cut

#
# evaluate ast as array of arrays with 0th element being the node id
#
sub ast_eval_AoA{
    my ($ast) = @_;
    # binop
    if (@$ast == 4){
        my ($e1, $op, $e2) = @$ast[1..3];
        if    ($op eq '+'){ ast_eval_AoA($e1) + ast_eval_AoA($e2) }
        elsif ($op eq '-'){ ast_eval_AoA($e1) - ast_eval_AoA($e2) }
        elsif ($op eq '*'){ ast_eval_AoA($e1) * ast_eval_AoA($e2) }
        elsif ($op eq '/'){ ast_eval_AoA($e1) / ast_eval_AoA($e2) }
        elsif ($op eq '**'){ ast_eval_AoA($e1) ** ast_eval_AoA($e2) }
    }
    # number
    elsif (@$ast == 2 and $ast->[1]->[0] eq 'Number'){
        return $ast->[1]->[1];
    }
    # parenthesized
    elsif (@$ast == 2){
        ast_eval_AoA($ast->[1]);
    }
    else{
        die "Unknown node: " . $ast->sprint;
    }
}

#
# evaluate ast using MarpaX::AST methods
#
sub ast_eval{
    my ($ast) = @_;
    # number
    if ($ast->id eq 'Expr' and $ast->first_child->id eq 'Number'){
        return $ast->first_grandchild;
    }
    # parenthesized
    elsif ($ast->children_count == 1 and $ast->first_child->id eq 'Expr'){
        ast_eval($ast->first_child);
    }
    # binop
    elsif ($ast->children_count == 3) {
        my ($e1, $op, $e2) = @{ $ast->children() };
        if    ($op eq '+'){ ast_eval($e1) + ast_eval($e2) }
        elsif ($op eq '-'){ ast_eval($e1) - ast_eval($e2) }
        elsif ($op eq '*'){ ast_eval($e1) * ast_eval($e2) }
        elsif ($op eq '/'){ ast_eval($e1) / ast_eval($e2) }
        elsif ($op eq '**'){ ast_eval($e1) ** ast_eval($e2) }
    }
    else{
        die "Unknown node: " . $ast->sprint;
    }
}

for my $input (@inputs){
    my $ast = MarpaX::AST->new( ${ $g->parse( \$input ) } );
    is ast_eval($ast), eval $input, "$input, ast_eval()";
}

#
# evaluate ast using Visitor pattern ($v is for visitor)
#
use_ok 'MarpaX::AST::Visitor';

package My::Visitor;

use parent 'MarpaX::AST::Visitor';

sub visit_num{
    my ($v, $ast) = @_;
    $ast->first_child->text
}

sub visit_add{
    my ($v, $ast) = @_;
    $v->visit( $ast->first_child ) + $v->visit( $ast->last_child )
}

sub visit_sub{
    my ($v, $ast) = @_;
    $v->visit( $ast->first_child ) - $v->visit( $ast->last_child )
}

sub visit_mul{
    my ($v, $ast) = @_;
    $v->visit( $ast->first_child ) * $v->visit( $ast->last_child )
}

sub visit_div{
    my ($v, $ast) = @_;
    $v->visit( $ast->first_child ) / $v->visit( $ast->last_child )
}

sub visit_pow{
    my ($v, $ast) = @_;
    $v->visit( $ast->first_child ) ** $v->visit( $ast->last_child )
}

sub visit_par{
    my ($v, $ast) = @_;
    $v->visit( $ast->first_child )
}

package main;

$g = Marpa::R2::Scanless::G->new( { source => \(<<'END_OF_SOURCE'),

:default ::= action => [ name, value ]
lexeme default = action => [ name, value ] latm => 1

    # as we dispatch based on node id -- methods are defined as visit_$node_id()
    # or interpreter's packages, we need to add name's, but may omit all literals
    Expr ::=
          Number                              name => 'num'
        | ('(') Expr (')')  assoc => group    name => 'par'
       || Expr ('**') Expr  assoc => right    name => 'pow'
       || Expr ('*') Expr                     name => 'mul'
        | Expr ('/') Expr                     name => 'div'
       || Expr ('+') Expr                     name => 'add'
        | Expr ('-') Expr                     name => 'sub'

    Number ~ [\d]+

:discard ~ whitespace
whitespace ~ [\s]+

END_OF_SOURCE
} );

my @inputs = qw{ 4+5*6+8 (4+5)*(6+8) (4+5)*(6+8)**12/5 };

for my $inp (@inputs){
    $ast = MarpaX::AST->new( ${ $g->parse( \$inp ) } );
#    warn $ast->sprint;
    my $v = My::Visitor->new();
    is $v->visit($ast), eval $inp, "$inp, Visitor pattern";
}

#
# evaluate ast using Interpreter pattern (context-free)
#
use_ok 'MarpaX::AST::Interpreter';

sub add::cmpt {
    my (undef, $interp, $ast) = @_;
    $interp->cmpt($ast->first_child) + $interp->cmpt($ast->last_child)
}

sub sub::cmpt { $_[1]->cmpt($_[2]->first_child) - $_[1]->cmpt($_[2]->last_child) }
sub mul::cmpt { $_[1]->cmpt($_[2]->first_child) * $_[1]->cmpt($_[2]->last_child) }
sub div::cmpt { $_[1]->cmpt($_[2]->first_child) / $_[1]->cmpt($_[2]->last_child) }
sub pow::cmpt { $_[1]->cmpt($_[2]->first_child) ** $_[1]->cmpt($_[2]->last_child) }

sub num::cmpt { $_[2]->first_child->text }
sub par::cmpt { $_[1]->cmpt($_[2]->first_child) }

package main;

for my $input (@inputs){
    my $ast = MarpaX::AST->new( ${ $g->parse( \$input ) } );
#    warn $ast->sprint;

    # create interpreter with default options:
    # node_id packages in main::*, context initialized to { }
    my $i = MarpaX::AST::Interpreter->new();

#    warn MarpaX::AST::dumper( $i->ast );
    is $i->cmpt($ast), # context-free interpreting :)
        eval $inp, "$inp, Interpreter pattern (context-free)";
}

#
# evaluate ast using Interpreter pattern (in context)
#
$g = Marpa::R2::Scanless::G->new( { source => \(<<'END_OF_SOURCE'),

:default ::= action => [ name, value ]
lexeme default = action => [ name, value ] latm => 1

    Expr ::=
          Var                                 name => 'var'
       || Number                              name => 'num'
       || ('(') Expr (')')  assoc => group    name => 'par'
       || Expr ('**') Expr  assoc => right    name => 'pow'
       || Expr ('*') Expr                     name => 'mul'
        | Expr ('/') Expr                     name => 'div'
       || Expr ('+') Expr                     name => 'add'
        | Expr ('-') Expr                     name => 'sub'

    Number ~ [\d]+
    Var    ~ [a-z]+

:discard ~ whitespace
whitespace ~ [\s]+

END_OF_SOURCE
} );

package My::Expr::add;

sub cmpt {
    my (undef, $interp, $ast) = @_;
    $interp->cmpt($ast->first_child) + $interp->cmpt($ast->last_child)
}

sub My::Expr::sub::cmpt { $_[1]->cmpt($_[2]->first_child) - $_[1]->cmpt($_[2]->last_child) }
sub My::Expr::mul::cmpt { $_[1]->cmpt($_[2]->first_child) * $_[1]->cmpt($_[2]->last_child) }
sub My::Expr::div::cmpt { $_[1]->cmpt($_[2]->first_child) / $_[1]->cmpt($_[2]->last_child) }
sub My::Expr::pow::cmpt { $_[1]->cmpt($_[2]->first_child) ** $_[1]->cmpt($_[2]->last_child) }

sub My::Expr::num::cmpt { $_[2]->first_child->text }
sub My::Expr::par::cmpt { $_[1]->cmpt($_[2]->first_child) }

sub My::Expr::var::cmpt{ $_[1]->ctx->{ $_[2]->first_child->text } }

package main;

@inputs = qw{   x-1+y*z    (x+y)*(a+b)    (a+b)*(x+y)**12/5   };
my $context = { a => 42, b => 84, x => 1, y => 2, z => 3 };

for my $inp (@inputs){
    $ast = MarpaX::AST->new( ${ $g->parse( \$inp ) } );
#    warn $ast->sprint;

    my $i = MarpaX::AST::Interpreter->new( {
        namespace => 'My::Expr',
        context => $context,
    } );

    my $subst = $inp;
    $subst =~ s/$_/$context->{$_}/g for keys %{ $context };

    is $i->cmpt($ast), # contextual interpreting
        eval $subst, "$inp, Interpreter pattern (in context)";
}

done_testing();

{'fraction_non_alphanumeric': 0.22720170010625665, 'fraction_numerical': 0.016063503968998062, 'mean_word_length': 2.62400906002265, 'pattern_counts': {'":': 2, '<': 17, '<?xml version=': 0, '>': 323, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5031649', 'n_tokens_mistral': 6375, 'n_tokens_neox': 6231, 'n_words': 1357}