#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;

use Test::More;

use FindBin;
use lib $FindBin::Bin;
use Parser;

my $ast = Parser->parse(\Parser::EXAMPLE_INPUT);

my $cloned_ast = $ast->clone;

isnt $cloned_ast, $ast, 'different identity';
is_deeply $cloned_ast, $ast, 'same contents';

done_testing;

package Ast;  # a parent class to define common methods

use Scalar::Util 'blessed';

# a constructor for convenience.
# Marpa won't use this but rather `bless` directly
sub new {
    my ($class, @args) = @_;
    bless \@args => $class;
}

sub clone {
    my ($self) = @_;
    my @childs = map { _clone_if_possible($_) } @$self;
    return ref($self)->new(@childs);
    # ref $self is the class of $self.
}

sub _clone_if_possible {
    my ($maybe_ast) = @_;
    if (blessed($maybe_ast) and $maybe_ast->can('clone')) {
        return $maybe_ast->clone;
    }
    return $maybe_ast;
}

package Ast::Binop;  # convenience base for all binary operators
use parent -norequire, 'Ast';

sub l   { shift()->[0] }
sub r   { shift()->[1] }

package Ast::Equals;
use parent -norequire, 'Ast::Binop';

package Ast::Assign;
use parent -norequire, 'Ast::Binop';

package Ast::Or;
use parent -norequire, 'Ast::Binop';

package Ast::Var;
use parent -norequire, 'Ast';

sub name { shift()->[0] }

package Ast::Cond;
use parent -norequire, 'Ast';

sub cond { shift()->[0] }
sub then { shift()->[1] }
sub else { shift()->[2] }

package Ast::Block;
use parent -norequire, 'Ast';

package Ast::Literal;
use parent -norequire, 'Ast';

sub val { shift()->[0] }
