File Coverage

blib/lib/Language/Farnsworth/FunctionDispatch.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Language::Farnsworth::FunctionDispatch;
2              
3 1     1   6 use strict;
  1         3  
  1         44  
4 1     1   7 use warnings;
  1         3  
  1         38  
5              
6 1     1   5 use Data::Dumper;
  1         2  
  1         50  
7              
8 1     1   693 use Language::Farnsworth::Variables;
  0            
  0            
9             use Language::Farnsworth::Value::Lambda;
10             use Language::Farnsworth::Value::Array;
11             use Language::Farnsworth::Error;
12              
13             sub new
14             {
15             my $self = {};
16             bless $self, (shift);
17             }
18              
19             sub addfunc
20             {
21             # debug 3, "ADDFUNC", Dumper(\@_);
22             my $self = shift;
23             my $name = shift;
24             my $args = shift;
25             my $value = shift;
26             my $scope = shift;
27            
28             error "No scope given for function $name" unless defined($scope);
29            
30             #generate a "false" lambda tree for this, this will go away
31             #bless [ @_[2,4] ], 'Lambda'
32             my $argbranch = bless [], 'Arglist';
33            
34             for (@$args)
35             {
36             push @$argbranch, bless $_, 'Argele';
37             }
38            
39             my $branch = bless [$argbranch, $value], 'Lambda';
40              
41             #i should really have some error checking here
42             #warn "Depreciated function definition encoutered";
43             # debug 3, "--------------------------", "FUNCTION: ".$name;
44             # debug 3, Dumper($branch);
45             # debug 3, Dumper($value);
46             # debug 3, Dumper($args);
47            
48             my $lambda = new Language::Farnsworth::Value::Lambda($scope, $args, $value, $branch, $name);
49            
50             $self->{funcs}{$name} = {name=>$name, lambda=>$lambda};
51             }
52              
53             sub addfunclamb
54             {
55             my $self = shift;
56             my $name = shift;
57             my $lambda = shift;
58            
59             $lambda->setname($name);
60            
61             $self->{funcs}{$name} = {name => $name, lambda => $lambda};
62             }
63              
64             sub getfunc
65             {
66             my $self = shift;
67             my $name = shift; #which one to get, we return the hashref
68             return $self->{funcs}{$name};
69             }
70              
71             sub isfunc
72             {
73             my $self = shift;
74             my $name = shift;
75              
76             return exists($self->{funcs}{$name});
77             }
78              
79             sub setupargs
80             {
81             my $self = shift;
82             my $eval = shift;
83             my $args = shift;
84             my $argtypes = shift;
85             my $name = shift; #name to display
86             #my $branch = shift;
87              
88             my $vars = $eval->{vars}; #get the scope we need
89              
90             ARG:for my $argc (0..$#$argtypes)
91             {
92             my $n = $argtypes->[$argc][0]; #the rest are defaults and constraints
93             my $v = $args->getarrayref()->[$argc];
94              
95             my $const = $argtypes->[$argc][2];
96              
97             if (ref($const) eq "VarArg")
98             {
99             warn "Working around bug in lambdas!";
100             $const = "VarArg";
101             }
102              
103             if (!defined($v))# || ($v->{dimen}{dimen}{"undef"})) #uncomment for undef== default value
104             {
105             #i need a default value!
106             if (!defined($argtypes->[$argc][1]) && defined($argtypes->[$argc][0]) && (defined($const) && ref($const) !~ /Language::Farnsworth::Value/ && $const ne "VarArg"))
107             {
108             error "Required argument $argc to function $name\[\] missing\n";
109             }
110              
111             $v = $argtypes->[$argc][1];
112             }
113              
114             if (defined($const) && ref($const) =~ /Language::Farnsworth::Value/)
115             {
116             #we have a constraint
117             if (!$v->conforms($const))
118             {
119             error "Constraint not met on argument $argc to $name\[\]\n";
120             }
121             }
122             elsif (defined($const) && $const eq "VarArg")
123             {
124             #we've got a variable argument, it needs to slurp all the rest of the arguments into an array!
125             my $last = $#{$args->getarrayref()};
126             my @vargs = @{$args->getarrayref()}[$argc..$last];
127             my $v = new Language::Farnsworth::Value::Array(\@vargs);
128             $vars->declare($n, $v); #set the variable
129             last ARG; #don't parse ANY more arguments
130             }
131              
132             if (defined $n) #happens when no arguments! so we check if the name is defined
133             {
134             #print "SETVAR $n: ";
135             #print Dumper($argtypes->[$argc]);
136             #print Dumper($vars->{vars});
137             if (!$argtypes->[$argc][3]) #make sure that it shouldn't be byref
138             {
139             $vars->declare($n, $v);
140             }
141             else
142             {
143             #it should be by ref
144             if ($v->getref())
145             {
146             $vars->setref($n, $v->getref());
147             }
148             else
149             {
150             error "Can't get reference from expression for argument $argc";
151             }
152             }
153              
154             #print Dumper($vars->{vars});
155             }
156             }
157             }
158              
159             sub callfunc
160             {
161             my $self = shift;
162             my $eval = shift;
163             my $name = shift;
164             my $args = shift;
165             my $branches = shift;
166              
167             error "Given object as function name, check should happen before this" if (ref($name));
168             error "Function $name is not defined" unless $self->isfunc($name);
169              
170             my $lambda = $self->{funcs}{$name}{lambda};
171              
172             # warn "-------------ATTEMPTING TO CALL FUNCTION!-------------\n";
173             # warn "FUNCTION NAME : $name\n";
174             # warn "Dumper of func: ".Dumper($lambda->{code});
175             # warn "$eval";
176             # warn "".$lambda->getscope();
177             # warn "--------------------THAT IS ALL\n";
178              
179             if ($name eq "eval")
180             {
181             return $lambda->eval($args, $eval);
182             }
183             else
184             {
185             return $lambda * $args;
186             }
187             }
188              
189             sub calllambda
190             {
191             my $self = shift;
192             my $lambda = shift;
193             my $args = shift;
194             my $eval = shift;
195              
196             $eval = $lambda->getscope() unless defined($eval);
197              
198             my $argtypes = $lambda->getargs();
199             my $fval = $lambda->getcode();
200             my $name = $lambda->getname();
201              
202             # warn "LAMBDA---------------\n";
203             # warn Dumper($argtypes, $args, $fval);
204              
205             my $nvars = new Language::Farnsworth::Variables($eval->{vars});
206              
207             my %nopts = (vars => $nvars, funcs => $self, units => $eval->{units}, parser => $eval->{parser});
208             my $neval = $eval->new(%nopts);
209              
210             unless($self->checkparams($args, $argtypes))
211             {
212             if ($lambda->getname())
213             {
214             error "Number of arguments not correct to ".$lambda->getname();
215             }
216             else
217             {
218             error "Number of arguments not correct to lambda";
219             }
220             }
221            
222              
223             $self->setupargs($neval, $args, $argtypes, $name);
224              
225             # warn ref($fval);
226              
227             if (ref($fval) ne "CODE")
228             {
229             # warn "-------------ATTEMPTING TO CALL LAMBDA!-------------\n";
230             #print "FUNCTION NAME : $name\n";
231             # warn "Dumper of lambda: ".Dumper($fval);
232             # warn "--------------------THAT IS ALL\n";
233              
234             return $self->callbranch($neval, $fval);
235             }
236             else
237             {
238             #we have a code ref, so we need to call it, we use perlwrap{} to capture
239             return perlwrap {$fval->($args, $neval, $eval)};
240             }
241             # return $self->callbranch($neval, $fval);
242             }
243              
244             sub callbranch
245             {
246             my $self = shift;
247             my $eval = shift;
248             my $branches = shift;
249             # my $name = shift; #unused
250              
251              
252             # print "CALLBRANCHES :: ";
253             # print $name if defined $name;
254             # print " :: $eval\n";
255              
256             my $return = eval {$eval->evalbranch($branches)};
257             #warn Dumper($@);
258             if (ref($@) && $@->isa("Language::Farnsworth::Error"))
259             {
260             #warn Dumper($@->isreturn);
261             if ($@->isreturn)
262             {
263             return $@->getmsg();
264             }
265             else
266             { #redie the error
267             die $@;
268             }
269             }
270             elsif ($@)
271             {
272             warn "Unhandled perl exception!!!!!!";
273             error EPERL, $@;
274             }
275            
276             return $return;
277             }
278              
279             #this was supposed to be the checks for types and such, but now its something else entirely, mostly
280             sub checkparams
281             {
282             my $self = shift;
283             my $args = shift;
284             my $argtypes = shift;
285              
286             my $vararg = 0;
287              
288             my $neededargs = 0;
289             my $badargs = 0;
290              
291             for my $argt (@$argtypes)
292             {
293             $neededargs++ unless (defined($argt->[1]) || !defined($argt->[0]));
294             $badargs++ if (!defined($argt->[0]));
295             }
296              
297             #might want to change the !~ to something else?
298             #warn "Strange bug here to investigate, lambdas produce blessed array refs for vararg... wtf";
299             $vararg = 1 if (grep {defined($_->[2]) && ref($_->[2]) !~ /Language::Farnsworth::Value/ && (($_->[2] eq "VarArg") || (ref($_->[2]) eq "VarArg"))} @{$argtypes}); #find out if there is a vararg arg
300              
301             #print "NEEDED: $neededargs :: $vararg\n";
302             #print Data::Dumper->Dump([$argtypes, $args->getarrayref()], [qw(argtypes args)]);
303              
304             return 1 if ($vararg || ($args->getarray() <= (@{$argtypes}-$badargs) && $args->getarray() >= $neededargs));
305              
306             #return 0 unless (ref($args) eq "Language::Farnsworth::Value") && ($args->{dimen}->compare({dimen=>{array=>1}}));
307              
308             return 0;
309             }
310              
311             sub getref
312             {
313             my $self = shift;
314             my $argc = shift;
315             my $branch = shift;
316             my $name = shift;
317              
318             #print "\n\nGETREF\n";
319             #print Dumper($branch);
320              
321             if (ref $branch->[1] ne "Array")
322             {
323             #this should add support for some other stuff
324             error "Cannot get a reference if function/lambda is called without []";
325             }
326              
327             my $argexpr = $branch->[1][$argc];
328            
329             #print Dumper($argbranches->[$argc]);
330            
331             if (ref $argexpr ne "Fetch")
332             {
333             error "Argument $argc to $name\[\] is not referencable";
334             }
335              
336             my $ref = $self->{funcs}{$name}->{scope}{vars}->getref($argexpr->[0]);
337              
338             #warn Dumper($argexpr, $ref);
339              
340             return $ref;
341             }
342              
343             1;