File Coverage

blib/lib/Language/MPI.pm
Criterion Covered Total %
statement 361 492 73.3
branch 39 74 52.7
condition 17 48 35.4
subroutine 71 103 68.9
pod 4 99 4.0
total 492 816 60.2


line stmt bran cond sub pod time code
1             # MPI.pm
2            
3             =pod
4            
5             =head1 NAME
6            
7             Language::MPI - 2008.0217 Message Parsing Interpreter
8            
9             =head1 SYNOPSIS
10            
11             Processor for the Message Parsing Interpreter text
12             composition language, based on the MPI found in MU* online
13             environments, adapted for more general semantics.
14            
15             http://en.wikipedia.org/wiki/Message_Parsing_Interpreter
16            
17             =head1 USAGE
18            
19             use Language::MPI;
20             $node = new Language::MPI($noderef);
21             $node->setvar("varname", "varval");
22             $results = $node->parse("tick {set:varname,{time:}} tock");
23             $val = $node->readvar("varname");
24            
25             MPI assumes an operating environment consisting of a set
26             of nodes each of which has a set of named properties. How
27             these nodes and properties are stored and structured is up
28             to the application except that:
29            
30             =over
31            
32             =item * noderefs are perl scalars used by application
33             supplied functions. Something with a printable value is
34             encouraged but not required.
35            
36             =item * properties may be identified by and resolve to
37             plain text strings.
38            
39             =back
40            
41             MPI, in the interest of more general usage, expects some
42             support subroutines to be supplied by app to access nodes
43             and properties. Should any of these not be supplied, errors
44             are trapped to prevent crashing. Functions not needing
45             these should still work properly. Should the application
46             designer wish, app data to be passed to these callbacks may
47             be set into and read from the object by the setvar() and
48             readvar() methods.
49            
50             =over
51            
52             =item mpi_neighbors($thisnode, $pattern, $obj)
53            
54             $thisnode is a noderef.
55             $pattern is a string pattern used to specify which nodes
56             'neighboring' the current node are of interest.
57             returns list of noderefs;
58            
59             =item mpi_prop($thisnode, $propname, $obj)
60            
61             $propname is the string name of a property.
62             returns propval;
63            
64             =item mpi_props($thisnode, $proppat, $obj)
65            
66             $propat is a string specifier to a property directory or a
67             subset of properties.
68             returns list of propnames;
69            
70             =item mpi_propset($thisnode, $propname, $val, $obj)
71            
72             =back
73            
74             =head1 INSTALATION
75            
76             perl Makefile.PL
77             make
78             make install
79            
80             Or simply copy the MPI.pm file to Language/ under the perl
81             modules directory. README and the man file for this package
82             exist as pod data in MPI.pm.
83            
84             =head1 STATUS
85            
86             Some MPI standard functions incomplete or unimplimented. Testing incomplete.
87            
88             =head1 Etc
89            
90             This code developed using perl 5.8.8. Might work with perl
91             5.6.0 or older with proper libraries. Uses strict and warning.
92            
93             Copyright (c)2007 Peter Hanely. All rights reserved.
94             This program is free software; you can redistribute it and/or
95             modify it under the same terms as Perl itself.
96            
97             =head1 LANGUAGE
98            
99             =head2 VARS
100            
101             =over
102            
103             =item Variable names of alphabetic characters are general MPI use.
104            
105             =item Names beginning with an underscore "_" are reserved
106             for mpi internal variables and should not be used by the
107             application.
108            
109             =item Names beginning with "\" are suggested for application
110             values placed in the mpi object.
111            
112             =cut
113            
114 1     1   28747 use strict;
  1         2  
  1         39  
115 1     1   6 use warnings;
  1         2  
  1         1296  
116             # warning good for debug, but produce noise from good code
117 1     1   8 no warnings qw(uninitialized);
  1         7  
  1         162  
118             #use Carp;
119            
120             package Language::MPI;
121            
122             our ($VERSION, @ISA, @EXPORT_OK, $perl_list);
123             #use vars qw($VERSION, @ISA, @EXPORT_OK, $perl_list);
124            
125             BEGIN
126             {
127 1     1   13 require Exporter;
128 1         2 our ($VERSION, @ISA, @EXPORT_OK);
129            
130 1         17 @ISA = qw(Exporter);
131 1         4 @EXPORT_OK = qw(parse setvar readvar simp_functions);
132            
133 1         13179 $VERSION = "2008.0217";
134             }
135            
136             # control functions alter execution of their parameters,
137             # and thus parse their own parameters.
138             my %ctrl_functions =
139             ( 'debug' => \&func_debug,
140             'debugif' => \&func_debugif,
141             'filter' => \&func_filter,
142             'fold' => \&func_fold,
143             'for' => \&func_for,
144             'foreach' => \&func_foreach,
145             # 'func' => \&func_func,
146             'if' => \&func_if,
147             'lit' => \&func_lit,
148             'lsort' => \&func_lsort,
149             'neighbors' => \&func_neighbors,
150             'parse' => \&func_parse,
151             'while' => \&func_while,
152             'with' => \&func_with
153             );
154            
155             # simple functions have their parameters parsed by calling code.
156             my %simp_functions =
157             ( 'abs' => \&func_abs,
158             'add' => \&func_add,
159             'and' => \&func_and,
160             'attr' => \&func_attr,
161             'convsecs' => \&func_convsecs,
162             'convtime' => \&func_convtime,
163             'count' => \&func_count,
164             'date' => \&func_date,
165             'debug' => \&func_debug,
166             'debugif' => \&func_debugif,
167             'dec' => \&func_dec,
168             'default' => \&func_default,
169             'delprop' => \&func_delprop,
170             'dice' => \&func_dice,
171             'dist' => \&func_dist,
172             'div' => \&func_div,
173             'eq' => \&func_eq,
174             'escape' => \&func_escape,
175             'eval' => \&func_eval,
176             'exec' => \&func_exec,
177             'filter' => \&func_filter,
178             'fold' => \&func_fold,
179             'for' => \&func_for,
180             'foreach' => \&func_foreach,
181             'ftime' => \&func_ftime,
182             'fullname' => \&func_fullname,
183             'func' => \&func_func,
184             'ge' => \&func_ge,
185             'gt' => \&func_gt,
186             'if' => \&func_if,
187             'inc' => \&func_inc,
188             'index' => \&func_index,
189             'instr' => \&func_instr,
190             'isnum' => \&func_isnum,
191             'lcommon' => \&func_lcommon,
192             'le' => \&func_le,
193             'list' => \&func_list,
194             'listprops' => \&func_listprops,
195             'lit' => \&func_lit,
196             'lmember' => \&func_lmember,
197             'lrand' => \&func_lrand,
198             'lremove' => \&func_lremove,
199             'lsort' => \&func_lsort,
200             'lt' => \&func_lt,
201             'ltimestr' => \&func_ltimestr,
202             'lunion' => \&func_lunion,
203             'lunique' => \&func_lunique,
204             'max' => \&func_max,
205             'midstr' => \&func_midstr,
206             'min' => \&func_min,
207             'mklist' => \&func_mklist,
208             'mod' => \&func_mod,
209             'mult' => \&func_mult,
210             'name' => \&func_name,
211             'ne' => \&func_ne,
212             'neighbors2' => \&func_neighbors2,
213             'nl' => \&func_nl,
214             'not' => \&func_not,
215             'null' => \&func_null,
216             'or' => \&func_or,
217             'parse' => \&func_parse,
218             'prop' => \&func_prop,
219             'rand' => \&func_rand,
220             'secs' => \&func_secs,
221             'select' => \&func_select,
222             'set' => \&func_set,
223             'sign' => \&func_sign,
224             'smatch' => \&func_smatch,
225             'stimestr' => \&func_stimestr,
226             'store' => \&func_store,
227             'strip' => \&func_strip,
228             'strlen' => \&func_strlen,
229             'sublist' => \&func_sublist,
230             'subst' => \&func_subst,
231             'subt' => \&func_subt,
232             'time' => \&func_time,
233             'timestr' => \&func_timestr,
234             'timesub' => \&func_timesub,
235             'tolower' => \&func_tolower,
236             'toupper' => \&func_toupper,
237             'tzoffset' => \&func_tzoffset,
238             'v' => \&func_v,
239             'version' => \&func_version,
240             'while' => \&func_while,
241             'with' => \&func_with,
242             'xor' => \&func_xor
243             );
244            
245             =head1 MPI primitives
246            
247             =cut
248            
249             =head2 {abs:num}
250            
251             =cut
252            
253             sub func_abs
254 1     1 0 3 { my ($this, $val) = @_;
255 1         4 abs $val->[0];
256             }
257            
258             =head2 {add:num1,num2...}
259            
260             =cut
261            
262             sub func_add
263 2     2 0 4 { my ($this, $val) = @_;
264 2         3 my ($num, $tot);
265 2         4 foreach $num (@$val)
266 5         11 { $tot += $num; }
267 2         5 $tot;
268             }
269            
270             =head2 {and:num1,num2...}
271            
272             =cut
273            
274             sub func_and
275 1     1 0 3 { my ($this, $val) = @_;
276 1         2 my ($num, $tot);
277 1         2 $tot = 1;
278 1         3 foreach $num (@$val)
279             { #$tot &&= $num;
280 3 50       8 if (!$num) { $tot = 0; }
  0         0  
281             }
282 1         4 $tot;
283             }
284            
285             =head2 {attr:attribute...,text}
286            
287             =cut
288            
289             sub func_attr
290 1     1 0 2 { my ($this, $val) = @_;
291             #stub
292 1         4 $$val[-1];
293             }
294            
295             sub func_convsecs
296 0     0 0 0 { my ($this, $val) = @_;
297            
298             }
299            
300             sub func_convtime
301 0     0 0 0 { my ($this, $val) = @_;
302            
303             }
304            
305             =head2 {count:array}
306            
307             =cut
308            
309             sub func_count
310 1     1 0 3 { my ($this, $val) = @_;
311 1         2 my (@arry);
312 1         3 @arry = &unpack_list($val->[0]);
313 1         3 return (scalar (@arry));
314             }
315            
316             =head2 {date:}
317            
318             =cut
319            
320             sub func_date
321 1     1 0 30 { my ($sec, $min, $hour, $mday, $mon, $year) = gmtime (time());
322 1 50       6 if ($year < 1000) { $year += 1900; }
  1         3  
323 1         1 $mon++;
324 1         13 "$mon/$mday/$year";
325             }
326            
327             sub func_debug
328 0     0 0 0 { }
329            
330             sub func_debugif
331 0     0 0 0 { }
332            
333             =head2 {dec:var,dec}
334            
335             =cut
336            
337             sub func_dec
338 3     3 0 6 { my ($this, $val) = @_;
339 3         8 my ($var, $inc) = @$val;
340 3   50     9 $inc = $inc || 1;
341 3         10 $this->{$var} -= $inc;
342             }
343            
344             =head2 {default:var1,var2...}
345            
346             =cut
347            
348             sub func_default
349 1     1 0 2 { my ($this, $val) = @_;
350 1         3 my ($indx) = 0;
351 1   33     9 while (($indx < @$val) && !($val->[$indx])) { $indx ++ }
  0         0  
352 1 50       16 if ($indx < @$val) { $val->[$indx]; }
  1         4  
353 0         0 else { ""; }
354             }
355            
356             =head2 {delprop:var[,obj]}
357            
358             =cut
359            
360             sub func_delprop
361 1     1 0 2 { my ($this, $val) = @_;
362 1         4 my ($prop, $obj) = @$val;
363 1   33     4 $obj = $obj || $this->{'_node'};
364 1 50       17 if (defined &mpi_propset)
365 1         4 { eval (&mpi_propset($obj, $prop, "", $this)); }
366             }
367            
368             =head2 {dice:range[,count[,bonus]]}
369            
370             =cut
371            
372             sub func_dice
373 1     1 0 2 { my ($this, $val) = @_;
374 1         4 my ($range, $count, $bonus) = @$val;
375 1         1 my ($indx, $tot);
376 1 50       6 if ($count <= 0) { $count = 1; }
  0         0  
377 1         4 for ($indx = 0; $indx < $count; $indx ++)
378 3         58 { $tot += int(rand($range)+1); }
379 1         4 $tot+$bonus;
380             }
381            
382             =head2 {dist:x1,y2...}
383            
384             =cut
385            
386             sub func_dist
387 1     1 0 2 { my ($this, $val) = @_;
388 1         3 my ($x1, $y1, $z1, $x2, $y2, $z2) = @$val;
389 1 50       5 if (@$val == 4)
390 0         0 { ($x2, $y2) = ($z1, $x2); }
391 1         5 my ($dx, $dy, $dz) = ($x2-$x1, $y2-$y1, $z2-$z1);
392 1         11 sqrt($dx*$dx + $dy*$dy + $dz*$dz);
393             }
394            
395             =head2 {div:num,num1...}
396            
397             =cut
398            
399             sub func_div
400 1     1 0 3 { my ($this, $val) = @_;
401 1         5 int($val->[0]/$val->[1]);
402             }
403            
404             =head2 {eq:var1,var2}
405            
406             =cut
407            
408             sub func_eq
409 1     1 0 3 { my ($this, $val) = @_;
410 1         5 $val->[0] eq $val->[1];
411             }
412            
413             sub func_escape
414 0     0 0 0 { }
415            
416             =head2 {eval:vars...}
417            
418             =cut
419            
420             sub func_eval
421 1     1 0 4 { my ($this, $val) = @_;
422 1         1 my ($tot, $param);
423 1         3 foreach $param(@$val)
424 1         5 { $tot .= &parse($this, $param); }
425 1         3 $tot;
426             }
427            
428             =head2 {exec:prop[,node]}
429            
430             =cut
431            
432             sub func_exec
433 1     1 0 2 { my ($this, $val) = @_;
434 1         3 my ($prop, $obj) = @$val;
435 1         2 my ($tmp) = "";
436 1   33     3 my ($propval) = eval {&mpi_prop($obj || $this->{'_node'}, $prop, $this)};
  1         8  
437 1 50       9 if ($propval)
438 1   50     3 { $tmp = &parse($this, $propval) || "" };
439 1         3 $tmp;
440             }
441            
442             sub func_filter
443 0     0 0 0 { }
444            
445             sub func_fold
446 0     0 0 0 { }
447            
448             =head2 {for:varname,start,end,increment,commands}
449            
450             =cut
451            
452             # control function, parses its own parameters
453             sub func_for
454 1     1 0 3 { my ($this, $params) = @_;
455 1         2 my ($varname,$start,$end,$increment,$command, $result, $results);
456 1         3 ($varname, $params) = &parse_parameter($this, $params);
457 1         4 ($start, $params) = &parse_parameter($this, $params);
458 1         3 ($end, $params) = &parse_parameter($this, $params);
459 1         3 ($increment, $command) = &parse_parameter($this, $params);
460 1         10 $this->{$varname} = $start;
461 1 50       4 if ($increment > 0)
    0          
462 1         5 { while ($this->{$varname} <= $end)
463 4         7 { ($result, $params) = &parse_parameters($this, $command);
464 4         12 $results .= join '', @$result;
465 4         11 $this->{$varname} += $increment;
466             }
467             }
468             elsif ($increment < 0)
469 0         0 { while ($this->{$varname} <= $end)
470 0         0 { ($result, $params) = &parse_parameters($this, $command);
471 0         0 $results .= join '', @$result;
472 0         0 $this->{$varname} += $increment;
473             }
474             }
475             else # sanity case
476 0         0 { ($result, $params) = &parse_parameters($this, $command);
477 0         0 $results .= join '', @$result;
478             }
479 1         4 ($results, $params);
480             }
481            
482             =head2 {foreach:varname,list,command[,list seperator]}
483            
484             =cut
485            
486             sub func_foreach
487 1     1 0 2 { my ($this, $params) = @_;
488 1         2 my ($varname,$list,$expr,$sep, @list, $val, $res, $result);
489 1         3 ($varname, $params) = &parse_parameter($this, $params);
490 1         4 ($list, $expr) = &parse_parameter($this, $params);
491 1         7 ($params) = &skip_param($this, $expr);
492             #$sep = $sep || "\n";
493 1         4 @list = unpack_list($list, $sep);
494 1         3 foreach $val(@list)
495 0         0 { $this->{$varname} = $val;
496 0         0 ($res) = &parse_parameter($this, $expr);
497 0         0 $result .= $res;
498             }
499 1         4 ($res, $params);
500             }
501            
502             sub func_ftime
503 0     0 0 0 { }
504            
505             sub func_fullname
506 0     0 0 0 { }
507            
508             =head2 {func:name,var1:var2...,commands}
509            
510             =cut
511            
512             sub func_func
513 0     0 0 0 { my ($this, $val) = @_;
514 0         0 my ($func, $vars, $code) = @$val;
515 0         0 $this->{"_f_$func"} = $code;
516 0         0 $this->{"_f_$func v"} = $vars;
517 0         0 "$func, $vars, $code";
518             }
519            
520             =head2 {ge:var1,var2}
521            
522             =cut
523            
524             sub func_ge
525 1     1 0 3 { my ($this, $val) = @_;
526 1         5 $val->[0] >= $val->[1];
527             }
528            
529             =head2 {gt:var1,var2}
530            
531             =cut
532            
533             sub func_gt
534 1     1 0 3 { my ($this, $val) = @_;
535 1         4 $val->[0] > $val->[1];
536             }
537            
538             =head2 {if:condition,true[,false]}
539            
540             =cut
541            
542             sub func_if
543 1     1 0 3 { my ($this, $params) = @_;
544 1         11 my ($check, $ret);
545 1         3 ($check, $params) = &parse_parameter($this, $params);
546 1 50       4 if ($check)
547 1         3 { ($ret, $params) = &parse_parameter($this, $params);
548 1         4 ($params) = &skip_param($this, $params);
549             }
550             else
551 0         0 { ($params) = &skip_param($this, $params);
552 0         0 ($ret, $params) = &parse_parameter($this, $params);
553             }
554 1         4 $ret;
555             }
556            
557             =head2 {inc:var,inc}
558            
559             =cut
560            
561             sub func_inc
562 1     1 0 3 { my ($this, $val) = @_;
563 1         4 my ($var, $inc) = @$val;
564 1   50     6 $inc = $inc || 1;
565 1         5 $this->{$var} += $inc;
566             }
567            
568             =head2 {index:prop[,obj]}
569            
570             =cut
571            
572             sub func_index
573 1     1 0 3 { my ($this, $val) = @_;
574 1         3 my ($prop, $obj) = @$val;
575 1   33     4 $obj = $obj || $this->{"_node"};
576 1         2 $prop = eval {&mpi_prop($obj, $prop, $this)};
  1         3  
577 1 50       9 if ($prop)
578 1 50       2 { eval {&mpi_prop($obj, $prop, $this)} || ""; }
  1         3  
579             }
580            
581             =head2 {insrt:string1,string2}
582            
583             =cut
584            
585             sub func_instr
586 0     0 0 0 { my ($this, $val) = @_;
587 0         0 my ($str1, $str2) = @$val;
588 0         0 index($str1, $str2) + 1;
589             }
590            
591             sub func_isnum
592 0     0 0 0 { my ($this, $val) = @_;
593 0         0 my ($num) = @$val;
594 0 0       0 if (!$num) { $num = '0e0'; }
  0         0  
595 0         0 $num;
596             }
597            
598             =head2 {lcommon:list1,list2}
599            
600             =cut
601            
602             sub func_lcommon
603 1     1 0 2 { my ($this, $val) = @_;
604 1         3 my ($l1, $l2) = @$val;
605 1         2 my (%h, $i, @res);
606 1         3 foreach $i(&unpack_list($l1))
607 0         0 { $h{$i} = 1; }
608 1         4 foreach $i(&unpack_list($l2))
609 1 50       45 { if ($h{$i})
610 0         0 { push @res, $i;
611 0         0 undef $h{$i}; # remove duplicates.
612             }
613             }
614 1         5 &pack_list(@res);
615             }
616            
617             =head2 {le:var1,var2}
618            
619             =cut
620            
621             sub func_le
622 1     1 0 3 { my ($this, $val) = @_;
623 1         5 $val->[0] <= $val->[1];
624             }
625            
626             =head2 {list:props[,obj]}
627            
628             =cut
629            
630             sub func_list
631 1     1 0 3 { my ($this, $val) = @_;
632 1         5 my ($list, $obj) = @$val;
633 1         2 my (@list, $i);
634 1   33     5 $obj = $obj || $this->{"_node"};
635 1         2 foreach $i(eval{&mpi_props($obj, $list, $this)})
  1         5  
636 3         117 { push @list, eval(&mpi_prop($obj, $i, $this)); }
637 1         47 &pack_list(@list);
638             }
639            
640             =head2 {listprops:props[,obj]}
641            
642             =cut
643            
644             sub func_listprops
645 1     1 0 2 { my ($this, $val) = @_;
646 1         3 my ($list, $obj) = @$val;
647 1   33     4 $obj = $obj || $this->{"_node"};
648 1         3 &pack_list(eval{&mpi_props($obj, $list, $this)});
  1         4  
649             }
650            
651             =head2 {lit:expression to not parse}
652            
653             =cut
654            
655             sub func_lit
656 1     1 0 3 { my ($this, $param) = @_;
657 1         2 my ($lit);
658 1         3 ($param, $lit) = &skip_parameters($this, $param);
659 1         4 $lit;
660             }
661            
662             =head2 {lmember:list,item[,delimiter]}
663            
664             =cut
665            
666             sub func_lmember
667 1     1 0 3 { my ($this, $val) = @_;
668 1         4 my ($list, $item, $del) = @$val;
669 1         2 my ($i, @list);
670 1         3 @list = &unpack_list($list, $del);
671 1   33     7 for ($i = 0; $i < @list && $list[$i] ne $item; $i++) { }
672 1 50       5 if ($list[$i] eq $item) { return $i+1; }
  0         0  
673 1         3 0;
674             }
675            
676             =head2 {lrand:list[,delimiter]}
677            
678             =cut
679            
680             sub func_lrand
681 0     0 0 0 { my ($this, $val) = @_;
682 0         0 my ($list, $del) = @$val;
683 0         0 my ($i, @list);
684 0         0 @list = &unpack_list($list, $del);
685 0         0 $list[int(rand @list)];
686             }
687            
688             =head2 {lremove:list1,list2}
689            
690             =cut
691            
692             sub func_lremove
693 0     0 0 0 { my ($this, $val) = @_;
694 0         0 my ($l1, $l2) = @$val;
695 0         0 my (%h, $i, @res);
696 0         0 foreach $i(&unpack_list($l1))
697 0         0 { $h{$i} = 1; }
698 0         0 foreach $i(&unpack_list($l2))
699 0 0       0 { if (!$h{$i})
700 0         0 { push @res, $i;
701 0         0 $h{$i} = 1; # remove duplicates.
702             }
703             }
704 0         0 &pack_list(@res);
705             }
706            
707             sub func_lsort
708 0     0 0 0 { my ($this, $params) = @_;
709 0         0 my ($list, @list, $var1, $var2, $code);
710 0         0 ($list, $params) = &parse_parameter($this, $params);
711             # do fancy sort later
712 0         0 &pack_list(sort &unpack_list($list));
713             }
714            
715             =head2 {lt:num1,num2}
716            
717             =cut
718            
719             sub func_lt
720 0     0 0 0 { my ($this, $val) = @_;
721 0         0 $val->[0] < $val->[1];
722             }
723            
724             sub func_ltimestr
725 0     0 0 0 { }
726            
727             =head2 {lunion:list1,list2}
728            
729             =cut
730            
731             sub func_lunion
732 0     0 0 0 { my ($this, $val) = @_;
733 0         0 my ($l1, $l2) = @$val;
734 0         0 my (%h, $i);
735 0         0 foreach $i(&unpack_list($l1))
736 0         0 { $h{$i} = 1; }
737 0         0 foreach $i(&unpack_list($l2))
738 0         0 { $h{$i} = 1; }
739 0         0 &pack_list(keys %h);
740             }
741            
742             =head2 {lunique:list}
743            
744             =cut
745            
746             sub func_lunique
747 0     0 0 0 { my ($this, $val) = @_;
748 0         0 my ($l1, $l2) = @$val;
749 0         0 my (%h, $i, @res);
750 0         0 foreach $i(&unpack_list($l1))
751 0 0       0 { if (!$h{$i})
752 0         0 { $h{$i} = 1;
753 0         0 push @res,$i;
754             }
755             }
756 0         0 &pack_list(@res);
757             }
758            
759             =head2 {max:var1,var2...}
760            
761             =cut
762            
763             sub func_max
764 1     1 0 3 { my ($this, $val) = @_;
765 1         2 my ($tot, $var);
766 1         3 $tot = $val->[0];
767 1         3 foreach $var(@$val)
768 3 50       12 { if ($tot > $var) { $tot = $var; } }
  0         0  
769 1         4 $tot;
770             }
771            
772             =head2 {midstr:string,start[,end]}
773            
774             =cut
775            
776             sub func_midstr
777 0     0 0 0 { my ($this, $val) = @_;
778 0         0 my ($str, $pos1, $pos2);
779 0         0 substr ($str, $pos1, $pos2);
780             }
781            
782             =head2 {min:var1,var2...}
783            
784             =cut
785            
786             sub func_min
787 1     1 0 3 { my ($this, $val) = @_;
788 1         2 my ($tot, $var);
789 1         3 $tot = $val->[0];
790 1         3 foreach $var(@$val)
791 3 50       11 { if ($tot > $var) { $tot = $var; } }
  0         0  
792 1         4 $tot;
793             }
794            
795             =head2 {mklist:list items}
796            
797             =cut
798            
799             sub func_mklist
800 3     3 0 5 { my ($this, $val) = @_;
801             #join "\n", @$val;
802 3         8 &pack_list(&unpack_list($val));
803             }
804            
805             =head2 {mod:num1,num2}
806            
807             =cut
808            
809             sub func_mod
810 1     1 0 2 { my ($this, $val) = @_;
811 1         6 $val->[0] % $val->[1];
812             }
813            
814             =head2 {mult:num1,num2...}
815            
816             =cut
817            
818             sub func_mult
819 1     1 0 2 { my ($this, $val) = @_;
820 1         2 my ($num, $tot);
821 1         2 $tot = 1;
822 1         3 foreach $num (@$val)
823 3         6 { $tot *= $num; }
824 1         16 $tot;
825             }
826            
827             sub func_name
828 0     0 0 0 { }
829            
830             =head2 {ne:var1,var2}
831            
832             =cut
833            
834             sub func_ne
835 1     1 0 2 { my ($this, $val) = @_;
836 1         4 $val->[0] ne $val->[1];
837             }
838            
839             =head2 {neighbors:varname,pattern,code}
840            
841             =cut
842            
843             sub func_neighbors
844 1     1 0 4 { my ($this, $params) = @_;
845 1         3 my ($varname,$pattern,$expr, @list, $val, $res, $result);
846 1         3 ($varname, $params) = &parse_parameter($this, $params);
847 1         4 ($pattern, $expr) = &parse_parameter($this, $params);
848 1         3 @list = eval {&mpi_neighbors($this->{'_node'}, $pattern, $this)};
  1         8  
849 1         10 foreach $val(@list)
850 1         3 { $this->{$varname} = $val;
851 1         10 ($res, $params) = &parse_parameter($this, $expr);
852 1         5 $result .= $res;
853             }
854 1 50       5 if (@list == 0)
855 0         0 { $res = "";
856 0         0 $params = &skip_parameters($this, $expr);
857 0         0 $params =~ /^\}(.*)/;
858 0   0     0 $params = $! || $params;
859             }
860 1         5 ($res, $params);
861             }
862            
863             =head2 {neighbors2:pattern}
864            
865             =cut
866            
867             sub func_neighbors2
868 0     0 0 0 { my ($this, $params) = @_;
869 0         0 my ($pattern) = @$params;
870 0         0 &pack_list(eval {&mpi_neighbors($this->{'_node'}, $pattern, $this)});
  0         0  
871             }
872            
873            
874             =head2 {nl:}
875            
876             =cut
877            
878             sub func_nl
879 1     1 0 4 { "\n"; }
880            
881             =head2 {not:var}
882            
883             =cut
884            
885             sub func_not
886 1     1 0 3 { my ($this, $val) = @_;
887 1         4 !($val->[0]);
888             }
889            
890             =head2 {null:...}
891            
892             =cut
893            
894             sub func_null
895 1     1 0 3 { ""; }
896            
897             =head2 {or:var1,var2...}
898            
899             =cut
900            
901             sub func_or
902 1     1 0 3 { my ($this, $val) = @_;
903 1         2 my ($num, $tot);
904 1         3 foreach $num (@$val)
905             { #$tot ||= $num;
906 3 100       7 if (!$num) { $tot = 0; }
  1         4  
907             }
908 1         3 $tot;
909             }
910            
911             sub func_parse
912 0     0 0 0 { }
913            
914             =head2 {prop:property,node}
915            
916             =cut
917            
918             sub func_prop
919 1     1 0 2 { my ($this, $val) = @_;
920 1         4 my ($prop, $obj) = @$val;
921 1   33     7 $obj = $obj || $this->{"_node"};
922 1 50       2 eval {&mpi_prop($obj, $prop, $this)} || "";
  1         4  
923             }
924            
925             =head2 {rand:props[,obj]}
926            
927             =cut
928            
929             sub func_rand
930 0     0 0 0 { my ($this, $val) = @_;
931 0         0 my ($list, $obj) = @$val;
932 0         0 my (@list, $i);
933 0   0     0 $obj = $obj || $this->{"_node"};
934 0         0 @list = eval{&mpi_props($obj, $list, $this)};
  0         0  
935 0         0 eval(&mpi_prop($obj, $list[int(rand @list)], $this));
936             }
937            
938             =head2 {secs:}
939            
940             =cut
941            
942             sub func_secs
943 1     1 0 3 { time(); }
944            
945             sub func_select
946 0     0 0 0 { }
947            
948             =head2 {set:var,val}
949            
950             =cut
951            
952             sub func_set
953 3     3 0 7 { my ($this, $val) = @_;
954 3         6 my ($var, $v) = @$val;
955 3 50       17 if ($var =~ /^[a..zA..Z]/) # some vars are reserved for engine use
956 0         0 { $this->{$var} = $v; }
957             }
958            
959             =head2 {sign:num}
960            
961             =cut
962            
963             sub func_sign
964 1     1 0 3 { my ($this, $val) = @_;
965 1         5 $val->[0] <=> 0;
966             }
967            
968             =head2 {smatch:string,pattern}
969            
970             =cut
971            
972             sub func_smatch
973 1     1 0 3 { my ($this, $val) = @_;
974 1         2 my ($str, $pat) = @$val;
975 1         23 $str =~ /($pat)/;
976 1         4 $1
977             }
978            
979             sub func_stimestr
980 0     0 0 0 { }
981            
982             =head2 {store:val,property[,node]}
983            
984             =cut
985            
986             sub func_store
987 1     1 0 3 { my ($this, $val) = @_;
988 1         4 my ($str, $prop, $obj) = @$val;
989 1   33     5 $obj = $obj || $this->{'_node'};
990 1 50       2 eval {&mpi_propset($obj, $prop, $str, $this)} || "";
  1         4  
991             }
992            
993             =head2 {strip:string}
994            
995             =cut
996            
997             sub func_strip
998 1     1 0 3 { my ($this, $val) = @_;
999 1         4 chomp $val->[0];
1000 1         5 $val->[0] =~ s/^\s*//;
1001 1         14 $val->[0] =~ s/\s*$//;
1002 1         3 $val->[0];
1003             }
1004            
1005             =head2 {strlen:string}
1006            
1007             =cut
1008            
1009             sub func_strlen
1010 1     1 0 2 { my ($this, $val) = @_;
1011 1         3 length $val->[0];
1012             }
1013            
1014             =head2 {sublist:list,pos1,pos2[,sep]}
1015            
1016             =cut
1017            
1018             sub func_sublist
1019 0     0 0 0 { my ($this, $val) = @_;
1020 0         0 my ($list, $pos1, $pos2, $sep) = @$val;
1021 0         0 my @list = &unpack_list($list, $sep);
1022 0 0       0 if (!defined($pos2)) { $pos2 = @list; }
  0         0  
1023 0         0 &pack_list( splice( @list, $pos1+1, $pos2-$pos1) );
1024             }
1025            
1026             =head2 {subst:string,old,new}
1027            
1028             =cut
1029            
1030             sub func_subst
1031 1     1 0 3 { my ($this, $val) = @_;
1032 1         4 my ($str, $old, $new) = @$val;
1033 1         14 $str =~ s/$old/$new/g;
1034 1         4 $str;
1035             }
1036            
1037             =head2 {subt:num1,num2...}
1038            
1039             =cut
1040            
1041             sub func_subt
1042 1     1 0 3 { my ($this, $val) = @_;
1043 1         2 my ($num, $tot);
1044 1         3 $tot = shift @$val;
1045 1         3 foreach $num (@$val)
1046 2         6 { $tot -= $num; }
1047 1         4 $tot;
1048             }
1049            
1050             =head2 {time:}
1051            
1052             =cut
1053            
1054             sub func_time
1055 1     1 0 7 { my ($sec, $min, $hour, $mday, $mon, $year) = gmtime(time());
1056 1 50       6 if ($year < 1000) { $year += 1900; }
  1         3  
1057 1         7 "$hour:$min:$sec";
1058             }
1059            
1060             sub func_timestr
1061 0     0 0 0 { }
1062            
1063             sub func_timesub
1064 0     0 0 0 { }
1065            
1066             =head2 {tolower:string}
1067            
1068             =cut
1069            
1070             sub func_tolower
1071 1     1 0 3 { my ($this, $val) = @_;
1072 1         3 lc $val->[0];
1073             }
1074            
1075             =head2 {toupper:string}
1076            
1077             =cut
1078            
1079             sub func_toupper
1080 2     2 0 3 { my ($this, $val) = @_;
1081 2         7 uc $val->[0];
1082             }
1083            
1084             sub func_tzoffset
1085 0     0 0 0 { }
1086            
1087             =head2 {v:varname}
1088            
1089             =cut
1090            
1091             sub func_v
1092 16     16 0 19 { my ($this, $val) = @_;
1093 16         43 $this->{$val->[0]};
1094             }
1095            
1096             =head2 {version:}
1097            
1098             =cut
1099            
1100             sub func_version
1101 1     1 0 3 { $VERSION; }
1102            
1103             =head2 {while:condition,command}
1104            
1105             =cut
1106            
1107             sub chk_cond
1108 3     3 0 4 { my ($this, $cond) = @_;
1109 3         9 my ($res) = &parse_parameter($this, $cond);
1110             # debug
1111             # print "cond $res -- ";
1112 3         16 $res;
1113             }
1114            
1115             sub func_while
1116 1     1 0 2 { my ($this, $params) = @_;
1117 1         3 my ($go,$cond,$expr,$sep, $val, $res, $result, %save, $maxloop);
1118 1         2 $cond = $params;
1119 1         4 ($expr) = &skip_param($this, $params);
1120 1         5 ($params) = &skip_param($this, $expr);
1121 1         3 $maxloop = 255; #sanity
1122 1   66     6 while (&chk_cond($this, $cond) && ($maxloop >= 0))
1123 2         5 { ($res, $params) = &parse_parameter($this, $expr);
1124 2         5 $result .= $res;
1125 2         5 $maxloop --;
1126             }
1127 1         5 ($result, $params);
1128             }
1129            
1130             =head2 {with:varname...}
1131            
1132             =cut
1133            
1134             sub func_with
1135 1     1 0 3 { my ($this, $params) = @_;
1136 1         2 my ($varname,$expr,$val, $res, %save);
1137 1         4 ($varname, $expr) = &parse_parameter($this, $params);
1138 1         4 foreach $val(split /:/, $varname)
1139 1         3 { $save{$val} = $this->{$val};
1140 1         13 $this->{$val} = ''; # a 'null' that isn't undef
1141             }
1142 1         4 ($res, $params) = &parse_parameter($this, $expr);
1143 1         5 foreach $val(split /:/, $varname)
1144 1         3 { $this->{$val} = $save{$val}; }
1145 1         5 ($res, $params);
1146             }
1147            
1148             =head2 {xor:num1,num2...}
1149            
1150             =cut
1151            
1152             sub func_xor
1153 1     1 0 2 { my ($this, $val) = @_;
1154 1         2 my ($num, $tot);
1155 1         2 $tot = shift @$val;
1156 1         4 foreach $num (@$val)
1157 2   50     13 { $tot = ($tot xor $num); }
1158 1         3 $tot;
1159             }
1160            
1161             # ====================================================
1162             # core routines
1163             # ====================================================
1164             =head2 -
1165            
1166             =cut
1167            
1168             =head1 Public object methods
1169            
1170             =cut
1171            
1172             =head2 new(noderef);
1173            
1174             Create new MPI object.
1175            
1176             =cut
1177            
1178             sub new
1179 1     1 1 13 { my ($class, $node) = @_;
1180 1         3 my (%this);
1181 1         4 $this{'_node'} = $node;
1182 1         5 bless \%this, $class;
1183             }
1184            
1185             =head2 $mpi->setvar(var,val);
1186            
1187             Sets a variable in the mpi object to a scalar value.
1188            
1189             =cut
1190            
1191             sub setvar
1192 0     0 1 0 { my ($this, $var, $val) = @_;
1193 0         0 $this->{$var} = $val;
1194             }
1195            
1196             =head2 $mpi->readvar(var);
1197            
1198             Reads a scalar value from the mpi object
1199            
1200             =cut
1201            
1202             sub readvar
1203 0     0 1 0 { my ($this, $var) = @_;
1204 0         0 $this->{$var};
1205             }
1206            
1207             # unpack a list in either MPI \n delimited string or perl list ref
1208             sub unpack_list
1209 8     8 0 14 { my ($list, $sep) = @_;
1210 8         9 my (@list);
1211 8 100       18 if (ref $list)
1212 3         8 { @list = @$list; }
1213             else
1214 5   50     41 { $sep = $sep || "\n";
1215 5         11 @list = split "\n", $list;
1216             }
1217 8         28 @list;
1218             }
1219            
1220             sub pack_list
1221 6 50   6 0 18 { if ($perl_list) {return \@_}
  0         0  
1222             else
1223 6         25 { join "\n", @_; }
1224             }
1225            
1226             # parse 1 parameter, which may contain a mix of plain text and MPI functions
1227             sub parse_parameter
1228 215     215 0 302 { my ($this, $text) = @_;
1229 215         260 my ($result, $prefix, $remainder, $match, $value);
1230 215         258 $result = "";
1231             # find start of MPI function or terminating comma
1232 215         1060 while ($text =~ /(,|\}|\{\w+:?)/ )
1233 230         471 { $match = $1;
1234             # terminating comma or '}', split remaining text into result and remainder
1235 230 100       930 if ($match =~ /(,|\})/)
    50          
1236 149         921 { ($prefix, $remainder) = split $match, $text, 2;
1237 149         216 $result .= $prefix;
1238 149         596 return ($result, $remainder, $match);
1239             }
1240             # mpi function, evaluate
1241             elsif ($match =~ /\{(\w+)/ )
1242 81         1011 { ($prefix, $remainder) = split $match, $text, 2;
1243 81         131 $result .= $prefix;
1244 81         174 ($value, $remainder) = &eval_mpi($this, $1, $remainder);
1245             #if (! defined($value))
1246             #{ "catch"; }
1247 81         138 $result .= $value;
1248 81         260 $text = $remainder;
1249             }
1250             }
1251             # nothing left to parse
1252 66         250 ($result.$text, '', '');
1253             }
1254            
1255             # skip a parameter
1256             sub skip_param
1257 4     4 0 8 { my ($this, $text) = @_;
1258 4         7 my ($match, $prefix, $remainder);
1259 4         22 while ($text =~ /(,|\}|\{\w+:?)/ )
1260 8         17 { $match = $1;
1261             # terminating comma or }, split remaining text into result and remainder
1262 8 100       69 if ($match =~ /([,\}])/)
    50          
1263 4         31 { ($prefix, $remainder) = split $1, $text, 2;
1264 4         16 return ($remainder, $match);
1265             }
1266             # mpi function, recurse in and skip
1267             elsif ($match =~ /\{(\w+)/ )
1268 4         35 { ($prefix, $remainder) = split $match, $text, 2;
1269 4         11 ($remainder) = &skip_parameters($this, $remainder);
1270 4         23 $text = $remainder;
1271             }
1272             }
1273             # nothing left to parse
1274 0         0 ('');
1275             }
1276            
1277             # skip all remaining parameters
1278             sub skip_parameters
1279 6     6 0 11 { my ($this, $text) = @_;
1280 6         7 my ($match, $prefix, $prefix1, $remainder);
1281 6         30 while ($text =~ /(\}|\{\w+:?)/ )
1282 7         13 { $match = $1;
1283             # terminating }, split remaining text into result and remainder
1284 7 100       56 if ($match =~ /([\}])/)
    50          
1285 6         26 { ($prefix, $remainder) = split $1, $text, 2;
1286 6         10 $prefix1 .= $prefix;
1287 6         29 return ($remainder, $prefix1, $1);
1288             }
1289             # mpi function, recurse in and skip
1290             elsif ($match =~ /\{(\w+)/ )
1291 1         10 { ($prefix, $remainder) = split $match, $text, 2;
1292 1         4 $prefix1 .= $prefix.$match;
1293 1         5 ($remainder, $prefix, $match) = &skip_parameters($this, $remainder);
1294 1         3 $prefix1 .= $prefix.$match;
1295 1         6 $text = $remainder;
1296             }
1297             }
1298             # nothing left to parse
1299 0         0 ('');
1300             }
1301            
1302             # parse all parameters for the current function
1303             sub parse_parameters
1304 78     78 0 105 { my ($this, $text) = @_;
1305 78         86 my @params;
1306 78         79 my ($result, $term);
1307 78         145 $term = "zz";
1308 78         247 while ($term =~ /[^\}]/)
1309 131         230 { ($result, $text, $term) = &parse_parameter($this, $text);
1310 131         568 push @params, $result;
1311             }
1312 78         264 (\@params, $text);
1313             }
1314            
1315             # evaluate 1 MPI function
1316             sub eval_mpi
1317 81     81 0 256 { my ($this, $function, $text) = @_;
1318 81         90 my ($result, $remainder, $params);
1319 81         111 $function = lc $function;
1320 81         107 $result = "";
1321             # if function is in control function list, pass raw text and let function parse.
1322 81 100       265 if ($ctrl_functions{$function})
    100          
    50          
1323 7         33 { ($result, $remainder) = &{$ctrl_functions{$function}}($this, $text);
  7         26  
1324             }
1325             # parse parameters and pass results to function.
1326             elsif ($simp_functions{$function})
1327 73         125 { ($params, $remainder) = &parse_parameters($this, $text);
1328 73         124 ($result) = &{$simp_functions{$function}}($this, $params);
  73         226  
1329             }
1330             # else concat parameters
1331             elsif ($this->{"_f_$function"})
1332 0         0 { my (@vars, $var, $i, %save);
1333 0         0 ($params, $remainder) = &parse_parameters($this, $text);
1334 0         0 @vars = split /:/, $this->{"_f_$function v"};
1335 0         0 for ($i = 0; $i < @vars; $i++)
1336 0         0 { $var = $vars[$i];
1337 0         0 $save{$var} = $this->{$var};
1338 0         0 $this->{$var} = $params->[$i];
1339             }
1340 0         0 $result = &parse($this, $this->{"_f_$function"});
1341 0         0 foreach $var(split /:/, $this->{"_f_$function v"})
1342 0         0 { $this->{$var} = $save{$var}; }
1343             }
1344             else
1345 1         3 { ($params, $remainder) = &parse_parameters($this, $text);
1346 1         4 $result = join (',', @$params);
1347             }
1348 81         599 ($result, $remainder);
1349             }
1350            
1351             =head2 $mpi->parse(string);
1352            
1353             Processes a string for MPI codes
1354            
1355             =cut
1356            
1357             # parse a text block. simular to parse_parameter, except not terminating at ','
1358             sub parse
1359 65     65 1 16750 { my ($this, $text) = @_;
1360 65         72 my ($result, $value, $term);
1361             # while we have unprocessed text
1362             # find MPI, if any.
1363             # preceeding text copied to result.
1364             # MPI evaluated and retuned values added to result.
1365            
1366 65         78 $term = "zz"; # meaningless except not null
1367 65         192 while ($term)
1368 66         130 { ($value, $text, $term) = &parse_parameter($this, $text);
1369 66         187 $result .= $value.$term;
1370             }
1371            
1372 65         186 $result;
1373             }
1374            
1375             1;
1376            
1377             __END__