summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2010-10-20 01:58:04 -0500
committerJesse Luehrs <doy@tozt.net>2010-10-20 01:58:04 -0500
commitefb592ef997e1772dc8bc03724d40ba3efe27717 (patch)
tree8fc59aebd310326e5d13e92b5c7293ad17856bb0 /lib
parentbab696380211755ae2f827c5ea3c174f62151295 (diff)
downloadeval-closure-efb592ef997e1772dc8bc03724d40ba3efe27717.tar.gz
eval-closure-efb592ef997e1772dc8bc03724d40ba3efe27717.zip
initial implementation
Diffstat (limited to 'lib')
-rw-r--r--lib/Eval/Closure.pm95
1 files changed, 95 insertions, 0 deletions
diff --git a/lib/Eval/Closure.pm b/lib/Eval/Closure.pm
index e69de29..963de51 100644
--- a/lib/Eval/Closure.pm
+++ b/lib/Eval/Closure.pm
@@ -0,0 +1,95 @@
+package Eval::Closure;
+use Sub::Exporter -setup => {
+ exports => [qw(eval_closure)],
+};
+
+use Carp;
+use overload ();
+use Scalar::Util qw(reftype);
+use Try::Tiny;
+
+sub eval_closure {
+ my (%args) = @_;
+ $args{source} = _canonicalize_source($args{source});
+
+ my ($code, $e) = _clean_eval_closure(@args{qw(source environment name)});
+
+ croak("Failed to compile source: $e\n\nsource:\n$args{source}")
+ unless $code;
+
+ return $code;
+}
+
+sub _canonicalize_source {
+ my ($source) = @_;
+
+ if (defined($source)) {
+ if (ref($source)) {
+ if (reftype($source) eq 'ARRAY'
+ || overload::Method($source, '@{}')) {
+ return join "\n", @$source;
+ }
+ elsif (overload::Method($source, '""')) {
+ return "$source";
+ }
+ else {
+ croak("The 'source' parameter to eval_closure must be a "
+ . "string or array reference");
+ }
+ }
+ else {
+ return $source;
+ }
+ }
+ else {
+ croak("The 'source' parameter to eval_closure is required");
+ }
+}
+
+sub _clean_eval_closure {
+ my $__captures = $_[1];
+
+ do {
+ local $@;
+ local $SIG{__DIE__};
+
+ if ($ENV{EVAL_CLOSURE_PRINT_SOURCE}) {
+ _dump_source(_make_source(@_), $_[2]);
+ }
+
+ my $code = eval _make_source(@_);
+ ($code, $@);
+ };
+}
+
+sub _make_source {
+ my ($source, $__captures) = @_;
+ return join "\n", (
+ (map {
+ die "Capture key should start with \@, \% or \$: $_"
+ unless /^([\@\%\$])/;
+ 'my ' . $_ . ' = ' . $1 . '{$__captures->{\'' . $_ . '\'}};';
+ } keys %$__captures),
+ $source,
+ );
+}
+
+sub _dump_source {
+ my ($source, $name) = @_;
+
+ my $output;
+ if (try { require Perl::Tidy }) {
+ Perl::Tidy::perltidy(
+ source => \$source,
+ destination => \$output,
+ );
+ }
+ else {
+ $output = $source;
+ }
+
+ $name = defined($name) ? $name : "__ANON__";
+ warn $name . ":\n" . $output . "\n";
+}
+
+1;