X-Git-Url: http://git.vpit.fr/?a=blobdiff_plain;f=lib%2FLaTeX%2FTikZ%2FFunctor.pm;fp=lib%2FLaTeX%2FTikZ%2FFunctor.pm;h=e302d3fbf591e31f9417555aa463f7367c5162ab;hb=92147217516a40b35ca00c8e08939e8aa5478426;hp=0000000000000000000000000000000000000000;hpb=787935f70e88c8225e15a46607c809e805af68da;p=perl%2Fmodules%2FLaTeX-TikZ.git diff --git a/lib/LaTeX/TikZ/Functor.pm b/lib/LaTeX/TikZ/Functor.pm new file mode 100644 index 0000000..e302d3f --- /dev/null +++ b/lib/LaTeX/TikZ/Functor.pm @@ -0,0 +1,168 @@ +package LaTeX::TikZ::Functor; + +use strict; +use warnings; + +=head1 NAME + +LaTeX::TikZ::Functor - Build functor methods that recursively visit nodes of a LaTeX::TikZ::Set tree. + +=head1 VERSION + +Version 0.01 + +=cut + +our $VERSION = '0.01'; + +use Carp (); + +use Sub::Name (); + +use LaTeX::TikZ::Tools; + +use Any::Moose 'Util' => [ 'does_role' ]; + +my $lts_tc = LaTeX::TikZ::Tools::type_constraint('LaTeX::TikZ::Set'); + +my @default_set_rules; +my @default_mod_rules; + +my ($validate_rule, $insert_rule); +BEGIN { + $validate_rule = Sub::Name::subname('validate_rule' => sub { + my ($target, $handler) = @_; + + unless (defined $target and ref $target eq '' + and $target =~ /[A-Za-z][A-Za-z0-9_]*(?:::[A-Za-z][A-Za-z0-9_]*)*/) { + Carp::confess("Invalid target $target"); + } + + (my $pm = $target) =~ s{::}{/}g; + $pm .= '.pm'; + require $pm; + + my $is_set; + if (does_role($target, 'LaTeX::TikZ::Set')) { + $is_set = 1; + } elsif (does_role($target, 'LaTeX::TikZ::Mod')) { + $is_set = 0; + } else { + Carp::confess("Target $target is neither a set nor a mod"); + } + + Carp::confess("Invalid handler for target $target") + unless ref $handler eq 'CODE'; + + return [ $target, $handler, $is_set ]; + }); + + $insert_rule = Sub::Name::subname('insert_rule' => sub { + my ($rule, $list) = @_; + + my $spec = $rule->[0]; + for my $i (0 .. $#$list) { + my $old_spec = $list->[$i]->[0]; + if ($old_spec->isa($spec) or does_role($old_spec, $spec)) { + splice @$list, $i, 1, $rule; + return 1; + } + } + + push @$list, $rule; + return $#$list; + }); +} + +sub default_rule { + shift; + + my $rule = $validate_rule->(@_); + + $insert_rule->($rule, $rule->[2] ? \@default_set_rules : \@default_mod_rules); +} + +sub new { + my ($class, %args) = @_; + + my @set_rules = @default_set_rules; + my @mod_rules = @default_mod_rules; + + my @user_rules = @{$args{rules} || []}; + while (@user_rules) { + my ($target, $handler) = splice @user_rules, 0, 2; + + my $rule = $validate_rule->($target, $handler); + + $insert_rule->($rule, $rule->[2] ? \@set_rules : \@mod_rules); + } + + my %dispatch = map { $_->[0] => $_ } @set_rules, @mod_rules; + + my $self; + + $self = bless sub { + my $set = shift; + + $lts_tc->assert_valid($set); + + my $rule = $dispatch{ref($set)}; + unless ($rule) { + ($set->isa($_->[0]) or $set->does($_->[0])) and $rule = $_ for @set_rules; + $rule = [ undef, sub { $_[1] } ] unless $rule; + } + my $new_set = $rule->[1]->($self, $set, @_); + my $is_new = $new_set ne $set; + + my @new_mods; +MOD: + for my $mod ($set->mods) { + my $rule = $dispatch{ref($mod)}; + unless ($rule) { + ($mod->isa($_->[0]) or $mod->does($_->[0])) and $rule = $_ for @mod_rules; + unless ($rule) { + push @new_mods, $mod; + next MOD; + } + } + push @new_mods, $rule->[1]->($self, $mod, @_); + } + + $new_set->mod(@new_mods) if $is_new; + + return $new_set; + }, $class; +} + +use LaTeX::TikZ::Interface functor => sub { + shift; + + __PACKAGE__->new(rules => \@_); +}; + +=head1 AUTHOR + +Vincent Pit, C<< >>, L. + +You can contact me by mail or on C (vincent). + +=head1 BUGS + +Please report any bugs or feature requests to C, or through the web interface at L. +I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. + +=head1 SUPPORT + +You can find documentation for this module with the perldoc command. + + perldoc LaTeX::TikZ + +=head1 COPYRIGHT & LICENSE + +Copyright 2010 Vincent Pit, all rights reserved. + +This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. + +=cut + +1; # End of LaTeX::TikZ::Functor