File Coverage

blib/lib/Language/FP.pm
Criterion Covered Total %
statement 221 285 77.5
branch 56 138 40.5
condition 8 24 33.3
subroutine 53 57 92.9
pod 3 34 8.8
total 341 538 63.3


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2             package Language::FP;
3 1     1   50021 use Parse::RecDescent;
  1         116762  
  1         10  
4 1     1   12702 use Regexp::Common;
  1         5095  
  1         7  
5              
6             require Exporter;
7             @EXPORT = qw/fp_eval/;
8             @EXPORT_OK = qw/perl2fp fp2perl bottom BOTTOM/;
9             %EXPORT_TAGS = (':all' => [@EXPORT_OK, @EXPORT]);
10             @ISA = qw(Exporter);
11              
12             $VERSION = 0.03;
13              
14             sub BOTTOM () { # the universal bad value
15 0 0   0 0 0 if ($::FP_DEBUG =~ /b/) {
16 1     1   161206 use Carp 'confess';
  1         9  
  1         1215  
17 0         0 confess("Bottom!");
18             }
19             undef
20 0         0 }
21              
22             sub bottom { # check for bottom
23 2514 50 33 2514 0 45039 (@_ > 0 && !defined($_[0])) ? 1 : 0
24             }
25              
26             sub numeric { # check for 2 integer args
27 1540   33 1540 0 47266 return ($_[0] =~ /$RE{num}{real}/o) && ($_[1] =~ /$RE{num}{real}/o);
28             }
29              
30             ######################################################################
31             ## Parser
32              
33             ##############################
34             # Debugging
35              
36             sub info { # pretty debugging output.
37 0     0 0 0 my ($pack, $fn, $line, $subr) = caller 1;
38 0         0 $subr =~ s/^.*:://;
39 0         0 print STDERR "[$subr] ", @_, "\n";
40             }
41              
42             sub Dparse { # parse-time debugging output
43 182 50   182 0 486 goto &info if $::FP_DEBUG =~ /p/;
44             }
45              
46             sub Drun { # run-time debugging.
47 25222 50   25222 0 50670 goto &info if $::FP_DEBUG =~ /r/;
48             }
49              
50             ##############################
51             # Utilities
52              
53             # XXX: this shouldn't be needed. It makes behave the same as X
54             # when passed as an argument list. Single-element lists and scalar
55             # values aren't the same kind of thing, but we're trying to pretend as
56             # if they are. Otherwise, perl functions called from FP will all have
57             # to take array-refs.
58             sub as_array($) {
59 1485     1485 0 1936 my $a = shift;
60 1485 100       2410 if (ref $a eq 'ARRAY') {
61 126         1710 @$a;
62             } else {
63 1359         3775 $a;
64             }
65             }
66              
67             # XXX: this is the disgusting inverse of as_array
68             sub to_arrayref {
69 4390 100   4390 0 7478 if (@_ == 1) {
70 4384         48483 return shift;
71             } else {
72 6         119 return [@_];
73             }
74             }
75              
76             sub call_it { # call a coderef, with verbosity
77 9395     9395 0 9394 my $f = shift;
78 9395         25909 Drun "Calling $f (@_)";
79 9395         59449 my @res = $f->(@_);
80 9395         24877 Drun "-> (@res)";
81 9395         32274 @res;
82             }
83              
84             sub term { # create a typed parse-tree node.
85 15716     15716 0 9553674 my $type = shift;
86 15716         392728 return { type => $type, val => [@_] };
87             }
88              
89             ##############################
90             # Symbol lookup.
91              
92             # Note: we need to do a bit of magic here to look up functions and
93             # variables in both Language::FP and the calling package.
94              
95             sub findsym { # look up a function
96 66     66 0 156 my ($sym, $type) = @_;
97 66 50       181 if (ref $sym eq $type) {
98 0         0 return $sym;
99             }
100 66         83 my ($where, $thing);
101 66         161 foreach ('Language::FP', pkg()) {
102 90         168 my $x = $_.'::'.$sym;
103 90 100       101 if (defined($thing = *{$x}{$type})) {
  90         552  
104 66         124 $where = $x;
105 66         112 last;
106             }
107             }
108 66 100       177 if (wantarray) {
109 33         103 ($thing, $where);
110             } else {
111 33         75 $thing;
112             }
113             }
114              
115             ######################################################################
116             ## The parser
117              
118             my $P = undef;
119             sub get_parser {
120 68 100   68 0 288 return $P if $P;
121 1 50       9 $P = new Parse::RecDescent <<'EOG' or die "Can't create parser!";
122              
123             {
124             use Regexp::Common;
125             BEGIN {
126             no strict 'refs';
127             foreach (qw|term findsym|) {
128             *{__PACKAGE__.'::'.$_} = \&{'Language::FP::'.$_};
129             }
130             }
131             }
132              
133             thing: 'val' id_undef '=' application
134             { $return = term 'val', @item{qw(id_undef application)} }
135             | 'def' id_undef '=' termlist
136             { $return = term 'def', @item{qw(id_undef termlist)} }
137             | application
138             { $return = $item[1];1; }
139             | /\s*/
140             |
141              
142             application: termlist ':' data
143             { $return = term 'application', @item{qw(termlist data)}; }
144             | data
145             { $return = $item[1]; }
146              
147             termlist: 'while' complist termlist
148             { $return = term 'while', @item{qw(complist termlist)} }
149             | complist '->' complist ';' termlist
150             { $return = term 'if', @item[1,4,6] }
151             | complist
152             { $return = $item[1];1; }
153             |
154              
155             complist:
156             { $return = term 'compose', @{$item[1]} }
157              
158             func: 'bu' func data
159             { $return = term 'bu', @item{qw(func data)} }
160             | '/' func
161             { $return = term 'insert', $item{func} }
162             | '@' func
163             { $return = term 'forall', $item{func} }
164             | '(' termlist ')'
165             { $return = $item{termlist} }
166             | '[' ']'
167             { $return = term 'distribute', @{$item[3]} }
168             | '`' data
169             { $return = term 'constant', $item{data} }
170             | sfunc
171             { $return = $item[1];1; }
172             | id
173             { $return = $item[1];1; }
174             |
175              
176             data: datum
177             { $return = term 'data', $item[1] }
178             |
179              
180             datum: '<' datum(s?) '>'
181             { $return = $item[3];1; }
182             | /$RE{num}{real}/o
183             { $return = $item[1];1; }
184             | /$RE{num}{int}/o
185             { $return = $item[1];1; }
186             | /$RE{quoted}/o
187             { $return = substr($item[1], 1, length($item[1]) - 2);1; }
188             | m{[a-rt-zA-Z_][\w\d]*}
189             {
190             no strict 'refs';
191             # XXX: actually interpolate variables during parse.
192             $return = findsym($item[1], 'ARRAY') || undef;
193             }
194             |
195              
196             sfunc: /\d+/
197             { $return = term 'sfunc', $item[1]; }
198              
199             id_undef: m{[a-zA-Z_][\w\d]*}
200             { $return = term 'id_undef', $item[1]; }
201              
202             id: m{[a-zA-Z_][\w\d]*}
203             { $return = term 'id', $item[1];1; }
204             | m{([!<>=]=) | [+*/<>-] | ([gln]e) | ([gl]t) | eq}x
205             { $return = term 'op', $item[1]; }
206              
207             EOG
208 1         135699 $P;
209             }
210              
211             ######################################################################
212             ## Builtin functions (for both compilers).
213              
214             # FP is supposed to be "bottom-preserving". In other words, once a
215             # single operaation fails, it taints all results that depend on it.
216             # The only way to recover from this is to explicitly recognize the
217             # "bottom" condition using the bottom() test.
218              
219             my %op_guts;
220             BEGIN {
221 1     1   74 %op_guts = (
222             ## List ops #####
223             # first/last element of list
224             hd => '@_ ? $_[0] : BOTTOM',
225             hdr => '@_ ? $_[-1] : BOTTOM',
226             # rest of list
227             tl => '@_ ? @_[1..$#_] : BOTTOM',
228             tlr => '@_ ? @_[0..$#_ - 1] : BOTTOM',
229             len => 'return BOTTOM if bottom @_; scalar @_',
230             'reverse' => 'reverse @_',
231             # append
232             apndl => '($_[0], @{$_[1]})',
233             apndr => '(@{$_[0]}, $_[1])',
234             # Rotate
235             rotl => '@_ ? @_[1..$#_,0] : ()',
236             rotr => '@_ ? @_[$#_, 0..$#_ - 1] : ()',
237             # Catenate
238             cat => 'map { as_array $_ } @_',
239             ## Logical ops #####
240             'and' => '$_[0] && $_[1]',
241             'or' => '$_[0] || $_[1]',
242             'not' => '!$_[0]',
243             ## Other ops #####
244             id => '@_',
245             out => 'print STDERR perl2fp(@_), "\n"; @_',
246             iota => '1 .. $_[0]',
247             atom => '@_ == 1 && ref($_[0]) eq "SCALAR"',
248             null => '@_ == 0',
249             ## "shaping" list-ops #####
250             distl => q{
251             my ($a, $b) = @_;
252             return BOTTOM unless !bottom($a) && ref $b eq 'ARRAY';
253             map { [$a, $_] } @$b;
254             },
255              
256             distr => q{
257             my ($a, $b) = @_;
258             return BOTTOM unless !bottom($b) && ref $a eq 'ARRAY';
259             map { [$_, $b] } @$a;
260             },
261              
262             trans => q{
263             my @ret;
264             return () unless @_;
265             my $len = scalar @{$_[0]};
266             foreach (@_[1..$#_]) {
267             return BOTTOM unless ref $_ eq 'ARRAY' && @$_ == $len;
268             }
269             for (my $i = 0; $i < $len; $i++) {
270             push @ret, [ map { $_->[$i] } @_ ];
271             }
272             @ret;
273             },
274             );
275             }
276              
277             ######################################################################
278             ## Closure-based "Compiler"
279              
280             sub defun { # 'def' X '=' ...
281 3     3 0 9 my ($name, $val) = @_;
282 1     1   8 no strict 'refs';
  1         2  
  1         123  
283 3         4 *{pkg().'::'.$name} = $val;
  3         9  
284 3         16 Drun "Defined function $name";
285 3         71 'ok';
286             }
287              
288             sub defvar { # 'val' X '=' ...
289 0     0 0 0 my ($name, $val) = @_;
290 1     1   7 no strict 'refs';
  1         1  
  1         191  
291 0         0 @{pkg().'::'.$name} = as_array $val;
  0         0  
292 0         0 Drun "Defined value $name";
293 0         0 'ok';
294             }
295              
296             sub do_bu { # bu (i.e. currying)
297 2     2 0 4 my ($f, $o) = @_;
298 2         13 Dparse "using $f($o, ...)";
299             return sub {
300 1     1   6 no strict 'refs';
  1         2  
  1         170  
301 5     5   18 Drun "bu $f ($o, @_)";
302 5         10 call_it($f, $o, @_);
303 2         13 };
304             }
305              
306             sub compose { # '.' operator
307 89     89 0 171 my @funcs = @_;
308 89         376 Dparse "using (@funcs)";
309             return sub {
310 1     1   7 no strict 'refs';
  1         3  
  1         191  
311 4133     4133   10706 Drun "compose (@funcs)";
312 4133         5398 foreach my $f (reverse @funcs) {
313 5224         8264 @_ = call_it($f, @_);
314             }
315 4133         10740 @_;
316 89         592 };
317             }
318              
319             sub distribute { # '[...]' list-of-functions
320 18     18 0 35 my @xs = @_;
321 18         75 Dparse "using (@xs)";
322             return sub {
323 1     1   7 no strict 'refs';
  1         2  
  1         692  
324 1053     1053   3870 Drun "distribute (@xs) : (@_)";
325 1053         1527 map { to_arrayref call_it $_, @_ } @xs;
  2106         3416  
326             }
327 18         112 }
328              
329             sub ifelse { # 'a -> b ; c' construct
330 4     4 0 8 my ($if, $then, $else) = @_;
331 4         21 Dparse "if $if then $then else $else";
332             return sub {
333             # XXX: having to call this in array context sucks, but is necessary.
334 490     490   1471 Drun "if $if then $then else $else";
335 490         984 my ($test) = call_it $if, @_;
336 490 50       860 if (bottom($test)) {
    100          
337 0         0 BOTTOM;
338             } elsif ($test) {
339 302         521 call_it $then, @_;
340             } else {
341 188         331 call_it $else, @_;
342             }
343 4         27 };
344             }
345              
346             sub awhile { # 'while x y'
347 3     3 0 7 my ($while, $do) = @_;
348 3         17 Dparse "while ($while) $do";
349             return sub {
350 46     46   196 Drun "while ($while) $do -> (@_)";
351 46         57 my $test;
352 46         99 while (!bottom($test = (call_it $while, @_)[0])) {
353 505 100       994 if (!$test) {
354 46         201 Drun "END while ($while): (@_)";
355 46         161 return @_;
356             }
357 459         774 @_ = call_it $do, @_;
358             }
359             # Bottom.
360 0         0 BOTTOM;
361             }
362 3         22 }
363              
364             sub forall { # '@' operator, i.e. map
365 6     6 0 12 my $f = shift;
366 6         27 Dparse "using $f";
367             return sub {
368 1     1   11 no strict 'refs';
  1         3  
  1         359  
369 6     6   70 Drun "forall $f (@_)";
370 6         10 map { to_arrayref call_it $f, as_array $_ } @_;
  63         118  
371 6         38 };
372             }
373              
374             sub insert { # '/' operator, i.e. reduce
375 4     4 0 6 my $f = shift;
376 4         28 Dparse "using $f";
377             return sub {
378 1     1   7 no strict 'refs';
  1         3  
  1         2511  
379 4     4   29 Drun "insert $f (@_)";
380 4 50       12 return () unless @_;
381 4         15 my $r = $_[0];
382 4 50       11 return BOTTOM if bottom($r);
383 4         22 foreach (@_[1..$#_]) {
384 53         287 $r = (call_it $f, $r, $_)[0];
385 53 50       455 return BOTTOM if bottom($r);
386             }
387 4         16 $r;
388             }
389 4         33 }
390              
391             sub constant { # constant '`' operator
392 19     19 0 35 my $x = shift;
393 19         42 Dparse $x;
394             return sub {
395 646     646   1382 Drun "constant $x";
396 646         1130 as_array $x;
397 19         114 };
398             }
399              
400             sub apply { # ':' operator
401 31     31 0 53 my ($func, $args) = @_;
402 31         87 return $func->(as_array $args);
403             }
404              
405             my %ops = (); # symbol table for binary operators
406             sub make_binary_ops {
407 34 100   34 0 228 return if keys %ops > 0;
408             # Build binary operator functions.
409 1         4 foreach my $f (qw|+ - * / ** == != < > <= >=|) {
410 11   50     978 $ops{$f} = eval qq{sub {
411             return BOTTOM unless numeric(\@_);
412             \$_[0] $f \$_[1]
413             }
414             } || die $@;
415             }
416             }
417              
418             local $::fp_caller = 'Language::FP';
419             sub pkg { # package in which to bind functions
420 72     72 0 371 $::fp_caller;
421             }
422              
423             my %compile =
424             (
425             val => \&defvar,
426             def => \&defun,
427             application => \&apply,
428             while => \&awhile,
429             if => \&ifelse,
430             compose => \&compose,
431             bu => \&do_bu,
432             insert => \&insert,
433             forall => \&forall,
434             distribute => \&distribute,
435             constant => \&constant,
436             sfunc => sub {
437             my $x = $_[0] || die "sfunc($#_): (@_)";
438             sub { $_[$x - 1] }
439             },
440             id => sub {
441             my $ret = findsym($_[0], 'CODE');
442             unless ($ret) {
443             warn "Undefined function $_[0].";
444             return \&BOTTOM;
445             }
446             $ret;
447             },
448             data => sub { @_ },
449             id_undef => sub { shift },
450             op => sub {
451             confess "unknown operator '$_[0]'" unless exists $ops{$_[0]};
452             return $ops{$_[0]};
453             }
454             );
455              
456             sub closure_compile { # internal compiler function
457 431     431 0 513 my $tree = shift;
458 431 100       907 if (ref $tree ne 'HASH') {
459 126         388 return $tree;
460             }
461 305         474 my $type = $tree->{type};
462 305 50       618 if (exists $compile{$type}) {
463 305         275 my @args = map { closure_compile($_) } @{$tree->{val}};
  397         691  
  305         565  
464 305         797 return $compile{$type}->(@args);
465             } else {
466 0         0 die "Can't handle $tree (type = $type)";
467             }
468             }
469              
470             sub CLOSURE_compile { # external compiler function
471 34     34 0 105 make_binary_ops;
472 34         108 closure_compile(@_);
473             }
474              
475             ######################################################################
476             # The "Big Heinous Eval" compiler.
477              
478             =for comment
479              
480             Since Perl's sub calls are slow, I decided to try compiling FP def's
481             down to single, heinous Perl functions. As I suspected, this turns
482             out to be much faster than the other implementation, though debugging
483             is much more of a challenge.
484              
485             Each code generating function should return an expression that will
486             evaluate to its result in list context, and that has enough parens
487             around it to avoid confusing Perl's parser.
488              
489             The functions should use temporaries where necessary to avoid
490             evaluating any of its arguments more than once. These temporaries
491             cannot be references, since the arguments generally won't be real
492             arrays, but expressions producing them.
493              
494             =cut
495              
496             sub seq($) { # Turn a sequence into an expression
497 114     114 0 4723 return '(do {
498             '.$_[0].'
499             })';
500             }
501              
502             my $gen = 0;
503             sub gensym { # yep.
504 62 100   62 0 130 if (wantarray) {
505 7         14 return map { $_.'_'.++$gen } @_;
  18         55  
506             } else {
507 55         169 return $_[0].'_'.++$gen;
508             }
509             }
510              
511             my %bhe_compile =
512             (
513             val => sub {
514             my ($name, $val, $rhs) = @_;
515             $name = bhe_compile($name, undef);
516             eval '@'.pkg()."::$name = ".bhe_compile($val, $rhs).";'ok';";
517             if ($@) {
518             warn "Compilation error in $name: $@" if $@;
519             BOTTOM;
520             } else {
521             q{'ok'};
522             }
523             },
524             def => sub {
525             my ($name, $val, $rhs) = @_;
526             my $BODY = bhe_compile($val, $rhs);
527             my $NAME = pkg().'::'.bhe_compile($name, undef);
528             Dparse "\n---\n", $body, "\n---\n";
529             eval "sub $NAME { $BODY }";
530             if ($@) {
531             warn "Compilation error in $NAME: $@" if $@;
532             BOTTOM;
533             } else {
534             q{'ok'};
535             }
536             },
537             application => sub {
538             my ($func, $args, $rhs) = @_;
539             my $arg = bhe_compile($args, undef);
540             return bhe_compile($func, $arg);
541             },
542             while => sub {
543             my ($while, $do, $rhs) = @_;
544             my ($test, $res) = gensym '$WHILE', '@WHILE';
545             my $WHILE = bhe_compile($while, $res);
546             my $DO = bhe_compile($do, $res);
547             seq <
548             my $res = $rhs;
549             while (my $test = ($WHILE)[0]) {
550             $res = $DO;
551             return BOTTOM if bottom($res);
552             }
553             $res;
554             END
555             },
556             if => sub {
557             my ($if, $then, $else, $rhs) = @_;
558             my $x = gensym '@IF';
559             my ($IF, $THEN, $ELSE)
560             = map { bhe_compile($_, $x) } ($if, $then, $else);
561             seq <
562             my $x = $rhs;
563             if (($IF)[0]) {
564             $THEN;
565             } else {
566             $ELSE;
567             }
568             END
569             },
570             compose => sub {
571             my $rhs = pop;
572             my @funcs = @_;
573             my $ret = $rhs;
574             while (my $x = pop @funcs) {
575             $ret = bhe_compile($x, $ret);
576             }
577             $ret;
578             },
579             bu => sub {
580             my ($f, $a, $rhs) = @_;
581             my $A = bhe_compile($a, undef);
582             return bhe_compile($f, "($A, $rhs)");
583             },
584             insert => sub {
585             my ($f, $rhs) = @_;
586             my ($r, $x, $xs) = gensym '$INSERT', '$INSERT', '@INSERT';
587             my $DOIT = bhe_compile($f, "($r, $x)");
588             return seq <
589             my $xs = $rhs;
590             if ($xs) {
591             my $r = shift $xs;
592             foreach my $x ($xs) {
593             $r = ($DOIT)[0];
594             return Language::FP::BOTTOM if Language::FP::bottom($r);
595             }
596             $r;
597             } else {
598             (); # nothing to insert
599             }
600             END
601             },
602             forall => sub {
603             my ($f, $rhs) = @_;
604             my $v = gensym '@FORALL';
605             my $body = bhe_compile($f, $v);
606             seq <
607             map {
608             my $v = Language::FP::as_array(\$_);
609             Language::FP::to_arrayref($body)
610             } $rhs
611             ENDS
612             },
613             distribute => sub {
614             my $rhs = pop;
615             my $args = gensym '@DISTRIBUTE';
616             my $ret = "my $args = $rhs;\n(";
617             $ret .= join ",\n\t", map {
618             'Language::FP::to_arrayref('.bhe_compile($_, $args).')'
619             } @_;
620             seq ($ret . ');');
621             },
622             constant => sub { return bhe_compile(shift, undef) },
623             sfunc => sub {
624             my ($x, $rhs) = @_;
625             --$x; # FP indices are one-based.
626             "(($rhs)[$x])";
627             },
628             id => sub {
629             my ($f, $rhs) = @_;
630             my ($code, $fullname) = findsym($f, 'CODE');
631             unless ($code) {
632             warn "Undefined function $f.";
633             return 'return BOTTOM';
634             }
635             unless ($fullname) {
636             warn "Anonymous sub not supported\n";
637             return 'return BOTTOM';
638             }
639             return '&{'.$fullname.'}('.$rhs.')';
640             },
641             data => sub {
642 1     1   1399 use Data::Dumper;
  1         11535  
  1         687  
643             pop; # get rid of rhs.
644             return 'Language::FP::as_array('.seq(Dumper(to_arrayref @_)).')';
645             },
646             id_undef => sub { shift },
647             op => sub {
648             my ($op, $rhs) = @_;
649             my $res = gensym 'OP';
650             seq "my \@$res = $rhs; \$$res\[0] $op \$$res\[1]";
651             }
652             );
653              
654             sub BHE_compile { # bootstrap function for BHE
655 34     34 0 158 my $compiled = bhe_compile(@_, '@_');
656 34         197 Dparse "---\n$compiled\n---\n";
657 34         5631 my @ret = eval $compiled;
658 34 50       319145 warn $@ if $@;
659 34         479 @ret;
660             }
661              
662             sub bhe_compile { # Internal BHE compile function
663 305     305 0 576 my $tree = shift;
664 305         412 my $rhs = shift;
665 305 50       710 if (ref $tree ne 'HASH') {
666             # Terminals should never call bhe_compile
667 0         0 die;
668             }
669 305         502 my $type = $tree->{type};
670 305 50       623 if (exists $compile{$type}) {
671 305         368 return $bhe_compile{$type}->(@{$tree->{val}}, $rhs);
  305         1040  
672             } else {
673 0         0 die "Can't handle $tree (type = $type)";
674             }
675             }
676              
677             ######################################################################
678             ## Exportables:
679              
680             sub import {
681 1     1   146 Language::FP->export_to_level(1, @_);
682              
683             # XXX: maybe consider autoloading these?
684              
685             # Build op-functions.
686 1         8 while (my ($f, $b) = each %op_guts) {
687 22 50 33 576   1927 *{$f} = eval qq{ sub { return BOTTOM if bottom(\@_); $b }};
  22 0 33     80  
  576 0 33     1365  
  576 0       6761  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  4 0       18  
  4 50       13  
  4 50       13  
  4 50       9  
  6 50       58  
  4 50       18  
  4 50       6  
  4         31  
  2         10  
  2         4  
  2         17  
  0         0  
  0         0  
  342         897  
  342         3711  
  0         0  
  0         0  
  0         0  
  0         0  
  4         17  
  4         59  
  6         86  
  6         16  
  6         18  
  6         16  
  96         210  
  2         10  
  2         5  
  2         9  
  2         3  
  2         5  
  2         10  
  2         20  
  2         8  
  6         10  
  12         139  
  2         19  
688 22 50       102 die "$f: $@" if $@;
689             }
690 1         22 1;
691             }
692              
693             sub perl2fp {
694 164     164 1 220 my @ret;
695 164         317 foreach (@_) {
696 224 100 66     1257 if (ref eq 'ARRAY') {
    50          
    100          
    50          
697 96         281 push @ret, '<'.perl2fp(@$_).'>';
698             } elsif (ref) {
699 0         0 die "Expecting ARRAY, got ".ref;
700             } elsif (/$RE{num}{int}/o || /$RE{num}{real}/o) {
701 98         394 push @ret, $_;
702             } elsif (defined) {
703 30         373 push @ret, qq{"$_"};
704             } else {
705 0         0 push @ret, '_|_';
706             }
707             }
708 164         970 join(' ', @ret);
709             }
710              
711             sub fp2perl {
712 0     0 1 0 my $str = shift;
713 0         0 return to_arrayref($P->data($str));
714             }
715              
716             sub fp_eval {
717 68     68 1 933 local $::fp_caller = caller;
718 68         208 my $p = get_parser;
719 68 50       255 if (@_ == 1) {
720 1     1   10 use Data::Dumper;
  1         3  
  1         358  
721 68         685 my $parsed = $P->thing(shift);
722 68 50       5228 unless ($parsed) {
723 0         0 warn "Parse error";
724 0         0 return undef;
725             }
726 68 100       306 if ($::FP_DEBUG =~ /C/) {
727 34         138 return [CLOSURE_compile($parsed)];
728             } else {
729 34         133 return [BHE_compile($parsed)];
730             }
731             }
732              
733 0           my %o = @_;
734 0   0       my $in = $o{in} || 'STDIN';
735 0   0       my $out = $o{out} || 'STDOUT';
736 0           while (<$in>) {
737 0           chomp;
738 0           my $res = $P->thing($_);
739 0 0         unless ($res) {
740 0           warn;
741 0           next;
742             }
743 0           print $out perl2fp($res), "\n";
744             }
745             }
746              
747             1;
748              
749             __END__