The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

package Blondie::Reducer;

use strict;
use warnings;

use UNIVERSAL::moniker;
use Scalar::Util ();

sub reduce {
    my $self = shift;
    my $node = shift;

	return $node unless $self->can_reduce($node);

	if (Scalar::Util::blessed($node) && (my $meth = $self->can("reduce_" . $node->moniker))) {
		return $self->$meth($node);
	} else {
		return $self->generic_reduce($node);
	}
}

sub can_reduce {
	my $self = shift;
	my $node = shift;;

	return unless Scalar::Util::blessed($node);
	return if $node->atomic;

	return 1;
}

sub generic_reduce {
    my $self = shift;
    my $node = shift;

	return $node unless Scalar::Util::blessed($node);

	$node->isa("Blondie::Node")
		or die "it doesn't look like $node is a piece of a Blondie program.";

   	$node->fmap(sub { $self->reduce($_[0]) });
}

__PACKAGE__;

__END__

=pod

=head1 NAME

Blondie::Reducer - generic reducing base class for fmapping nodes.

=head1 SYNOPSIS

    package My::ASTWalker;

    use base qw/Blondie::Reducer/;

    sub reduce_sym {
        my $self = shift;
        my $sym_node = shift;

        my $symbol = $sym_node->val;

        $table{$symbol}; # find the symbol
    }

    # and then

    my $reduced = My::ASTWalker->new->reduce($program);

=head1 DESCRIPTION

=cut