File Coverage

lib/Parse/RPN.pm
Criterion Covered Total %
statement 166 189 87.8
branch 60 76 78.9
condition 12 14 85.7
subroutine 12 16 75.0
pod 3 7 42.8
total 253 302 83.7


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             ###########################################################
3             # RPN package with DICT
4             # Gnu GPL2 license
5             #
6             # Fabrice Dulaunoy
7             ###########################################################
8             # ChangeLog:
9             #
10             ###########################################################
11              
12             =head1 NAME
13              
14             Parse::RPN (2.xx) - Is a minimalist RPN parser/processor (a little like FORTH)
15              
16             =head1 SYNOPSIS
17              
18             use Parse::RPN;
19             $result=rpn(string ...);
20             @results=rpn(string ...);
21            
22             $error=rpn_error();
23              
24             string... is a list of RPN operators and values separated by a coma
25             in scalar mode RPN return the result of the calculation (If the stack contain more then one element,
26             you receive a warning and the top value on the stack)
27             in array mode, you receive the content of the stack after evaluation
28              
29             =head1 DESCRIPTION
30              
31             rpn() receive in entry a scalar of one or more elements coma separated
32             and evaluate as an RPN (Reverse Polish Notation) command.
33             The function split all elements and put in the stack.
34             The operator are case sensitive.
35             The operator are detect as is, if they are alone in the element of the stack.
36             Extra space before or after are allowed
37             (e.g "3,4,MOD" here MOD is an operator but it is not the case in "3,4,MOD 1")
38             If element is not part of the predefined operator (dictionary), the element is push as a litteral.
39             If you would like to put a string which is part of the dictionary, put it between quote or double-quote
40             (e.g "3,4,'MOD'" here MOD is a literal and the evaluation return MOD)
41             If the string contain a coma, you need also to quote or double-quote the string.
42             (be care to close your quoted or double-quoted string)
43              
44             The evaluation follow the rule of RPN or FORTH or POSTCRIPT or pockect calcutor HP.
45             Look on web for documentation about the use of RPN notation.
46            
47             I use this module in a application where the final user need to create and maintain
48             a configuration file with the possibility to do calculation on variables returned from application.
49            
50             The idea of this module is comming from Math::RPN of Owen DeLong, owen@delong.com that I used for more then a year
51             before some of my customer would like more...
52              
53             rpn_error() return the last error from the evaluation (illegal division by 0, error from the PERL function execution...)
54             each time that rpn() is call the rpn_error() is reinitianised.
55              
56             =cut
57              
58             package Parse::RPN;
59 15     15   2108306 use strict;
  15         32  
  15         618  
60 15     15   7662 use HTTP::Date;
  15         99045  
  15         1106  
61 15     15   125 use Fcntl;
  15         28  
  15         5396  
62              
63 15     15   126 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  15         58  
  15         1613  
64              
65             require Exporter;
66             require AutoLoader;
67              
68 15     15   12859 use Data::Dumper;
  15         152608  
  15         1281  
69 15     15   113 use Carp qw(carp);
  15         27  
  15         366292  
70              
71             sub cc
72             {
73 0     0 0 0 my $info = shift;
74 0   0     0 my $line = ( caller( 0 ) )[2] || 0;
75 0         0 carp "[$line] $info";
76             }
77              
78             @ISA = qw(Exporter AutoLoader);
79              
80             @EXPORT = qw(rpn rpn_error rpn_separator_out rpn_separator_in);
81              
82             $VERSION = '2.89';
83              
84             my %dict;
85             my %pub_dict;
86             my %var;
87              
88             my @loop;
89             my @begin;
90             my @return;
91              
92             my $DEBUG;
93              
94             my $separator_out = ' ';
95             my $separator_in = ',';
96             ########################
97             # mathematic operators
98             ########################
99              
100             =head1 MATHEMATIC operators
101            
102             =cut
103              
104             =head2 a b +
105              
106             return the result of 'a' + 'b'
107            
108             =cut
109              
110             $dict{'+'} = sub {
111              
112             my $work1 = shift;
113             my $a = pop @{ $work1 };
114             my $b = pop @{ $work1 };
115             my @ret;
116             push @ret, $a + $b;
117             return \@ret, 2, 0;
118             };
119              
120             =head2 a b -
121              
122             return the result of 'a' - 'b'
123            
124             =cut
125              
126             $dict{'-'} = sub {
127             my $work1 = shift;
128             my $a = pop @{ $work1 };
129             my $b = pop @{ $work1 };
130             my @ret;
131             push @ret, $b - $a;
132             return \@ret, 2, 0;
133             };
134              
135             =head2 a b *
136              
137             return the result of 'a' * 'b'
138            
139             =cut
140              
141             $dict{'*'} = sub {
142             my $work1 = shift;
143             my $a = pop @{ $work1 };
144             my $b = pop @{ $work1 };
145             my @ret;
146             push @ret, $b * $a;
147             return \@ret, 2, 0;
148             };
149              
150             =head2 a b /
151              
152             return the result of 'a' / 'b'
153             if b =0 return '' (to prevent exception raise)
154            
155             =cut
156              
157             $dict{'/'} = sub {
158             my $work1 = shift;
159             my $a = pop @{ $work1 };
160             my $b = pop @{ $work1 };
161             my @ret;
162             my $c;
163             eval { ( $c = $b / $a ) };
164             if ( $@ )
165             {
166             chomp $@;
167             $DEBUG = $@;
168             @ret = ();
169             }
170             else
171             {
172             push @ret, $c;
173             }
174             return \@ret, 2, 0;
175             };
176              
177             =head2 a b **
178              
179             return the result of 'a' ** 'b' (exponant)
180            
181             =cut
182              
183             $dict{'**'} = sub {
184             my $work1 = shift;
185             my $a = pop @{ $work1 };
186             my $b = pop @{ $work1 };
187             my @ret;
188             push @ret, $b**$a;
189             return \@ret, 2, 0;
190             };
191              
192             =head2 a SRQRT
193              
194             return the square root of a
195            
196             =cut
197              
198             $dict{'SQRT'} = sub {
199             my $work1 = shift;
200             my $a = pop @{ $work1 };
201            
202             my @ret;
203             push @ret, $a**.5;
204             return \@ret, 1, 0;
205             };
206             =head2 a 1+
207              
208             return the result of 'a' +1
209            
210             =cut
211              
212             $dict{'1+'} = sub {
213             my $work1 = shift;
214             my $a = pop @{ $work1 };
215             my @ret;
216             push @ret, $a + 1;
217             return \@ret, 1, 0;
218             };
219              
220             =head2 a 1-
221              
222             return the result of 'a' -1
223            
224             =cut
225              
226             $dict{'1-'} = sub {
227             my $work1 = shift;
228             my $a = pop @{ $work1 };
229             my @ret;
230             push @ret, $a - 1;
231             return \@ret, 1, 0;
232             };
233              
234             =head2 a 2-
235              
236             return the result of 'a' -2
237            
238             =cut
239              
240             $dict{'2-'} = sub {
241             my $work1 = shift;
242             my $a = pop @{ $work1 };
243             my @ret;
244             push @ret, $a - 2;
245             return \@ret, 1, 0;
246             };
247              
248             =head2 a 2+
249              
250             return the result of 'a' +2
251            
252             =cut
253              
254             $dict{'2+'} = sub {
255             my $work1 = shift;
256             my $a = pop @{ $work1 };
257             my @ret;
258             push @ret, $a + 2;
259             return \@ret, 1, 0;
260             };
261              
262             =head2 a b MOD
263              
264             return the result of 'a' % 'b'
265            
266             =cut
267              
268             $dict{MOD} = sub {
269             my $work1 = shift;
270             my $a = pop @{ $work1 };
271             my $b = pop @{ $work1 };
272             my @ret;
273             push @ret, $b % $a;
274             return \@ret, 2, 0;
275             };
276              
277             =head2 a ABS
278              
279             return the result of abs 'a'
280            
281             =cut
282              
283             $dict{ABS} = sub {
284             my $work1 = shift;
285             my $a = pop @{ $work1 };
286             my @ret;
287             push @ret, abs( $a );
288             return \@ret, 1, 0;
289              
290             };
291              
292             =head2 a INT
293              
294             return the result of INT 'a'
295            
296             =cut
297              
298             $dict{INT} = sub {
299             my $work1 = shift;
300             my $a = pop @{ $work1 };
301             my @ret;
302             push @ret, int( $a );
303             return \@ret, 1, 0;
304             };
305              
306             =head2 a +-
307              
308             return the result negate value of 'a' (- 'a' )
309            
310             =cut
311              
312             $dict{'+-'} = sub {
313             my $work1 = shift;
314             my $a = pop @{ $work1 };
315             my @ret;
316             push @ret, -( $a );
317             return \@ret, 1, 0;
318             };
319              
320             =head2 a REMAIN
321              
322             return the result of 'a' - int 'a' (fractional part of 'a' )
323            
324             =cut
325              
326             $dict{REMAIN} = sub {
327             my $work1 = shift;
328             my $a = pop @{ $work1 };
329             my @ret;
330             push @ret, $a - int( $a );
331             return \@ret, 2, 0;
332             };
333              
334             =head2 a SIN
335              
336             return the result of sin 'a' ('a' in RADIAN)
337            
338             =cut
339              
340             $dict{SIN} = sub {
341             my $work1 = shift;
342             my $a = pop @{ $work1 };
343             my @ret;
344             push @ret, sin( $a );
345             return \@ret, 1, 0;
346             };
347              
348             =head2 a COS
349              
350             return the result of cos 'a' ('a' in RADIAN)
351            
352             =cut
353              
354             $dict{COS} = sub {
355             my $work1 = shift;
356             my $a = pop @{ $work1 };
357             my @ret;
358             push @ret, cos( $a );
359             return \@ret, 1, 0;
360             };
361              
362             =head2 a TAN
363              
364             return the result of tan 'a' ('a' in RADIAN)
365            
366             =cut
367              
368             $dict{TAN} = sub {
369             my $work1 = shift;
370             my $a = pop @{ $work1 };
371             my @ret;
372             push @ret, ( sin( $a ) / cos( $a ) );
373             return \@ret, 1, 0;
374             };
375              
376             =head2 a CTAN
377              
378             return the result of cotan 'a' ('a' in RADIAN)
379            
380             =cut
381              
382             $dict{CTAN} = sub {
383             my $work1 = shift;
384             my $a = pop @{ $work1 };
385             my @ret;
386             push @ret, ( cos( $a ) / sin( $a ) );
387             return \@ret, 1, 0;
388             };
389              
390             =head2 a LN
391              
392             return the result of ln 'a'
393             if = 0 return '' (to prevent exception raise)
394            
395             =cut
396              
397             $dict{LN} = sub {
398             my $work1 = shift;
399             my $a = pop @{ $work1 };
400             my @ret;
401             my $c;
402             eval { ( $c = log( $a ) ) };
403             if ( $@ )
404             {
405             chomp $@;
406             $DEBUG = $@;
407             @ret = ();
408             }
409             else
410             {
411             push @ret, $c;
412             }
413             return \@ret, 1, 0;
414             };
415              
416              
417             =head2 a LOGB
418              
419             return the result of log 'a' in base 'b'
420             if = 0 return '' (to prevent exception raise)
421            
422             =cut
423              
424             $dict{LOGB} = sub {
425             my $work1 = shift;
426             my $a = pop @{ $work1 };
427             my $b = pop @{ $work1 };
428             my @ret;
429             my @ret;
430             my $c;
431             my $d;
432             eval {
433             ( $c = log( $a ) );
434             ( $d = log( $b ) );
435             };
436             if ( $@ )
437             {
438             chomp $@;
439             $DEBUG = $@;
440             @ret = ();
441             }
442             else
443             {
444             push @ret, $d/$c;
445             }
446             return \@ret, 2, 0;
447             };
448              
449              
450              
451             =head2 a EXP
452              
453             return the result of 'e' ** 'a'
454            
455             =cut
456              
457             $dict{EXP} = sub {
458             my $work1 = shift;
459             my $a = pop @{ $work1 };
460             my @ret;
461             push @ret, exp( $a );
462             return \@ret, 1, 0;
463             };
464              
465             =head2 PI
466              
467             return the value of PI (3.14159265358979)
468            
469             =cut
470              
471             $dict{PI} = sub {
472             my @ret;
473             push @ret, "3.1415926535898";
474             return \@ret, 0, 0;
475             };
476              
477             =head2 a b MIN
478              
479             return the smallest value of the 2 arguments
480            
481             =cut
482              
483             $dict{MIN} = sub {
484             my $work1 = shift;
485             my $a = pop @{ $work1 };
486             my $b = pop @{ $work1 };
487             my @ret;
488             push @ret, ( $a < $b ? $a : $b );
489             return \@ret, 2, 0;
490             };
491              
492             =head2 a b MAX
493              
494             return the greatest value of the 2 arguments
495            
496             =cut
497              
498             $dict{MAX} = sub {
499             my $work1 = shift;
500             my $a = pop @{ $work1 };
501             my $b = pop @{ $work1 };
502             my @ret;
503             push @ret, ( $a > $b ? $a : $b );
504             return \@ret, 2, 0;
505             };
506              
507             =head2 a MINX
508              
509             return the smallest value from the a elements from the stack
510            
511             =cut
512              
513             $dict{MINX} = sub {
514             my $work1 = shift;
515             my $nbr = pop @{ $work1 };
516             my $len = scalar( @{ $work1 } );
517             my @ret;
518             my $tmp =@{ $work1 }[ $len -1];
519             for my $i ( 1 .. $nbr )
520             {
521             my $b = @{ $work1 }[ $len - $i ];
522             $tmp = $tmp < $b ? $tmp : $b;
523             }
524             push @ret, $tmp;
525             return \@ret, $nbr + 1, 0;
526             };
527              
528             =head2 a b MAXX
529              
530             return the greatest value from the a elements from the stack
531            
532             =cut
533              
534             $dict{MAXX} = sub {
535             my $work1 = shift;
536             my $nbr = pop @{ $work1 };
537             my $len = scalar( @{ $work1 } );
538             my @ret;
539             my $tmp = 0;
540             for my $i ( 1 .. $nbr )
541             {
542             my $b = @{ $work1 }[ $len - $i ];
543             $tmp = $tmp > $b ? $tmp : $b;
544             }
545             push @ret, $tmp;
546             return \@ret, $nbr + 1, 0;
547             };
548             =head2 a SUM
549            
550             sum the a elements from the top of the stack
551             remove these a elements
552             and return the result value on the stack
553              
554             =cut
555              
556             $dict{SUM} = sub {
557             my $work1 = shift;
558             my $nbr = pop @{ $work1 };
559             my $len = scalar( @{ $work1 } );
560             my @ret;
561             my $tmp;
562             for my $i ( 1 .. $nbr )
563             {
564             my $b = @{ $work1 }[ $len - $i ];
565             $tmp += $b;
566             }
567             push @ret, $tmp;
568             return \@ret, $nbr + 1, 0;
569             };
570              
571             =head2 a STATS
572            
573             STATS the a element on top of the stack
574             remove these a element
575             the new variable _SUM_, _MULT_, _ARITH_MEAN_, _GEOM_MEAN_, _QUAD_MEAN_ (= _RMS_), _HARM_MEAN_, _STD_DEV_, _SAMPLE_STD_DEV_, _VARIANCE_,
576              
577             =cut
578              
579             $dict{STATS} = sub {
580             my $work1 = shift;
581             my $nbr = pop @{ $work1 };
582             my $len = scalar( @{ $work1 } );
583             my @ret;
584             my $sum;
585             my $mul = 1;
586             my $harm;
587             my $quad;
588             my @elem;
589             my $std_dev;
590              
591             for my $i ( 1 .. $nbr )
592             {
593             my $b = @{ $work1 }[ $len - $i ];
594             push @elem, $b;
595             $sum += $b;
596             $mul *= $b;
597             $harm += 1 / $b if ( $b );
598             $quad += $b**2;
599             }
600              
601             $var{ _ARITH_MEAN_ } = 0;
602             $var{ _GEOM_MEAN_ } = 0;
603             $var{ _HARM_MEAN_ } = 0;
604             $var{ _VARIANCE_ } = 0;
605             $var{ _STD_DEV_ } = 0;
606              
607             $var{ _SUM_ } = $sum;
608             $var{ _MULT_ } = $mul;
609             $var{ _ARITH_MEAN_ } = $sum / $nbr if ( $nbr != 0 );
610             $var{ _GEOM_MEAN_ } = $mul**( 1 / $nbr ) if ( $nbr != 0 );
611             $var{ _HARM_MEAN_ } = $nbr / $harm if ( $harm );
612             $var{ _QUAD_MEAN_ } = $var{ _RMS_ } = $quad**.5;
613             foreach my $c ( @elem )
614             {
615             $std_dev += ( $c - $var{ _ARITH_MEAN_ } )**2;
616             }
617             $var{ _VARIANCE_ } = ( $std_dev / ( $nbr - 1 ) ) if ( $nbr != 1 );
618             $var{ _STD_DEV_ } = ( $std_dev / $nbr )**.5 if ( $nbr != 0 );
619             $var{ _SAMPLE_STD_DEV_ } = $var{ _VARIANCE_ }**.5;
620              
621             return \@ret, $nbr + 1, 0;
622             };
623              
624             ########################
625             # relational operators
626             ########################
627              
628             =head1 RELATIONAL operators
629              
630             =cut
631              
632             =head2 a b <
633              
634             return the result of 'a' < 'b' ( BOOLEAN value )
635            
636             =cut
637              
638             $dict{'<'} = sub {
639             my $work1 = shift;
640             my $a = pop @{ $work1 };
641             my $b = pop @{ $work1 };
642             my @ret;
643             push @ret, ( $a > $b ? 1 : 0 );
644             return \@ret, 2, 0;
645             };
646              
647             =head2 a b <=
648              
649             return the result of 'a' <= 'b' ( BOOLEAN value )
650            
651             =cut
652              
653             $dict{'<='} = sub {
654             my $work1 = shift;
655             my $a = pop @{ $work1 };
656             my $b = pop @{ $work1 };
657             my @ret;
658             push @ret, ( $a >= $b ? 1 : 0 );
659             return \@ret, 2, 0;
660             };
661              
662             =head2 a b >
663              
664             return the result of 'a' > 'b' ( BOOLEAN value )
665            
666             =cut
667              
668             $dict{'>'} = sub {
669             my $work1 = shift;
670             my $a = pop @{ $work1 };
671             my $b = pop @{ $work1 };
672             my @ret;
673             push @ret, ( $a < $b ? 1 : 0 );
674             return \@ret, 2, 0;
675             };
676              
677             =head2 a b >=
678              
679             return the result of 'a' >= 'b' ( BOOLEAN value )
680            
681             =cut
682              
683             $dict{'>='} = sub {
684             my $work1 = shift;
685             my $a = pop @{ $work1 };
686             my $b = pop @{ $work1 };
687             my @ret;
688             push @ret, ( $a <= $b ? 1 : 0 );
689             return \@ret, 2, 0;
690             };
691              
692             =head2 a b ==
693              
694             return the result of 'a' == 'b' ( BOOLEAN value ) 1 if a == b else 0
695            
696             =cut
697              
698             $dict{'=='} = sub {
699             my $work1 = shift;
700             my $a = pop @{ $work1 };
701             my $b = pop @{ $work1 };
702             my @ret;
703             push @ret, ( $b == $a ? 1 : 0 );
704             return \@ret, 2, 0;
705             };
706              
707             =head2 a b <=>
708              
709             return the result of 'a' <=> 'b' ( BOOLEAN value ) -1 if a < b ,0 if a == b, 1 if a > b
710            
711             =cut
712              
713             $dict{'<=>'} = sub {
714             my $work1 = shift;
715             my $a = pop @{ $work1 };
716             my $b = pop @{ $work1 };
717             my @ret;
718             push @ret, ( $b <=> $a );
719             return \@ret, 2, 0;
720             };
721              
722             =head2 a b !=
723              
724             return the result of 'a' != 'b' ( BOOLEAN value ) 0 if a == b else 1
725            
726             =cut
727              
728             $dict{'!='} = sub {
729             my $work1 = shift;
730             my $a = pop @{ $work1 };
731             my $b = pop @{ $work1 };
732             my @ret;
733             push @ret, ( $b != $a ? 1 : 0 );
734             return \@ret, 2, 0;
735             };
736              
737             =head2 a b v ><
738              
739             return the 1 ( BOOLEAN value ) if v greater than a but lower than b. Otherwise return 0
740             ( aka between boundaries excluded )
741             =cut
742              
743             $dict{'><'} = sub {
744             my $work1 = shift;
745             my $v = pop @{ $work1 };
746             my $b = pop @{ $work1 };
747             my $a = pop @{ $work1 };
748             my @ret;
749             push @ret, ( ( $v > $a && $v < $b ) ? 1 : 0 );
750             return \@ret, 3, 0;
751             };
752              
753             =head2 a b v >=<
754              
755             return 1 ( BOOLEAN value ) if v greater or equal to a but lower or equal to b. Otherwise return 0
756             ( aka between boundaries included )
757            
758             =cut
759              
760             $dict{'>=<'} = sub {
761             my $work1 = shift;
762             my $v = pop @{ $work1 };
763             my $b = pop @{ $work1 };
764             my $a = pop @{ $work1 };
765             my @ret;
766             push @ret, ( ( $v >= $a && $v <= $b ) ? 1 : 0 );
767             return \@ret, 3, 0;
768             };
769              
770             =head2 a b NE
771              
772             return the result of 'a' N< 'b' ( BOOLEAN value ) if a is ISNUM
773            
774             =cut
775              
776             $dict{'N<'} = sub {
777             my $work1 = shift;
778             my $a = pop @{ $work1 };
779             my $b = pop @{ $work1 };
780             my @ret;
781             push @ret, ( ( $b =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/ && $a > $b ) ? 1 : 0 );
782             return \@ret, 2, 0;
783             };
784              
785             =head2 a b NE=
786              
787             return the result of 'a' N<= 'b' ( BOOLEAN value ) if a is ISNUM
788            
789             =cut
790              
791             $dict{'N<='} = sub {
792             my $work1 = shift;
793             my $a = pop @{ $work1 };
794             my $b = pop @{ $work1 };
795             my @ret;
796             push @ret, ( ( $b =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/ && $a >= $b ) ? 1 : 0 );
797             return \@ret, 2, 0;
798             };
799              
800             =head2 a b N>
801              
802             return the result of 'a' N> 'b' ( BOOLEAN value ) if a is ISNUM
803            
804             =cut
805              
806             $dict{'N>'} = sub {
807             my $work1 = shift;
808             my $a = pop @{ $work1 };
809             my $b = pop @{ $work1 };
810             my @ret;
811             push @ret, ( ( $b =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/ && $a < $b ) ? 1 : 0 );
812             return \@ret, 2, 0;
813             };
814              
815             =head2 a b N>=
816              
817             return the result of 'a' N>= 'b' ( BOOLEAN value ) if a is ISNUM
818            
819             =cut
820              
821             $dict{'N>='} = sub {
822             my $work1 = shift;
823             my $a = pop @{ $work1 };
824             my $b = pop @{ $work1 };
825             my @ret;
826             push @ret, ( ( $b =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/ && $a <= $b ) ? 1 : 0 );
827             return \@ret, 2, 0;
828             };
829              
830             =head2 a b N==
831              
832             return the result of 'a' N== 'b' ( BOOLEAN value ) 1 if a == b and a ISNUM else 0
833            
834             =cut
835              
836             $dict{'N=='} = sub {
837             my $work1 = shift;
838             my $a = pop @{ $work1 };
839             my $b = pop @{ $work1 };
840             my @ret;
841             push @ret, ( ( $b =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/ && $b == $a ) ? 1 : 0 );
842             return \@ret, 2, 0;
843             };
844              
845             =head2 a b N!=
846              
847             return the result of 'a' != 'b' ( BOOLEAN value ) 0 if a == b and a ISNUM else 1
848            
849             =cut
850              
851             $dict{'N!='} = sub {
852             my $work1 = shift;
853             my $a = pop @{ $work1 };
854             my $b = pop @{ $work1 };
855             my @ret;
856             push @ret, ( ( $b =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/ && $b != $a ) ? 1 : 0 );
857             return \@ret, 2, 0;
858             };
859              
860              
861              
862             ########################
863             # logical operators
864             ########################
865              
866             =head1 LOGICAL operators
867              
868             =cut
869              
870             =head2 a b OR
871              
872             return the 1 one of the 2 argument are not equal to 0
873            
874             =cut
875              
876             $dict{OR} = sub {
877             my $work1 = shift;
878             my $a = pop @{ $work1 };
879             my $b = pop @{ $work1 };
880             my @ret;
881             push @ret, ( $a || $b );
882             return \@ret, 2, 0;
883             };
884              
885             =head2 a b AND
886              
887             return the 0 one of the 2 argument are equal to 0
888            
889             =cut
890              
891             $dict{AND} = sub {
892             my $work1 = shift;
893             my $a = pop @{ $work1 };
894             my $b = pop @{ $work1 };
895             my @ret;
896             push @ret, ( $a && $b );
897             return \@ret, 2, 0;
898             };
899              
900             =head2 a b XOR
901              
902             return the 0 if the 2 argument are equal
903            
904             =cut
905              
906             $dict{XOR} = sub {
907             my $work1 = shift;
908             my $a = pop @{ $work1 };
909             my $b = pop @{ $work1 };
910             my @ret;
911             push @ret, ( $a xor $b ) ? 1 : 0;
912             return \@ret, 2, 0;
913             };
914              
915             =head2 a b NXOR
916              
917             return the 0 if the 2 argument are equal. Any non numeric elements is seen as a 0.
918            
919             =cut
920              
921             $dict{NXOR} = sub {
922             my $work1 = shift;
923             my $a = pop @{ $work1 };
924             my $b = pop @{ $work1 };
925             $a = $a =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/ ? $a : 0;
926             $b = $b =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/ ? $b : 0;
927             my @ret;
928             push @ret, ( $a xor $b ) ? 1 : 0;
929             return \@ret, 2, 0;
930             };
931              
932             =head2 a NOT
933              
934             return the 0 if the argument is not eqauk to 0
935             return the 1 if the argument is eqauk to 0
936            
937             =cut
938              
939             $dict{NOT} = sub {
940             my $work1 = shift;
941             my $a = pop @{ $work1 };
942              
943             my @ret;
944             push @ret, ( not $a ) ? 1 : 0;
945             return \@ret, 1, 0;
946             };
947              
948             =head2 a TRUE
949              
950             return the 1 if the top of stack is !=0 and if stack not empty
951            
952             =cut
953              
954             $dict{TRUE} = sub {
955             my $work1 = shift;
956             my $a;
957             my $b = 0;
958             if ( scalar @{ $work1 } )
959             {
960             $b = 1;
961             $a = pop @{ $work1 };
962             $a = $a =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/ ? $a : 0;
963             if ( $a > 0 )
964             {
965             $b = 1;
966             }
967             else
968             {
969             $b = 0;
970             }
971             }
972             my @ret;
973             push @ret, $b;
974             return \@ret, 1, 0;
975             };
976              
977             =head2 a FALSE
978              
979             return the 0 if the top of stack is !=0
980            
981             =cut
982              
983             $dict{FALSE} = sub {
984             my $work1 = shift;
985             my $a;
986             my $b = 1;
987             if ( scalar @{ $work1 } )
988             {
989             $b = 0;
990             $a = pop @{ $work1 };
991             $a = $a =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/ ? $a : 0;
992             if ( $a > 0 )
993             {
994             $b = 0;
995             }
996             else
997             {
998             $b = 1;
999             }
1000             }
1001             my @ret;
1002             push @ret, $b;
1003             return \@ret, 1, 0;
1004             };
1005              
1006              
1007             =head2 a b >>
1008              
1009             bitwise shift to the right
1010             shift the bits in a to the left of b level
1011            
1012             =cut
1013              
1014             $dict{'>>'} = sub {
1015             my $work1 = shift;
1016             my $a = pop @{ $work1 };
1017             my $b = pop @{ $work1 };
1018             my @ret;
1019             push @ret, ( $b >> $a );
1020             return \@ret, 2, 0;
1021             };
1022              
1023             =head2 a b <<
1024              
1025             bitwise shift to the left
1026             shift the bits in a to the left of b level
1027            
1028             =cut
1029              
1030             $dict{'<<'} = sub {
1031             my $work1 = shift;
1032             my $a = pop @{ $work1 };
1033             my $b = pop @{ $work1 };
1034             my @ret;
1035             push @ret, ( $b << $a );
1036             return \@ret, 2, 0;
1037             };
1038              
1039              
1040             ########################
1041             # misc operators
1042             ########################
1043              
1044             =head1 MISC operators
1045              
1046             =cut
1047              
1048             =head2 a VAL,RET, "operator" LOOKUP
1049              
1050             test with the "operator" the [a] value on each elements of VAL and if test succeed return the value from array RET with the same index
1051             the "operator" must be quoted to prevent evaluation
1052            
1053             =cut
1054              
1055             $dict{LOOKUP} = sub {
1056             my $work1 = shift;
1057             my $ope = pop @{ $work1 };
1058              
1059             my @RET = @{ $var{ pop @{ $work1 } } };
1060             my @VAL = @{ $var{ pop @{ $work1 } } };
1061             my $item = pop @{ $work1 };
1062             my @ret;
1063             for my $ind ( 0 .. $#VAL )
1064             {
1065             my @tmp;
1066             # push @tmp, $item, $VAL[$ind], $ope;
1067             push @tmp, $VAL[$ind], $item, $ope;
1068             process( \@tmp );
1069             if ( $tmp[0] )
1070             {
1071             push @ret, $RET[$ind];
1072             last;
1073             }
1074             }
1075             return \@ret, 4, 0;
1076             };
1077              
1078             =head2 a VAL,RET, "operator" LOOKUPP
1079              
1080             Test with the perl "operator" the [a] value on each elements of VAL
1081             and if test succeed return the value from array RET with the same index
1082             The "operator" must be quoted to prevent evaluation
1083            
1084             =cut
1085              
1086             $dict{LOOKUPP} = sub {
1087             my $work1 = shift;
1088             my $ope = pop @{ $work1 };
1089             my @RET = @{ $var{ pop @{ $work1 } } };
1090             my @VAL = @{ $var{ pop @{ $work1 } } };
1091             my $item = pop @{ $work1 };
1092             my @ret;
1093             for my $ind ( 0 .. $#VAL )
1094             {
1095             my $test = $item . $ope . $VAL[$ind];
1096             my $state = eval $test;
1097             if ( $state )
1098             {
1099             push @ret, $RET[$ind];
1100             last;
1101             }
1102             }
1103             return \@ret, 4, 0;
1104             };
1105              
1106             =head2 a VAL,RET,OPE LOOKUPOP
1107              
1108             Loop on each item of array VAL and test the value [ a ] with the operator from ope ARRAY
1109             against the corresponding value in array VAL and return the value from array RET with the same index
1110            
1111             =cut
1112              
1113             $dict{LOOKUPOP} = sub {
1114             my $work1 = shift;
1115             my @OPE = @{ $var{ pop @{ $work1 } } };
1116             my @RET = @{ $var{ pop @{ $work1 } } };
1117             my @VAL = @{ $var{ pop @{ $work1 } } };
1118             my $item = pop @{ $work1 };
1119             my @ret;
1120             for my $ind ( 0 .. $#VAL )
1121             {
1122             my @tmp;
1123             # push @tmp, $item, $VAL[$ind], $OPE[$ind];
1124             push @tmp, $VAL[$ind], $item, $OPE[$ind];
1125             process( \@tmp );
1126             if ( $tmp[0] )
1127             {
1128             push @ret, $RET[$ind];
1129             last;
1130             }
1131             }
1132             return \@ret, 4, 0;
1133             };
1134              
1135             =head2 a VAL,RET,OPE LOOKUPOPP
1136              
1137             Loop on each item of array VAL and test the value [ a ] with the perl operator from ope ARRAY
1138             against the corresponding value in array VAL and return the value from array RET with the same index
1139            
1140             =cut
1141              
1142             $dict{LOOKUPOPP} = sub {
1143             my $work1 = shift;
1144             my @OPE = @{ $var{ pop @{ $work1 } } };
1145             my @RET = @{ $var{ pop @{ $work1 } } };
1146             my @VAL = @{ $var{ pop @{ $work1 } } };
1147             my $item = pop @{ $work1 };
1148             my @ret;
1149             for my $ind ( 0 .. $#VAL )
1150             {
1151             my $test = $item . $OPE[$ind] . $VAL[$ind];
1152             my $state = eval $test;
1153             if ( $state )
1154             {
1155             push @ret, $RET[$ind];
1156             last;
1157             }
1158             }
1159             return \@ret, 4, 0;
1160             };
1161              
1162             =head2 TICK
1163              
1164             return the current time in ticks
1165            
1166             =cut
1167              
1168             $dict{TICK} = sub {
1169             my @ret;
1170             push @ret, ( time() );
1171             return \@ret, 0, 0;
1172             };
1173              
1174             =head2 a LTIME
1175              
1176             return the localtime coresponding to the ticks value 'a'
1177             the format is 'sec' 'min' 'hour' 'day_in_the_month' 'month' 'year' 'day_in_week' 'day_year' 'dayloight_saving'
1178             'year' is the elapsed year since 1900
1179             'month' start to 0
1180             The format is the same as localtime() in perl
1181            
1182             =cut
1183              
1184             $dict{LTIME} = sub {
1185             my $work1 = shift;
1186             my $a = pop @{ $work1 };
1187             my @ret;
1188             push @ret, ( localtime( $a ) );
1189             return \@ret, 1, 0;
1190             };
1191              
1192             =head2 a GTIME
1193              
1194             return the gmtime coresponding to the ticks value 'a'
1195             the format is 'sec' 'min' 'hour' 'day_in_the_month' 'month' 'year' 'day_in_week' 'day_year' 'dayloight_saving'
1196             'year' is the elapsed year since 1900
1197             'month' start to 0
1198             The format is the same as gmtime() in perl
1199            
1200             =cut
1201              
1202             $dict{GTIME} = sub {
1203             my $work1 = shift;
1204             my $a = pop @{ $work1 };
1205             my @ret;
1206             push @ret, ( gmtime( $a ) );
1207             return \@ret, 1, 0;
1208             };
1209              
1210             =head2 a HLTIME
1211              
1212             return the localtime coresponding to the ticks value 'a' in a human readable format
1213            
1214             =cut
1215              
1216             $dict{HLTIME} = sub {
1217             my $work1 = shift;
1218             my $a = pop @{ $work1 };
1219             my @ret;
1220             push @ret, scalar( localtime( $a ) );
1221             return \@ret, 1, 0;
1222             };
1223              
1224             =head2 a HGTIME
1225              
1226             return the gmtime coresponding to the ticks value 'a' in a human readable format
1227            
1228             =cut
1229              
1230             $dict{HGTIME} = sub {
1231             my $work1 = shift;
1232             my $a = pop @{ $work1 };
1233             my @ret;
1234             push @ret, scalar( gmtime( $a ) );
1235             return \@ret, 1, 0;
1236             };
1237              
1238             =head2 a HTTPTIME
1239              
1240             return the ticks coresponding to the time value in a format accepted by HTTP::Date
1241            
1242             =cut
1243              
1244             $dict{HTTPTIME} = sub {
1245             my $work1 = shift;
1246             my $a = pop @{ $work1 };
1247             my @ret;
1248             push @ret, str2time( $a );
1249             return \@ret, 1, 0;
1250             };
1251              
1252             =head2 RAND
1253              
1254             return a random value in the range [0,1[
1255            
1256             =cut
1257              
1258             $dict{RAND} = sub {
1259             my @ret;
1260             push @ret, rand();
1261             return \@ret, 0, 0;
1262             };
1263              
1264             =head2 a LRAND
1265              
1266             return a random value in the range [0,'a'[
1267            
1268             =cut
1269              
1270             $dict{LRAND} = sub {
1271             my $work1 = shift;
1272             my $a = pop @{ $work1 };
1273             my @ret;
1274             push @ret, rand( $a );
1275             return \@ret, 1, 0;
1276             };
1277              
1278             =head2 a SPACE
1279              
1280             return the number 'a' formated with space each 3 digits
1281            
1282             =cut
1283              
1284             $dict{SPACE} = sub {
1285             my $work1 = shift;
1286             my $a = pop @{ $work1 };
1287             my $text = reverse $a;
1288             $text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1 /g;
1289             $text = reverse $text;
1290             my @ret;
1291             push @ret, $text;
1292             return \@ret, 1, 0;
1293             };
1294              
1295             =head2 a DOT
1296              
1297             return the number 'a' formated with . (dot) each 3 digits
1298            
1299             =cut
1300              
1301             $dict{DOT} = sub {
1302             my $work1 = shift;
1303             my $a = pop @{ $work1 };
1304             my $text = reverse $a;
1305             $text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1./g;
1306             $text = reverse $text;
1307             my @ret;
1308             push @ret, $text;
1309             return \@ret, 1, 0;
1310             };
1311              
1312             =head2 a NORM
1313              
1314             return the number 'a' normalize by slice of 1000 with extra power value "K", "M", "G", "T", "P" (or nothing if lower than 1000)
1315            
1316             =cut
1317              
1318             $dict{NORM} = sub {
1319             my $work1 = shift;
1320             my $a = pop @{ $work1 };
1321             my $exp;
1322             $a = $a ? $a : 0;
1323             my @EXP = ( " ", "K", "M", "G", "T", "P" );
1324             while ( $a > 1000 )
1325             {
1326             $a = $a / 1000;
1327             $exp++;
1328             }
1329             $a = sprintf "%.2f", $a;
1330             my $ret = "$a $EXP[$exp]";
1331             my @ret;
1332             push @ret, "'" . $ret . "'";
1333             return \@ret, 1, 0;
1334             };
1335              
1336             =head2 a NORM2
1337              
1338             return the number 'a' normalize by slice of 1024 with extra power value "K", "M", "G", "T", "P" (or nothing if lower than 1024)
1339            
1340             =cut
1341              
1342             $dict{NORM2} = sub {
1343             my $work1 = shift;
1344             my $a = pop @{ $work1 };
1345             my $exp;
1346             $a = $a ? $a : 0;
1347             my @EXP = ( " ", "K", "M", "G", "T", "P" );
1348             while ( $a > 1024 )
1349             {
1350             $a = $a / 1024;
1351             $exp++;
1352             }
1353             $a = sprintf "%.2f", $a;
1354             my $ret = "$a $EXP[$exp]";
1355             my @ret;
1356             push @ret, "'" . $ret . "'";
1357             return \@ret, 1, 0;
1358             };
1359              
1360             =head2 a UNORM
1361              
1362             reverse function of NORM
1363             return the number from a 'a' with a sufix "K", "M", "G", "T", "P" (or nothing if lower than 1000)
1364             and calculate the real value base 1000 ( e.g 7k = 7000)
1365            
1366             =cut
1367              
1368             $dict{UNORM} = sub {
1369             my $work1 = shift;
1370             my $a = pop @{ $work1 };
1371             $a = $a ? $a : 0;
1372             $a =~ /(\d+(\.{0,1}\d*)\s*)(\D)/;
1373             my $num = $1;
1374             my $suff = lc( $3 );
1375             my %EXP = (
1376             "k" => 1,
1377             "m" => 2,
1378             "g" => 3,
1379             "t" => 4,
1380             "p" => 5
1381             );
1382             my $mult = 0;
1383              
1384             if ( exists( $EXP{ $suff } ) )
1385             {
1386             $mult = $EXP{ $suff };
1387             }
1388             my $ret = $num * ( 1000**$mult );
1389             my @ret;
1390             push @ret, "'" . $ret . "'";
1391             return \@ret, 1, 0;
1392             };
1393              
1394             =head2 a UNORM2
1395              
1396             reverse function of NORM2
1397             return the number from a 'a' with a sufix "K", "M", "G", "T", "P" (or nothing if lower than 1024)
1398             and calculate the real value base 1024 ( e.g 7k = 7168)
1399            
1400             =cut
1401              
1402             $dict{UNORM2} = sub {
1403             my $work1 = shift;
1404             my $a = pop @{ $work1 };
1405             $a = $a ? $a : 0;
1406             $a =~ /(\d+(\.{0,1}\d*)\s*)(\D)/;
1407             my $num = $1;
1408             my $suff = lc( $3 );
1409             my %EXP = (
1410             "k" => 1,
1411             "m" => 2,
1412             "g" => 3,
1413             "t" => 4,
1414             "p" => 5
1415             );
1416             my $mult = 0;
1417              
1418             if ( exists( $EXP{ $suff } ) )
1419             {
1420             $mult = $EXP{ $suff };
1421             }
1422             my $ret = $num * ( 1024**$mult );
1423             my @ret;
1424             push @ret, "'" . $ret . "'";
1425             return \@ret, 1, 0;
1426             };
1427              
1428             =head2 a OCT
1429              
1430             return the decimal value for the HEX, BINARY or OCTAL value 'a'
1431             OCTAL is like '0nn' where n is in the range of 0-7
1432             BINARY is like '0bnnn...' where n is in the range of 0-1
1433             HEX is like '0xnnn' where n is in the range of 0-9A-F
1434             if no specific format convert as an hexadecimal by default
1435            
1436             =cut
1437              
1438             $dict{OCT} = sub {
1439             my $work1 = shift;
1440             my $a = pop @{ $work1 };
1441             my @ret;
1442             if ( $a !~ /^0(x|b|([0-7][0-7]))/ )
1443             {
1444             $a = "0x" . $a;
1445             }
1446             push @ret, oct( $a );
1447             return \@ret, 1, 0;
1448             };
1449              
1450             =head2 a OCTSTR2HEX
1451              
1452             return a HEX string from a OCTETSTRING.
1453             useful when receiving an SNMP ASN.1 OCTETSTRING like mac address
1454            
1455             =cut
1456              
1457             $dict{OCTSTR2HEX} = sub {
1458             my $work1 = shift;
1459             my $a = pop @{ $work1 };
1460             my @ret;
1461             push @ret, unpack( "H*", pack( "a*", $a ) );
1462             return \@ret, 1, 0;
1463             };
1464              
1465             =head2 a HEX2OCTSTR
1466              
1467             return a OCTETSTRING string from a HEX
1468             useful when you need to check if an SNMP ASN.1 OCTETSTRING if matching the hex value provided
1469            
1470             =cut
1471              
1472             $dict{HEX2OCTSTR} = sub {
1473             my $work1 = shift;
1474             my $a = pop @{ $work1 };
1475             my @ret;
1476             push @ret, unpack( "a*", pack( "H*", $a ) );
1477             return \@ret, 1, 0;
1478             };
1479              
1480             =head2 a DDEC2STR
1481              
1482             return a string from a dotted DEC string
1483             useful when you need to manipulate an SNMP extension with 'exec'
1484            
1485             =cut
1486              
1487             $dict{DDEC2STR} = sub {
1488             my $work1 = shift;
1489             my $a = pop @{ $work1 };
1490             my @ret;
1491             push @ret, join "", map { sprintf( "%c", $_ ) } ( split /\./, $a );
1492             return \@ret, 1, 0;
1493             };
1494              
1495             =head2 a STR2DDEC
1496              
1497             return a dotted DEC string to a string
1498             useful when you need to manipulate an SNMP extension with 'exec'
1499            
1500             =cut
1501              
1502             $dict{STR2DDEC} = sub {
1503             my $work1 = shift;
1504             my $a = pop @{ $work1 };
1505             my @ret;
1506             push @ret, join '.', map { unpack( "c", $_ ) } ( split //, $a );
1507             return \@ret, 1, 0;
1508             };
1509              
1510              
1511             ########################
1512             # structurated string operators
1513             ########################
1514              
1515             =head1 Structurated string (SLxxx) operators
1516              
1517             =cut
1518              
1519             =head2 string a b SLSLICE
1520              
1521             return the STRUCTURATED list slice from 'a' to 'b' extracted from STRUCTURATED list.
1522             string are the STRUCTURATED list
1523             the STRUCTURATED LIST use this format:
1524             each entries are separated by ' # ' and inside each entry , the KEY and the VAL are separated by ' | '
1525             'keys1 | val1 # key2 | val2 # Keys3 | val3 # Keys4 | val4 #'
1526             example:
1527             'keys1 | val1 # key2 | val2 # Keys3 | val3 # Keys4 | val4 #,1,2,SLSLICE'
1528             return:
1529             # key2 | val2 # Keys3 | val3 #
1530              
1531             =cut
1532              
1533             $dict{SLSLICE} = sub {
1534             my $work1 = shift;
1535            
1536             my $to = pop @{ $work1 };
1537             my $from = pop @{ $work1 };
1538             my $string = pop @{ $work1 };
1539             ( $from, $to ) = ( $to, $from ) if ( $from > $to );
1540             my @ret;
1541            
1542             $string =~ s/^#\s*//;
1543             my @tmp = ( split /\s?\#\s?/, $string )[$from..$to];
1544             my $res = '# ' . join ( ' # ' , @tmp ).' #' if ( scalar @tmp );
1545             push @ret, $res;
1546             return \@ret, 3, 0;
1547             };
1548              
1549             =head2 string a SLITEM
1550              
1551             return the STRUCTURATED item at position 'a' from a STRUCTURATED list.
1552             string are the STRUCTURATED list
1553             the STRUCTURATED LIST use this format:
1554             each entries are separated by ' # ' and inside each entry , the KEY and the VAL are separated by ' | '
1555             'keys1 | val1 # key2 | val2 # Keys3 | val3 #'
1556             example:
1557             'keys1 | val1 # key2 | val2 # Keys3 | val3 #,1,SLITEM'
1558             return:
1559             # key2 | val2 #
1560              
1561             =cut
1562              
1563             $dict{SLITEM} = sub {
1564             my $work1 = shift;
1565              
1566             my $item = pop @{ $work1 };
1567             my $string = pop @{ $work1 };
1568              
1569             my @ret;
1570             $string =~ s/^#\s*//;
1571             my $res = ( split /\s?\#\s?/, $string )[$item];
1572             $res = '# ' . $res .' #' if ( $res );
1573             push @ret, $res;
1574             return \@ret, 2, 0;
1575             };
1576              
1577             =head2 string a SLGREP
1578              
1579             return a STRUCTURATED list from a STRUCTURATED list where the STRUCTURATED LIST match the REGEX a.
1580             string are the STRUCTURATED list
1581             the STRUCTURATED LIST use this format:
1582             each entries are separated by ' # ' and inside each entry , the KEY and the VAL are separated by ' | '
1583             'keys1 | val1 # key2 | val2 # Keys3 | val3 #'
1584             example:
1585             'keys1 | val1 # key2 | val2 # Keys3 | val3 #,Keys,SLGREP'
1586             return:
1587             # Keys3 | val3 #
1588              
1589             =cut
1590              
1591             $dict{SLGREP} = sub {
1592             my $work1 = shift;
1593              
1594             my $regex = pop @{ $work1 };
1595             my $string = pop @{ $work1 };
1596              
1597             my @ret;
1598             my $res;
1599             $string =~ s/^#\s*//;
1600             foreach my $i ( split /\s?\#\s?/, $string )
1601             {
1602             next unless ( $i );
1603             if ( $i =~ /$regex/ )
1604             {
1605             $res .= $i . ' # ';
1606             }
1607             }
1608             $res = '# ' . $res if ( $res );
1609             $res =~ s/\s+$//;
1610             push @ret, $res;
1611             return \@ret, 2, 0;
1612             };
1613              
1614             =head2 string a SLGREPI
1615              
1616             return a STRUCTURATED list from a STRUCTURATED list where the STRUCTURATED LIST match the REGEX a (case insensitive).
1617             string are the STRUCTURATED list
1618             the STRUCTURATED LIST use this format:
1619             each entries are separated by ' # ' and inside each entry , the KEY and the VAL are separated by ' | '
1620             'keys1 | val1 # key2 | val2 # Keys3 | val3 #'
1621             example:
1622             'keys1 | val1 # key2 | val2 # Keys3 | val3 #,Keys,SLGREPI'
1623             return:
1624             # keys1 | val1 # Keys3 | val3 #
1625              
1626             =cut
1627              
1628             $dict{SLGREPI} = sub {
1629             my $work1 = shift;
1630              
1631             my $regex = pop @{ $work1 };
1632             my $string = pop @{ $work1 };
1633              
1634             my @ret;
1635             my $res;
1636             $string =~ s/^#\s*//;
1637             foreach my $i ( split /\s?\#\s?/, $string )
1638             {
1639             next unless ( $i );
1640             if ( $i =~ /$regex/i )
1641             {
1642             $res .= $i . ' # ';
1643             }
1644             }
1645             $res = '# ' . $res if ( $res );
1646             $res =~ s/\s+$//;
1647             push @ret, $res;
1648             return \@ret, 2, 0;
1649             };
1650              
1651             =head2 string a SLSEARCHALL
1652              
1653             return all KEYS from a STRUCTURATED LIST where the STRUCTURATED LIST val match the REGEX a.
1654             string are the STRUCTURATED list
1655             the STRUCTURATED LIST use this format:
1656             each entries are separated by ' # ' and inside each entry , the KEY and the VAL are separated by ' | '
1657            
1658             example:
1659             '# 1.3.6.1.2.1.25.3.3.1.2.779 | 5 # 1.3.6.1.2.1.25.3.3.1.2.780 | 25 # 1.3.6.1.2.1.25.3.3.1.2.781 | 6 # 1.3.6.1.2.1.25.3.3.1.2.782 | 2 #,2,SLSEARCHALL'
1660             return:
1661             1.3.6.1.2.1.25.3.3.1.2.780 1.3.6.1.2.1.25.3.3.1.2.782
1662              
1663             =cut
1664              
1665             $dict{SLSEARCHALL} = sub {
1666             my $work1 = shift;
1667              
1668             my $regex = pop @{ $work1 };
1669             my $string = pop @{ $work1 };
1670              
1671             my @ret;
1672             $string =~ s/^#\s*//;
1673             foreach my $i ( split /\s?\#\s?/, $string )
1674             {
1675             next unless ( $i );
1676             my ( $key, $val ) = split /\s\|\s/, $i;
1677             if ( $val =~ /$regex/ )
1678             {
1679             push @ret, $key;
1680             }
1681             }
1682             return \@ret, 2, 0;
1683             };
1684              
1685             =head2 string a SLSEARCHALLI
1686              
1687             return all KEYS from a STRUCTURATED LIST where the STRUCTURATED LIST val match the REGEX a (case insensitive).
1688             string are the STRUCTURATED list
1689             the STRUCTURATED LIST use this format:
1690             each entries are separated by ' # ' and inside each entry , the KEY and the VAL are separated by ' | '
1691             '# key1 | val1 # key2 | val2 # key12 | VAL12 #,val1,SLSEARCHALLI'
1692             example:
1693             '# key1 | val1 # key2 | val2 # key12 | VAL12 #,val1,SLSEARCHALLI'
1694             return:
1695             key1 key12
1696              
1697             =cut
1698              
1699             $dict{SLSEARCHALLI} = sub {
1700             my $work1 = shift;
1701              
1702             my $regex = pop @{ $work1 };
1703             my $string = pop @{ $work1 };
1704              
1705             my @ret;
1706             $string =~ s/^#\s*//;
1707             foreach my $i ( split /\s?\#\s?/, $string )
1708             {
1709             next unless ( $i );
1710             my ( $key, $val ) = split /\s\|\s/, $i;
1711             if ( $val =~ /$regex/i )
1712             {
1713             push @ret, $key;
1714             }
1715             }
1716             return \@ret, 2, 0;
1717             };
1718              
1719             =head2 string a SLSEARCHALLKEYS
1720              
1721             return all VALUES from a STRUCTURATED LIST where the STRUCTURATED LIST keys match the REGEX a
1722             string are the STRUCTURATED list
1723             the STRUCTURATED LIST use this format:
1724             each entries are separated by ' # ' and inside each entry , the KEY and the VAL are separated by ' | '
1725             '# 1.3.6.1.2.1.25.3.3.1.2.779 | 1 # 1.3.6.1.2.1.25.3.3.1.2.780 | 5 # 1.3.6.1.2.1.25.3.3.1.2.781 | 6 # 1.3.6.1.2.1.25.3.3.1.2.782 | 2 #'
1726             example:
1727             '# 1.3.6.1.2.1.25.3.3.1.2.779 | 1 # 1.3.6.1.2.1.25.3.3.1.2.780 | 5 # 1.3.6.1.2.1.25.3.3.1.2.781 | 6 # 1.3.6.1.2.1.25.3.3.1.2.782 | 2 #,1.3.6.1.2.1.25.3.3.1.2.,SLSEARCHALLKEYS'
1728             return:
1729             1 5 6 2
1730              
1731             =cut
1732              
1733             $dict{SLSEARCHALLKEYS} = sub {
1734             my $work1 = shift;
1735              
1736             my $regex = pop @{ $work1 };
1737             my $string = pop @{ $work1 };
1738              
1739             my @ret;
1740             $string =~ s/^#\s*//;
1741             foreach my $i ( split /\s?\#\s?/, $string )
1742             {
1743             next unless ( $i );
1744             my $match = $1;
1745             my ( $key, $val ) = split /\s\|\s/, $i;
1746             if ( $key =~ /$regex/ )
1747             {
1748             push @ret, $val;
1749             }
1750             }
1751             return \@ret, 2, 0;
1752             };
1753              
1754             =head2 string a SLSEARCHALLKEYSI
1755              
1756             return all VALUES from a STRUCTURATED LIST where the STRUCTURATED LIST key match the REGEX a.
1757             string are the STRUCTURATED list.
1758             the STRUCTURATED LIST use this format:
1759             each entries are separated by ' # ' and inside each entry , the KEY and the VAL are separated by ' | '
1760             '# tata is not happy | and what? # tata is happy | and?? # toto is not happy | oops # toto is happy | yeah #'
1761             example:
1762             '# tata is not happy | and what? # tata is happy | and?? # toto is not happy | oops # toto is happy | yeah #,toto,SLSEARCHALLKEYSI'
1763             return:
1764             oops yeah
1765              
1766             =cut
1767              
1768             $dict{SLSEARCHALLKEYSI} = sub {
1769             my $work1 = shift;
1770              
1771             my $regex = pop @{ $work1 };
1772             my $string = pop @{ $work1 };
1773              
1774             my @ret;
1775             $string =~ s/^#\s*//;
1776             foreach my $i ( split /\s?\#\s?/, $string )
1777             {
1778             next unless ( $i );
1779             my $match = $1;
1780             my ( $key, $val ) = split /\s\|\s/, $i;
1781             if ( $key =~ /$regex/i )
1782             {
1783             push @ret, $val;
1784             }
1785             }
1786             return \@ret, 2, 0;
1787             };
1788              
1789             =head2 string a OIDSEARCHALLVAL
1790              
1791             return all OID leaf from a snmpwalk macthing the REGEX a
1792             string are the OID walk list
1793             the OID walk result use this format:
1794             each snmpwalk entries are separated by ' # ' and inside each entry , the OID and the VAL are separated by ' | '
1795             '# .1.3.6.1.2.1.25.4.2.1.2.4704 | "TASKMGR.EXE" # .1.3.6.1.2.1.25.4.2.1.2.2692 | "winvnc4.exe" # .1.3.6.1.2.1.25.4.2.1.2.3128 | "CSRSS.EXE" #
1796             example:
1797             '# .1.3.6.1.2.1.25.4.2.1.2.488 | "termsrv.exe" # .1.3.6.1.2.1.25.4.2.1.2.688 | "Apache.exe" # .1.3.6.1.2.1.25.4.2.1.2.5384 | "aimsserver.exe" # .1.3.6.1.2.1.25.4.2.1.2.2392 | "Apache.exe" # .1.3.6.1.2.1.25.4.2.1.2.2600 | "cpqnimgt.exe" #,Apache\.exe,OIDSEARCHALLVAL'
1798             return:
1799             688 2392
1800            
1801             =cut
1802              
1803             $dict{OIDSEARCHALLVAL} = sub {
1804             my $work1 = shift;
1805              
1806             my $regex = pop @{ $work1 };
1807             my $string = pop @{ $work1 };
1808              
1809             my @ret;
1810             $string =~ s/^#\s*//;
1811             foreach my $i ( split /\s?\#\s?/, $string )
1812             {
1813             next unless ( $i );
1814             if ( $i =~ /$regex/ )
1815             {
1816             my $match = $1;
1817             my ( $oid, undef ) = split /\s\|\s/, $i;
1818             $oid =~ /\.(\d+)$/;
1819             push @ret, $1;
1820             }
1821             }
1822             return \@ret, 2, 0;
1823             };
1824              
1825             =head2 string a OIDSEARCHALLVALI
1826              
1827             return all OID leaf from a snmpwalk macthing the REGEX a ( case insensitive )
1828             string are the OID walk list
1829             the OID walk result use this format:
1830             each snmpwalk entries are separated by ' # ' and inside each entry , the OID and the VAL are separated by ' | '
1831             '# .1.3.6.1.2.1.25.4.2.1.2.4704 | "TASKMGR.EXE" # .1.3.6.1.2.1.25.4.2.1.2.2692 | "winvnc4.exe" # .1.3.6.1.2.1.25.4.2.1.2.3128 | "CSRSS.EXE" #
1832             example:
1833             '# .1.3.6.1.2.1.25.4.2.1.2.488 | "termsrv.exe" # .1.3.6.1.2.1.25.4.2.1.2.688 | "Apache.exe" # .1.3.6.1.2.1.25.4.2.1.2.5384 | "aimsserver.exe" # .1.3.6.1.2.1.25.4.2.1.2.2392 | "Apache.exe" # .1.3.6.1.2.1.25.4.2.1.2.2600 | "cpqnimgt.exe" #,Apache\.exe,OIDSEARCHALLVALI'
1834             return:
1835             688 2392
1836            
1837             =cut
1838              
1839             $dict{OIDSEARCHALLVALI} = sub {
1840             my $work1 = shift;
1841              
1842             my $regex = pop @{ $work1 };
1843             my $string = pop @{ $work1 };
1844              
1845             my @ret;
1846             $string =~ s/^#\s*//;
1847             foreach my $i ( split /\s?\#\s?/, $string )
1848             {
1849             next unless ( $i );
1850             if ( $i =~ /$regex/i )
1851             {
1852             my $match = $1;
1853             my ( $oid, undef ) = split /\s\|\s/, $i;
1854             $oid =~ /\.(\d+)$/;
1855             push @ret, $1;
1856             }
1857             }
1858             return \@ret, 2, 0;
1859             };
1860              
1861             =head2 string x x x a OIDSEARCHLEAF
1862              
1863             return all VAL leaf from a snmpwalk when the OID leaf match each REGEX
1864             a is the number of leaf to pick from the stack
1865             x are all the leaf
1866             string are the OID walk list
1867             the OID walk result use this format:
1868             each snmpwalk entries are separated by ' # ' and inside each entry , the OID and the VAL are separated by ' | '
1869             '# .1.3.6.1.2.1.25.4.2.1.2.4704 | "TASKMGR.EXE" # .1.3.6.1.2.1.25.4.2.1.2.2692 | "winvnc4.exe" # .1.3.6.1.2.1.25.4.2.1.2.3128 | "CSRSS.EXE" #
1870             example:
1871             '# .1.3.6.1.2.1.25.4.2.1.7.384 | running # .1.3.6.1.2.1.25.4.2.1.7.688 | running # .1.3.6.1.2.1.25.4.2.1.7.2384 | invalid #,688,2384,2,OIDSEARCHLEAF'
1872             return:
1873             running invalid
1874            
1875             =cut
1876              
1877             $dict{OIDSEARCHLEAF} = sub {
1878             my $work1 = shift;
1879              
1880             my $nbr = pop @{ $work1 };
1881             my @all = splice @{ $work1 }, 1, $nbr;
1882              
1883             my $string = pop @{ $work1 };
1884             my @ret;
1885             $string =~ s/^#\s*//;
1886             foreach my $i ( split /\s?\#\s?/, $string )
1887             {
1888             next unless ( $i );
1889             foreach my $regex ( @all )
1890             {
1891             if ( $i =~ /\.$regex\s?\|\s/ )
1892             {
1893             my ( undef, $val ) = split /\s\|\s/, $i;
1894             push @ret, $val;
1895             }
1896             }
1897             }
1898             return \@ret, 3 + $nbr, 0;
1899             };
1900              
1901             =head2 string x x x a OIDSEARCHLEAFI
1902              
1903             return all VAL leaf from a snmpwalk when the OID leaf match each REGEX
1904             a ( case insensitive ) is the number of leaf to pick from the stack
1905             x are all the leaf
1906             string are the OID walk list
1907             the OID walk result use this format:
1908             each snmpwalk entries are separated by ' # ' and inside each entriy , the OID and the VAL are separated by ' | '
1909             '# .1.3.6.1.2.1.25.4.2.1.2.4704 | "TASKMGR.EXE" # .1.3.6.1.2.1.25.4.2.1.2.2692 | "winvnc4.exe" # .1.3.6.1.2.1.25.4.2.1.2.3128 | "CSRSS.EXE" #'
1910             example:
1911             '# .1.3.6.1.2.1.25.4.2.1.7.384 | running # .1.3.6.1.2.1.25.4.2.1.7.688 | running # .1.3.6.1.2.1.25.4.2.1.7.2384 | invalid #,688,2384,2,OIDSEARCHLEAFI'
1912             return:
1913             running invalid
1914            
1915             =cut
1916              
1917             $dict{OIDSEARCHLEAFI} = sub {
1918             my $work1 = shift;
1919              
1920             my $nbr = pop @{ $work1 };
1921             my @all = splice @{ $work1 }, 1, $nbr;
1922              
1923             my $string = pop @{ $work1 };
1924             my @ret;
1925             $string =~ s/^#\s*//;
1926             foreach my $i ( split /\s?\#\s?/, $string )
1927             {
1928             next unless ( $i );
1929             foreach my $regex ( @all )
1930             {
1931             if ( $i =~ /\.$regex\s?\|\s/ )
1932             {
1933             my ( undef, $val ) = split /\s+\|\s+/, $i;
1934             push @ret, $val;
1935             }
1936             }
1937             }
1938             return \@ret, 3 + $nbr, 0;
1939             };
1940              
1941             ########################
1942             # string operators
1943             ########################
1944              
1945             =head1 STRING operators
1946              
1947             =cut
1948              
1949             =head2 a b EQ
1950              
1951             return the result of 'a' EQ 'b' ( BOOLEAN value )
1952            
1953             =cut
1954              
1955             $dict{EQ} = sub {
1956             my $work1 = shift;
1957             my $a = pop @{ $work1 };
1958             my $b = pop @{ $work1 };
1959             my @ret;
1960             push @ret, ( $b eq $a ? 1 : 0 );
1961             return \@ret, 2, 0;
1962             };
1963              
1964             =head2 a b NE
1965              
1966             return the result of 'a' NE 'b' ( BOOLEAN value )
1967            
1968             =cut
1969              
1970             $dict{NE} = sub {
1971             my $work1 = shift;
1972             my $a = pop @{ $work1 };
1973             my $b = pop @{ $work1 };
1974             my @ret;
1975             push @ret, ( $b ne $a ? 1 : 0 );
1976             return \@ret, 2, 0;
1977             };
1978              
1979             =head2 a b LT
1980              
1981             return the result of 'a' LT 'b' ( BOOLEAN value )
1982            
1983             =cut
1984              
1985             $dict{LT} = sub {
1986             my $work1 = shift;
1987             my $a = pop @{ $work1 };
1988             my $b = pop @{ $work1 };
1989             my @ret;
1990             push @ret, ( $b lt $a ? 1 : 0 );
1991             return \@ret, 2, 0;
1992             };
1993              
1994             =head2 a b GT
1995              
1996             return the result of 'a' GT 'b' ( BOOLEAN value )
1997            
1998             =cut
1999              
2000             $dict{GT} = sub {
2001             my $work1 = shift;
2002             my $a = pop @{ $work1 };
2003             my $b = pop @{ $work1 };
2004             my @ret;
2005             push @ret, ( $b gt $a ? 1 : 0 );
2006             return \@ret, 2, 0;
2007             };
2008              
2009             =head2 a b LE
2010              
2011             return the result of 'a' LE 'b' ( BOOLEAN value )
2012            
2013             =cut
2014              
2015             $dict{LE} = sub {
2016             my $work1 = shift;
2017             my $a = pop @{ $work1 };
2018             my $b = pop @{ $work1 };
2019             my @ret;
2020             push @ret, ( $b le $a ? 1 : 0 );
2021             return \@ret, 2, 0;
2022             };
2023              
2024             =head2 a b GE
2025              
2026             return the result of 'a' GE 'b' ( BOOLEAN value )
2027            
2028             =cut
2029              
2030             $dict{GE} = sub {
2031             my $work1 = shift;
2032             my $a = pop @{ $work1 };
2033             my $b = pop @{ $work1 };
2034             my @ret;
2035             push @ret, ( $b ge $a ? 1 : 0 );
2036             return \@ret, 2, 0;
2037             };
2038              
2039             =head2 a b CMP
2040              
2041             return the result of 'a' CMP 'b' ( BOOLEAN value )
2042            
2043             =cut
2044              
2045             $dict{CMP} = sub {
2046             my $work1 = shift;
2047             my $a = pop @{ $work1 };
2048             my $b = pop @{ $work1 };
2049             my @ret;
2050             push @ret, ( $b cmp $a );
2051             return \@ret, 2, 0;
2052             };
2053              
2054             =head2 a LEN
2055              
2056             return the length of 'a'
2057            
2058             =cut
2059              
2060             $dict{LEN} = sub {
2061             my $work1 = shift;
2062             my $a = pop @{ $work1 };
2063             my @ret;
2064             push @ret, ( length $a );
2065             return \@ret, 1, 0;
2066             };
2067              
2068             =head2 a CHOMP
2069              
2070             remove any terminaison line charecter ( CR CR/LF) from 'a'
2071            
2072             =cut
2073              
2074             $dict{CHOMP} = sub {
2075             my $work1 = shift;
2076             my $a = pop @{ $work1 };
2077             my @ret;
2078             chomp $a;
2079             push @ret, $a ;
2080             return \@ret, 1, 0;
2081             };
2082              
2083             =head2 a b CAT
2084              
2085             return the concatenation 'a' and 'b'
2086            
2087             =cut
2088              
2089             $dict{CAT} = sub {
2090             my $work1 = shift;
2091             my $a = pop @{ $work1 };
2092             my $b = pop @{ $work1 };
2093             my @ret;
2094             push @ret, ( "'" . $b . $a . "'" );
2095             return \@ret, 2, 0;
2096             };
2097              
2098             =head2 a b ... n x CATN
2099              
2100             return the concatenation of the 'x' element from the stack
2101            
2102             =cut
2103              
2104             $dict{CATN} = sub {
2105             my $work1 = shift;
2106             my $a = pop @{ $work1 };
2107            
2108             my $ret;
2109             my @ret;
2110             for ( 1 .. $a )
2111             {
2112             $ret .= pop @{ $work1 };
2113             }
2114             push @ret, $ret;
2115             return \@ret, 1 +$a, 0;
2116             };
2117              
2118             =head2 a b CATALL
2119              
2120             return the concatenation all element on the stack
2121            
2122             =cut
2123              
2124             $dict{CATALL} = sub {
2125             my $work1 = shift;
2126             my $dep = scalar @{ $work1 };
2127             my $ret;
2128             for ( 1 .. $dep )
2129             {
2130             $ret .= shift @{ $work1 };
2131             }
2132             my @ret;
2133             push @ret, $ret;
2134             return \@ret, 1 + $dep, 0;
2135             };
2136              
2137              
2138             =head2 a b x JOIN
2139              
2140             return the concatenation 'a', 'x' and 'b'
2141            
2142             =cut
2143              
2144             $dict{JOIN} = sub {
2145             my $work1 = shift;
2146             my $x = pop @{ $work1 };
2147             my $a = pop @{ $work1 };
2148             my $b = pop @{ $work1 };
2149             my @ret;
2150             push @ret, ( "'" . $b .$x. $a . "'" );
2151             return \@ret, 3, 0;
2152             };
2153              
2154             =head2 a b ... n x y JOINN
2155              
2156             return the concatenation of the 'y' element from the stack with 'x' as separator
2157            
2158             =cut
2159              
2160             $dict{JOINN} = sub {
2161             my $work1 = shift;
2162             my $a = pop @{ $work1 };
2163             my $x = pop @{ $work1 };
2164             my $ret;
2165             for ( 1 .. $a-1 )
2166             {
2167             $ret .= (pop @{ $work1 }) . $x;
2168             }
2169             $ret .= pop @{ $work1 };
2170             my @ret = ( $ret );
2171             return \@ret, 2 +$a, 0;
2172             };
2173              
2174             =head2 a b x JOINALL
2175              
2176             return the concatenation all element on the stack with 'x' as separator
2177            
2178             =cut
2179              
2180             $dict{JOINALL} = sub {
2181             my $work1 = shift;
2182             my $x = pop @{ $work1 };
2183             my $dep = scalar @{ $work1 };
2184             my $ret;
2185             for ( 1 .. $dep-1 )
2186             {
2187             $ret .= (shift @{ $work1 }) .$x;
2188             }
2189             $ret .= pop @{ $work1 };
2190             my @ret =( $ret );
2191             return \@ret, 1 + $dep, 0;
2192             };
2193              
2194             =head2 a b REP
2195              
2196             return the result of 'a' x 'b' duplicate 'a' by the number of 'b'
2197            
2198             =cut
2199              
2200             $dict{REP} = sub {
2201             my $work1 = shift;
2202             my $a = pop @{ $work1 };
2203             my $b = pop @{ $work1 };
2204             my @ret;
2205             push @ret, ( $b x $a );
2206             return \@ret, 2, 0;
2207             };
2208              
2209             =head2 a REV
2210              
2211             return the reverse of 'a'
2212            
2213             =cut
2214              
2215             $dict{REV} = sub {
2216             my $work1 = shift;
2217             my $a = pop @{ $work1 };
2218             my $b = reverse $a;
2219             my @ret;
2220             push @ret, ( $b );
2221             return \@ret, 1, 0;
2222             };
2223              
2224             =head2 a b c SUBSTR
2225              
2226             return the substring of 'c' starting at 'b' with the length of 'a'
2227            
2228             =cut
2229              
2230             $dict{SUBSTR} = sub {
2231             my $work1 = shift;
2232             my $a = pop @{ $work1 };
2233             my $b = pop @{ $work1 };
2234             my $c = pop @{ $work1 };
2235             my @ret;
2236             push @ret, ( substr( $c, $b, $a ) );
2237             return \@ret, 3, 0;
2238             };
2239              
2240             =head2 a UC
2241              
2242             return 'a' in uppercase
2243            
2244             =cut
2245              
2246             $dict{UC} = sub {
2247             my $work1 = shift;
2248             my $a = pop @{ $work1 };
2249             my @ret;
2250             push @ret, ( uc $a );
2251             return \@ret, 1, 0;
2252             };
2253              
2254             =head2 a LC
2255              
2256             return 'a' in lowercase
2257            
2258             =cut
2259              
2260             $dict{LC} = sub {
2261             my $work1 = shift;
2262             my $a = pop @{ $work1 };
2263             my @ret;
2264             push @ret, ( lc $a );
2265             return \@ret, 1, 0;
2266             };
2267              
2268             =head2 a UCFIRST
2269              
2270             return 'a' with the first letter in uppercase
2271            
2272             =cut
2273              
2274             $dict{UCFIRST} = sub {
2275             my $work1 = shift;
2276             my $a = pop @{ $work1 };
2277             my @ret;
2278             push @ret, ( ucfirst $a );
2279             return \@ret, 1, 0;
2280             };
2281              
2282             =head2 a LCFIRST
2283              
2284             return 'a' with the first letter in lowercase
2285            
2286             =cut
2287              
2288             $dict{LCFIRST} = sub {
2289             my $work1 = shift;
2290             my $a = pop @{ $work1 };
2291             my @ret;
2292             push @ret, ( lcfirst $a );
2293             return \@ret, 1, 0;
2294             };
2295              
2296             =head2 a R1 R2 K V SPLIT2
2297              
2298             split a with the REGEX R1
2299             each result are splitted with the REGEX R2
2300             the result are stored in the variable k and v
2301            
2302             # .1.3.6.1.2.1.25.3.3.1.2.768 | 48 # .1.3.6.1.2.1.25.3.3.1.2.769 | 38 # .1.3.6.1.2.1.25.3.3.1.2.771 | 42 # .1.3.6.1.2.1.25.3.3.1.2.770 | 58 #,\s?#\s?,\s\|\s,a,b,SPLIT2
2303             return a with .1.3.6.1.2.1.25.3.3.1.2.768,.1.3.6.1.2.1.25.3.3.1.2.769,.1.3.6.1.2.1.25.3.3.1.2.771,.1.3.6.1.2.1.25.3.3.1.2.770
2304             and b with 48,38,42,58
2305            
2306             !!! becare, if you need to use : as a regex, you need to backslash to prevent overlap with new dictionary entry
2307             SPLIT return the matched value WITHOUT the empty string of the beginning
2308            
2309             =cut
2310              
2311             $dict{SPLIT2} = sub {
2312             my $work1 = shift;
2313             my $v2 = pop @{ $work1 };
2314             my $v1 = pop @{ $work1 };
2315             my $r2 = pop @{ $work1 };
2316             my $r1 = pop @{ $work1 };
2317             my $b = pop @{ $work1 };
2318             my @T1;
2319             my @T2;
2320              
2321             foreach my $i ( split /$r1/, $b )
2322             {
2323             next unless ( $i );
2324             my ( $k, $v ) = split /$r2/, $i, 2;
2325             if ( $k )
2326             {
2327             push @T1, $k;
2328             push @T2, $v;
2329             }
2330             }
2331             $var{ $v1 } = \@T1;
2332             $var{ $v2 } = \@T2;
2333             my @ret;
2334             return \@ret, 5, 0;
2335             };
2336              
2337             =head2 a b SPLIT
2338              
2339             return all splitted item of 'a' by the separator 'b'
2340             'b' is a REGEX
2341             !!! becare, if you need to use : as a regex, you need to backslash to prevent overlap with new dictionary entry
2342             !!! if the split match on the beginning of string,
2343             SPLIT return the matched value WITHOUT the empty string of the beginning
2344            
2345             =cut
2346              
2347             $dict{SPLIT} = sub {
2348             my $work1 = shift;
2349             my $a = pop @{ $work1 };
2350             my $b = pop @{ $work1 };
2351             my @r = grep /[^(^$)]/, split /$a/, $b;
2352             my @ret;
2353             push @ret, @r;
2354             return \@ret, 2, 0;
2355             };
2356              
2357             =head2 a b SPLITI
2358              
2359             return all splitted item of 'a' by the separator 'b'
2360             'b' is a REGEX case insensitive
2361             !!! becare, if you need to use : as a regex, you need to backslash to prevent overlap with new dictionary entry
2362             !!! if the split match on the beginning of string,
2363             SPLIT return the matched value WITHOUT the empty string of the beginning
2364            
2365             =cut
2366              
2367             $dict{SPLITI} = sub {
2368             my $work1 = shift;
2369             my $a = pop @{ $work1 };
2370             my $b = pop @{ $work1 };
2371             my @r = grep /[^(^$)]/, split /$a/i, $b;
2372             my @ret;
2373             push @ret, @r;
2374             return \@ret, 2, 0;
2375             };
2376              
2377             =head2 a b PAT
2378              
2379             return one or more occurance of 'b' in 'a'
2380             'b' is a REGEX
2381             !!! becare, if you need to use : as a regex, you need to backslash to prevent overlap with new dictionary entry
2382            
2383             =cut
2384              
2385             $dict{PAT} = sub {
2386             my $work1 = shift;
2387             my $a = pop @{ $work1 };
2388             my $b = pop @{ $work1 };
2389             my @r = ( $b =~ m/\Q$a\E/g );
2390             my @ret;
2391             push @ret, @r;
2392             return \@ret, 2, 0;
2393             };
2394              
2395             =head2 a b PATI
2396              
2397             return one or more occurance of 'b' in 'a'
2398             'b' is a REGEX case insensitive
2399             !!! becare, if you need to use : as a regex, you need to backslash to prevent overlap with new dictionary entry
2400            
2401             =cut
2402              
2403             $dict{PATI} = sub {
2404             my $work1 = shift;
2405             my $a = pop @{ $work1 };
2406             my $b = pop @{ $work1 };
2407             my @r = ( $b =~ m/$a/ig );
2408             my @ret;
2409             push @ret, @r;
2410             return \@ret, 2, 0;
2411             };
2412              
2413             =head2 a b TPAT
2414              
2415             test if the pattern 'b' is in 'a'
2416             'b' is a REGEX
2417             !!! becare, if you need to use : as a regex, you need to backslash to prevent overlap with new dictionary entry
2418            
2419             =cut
2420              
2421             $dict{TPAT} = sub {
2422             my $work1 = shift;
2423             my $a = pop @{ $work1 };
2424             my $b = pop @{ $work1 };
2425             my $r = ( $b =~ m/$a/g );
2426             my @ret;
2427             push @ret, ( $r ? 1 : 0 );
2428             return \@ret, 2, 0;
2429             };
2430              
2431             =head2 a b TPATI
2432              
2433             test if the pattern 'b' is in 'a'
2434             'b' is a REGEX
2435             !!! becare, if you need to use : as a regex, you need to backslash to prevent overlap with new dictionary entry
2436            
2437             =cut
2438              
2439             $dict{TPATI} = sub {
2440             my $work1 = shift;
2441             my $a = pop @{ $work1 };
2442             my $b = pop @{ $work1 };
2443             my $r = ( $b =~ m/$a/ig );
2444             my @ret;
2445             push @ret, ( $r ? 1 : 0 );
2446             return \@ret, 2, 0;
2447             };
2448              
2449             =head2 a b c SPAT
2450              
2451             substitute the pattern 'b' by the pattern 'a' in 'c'
2452             'b' and 'c' are a REGEX
2453             !!! becare, if you need to use : as a regex, you need to backslash to prevent overlap with new dictionary entry
2454            
2455             =cut
2456              
2457             $dict{SPAT} = sub {
2458             my $work1 = shift;
2459             my $a = pop @{ $work1 };
2460             my $b = pop @{ $work1 };
2461             my $c = pop @{ $work1 } || '';
2462             my $to_eval = qq{\$c =~ s#$b#$a#};
2463             eval( $to_eval );
2464             my @ret;
2465             push @ret, $c;
2466             return \@ret, 3, 0;
2467             };
2468              
2469             =head2 a b c SPATG
2470              
2471             substitute the pattern 'b' by the pattern 'a' in 'c' as many time as possible (g flag in REGEX)
2472             'b' and 'c' are a REGEX
2473             !!! becare, if you need to use : as a regex, you need to backslash to prevent overlap with new dictionary entry
2474            
2475             =cut
2476              
2477             $dict{SPATG} = sub {
2478             my $work1 = shift;
2479             my $a = pop @{ $work1 };
2480             my $b = pop @{ $work1 };
2481             my $c = pop @{ $work1 };
2482             my $to_eval = qq{\$c =~ s#$b#$a#g};
2483             eval( $to_eval );
2484             my @ret;
2485             push @ret, $c;
2486             return \@ret, 3, 0;
2487             };
2488              
2489             =head2 a b c SPATI
2490              
2491             substitute the pattern 'b' by the pattern 'a' in 'c'case insensitive (i flag in REGEX)
2492             'b' and 'c' are a REGEX
2493             !!! becare, if you need to use : as a regex, you need to backslash to prevent overlap with new dictionary entry
2494            
2495             =cut
2496              
2497             $dict{SPATI} = sub {
2498             my $work1 = shift;
2499             my $a = pop @{ $work1 };
2500             my $b = pop @{ $work1 };
2501             my $c = pop @{ $work1 };
2502             my $to_eval = qq{\$c =~ s#$b#$a#i};
2503             eval( $to_eval );
2504             my @ret;
2505             push @ret, $c;
2506             return \@ret, 3, 0;
2507             };
2508              
2509             =head2 a b c SPATGI
2510              
2511             substitute the pattern 'b' by the pattern 'a' in 'c' as many time as possible (g flag in REGEX)
2512             and case insensitive (1 flag in REGEX)
2513             'b' and 'c' are a REGEX
2514             !!! becare, if you need to use : as a regex, you need to backslash to prevent overlap with new dictionary entry
2515            
2516             =cut
2517              
2518             $dict{SPATGI} = sub {
2519             my $work1 = shift;
2520             my $a = pop @{ $work1 };
2521             my $b = pop @{ $work1 };
2522             my $c = pop @{ $work1 };
2523             my $to_eval = qq{\$c =~ s#$b#$a#ig};
2524             eval( $to_eval );
2525             my @ret;
2526             push @ret, $c;
2527             return \@ret, 3, 0;
2528             };
2529              
2530             =head2 a ... z PRINTF
2531              
2532             use the format 'z' to print the value(s) on the stack
2533             7,3,/,10,3,/,%d %f,PRINTF -> 2 3.333333
2534             see printf in perl
2535            
2536             =cut
2537              
2538             $dict{PRINTF} = sub {
2539              
2540             my $work1 = shift;
2541             my $format = pop @{ $work1 };
2542             my @r = ( $format =~ m/(%[^ ])/g );
2543             my @var;
2544             for ( 0 .. $#r )
2545             {
2546             unshift @var, pop @{ $work1 };
2547             }
2548             my @ret;
2549             push @ret, sprintf $format, @var;
2550             return \@ret, 2 + $#r, 0;
2551             };
2552              
2553             =head2 a b PACK
2554              
2555             pack the value 'a' with the format 'b'
2556              
2557             2004,06,08,a4 a2 a2,PACK
2558             result: 20040608
2559              
2560             see pack in perl
2561            
2562             =cut
2563              
2564             $dict{PACK} = sub {
2565             my $work1 = shift;
2566             my $format = " " . ( pop( @{ $work1 } ) ) . " ";
2567             my @r = ( $format =~ m/([a-zA-Z]\d*\s*)/g );
2568             my @var;
2569             for ( 0 .. $#r )
2570             {
2571             unshift @var, pop @{ $work1 };
2572             }
2573             my @ret;
2574             push @ret,, pack( $format, @var );
2575             return \@ret, 2 + $#r, 0;
2576             };
2577              
2578             =head2 a b UNPACK
2579              
2580             unpack the value 'a' with the format 'b'
2581              
2582             20040608,a4 a2 a2,UNPACK
2583             result: 2004,06,08
2584              
2585             see unpack in perl
2586            
2587             =cut
2588              
2589             $dict{UNPACK} = sub {
2590             my $work1 = shift;
2591             my $format = pop @{ $work1 };
2592             my $var = pop @{ $work1 };
2593             my @ret;
2594             push @ret, unpack( $format, $var );
2595             return \@ret, 2, 0;
2596             };
2597              
2598             =head2 a b ISNUM
2599              
2600             test if top of the stack is a number
2601             return 1 if if it is a NUMBER otherwise return 0
2602            
2603             =cut
2604              
2605             $dict{ISNUM} = sub {
2606             my $work1 = shift;
2607             my $a = pop @{ $work1 };
2608             my @ret;
2609             push @ret, ( $a =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/ ? 1 : 0 );
2610             return \@ret, 0, 0;
2611             };
2612              
2613             =head2 a b ISNUMD
2614              
2615             test if top of the stack is a number
2616             delete the top element on the statck and return 1 if it is a NUMBER otherwise return 0
2617            
2618             =cut
2619              
2620             $dict{ISNUMD} = sub {
2621             my $work1 = shift;
2622             my $a = pop @{ $work1 };
2623             my @ret;
2624             push @ret, ( $a =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/ ? 1 : 0 );
2625             return \@ret, 1, 0;
2626             };
2627              
2628             =head2 a b ISINT
2629              
2630             test if top of the stack is a integer (natural number)
2631             return 1 if if it is a INTEGER otherwise return 0
2632            
2633             =cut
2634              
2635             $dict{ISINT} = sub {
2636             my $work1 = shift;
2637             my $a = pop @{ $work1 };
2638             my @ret;
2639             push @ret, ( $a =~ /^\d+$/ ? 1 : 0 );
2640             return \@ret, 0, 0;
2641             };
2642              
2643             =head2 a b ISINTD
2644              
2645             test if top of the stack is a integer (natural number)
2646             delete the top element on the statck and return 1 if it is a INTEGER otherwise return 0
2647            
2648             =cut
2649              
2650             $dict{ISINTD} = sub {
2651             my $work1 = shift;
2652             my $a = pop @{ $work1 };
2653             my @ret;
2654             push @ret, ( $a =~ /^\d+$/ ? 1 : 0 );
2655             return \@ret, 1, 0;
2656             };
2657              
2658             =head2 a b ISHEX
2659              
2660             test if top of the stack is a hexadecimal value (starting with 0x or 0X or # )
2661             return 1 if if it is a HEXADECIMAL otherwise return 0
2662            
2663             =cut
2664              
2665             $dict{ISHEX} = sub {
2666             my $work1 = shift;
2667             my $a = pop @{ $work1 };
2668             my @ret;
2669             push @ret, ( $a =~ /^(#|0x|0X)(\p{IsXDigit})+$/ ? 1 : 0 );
2670             return \@ret, 0, 0;
2671             };
2672              
2673             =head2 a b ISHEXD
2674              
2675             test if top of the stack is a hexadecimal value (starting with 0x or 0X or # )
2676             delete the top element on the statck and return 1 if it is a HEXADECIMAL otherwise return 0
2677            
2678             =cut
2679              
2680             $dict{ISHEXD} = sub {
2681             my $work1 = shift;
2682             my $a = pop @{ $work1 };
2683             my @ret;
2684             push @ret, ( $a =~ /^(#|0x|0X)(\p{IsXDigit})+$/ ? 1 : 0 );
2685             return \@ret, 1, 0;
2686             };
2687              
2688             ########################
2689             # stack operators
2690             ########################
2691              
2692             =head1 STACK operators
2693              
2694             =cut
2695              
2696             =head2 a b SWAP
2697              
2698             return 'b' 'a'
2699              
2700             =cut
2701              
2702             $dict{SWAP} = sub {
2703             my $work1 = shift;
2704             my $a = pop @{ $work1 };
2705             my $b = pop @{ $work1 };
2706             my @ret;
2707             push @ret, $a, $b;
2708             return \@ret, 2, 0;
2709             };
2710              
2711             =head2 a b OVER
2712              
2713             return 'a' 'b' 'a'
2714              
2715             =cut
2716              
2717             $dict{OVER} = sub {
2718             my $work1 = shift;
2719             my @ret;
2720             push @ret, @{ $work1 }[-2];
2721             return \@ret, 0, 0;
2722             };
2723              
2724             =head2 a DUP
2725              
2726             return 'a' 'a'
2727              
2728             =cut
2729              
2730             $dict{DUP} = sub {
2731             my $work1 = shift;
2732             my @ret;
2733             push @ret, @{ $work1 }[-1];
2734             return \@ret, 0, 0;
2735             };
2736              
2737             =head2 a b DDUP
2738              
2739             return 'a' 'b' 'a' 'b'
2740              
2741             =cut
2742              
2743             $dict{DDUP} = sub {
2744             my $work1 = shift;
2745             my @ret;
2746             push @ret, @{ $work1 }[-2], @{ $work1 }[-1];
2747             return \@ret, 0, 0;
2748             };
2749              
2750             =head2 a b c ROT
2751              
2752             return 'b' 'c' 'a'
2753              
2754             =cut
2755              
2756             $dict{ROT} = sub {
2757             my $work1 = shift;
2758             my $a = pop @{ $work1 };
2759             my $b = pop @{ $work1 };
2760             my $c = pop @{ $work1 };
2761             my @ret;
2762             push @ret, $b, $a, $c;
2763             return \@ret, 3, 0;
2764             };
2765              
2766             =head2 a b c RROT
2767              
2768             return 'c' 'a' 'b'
2769              
2770             =cut
2771              
2772             $dict{RROT} = sub {
2773             my $work1 = shift;
2774             my $a = pop @{ $work1 };
2775             my $b = pop @{ $work1 };
2776             my $c = pop @{ $work1 };
2777             my @ret;
2778             push @ret, $a, $c, $b;
2779             return \@ret, 3, 0;
2780             };
2781              
2782             =head2 DEPTH
2783              
2784             return the number of elements on the stack
2785              
2786             =cut
2787              
2788             $dict{DEPTH} = sub {
2789             my $work1 = shift;
2790             my $ret = scalar @{ $work1 };
2791             my @ret;
2792             push @ret, $ret;
2793             return \@ret, 0, 0;
2794             };
2795              
2796             =head2 a b POP
2797              
2798             remove the last element on the stack
2799              
2800             =cut
2801              
2802             $dict{POP} = sub {
2803             my $work1 = shift;
2804             my $a = pop @{ $work1 };
2805             my @ret;
2806             return \@ret, 1, 0;
2807             };
2808              
2809             =head2 a ... z POPN
2810              
2811             remove the 'z' last element(s) from the stack
2812              
2813             =cut
2814              
2815             $dict{POPN} = sub {
2816             my $work1 = shift;
2817             my $a = pop @{ $work1 };
2818             for ( 1 .. $a )
2819             {
2820             pop @{ $work1 };
2821             }
2822             my @ret;
2823             return \@ret, 1 + $a, 0;
2824             };
2825              
2826             =head2 a b c d e n ROLL
2827              
2828             rotate the stack on 'n' element
2829             a,b,c,d,e,f,4,ROLL -> a b d e f c
2830             if n = 3 <=> ROT
2831             if -2 < n < 2 nothing is done
2832             if n < -1 ROLL in reverse order
2833             a,b,c,d,e,f,-4,ROLL -> a b f e d c
2834             To reveerse a stack content use this:
2835             a,b,c,d,e,f,DEPTH,+-,ROLL => f e d c b a
2836              
2837             =cut
2838              
2839             $dict{ROLL} = sub {
2840             my $work1 = shift;
2841             my $a = pop @{ $work1 };
2842              
2843             my @tmp;
2844             my $b;
2845             if ( $a > 1 )
2846             {
2847             @tmp = splice @{ $work1 }, -( $a - 1 );
2848             $b = pop @{ $work1 };
2849             }
2850             if ( $a < -1 )
2851             {
2852             @tmp = reverse( splice @{ $work1 }, ( $a ) );
2853             $a *= -1;
2854             }
2855             my @ret;
2856             if ( $a < 2 && $a > -2 )
2857             {
2858             return \@ret, 1, 0;
2859             }
2860             if ( defined $b )
2861             {
2862             push @ret, @tmp, $b;
2863             }
2864             else
2865             {
2866             push @ret, @tmp;
2867             }
2868             return \@ret, 1 + $a, 0;
2869             };
2870              
2871             =head2 a PICK
2872            
2873             copy element from depth 'a' to the stack
2874              
2875             =cut
2876              
2877             $dict{PICK} = sub {
2878             my $work1 = shift;
2879             my $a = pop @{ $work1 };
2880             my @ret;
2881             if ( $a <= scalar @{ $work1 } )
2882             {
2883             push @ret, @{ $work1 }[ -( $a ) ];
2884             }
2885              
2886             return \@ret, 1, 0;
2887             };
2888              
2889             =head2 a GET
2890            
2891             get (remove) element from depth 'a'
2892             and put on top of stack
2893              
2894             =cut
2895              
2896             $dict{GET} = sub {
2897             my $work1 = shift;
2898             my $a = pop @{ $work1 };
2899             my @ret;
2900             my $b;
2901             if ( $a <= ( scalar @{ $work1 } ) && ( $a > 1 ) )
2902             {
2903             my $line = join " | ", @{ $work1 };
2904             my @tmp = splice @{ $work1 }, -( $a - 1 );
2905             $line = join " | ", @tmp;
2906             $b = pop @{ $work1 };
2907             push @ret, @tmp, $b;
2908             return \@ret, 1 + $a, 0;
2909             }
2910             else
2911             {
2912             return \@ret, 1, 0;
2913             }
2914              
2915             };
2916              
2917             =head2 a b PUT
2918            
2919             put element 'a' at the level 'b' of the stack
2920             if 'b' greater than the stack put at first place
2921             if 'b' < 0 start to the reverse order of the stack
2922              
2923             =cut
2924              
2925             $dict{PUT} = sub {
2926             my $work1 = shift;
2927             my $len = scalar @{ $work1 };
2928             my $a = pop @{ $work1 };
2929             my $b = pop @{ $work1 };
2930             my @tmp;
2931             my @ret = @{ $work1 };
2932             if ( $a >= ( scalar( @{ $work1 } ) ) )
2933             {
2934             $a = scalar( @{ $work1 } );
2935             }
2936             if ( $a )
2937             {
2938             @tmp = splice @ret, -( $a - 1 );
2939             }
2940             push( @ret, $b, @tmp );
2941             return \@ret, $len, 0;
2942             };
2943              
2944             =head2 a b DEL
2945            
2946             delete 'b' element on the stack from level 'a'
2947             'a' and 'b' is get in absolute value
2948              
2949             =cut
2950              
2951             $dict{DEL} = sub {
2952             my $work1 = shift;
2953             my $len = scalar( @{ $work1 } );
2954             my $start = abs pop @{ $work1 };
2955             my $length1 = abs pop @{ $work1 };
2956             my $length = ( $length1 + $start + 2 > $len ? $len - $start - 2 : $length1 );
2957             my @temp;
2958             @temp = splice @{ $work1 }, $len - 2 - $start - $length, $length;
2959             my @ret;
2960             push( @ret, @{ $work1 } );
2961             return \@ret, $len, 0;
2962             };
2963              
2964             =head2 a FIND
2965            
2966             get the level of stack containing the exact value 'a'
2967             if no match, return 0
2968              
2969             =cut
2970              
2971             $dict{FIND} = sub {
2972             my $work1 = shift;
2973             my $a = pop @{ $work1 };
2974              
2975             my $nbr = scalar( @{ $work1 } );
2976             my $ret = 0;
2977             for ( 1 .. $nbr )
2978             {
2979             my $b = @{ $work1 }[ $nbr - $_ ];
2980             if ( $a =~ /^(\d+|\d+\.\d*|\.\d*)$/ )
2981             {
2982             if ( $b == $a )
2983             {
2984             $ret = $_;
2985             last;
2986             }
2987             }
2988             else
2989             {
2990             if ( $b eq $a )
2991             {
2992             $ret = $_;
2993             last;
2994             }
2995             }
2996             }
2997             my @ret;
2998             push( @ret, $ret );
2999             return \@ret, 1, 0;
3000             };
3001              
3002             =head2 a FINDK
3003            
3004             keep the level of stack containing the exact value 'a'
3005             f no match, return an empty stack
3006             ( shortcut for a,FIND,KEEP )
3007            
3008             =cut
3009              
3010             $dict{FINDK} = sub {
3011             my $work1 = shift;
3012             my $a = pop @{ $work1 };
3013              
3014             my $nbr = scalar( @{ $work1 } );
3015             my $ret;
3016             for ( 1 .. $nbr )
3017             {
3018             my $b = @{ $work1 }[ $nbr - $_ ];
3019             if ( $a =~ /^(\d+|\d+\.\d*|\.\d*)$/ )
3020             {
3021             if ( $b == $a )
3022             {
3023             $ret = $a;
3024             last;
3025             }
3026             }
3027             else
3028             {
3029             if ( $b eq $a )
3030             {
3031             $ret = $a;
3032             last;
3033             }
3034             }
3035             }
3036             my @ret;
3037             push( @ret, $ret );
3038             return \@ret, $nbr + 1, 0;
3039             };
3040              
3041             =head2 a SEARCH
3042            
3043             get the first level of stack containing the REGEX 'a'
3044              
3045             =cut
3046              
3047             $dict{SEARCH} = sub {
3048             my $work1 = shift;
3049             my $a = pop @{ $work1 };
3050             my $ret = 1;
3051             my $nbr = scalar( @{ $work1 } );
3052             my @ret;
3053             for ( my $i = $nbr ; $i ; $i-- )
3054             {
3055             my $b = @{ $work1 }[ $nbr - $i ];
3056             if ( $b =~ /$a/ )
3057             {
3058             $ret = $i;
3059             push( @ret, $ret );
3060             return \@ret, 1, 0;
3061             }
3062             }
3063             push( @ret, 0 );
3064             return \@ret, 1, 0;
3065             };
3066              
3067             =head2 a SEARCHI
3068            
3069             get the first level of stack containing the REGEX 'a' (cas insensitive)
3070              
3071             =cut
3072              
3073             $dict{SEARCHI} = sub {
3074             my $work1 = shift;
3075             my $a = pop @{ $work1 };
3076             my $ret = 1;
3077             my $nbr = scalar( @{ $work1 } );
3078             my @ret;
3079             for ( my $i = $nbr ; $i ; $i-- )
3080             {
3081             my $b = @{ $work1 }[ $nbr - $i ];
3082             if ( $b =~ /$a/i )
3083             {
3084             $ret = $i;
3085             push( @ret, $ret );
3086             return \@ret, 1, 0;
3087             }
3088             }
3089             push( @ret, 0 );
3090             return \@ret, 1, 0;
3091             };
3092              
3093             =head2 a SEARCHIA
3094              
3095             get all level of stack containing the REGEX 'a' (cas insensitive)
3096             empty the stack and return all the index of item matching
3097              
3098             =cut
3099              
3100             $dict{SEARCHIA} = sub {
3101             my $work1 = shift;
3102             my $a = pop @{ $work1 };
3103             my $ret;
3104             my $nbr = scalar( @{ $work1 } );
3105             my @ret;
3106             for ( my $i = $nbr ; $i ; $i-- )
3107             {
3108             my $b = @{ $work1 }[ $nbr - $i ];
3109             if ( $b =~ /$a/i )
3110             {
3111             $ret++;
3112             push @ret, $i;
3113             }
3114             }
3115             return \@ret, 1 + $nbr, 0;
3116             };
3117              
3118             =head2 a SEARCHA
3119              
3120             get all level of stack containing the REGEX 'a' (cas sensitive)
3121             empty the stack and return all the index of item matching
3122              
3123             toto,toti,titi,tata,tota,tito,tutot,truc,tot,SEARCHA
3124             result: 8 7 4 2
3125              
3126             =cut
3127              
3128             $dict{SEARCHA} = sub {
3129             my $work1 = shift;
3130             my $a = pop @{ $work1 };
3131             my $ret;
3132             my $nbr = scalar( @{ $work1 } );
3133             my @ret;
3134             for ( my $i = $nbr ; $i ; $i-- )
3135             {
3136             my $b = @{ $work1 }[ $nbr - $i ];
3137             if ( $b =~ /$a/ )
3138             {
3139             $ret++;
3140             push @ret, $i;
3141             }
3142             }
3143             return \@ret, 1 + $nbr, 0;
3144             };
3145              
3146             =head2 a SEARCHK
3147            
3148             keep all level of stack containing the REGEX 'a' (cas sensitive)
3149              
3150             toto,toti,titi,tata,tota,tito,tutot,truc,tot,SEARCHK
3151             result: toto toti tota tutot
3152              
3153             =cut
3154              
3155             $dict{SEARCHK} = sub {
3156             my $work1 = shift;
3157             my $a = pop @{ $work1 };
3158             my $ret = 1;
3159             my $nbr = scalar( @{ $work1 } );
3160             my @ret;
3161             for ( my $i = $nbr ; $i ; $i-- )
3162             {
3163             my $b = @{ $work1 }[ $nbr - $i ];
3164             if ( $b =~ /$a/ )
3165             {
3166             $ret = $i;
3167             push @ret, $b;
3168             }
3169             }
3170             return \@ret, $nbr + 1, 0;
3171             };
3172              
3173             =head2 a SEARCHIK
3174            
3175             keep all level of stack containing the REGEX 'a' (cas insensitive)
3176              
3177             =cut
3178              
3179             $dict{SEARCHIK} = sub {
3180             my $work1 = shift;
3181             my $a = pop @{ $work1 };
3182             my $ret = 1;
3183             my $nbr = scalar( @{ $work1 } );
3184             my @ret;
3185             for ( my $i = $nbr ; $i ; $i-- )
3186             {
3187             my $b = @{ $work1 }[ $nbr - $i ];
3188             if ( $b =~ /$a/i )
3189             {
3190             $ret = $i;
3191             push @ret, $b;
3192             }
3193             }
3194             return \@ret, $nbr + 1, 0;
3195             };
3196              
3197             =head2 a KEEP
3198            
3199             delete all element on the stack except the level 'a'
3200             if 'a' is deeper then stack, keep the stack untouched
3201            
3202             =cut
3203              
3204             $dict{KEEP} = sub {
3205             my $work1 = shift;
3206             my $a = pop @{ $work1 };
3207             my @ret;
3208             if ( $a <= 0 )
3209             {
3210             return \@ret, 1 + ( scalar @{ $work1 } );
3211             }
3212             if ( $a < ( ( scalar @{ $work1 } ) + 1 ) )
3213             {
3214             push @ret, @{ $work1 }[ -( $a ) ];
3215             return \@ret, 1 + ( scalar @{ $work1 } ), 0;
3216             }
3217             else
3218             {
3219             return \@ret, 1, 0;
3220             }
3221             };
3222              
3223             =head2 a KEEPV
3224            
3225             delete all element on the stack except the levels with indice in the var A
3226              
3227             1,5,2,3,A,!!,a,b,c,d,e,f,g,i,A,KEEPV
3228             result: i d g
3229            
3230             =cut
3231              
3232             $dict{KEEPV} = sub {
3233             my $work1 = shift;
3234             my $name = pop @{ $work1 };
3235             my @ret;
3236              
3237             if ( exists $var{ $name } )
3238             {
3239             if ( ref $var{ $name } eq 'ARRAY' )
3240             {
3241             foreach my $ind ( @{ $var{ $name } } )
3242             {
3243             push @ret, @{ $work1 }[ -$ind ] if ( defined @{ $work1 }[ -$ind ] );
3244             }
3245             }
3246             else
3247             {
3248             push @ret, @{ $work1 }[ -$var{ $name } ] if ( defined @{ $work1 }[ -$var{ $name } ] );
3249             }
3250             }
3251             return \@ret, scalar( @{ $work1 } ) + 1, 0;
3252             };
3253              
3254             =head2 a KEEPVV
3255            
3256             keep element from array B with indice from ARRAY A
3257              
3258             1,5,2,3,A,!!,a,b,c,d,e,f,g,i,8,B,!!,B,A,KEEPVV
3259             result: i d g
3260            
3261             =cut
3262              
3263             $dict{KEEPVV} = sub {
3264             my $work1 = shift;
3265             my $name1 = pop @{ $work1 };
3266             my $name2 = pop @{ $work1 };
3267             my @ret;
3268             my @tmp;
3269              
3270             if ( exists $var{ $name1 } && exists $var{ $name2 } )
3271             {
3272             if ( ref $var{ $name2 } eq 'ARRAY' )
3273             {
3274             @tmp = @{ $var{ $name2 } };
3275             }
3276             else
3277             {
3278             @tmp = $var{ $name2 };
3279             }
3280             if ( ref $var{ $name1 } eq 'ARRAY' )
3281             {
3282             foreach my $ind ( @{ $var{ $name1 } } )
3283             {
3284             push @ret, $tmp[ -$ind ] if ( defined $tmp[ -$ind ] );
3285             }
3286             }
3287             else
3288             {
3289             push @ret, $tmp[ -$var{ $name1 } ] if ( defined $tmp[ -$var{ $name1 } ] );
3290             }
3291             }
3292             return \@ret, 2, 0;
3293             };
3294              
3295             =head2 b a KEEPN
3296            
3297             keep 'b' element on the stack from level 'a'
3298             and delete all other element
3299             'a' and 'b' is get in absolute value
3300              
3301             a,b,c,d,e,f,g,h,4,3,KEEPN
3302             result: c d e f
3303              
3304             =cut
3305              
3306             $dict{KEEPN} = sub {
3307             my $work1 = shift;
3308             my $len = scalar( @{ $work1 } );
3309             my $start = abs pop @{ $work1 };
3310             my $length1 = abs pop @{ $work1 };
3311             my $length = ( $length1 + $start + 2 > $len ? $len - $start - 1 : $length1 );
3312             my @ret = splice @{ $work1 }, $len - 1 - $start - $length, $length;
3313             return \@ret, $len, 0;
3314             };
3315              
3316             =head2 b a KEEPR
3317            
3318             delete all elements on the stack except the level 'a' and keep all element deeper than 'b'
3319             if 'a' is deeper then stack, keep the stack untouched
3320              
3321             a,b,c,d,e,f,g,h,6,3,KEEPR
3322             result: a b f
3323              
3324             =cut
3325              
3326             $dict{KEEPR} = sub {
3327             my $work1 = shift;
3328             my $a = pop @{ $work1 };
3329             my $b = pop @{ $work1 };
3330             my @tmp = splice @{ $work1 }, scalar( @{ $work1 } ) - $b;
3331              
3332             my @ret;
3333             if ( $a <= 0 )
3334             {
3335             return \@ret, 1 + ( scalar @tmp );
3336             }
3337             if ( $a < ( ( scalar @tmp ) + 1 ) )
3338             {
3339             push @ret, @tmp[ -( $a ) ];
3340             return \@ret, 2 + ( scalar @tmp ), 0;
3341             }
3342             else
3343             {
3344             return \@ret, 2, 0;
3345             }
3346             };
3347              
3348             =head2 c b a KEEPRN
3349            
3350             keep 'b' element on the stack from level 'a' and keep all element deeper than 'c'
3351             if 'a' is deeper then stack, keep the stack untouched
3352              
3353             a,b,c,d,e,f,g,h,i,j,7,3,2,KEEPRN
3354             result: a b c g h i
3355              
3356             =cut
3357              
3358             $dict{KEEPRN} = sub {
3359             my $work1 = shift;
3360              
3361             my $start = abs pop @{ $work1 };
3362             my @ret;
3363             unless ( $start )
3364             {
3365             return \@ret, +3, 0;
3366             }
3367             my $length1 = abs pop @{ $work1 };
3368             my $deepth = abs pop @{ $work1 };
3369             my @tmp = splice @{ $work1 }, scalar( @{ $work1 } ) - $deepth;
3370             my $len = scalar( @tmp );
3371             my @t = reverse @tmp;
3372             @ret = reverse splice @t, $start - 1, $length1;
3373             return \@ret, $len + 3, 0;
3374             };
3375              
3376             =head2 a b PRESERVE
3377            
3378             keep element on the stack from level 'a'
3379             to level 'b'
3380             and delete all other element
3381             'a' and 'b' is get in absolute value
3382             if 'a' > 'b' keep the reverse of selection (boustrophedon)
3383              
3384             =cut
3385              
3386             $dict{PRESERVE} = sub {
3387             my $work1 = shift;
3388             my $len = scalar( @{ $work1 } );
3389             my $start = ( abs pop @{ $work1 } );
3390             my $end = ( abs pop @{ $work1 } );
3391             my $len1 = scalar( @{ $work1 } );
3392             my @temp;
3393             if ( $start <= $end )
3394             {
3395             @temp = @{ $work1 }[ ( $len1 - $end ) .. ( $len1 - $start ) ];
3396             }
3397             else
3398             {
3399             push @temp, @{ $work1 }[ ( $start - 1 ) .. ( $#$work1 ) ];
3400             push @temp, @{ $work1 }[ 0 .. ( $end - 1 ) ];
3401             }
3402             return \@temp, $len, 0;
3403             };
3404              
3405             =head2 a b COPY
3406            
3407             copy element on the stack from level 'a'
3408             to level 'b'
3409             'a' and 'b' is get in absolute value
3410             if 'a' > 'b' keep the reverse of selection (boustrophedon)
3411              
3412             =cut
3413              
3414             $dict{COPY} = sub {
3415             my $work1 = shift;
3416             my $len = scalar( @{ $work1 } );
3417             my $start = ( abs pop @{ $work1 } );
3418             my $end = ( abs pop @{ $work1 } );
3419             my $len1 = scalar( @{ $work1 } );
3420             my @temp;
3421             if ( $start <= $end )
3422             {
3423             @temp = @{ $work1 }[ ( $len1 - $end ) .. ( $len1 - $start ) ];
3424             }
3425             else
3426             {
3427             push @temp, @{ $work1 }[ ( $len1 - $end ) .. ( $#$work1 ) ];
3428             push @temp, @{ $work1 }[ ( 0 ) .. ( $len1 - $start ) ];
3429             }
3430             return \@temp, 2, 0;
3431             };
3432              
3433             ########################
3434             # DICT operator
3435             ########################
3436              
3437             =head1 DICTIONARY and VARS operators
3438              
3439             =cut
3440              
3441             =head2 WORDS
3442              
3443             return as one stack element the list of WORD in DICT separated by a |
3444            
3445             =cut
3446              
3447             $dict{WORDS} = sub {
3448             my @tmp = join " | ", sort keys( %dict );
3449             my @ret;
3450             push @ret, @tmp;
3451             return \@ret, 0, 0;
3452             };
3453              
3454             =head2 VARS
3455              
3456             return as one stack element the list of VARS separated by a |
3457            
3458             =cut
3459              
3460             $dict{VARS} = sub {
3461             my @tmp = join " | ", sort keys( %var );
3462             my @ret;
3463             push @ret, @tmp;
3464             return \@ret, 0, 0;
3465             };
3466              
3467             =head2 v SIZE
3468              
3469             return the size of the variable on the stack
3470            
3471             =cut
3472              
3473             $dict{SIZE} = sub {
3474             my $work1 = shift;
3475             my $a = pop @{ $work1 };
3476             my $ret = 0;
3477             if ( exists $var{ $a } )
3478             {
3479             if ( ( ref( $var{ $a } ) eq 'ARRAY' ) )
3480             {
3481             $ret = scalar( @{ $var{ $a } } );
3482             }
3483             else
3484             {
3485             $ret = 1;
3486             }
3487             }
3488             my @ret;
3489             push @ret, $ret;
3490             return \@ret, 1, 0;
3491             };
3492              
3493             =head2 v POPV
3494              
3495             remove return the first item of the variable on the stack
3496            
3497             =cut
3498              
3499             $dict{POPV} = sub {
3500             my $work1 = shift;
3501             my $a = pop @{ $work1 };
3502             my $ret = 0;
3503             if ( exists $var{ $a } )
3504             {
3505             if ( ( ref( $var{ $a } ) eq 'ARRAY' ) )
3506             {
3507             $ret = pop( @{ $var{ $a } } );
3508             }
3509             else
3510             {
3511             $ret = $var{ $a };
3512             $var{ $a } = '';
3513             }
3514             }
3515             my @ret;
3516             push @ret, $ret;
3517             return \@ret, 1, 0;
3518             };
3519              
3520             =head2 v SHIFTV
3521              
3522             remove return the latest item of the variable on the stack
3523            
3524             =cut
3525              
3526             $dict{SHIFTV} = sub {
3527             my $work1 = shift;
3528             my $a = pop @{ $work1 };
3529             my $ret = 0;
3530             if ( exists $var{ $a } )
3531             {
3532             if ( ( ref( $var{ $a } ) eq 'ARRAY' ) )
3533             {
3534             $ret = shift( @{ $var{ $a } } );
3535             }
3536             else
3537             {
3538             $ret = $var{ $a };
3539             $var{ $a } = '';
3540             }
3541             }
3542             my @ret;
3543             push @ret, $ret;
3544             return \@ret, 1, 0;
3545             };
3546              
3547             =head2 v a IND
3548              
3549             return the element of the variable at the indice a ( ARRAY emulation )
3550            
3551             =cut
3552              
3553             $dict{IND} = sub {
3554             my $work1 = shift;
3555             my $ind = pop @{ $work1 };
3556             my $name = pop @{ $work1 };
3557             my $ret = 0;
3558              
3559             if ( exists $var{ $name } )
3560             {
3561             if ( ( ref( $var{ $name } ) eq 'ARRAY' ) )
3562             {
3563             my $size = scalar @{ $var{ $name } };
3564             $ret = $var{ $name }->[ $size - $ind ];
3565             }
3566             else
3567             {
3568             $ret = $var{ $name };
3569             }
3570             }
3571             my @ret;
3572             push @ret, $ret;
3573             return \@ret, 2, 0;
3574             };
3575              
3576             =head2 v INC
3577              
3578             incremente (+ 1) the value of the variable on the statck
3579            
3580             =cut
3581              
3582             $dict{INC} = sub {
3583             my $work1 = shift;
3584             my $a = pop @{ $work1 };
3585             if ( ( !ref( $var{ $a } ) ) && $var{ $a } =~ /\d+/ )
3586             {
3587             ( $var{ $a } )++;
3588             }
3589             my @ret;
3590             return \@ret, 1, 0;
3591             };
3592              
3593             =head2 v DEC
3594              
3595             decremente (- 1) the value of the variable on the statck
3596            
3597             =cut
3598              
3599             $dict{DEC} = sub {
3600             my $work1 = shift;
3601             my $a = pop @{ $work1 };
3602             if ( ( !ref( $var{ $a } ) ) && $var{ $a } =~ /\d+/ )
3603             {
3604             ( $var{ $a } )--;
3605             }
3606             my @ret;
3607             return \@ret, 1, 0;
3608             };
3609              
3610             =head2 VARIABLE xxx
3611              
3612             declare the variable 'xxx' (reserve memory)
3613            
3614             =cut
3615              
3616             $dict{VARIABLE} = sub {
3617             my $work1 = shift;
3618             my $a = pop @{ $work1 };
3619             my @ret;
3620             if ( $a )
3621             {
3622             $var{ $a } = '';
3623             return \@ret, 1, 0;
3624             }
3625             return \@ret, 0, 0;
3626             };
3627              
3628             =head2 v UNSET
3629              
3630             delete the variable v
3631            
3632             =cut
3633              
3634             $dict{UNSET} = sub {
3635             my $work1 = shift;
3636             my $a = pop @{ $work1 };
3637             my @ret;
3638             delete $var{$a} if exists $var{$a};
3639             return \@ret, 1, 0;
3640             };
3641              
3642             =head2 xx var !
3643              
3644             set and delete from the stack the value xx to the variable 'var'
3645            
3646             =cut
3647              
3648             $dict{'!'} = sub {
3649             my $work1 = shift;
3650             my $name = pop @{ $work1 };
3651             my $val = pop @{ $work1 };
3652             $var{ $name } = $val;
3653             my @ret;
3654             return \@ret, 2, 0;
3655             };
3656              
3657             =head2 xx var !A
3658              
3659             append to the variable and delete from the stack the value xx to the variable 'var'
3660            
3661             =cut
3662              
3663             $dict{'!A'} = sub {
3664             my $work1 = shift;
3665             my $name = pop @{ $work1 };
3666             my $val = pop @{ $work1 };
3667             my @ret;
3668             my @TMP;
3669             if ( exists $var{ $name } )
3670             {
3671             if ( ref $var{ $name } eq 'ARRAY' )
3672             {
3673             unshift @TMP, $val, @{ $var{ $name } };
3674             }
3675             else
3676             {
3677             unshift @TMP, $val, $var{ $name };
3678             }
3679             $var{ $name } = \@TMP;
3680             }
3681             else
3682             {
3683             $var{ $name } = $val;
3684             }
3685             return \@ret, 2, 0;
3686             };
3687              
3688             =head2 x1 x2 x3 ... n var !!
3689            
3690             put and delete from the stack 'n' element(s) from the stack in the variable 'var'
3691             'n' is in absolute value
3692              
3693             =cut
3694              
3695             $dict{'!!'} = sub {
3696              
3697             my $work1 = shift;
3698             my $len = scalar( @{ $work1 } );
3699             my $name = pop @{ $work1 };
3700             my $len_to_rm = ( abs pop @{ $work1 } );
3701             my @temp;
3702             my $from = ( 1 + ( $#$work1 ) - $len_to_rm );
3703             $from = $from < 0 ? 0 : $from;
3704             my @TMP = @{ $work1 }[ $from .. ( $#$work1 ) ];
3705             $var{ $name } = \@TMP;
3706             return \@temp, $len_to_rm + 2, 0;
3707             };
3708              
3709             =head2 x1 x2 x3 ... n var !!A
3710            
3711             append and delete 'n' element(s) from the stack in the variable 'var'
3712             'n' is in absolute value
3713              
3714             =cut
3715              
3716             $dict{'!!A'} = sub {
3717              
3718             my $work1 = shift;
3719             my $len = scalar( @{ $work1 } );
3720             my $name = pop @{ $work1 };
3721             my $len_to_rm = ( abs pop @{ $work1 } );
3722             my @temp;
3723             my $from = ( 1 + ( $#$work1 ) - $len_to_rm );
3724             $from = $from < 0 ? 0 : $from;
3725             my @TMP = @{ $work1 }[ $from .. ( $#$work1 ) ];
3726             if ( exists $var{ $name } )
3727             {
3728              
3729             if ( ref $var{ $name } eq 'ARRAY' )
3730             {
3731             unshift @TMP, @{ $var{ $name } };
3732             }
3733             else
3734             {
3735             unshift @TMP, $var{ $name };
3736             }
3737             $var{ $name } = \@TMP;
3738             }
3739             else
3740             {
3741             $var{ $name } = \@TMP;
3742             }
3743             return \@temp, $len_to_rm + 2, 0;
3744             };
3745              
3746             =head2 x1 x2 x3 ... n var !!C
3747            
3748             copy 'n' element(s) from the stack in the variable 'var'
3749             'n' is in absolute value
3750              
3751             =cut
3752              
3753             $dict{'!!C'} = sub {
3754              
3755             my $work1 = shift;
3756             my $len = scalar( @{ $work1 } );
3757             my $name = pop @{ $work1 };
3758             my $len_to_rm = ( abs pop @{ $work1 } );
3759             my @temp;
3760             my $from = ( 1 + ( $#$work1 ) - $len_to_rm );
3761             $from = $from < 0 ? 0 : $from;
3762             my @TMP = @{ $work1 }[ $from .. ( $#$work1 ) ];
3763             $var{ $name } = \@TMP;
3764             return \@temp, 2, 0;
3765             };
3766              
3767             =head2 x1 x2 x3 ... n var !!CA
3768            
3769             append 'n' element(s) from the stack in the variable 'var'
3770             'n' is in absolute value
3771              
3772             =cut
3773              
3774             $dict{'!!CA'} = sub {
3775              
3776             my $work1 = shift;
3777             my $len = scalar( @{ $work1 } );
3778             my $name = pop @{ $work1 };
3779             my $len_to_rm = ( abs pop @{ $work1 } );
3780             my @temp;
3781             my $from = ( 1 + ( $#$work1 ) - $len_to_rm );
3782             $from = $from < 0 ? 0 : $from;
3783             my @TMP = @{ $work1 }[ $from .. ( $#$work1 ) ];
3784             if ( exists $var{ $name } )
3785             {
3786              
3787             if ( ref $var{ $name } eq 'ARRAY' )
3788             {
3789             unshift @TMP, @{ $var{ $name } };
3790             }
3791             else
3792             {
3793             unshift @TMP, $var{ $name };
3794             }
3795             $var{ $name } = \@TMP;
3796             }
3797             else
3798             {
3799             $var{ $name } = \@TMP;
3800             }
3801             return \@temp, 2, 0;
3802             };
3803              
3804             =head2 x1 x2 x3 ... b a var !!!
3805            
3806             put and delete ' element(s) from the stack in the variable 'var'
3807             starting at element 'a' to element 'b'
3808             'a' and 'b' in absolute value
3809             if 'a' > 'b' keep the reverse of selection (boustrophedon)
3810              
3811             =cut
3812              
3813             $dict{'!!!'} = sub {
3814             my $work1 = shift;
3815             my $len = scalar( @{ $work1 } );
3816             my $name = pop @{ $work1 };
3817             my $start = ( abs pop @{ $work1 } );
3818             my $end = ( abs pop @{ $work1 } );
3819             my $len1 = scalar( @{ $work1 } );
3820             my @temp;
3821             my @TMP;
3822              
3823             if ( $start <= $end )
3824             {
3825             @TMP = @{ $work1 }[ ( $len1 - $end ) .. ( $len1 - $start ) ];
3826             push @temp, @{ $work1 }[ 0 .. ( $len1 - $end - 1 ) ];
3827             push @temp, @{ $work1 }[ ( $len1 - $start + 1 ) .. ( $#$work1 ) ];
3828             }
3829             else
3830             {
3831             push @TMP, @{ $work1 }[ ( $len1 - $end ) .. ( $#$work1 ) ];
3832             push @TMP, @{ $work1 }[ ( 0 ) .. ( $len1 - $start ) ];
3833             @temp = @{ $work1 }[ ( $len1 - $start + 1 ) .. ( $len1 - $end - 1 ) ];
3834             }
3835             $var{ $name } = \@TMP;
3836             return \@temp, $len, 0;
3837             };
3838              
3839             =head2 x1 x2 x3 ... b a var !!!A
3840            
3841             append and delete ' element(s) from the stack in the variable 'var'
3842             starting at element 'a' to element 'b'
3843             'a' and 'b' in absolute value
3844             if 'a' > 'b' keep the reverse of selection (boustrophedon)
3845              
3846             =cut
3847              
3848             $dict{'!!!A'} = sub {
3849             my $work1 = shift;
3850             my $len = scalar( @{ $work1 } );
3851             my $name = pop @{ $work1 };
3852             my $start = ( abs pop @{ $work1 } );
3853             my $end = ( abs pop @{ $work1 } );
3854             my $len1 = scalar( @{ $work1 } );
3855             my @temp;
3856             my @TMP;
3857              
3858             if ( $start <= $end )
3859             {
3860             @TMP = @{ $work1 }[ ( $len1 - $end ) .. ( $len1 - $start ) ];
3861             push @temp, @{ $work1 }[ 0 .. ( $len1 - $end - 1 ) ];
3862             push @temp, @{ $work1 }[ ( $len1 - $start + 1 ) .. ( $#$work1 ) ];
3863             }
3864             else
3865             {
3866             push @TMP, @{ $work1 }[ ( $len1 - $end ) .. ( $#$work1 ) ];
3867             push @TMP, @{ $work1 }[ ( 0 ) .. ( $len1 - $start ) ];
3868             @temp = @{ $work1 }[ ( $len1 - $start + 1 ) .. ( $len1 - $end - 1 ) ];
3869             }
3870             if ( exists $var{ $name } )
3871             {
3872             if ( ref $var{ $name } eq 'ARRAY' )
3873             {
3874             unshift @TMP, @{ $var{ $name } };
3875             }
3876             else
3877             {
3878             unshift @TMP, $var{ $name };
3879             }
3880             $var{ $name } = \@TMP;
3881             }
3882             else
3883             {
3884             $var{ $name } = \@TMP;
3885             }
3886             return \@temp, $len, 0;
3887             };
3888              
3889             =head2 x1 x2 x3 ... b a var !!!C
3890            
3891             copy element(s) on the stack in the variable 'var'
3892             starting at element 'a' to element 'b'
3893             'a' and 'b' in absolute value
3894             if 'a' > 'b' keep the reverse of selection (boustrophedon)
3895              
3896             =cut
3897              
3898             $dict{'!!!C'} = sub {
3899              
3900             my $work1 = shift;
3901             my $len = scalar( @{ $work1 } );
3902             my $name = pop @{ $work1 };
3903             my $start = ( abs pop @{ $work1 } );
3904             my $end = ( abs pop @{ $work1 } );
3905             my $len1 = scalar( @{ $work1 } );
3906             my $len_to_rm = abs( $start - $end );
3907             my @temp;
3908             my @TMP;
3909              
3910             if ( $start <= $end )
3911             {
3912             @TMP = @{ $work1 }[ ( $len1 - $end ) .. ( $len1 - $start ) ];
3913             }
3914             else
3915             {
3916             push @TMP, @{ $work1 }[ ( $len1 - $end ) .. ( $#$work1 ) ];
3917             push @TMP, @{ $work1 }[ ( 0 ) .. ( $len1 - $start ) ];
3918             }
3919             $var{ $name } = \@TMP;
3920             return \@temp, 3, 0;
3921             };
3922              
3923             =head2 x1 x2 x3 ... b a var !!!CA
3924            
3925             append element(s) on the stack in the variable 'var'
3926             starting at element 'a' to element 'b'
3927             'a' and 'b' in absolute value
3928             if 'a' > 'b' keep the reverse of selection (boustrophedon)
3929              
3930             =cut
3931              
3932             $dict{'!!!CA'} = sub {
3933              
3934             my $work1 = shift;
3935             my $len = scalar( @{ $work1 } );
3936             my $name = pop @{ $work1 };
3937             my $start = ( abs pop @{ $work1 } );
3938             my $end = ( abs pop @{ $work1 } );
3939             my $len1 = scalar( @{ $work1 } );
3940             my $len_to_rm = abs( $start - $end );
3941             my @temp;
3942             my @TMP;
3943              
3944             if ( $start <= $end )
3945             {
3946             @TMP = @{ $work1 }[ ( $len1 - $end ) .. ( $len1 - $start ) ];
3947             }
3948             else
3949             {
3950             push @TMP, @{ $work1 }[ ( $len1 - $end ) .. ( $#$work1 ) ];
3951             push @TMP, @{ $work1 }[ ( 0 ) .. ( $len1 - $start ) ];
3952             }
3953             if ( exists $var{ $name } )
3954             {
3955             if ( ref $var{ $name } eq 'ARRAY' )
3956             {
3957             unshift @TMP, @{ $var{ $name } };
3958             }
3959             else
3960             {
3961             unshift @TMP, $var{ $name };
3962             }
3963             $var{ $name } = \@TMP;
3964             }
3965             else
3966             {
3967             $var{ $name } = \@TMP;
3968             }
3969             return \@temp, 3, 0;
3970             };
3971              
3972              
3973             =head2 var @
3974              
3975             return the value of the variable 'var'
3976            
3977             =cut
3978              
3979             $dict{'@'} = sub {
3980             my $work1 = shift;
3981             my $name = pop @{ $work1 };
3982             my @ret;
3983             if ( ref( $var{ $name } ) =~ /ARRAY/i )
3984             {
3985             push @ret, @{ $var{ $name } };
3986             }
3987             else
3988             {
3989             push @ret, $var{ $name };
3990             }
3991             return \@ret, 1, 0;
3992             };
3993              
3994             =head2 : xxx name1 ;
3995              
3996             create a new entry in the dictionary whith name name1 and store the progam xxx
3997            
3998             =cut
3999              
4000             $dict{';'} = sub {
4001             my $work1 = shift;
4002             my $return1 = shift;
4003             my $len = scalar( @{ $work1 } );
4004             my $b_ref = pop @{ $return1 };
4005             my $a_ref = pop @{ $return1 };
4006             my @pre = @{ $work1 };
4007             my @BLOCK = splice @pre, $a_ref, $b_ref - $a_ref;
4008             my @ret;
4009             pop @pre;
4010             my $name = pop @BLOCK;
4011             unless ( exists $dict{ $name } )
4012             {
4013             $pub_dict{ $name } = 1;
4014             $dict{ $name } = sub {
4015             my $ret;
4016             @ret = @BLOCK;
4017             return \@ret, 0, 0;
4018             };
4019             return \@ret, $#BLOCK + 2, 2;
4020             }
4021             return \@ret, $#BLOCK + 2, 0;
4022             };
4023              
4024             =head2 name1 FORGOT
4025              
4026             delete/erase a create word (name1 )
4027            
4028             =cut
4029              
4030             $dict{FORGOT} = sub {
4031             my $work1 = shift;
4032             my $name = pop @{ $work1 };
4033             my @ret;
4034             if ( exists $pub_dict{$name} )
4035             {
4036             delete $pub_dict{$name} ;
4037             delete $dict{$name} ;
4038             }
4039             return \@ret, 1, 0;
4040             };
4041              
4042             =head2 : xxx yyy name1 PERL
4043              
4044             execute the PERL code
4045             with parameter(s) xxx yyy
4046             !!! be care if the perl code need to use a coma (,)
4047             you need to enclose the line inside double quote
4048             if you need double quote in code use qq{ ... }
4049            
4050             =cut
4051              
4052             $dict{PERL} = sub {
4053             my $work1 = shift;
4054             my $return1 = shift;
4055              
4056             my $b_ref = pop @{ $return1 };
4057             my $a_ref = pop @{ $return1 } // 0;
4058             my @in = @{ $work1 };
4059             my @pre = splice @in, 0, $a_ref;
4060             my @tmp = ( @pre, @in );
4061             my $len_before = scalar( @tmp );
4062             my $len_after = scalar( @tmp );
4063             my $delta = $len_before - $len_after;
4064             my @BLOCK = splice( @tmp, -$delta, $len_before - $delta );
4065             my $name = join ";", @BLOCK;
4066              
4067             my $not_stdout;
4068             open($not_stdout,'>', \my $buf );
4069             select($not_stdout);
4070             eval $name;
4071             if ( $@ )
4072             {
4073             chomp $@;
4074             $DEBUG = $@;
4075             }
4076             select(STDOUT);
4077             close $not_stdout;
4078             my @ret = @pre;
4079             push @ret, $buf;
4080             return \@ret, scalar @BLOCK + $delta, 2;
4081             };
4082              
4083             =head2 : xxx name1 PERLFUNC
4084              
4085             execute the PERL function name1 with the parameter xxx
4086             the default name space is "main::"
4087             It is possible tu use a specific name space
4088             the parameter are "stringified"
4089             e.g. ':,5,filename,save,PERLFUNC'
4090             call the function save("filename", 5);
4091            
4092             =cut
4093              
4094             $dict{PERLFUNC} = sub {
4095             my $work1 = shift;
4096             my $return1 = shift;
4097              
4098             my $b_ref = pop @{ $return1 };
4099             my $a_ref = pop @{ $return1 };
4100             my @pre = @{ $work1 };
4101             my @BLOCK = splice @pre, $a_ref, $b_ref - $a_ref;
4102             my @tmp = ( @pre, @BLOCK );
4103             pop @tmp;
4104             my $name = pop @BLOCK;
4105             my $len_before = scalar( @BLOCK );
4106             process( \@BLOCK );
4107             foreach my $item ( @BLOCK )
4108             {
4109             if ( $item =~ /^(\d+|^\$\w+)$/ )
4110             {
4111             next;
4112             }
4113             $item =~ s/^(.*)$/"$1"/;
4114             }
4115             my $len_after = scalar( @BLOCK );
4116             my $delta = $len_before - $len_after;
4117             my $arg = join ',', reverse @BLOCK;
4118             my $todo;
4119             if ( $name !~ /::[^:]*$/ )
4120             {
4121             $todo = "main::" . $name . '(' . $arg . ');';
4122             }
4123             else
4124             {
4125             my $before = $`;
4126             eval "require $before";
4127             $todo = $name . '(' . $arg . ');';
4128             }
4129             my @ret = eval( $todo );
4130             if ( $@ )
4131             {
4132             chomp $@;
4133             $DEBUG = $@;
4134             @ret = ();
4135             }
4136             return \@ret, scalar( @BLOCK ) + $delta + 1, 2;
4137             };
4138              
4139             =head2 name1 PERLFUNC0
4140              
4141             execute the PERL function name1 with no parameters
4142             the default name space is "main::"
4143             It is possible tu use a specific name space
4144             the parameter are "stringified"
4145             !!! because this function don't know the namescape of the caller
4146             !!! the parameter for the function must be scalar
4147             !!! and not a perl variable or a ref to a perl compenent
4148             !!! see PERLVAR
4149             e.g. 'Test2,PERLFUNC0'
4150             call the function Test2();
4151            
4152             =cut
4153              
4154             $dict{PERLFUNC0} = sub {
4155             my $work1 = shift;
4156             my $name = pop @{ $work1 };
4157              
4158             my $todo;
4159             my $ref_var = peek_my( 3 );
4160              
4161             if ( $name !~ /::[^:]*$/ )
4162             {
4163             $todo = "main::" . $name . ';';
4164             }
4165             else
4166             {
4167             my $before = $`;
4168             eval "require $before";
4169             $todo = $name . ';';
4170             }
4171              
4172             my @ret = eval( $todo );
4173             if ( $@ )
4174             {
4175             chomp $@;
4176             $DEBUG = $@;
4177             @ret = ();
4178             }
4179             return \@ret, 1 , 0;
4180             };
4181              
4182             =head2 xxx nbr name1 PERLFUNCX
4183              
4184             execute the PERL function name1 with nbr parameters from the stack xxx
4185             the default name space is "main::"
4186             It is possible tu use a specific name space
4187             the parameter are "stringified"
4188             !!! because this function don't know the namescape of the caller
4189             !!! the parameter for the function must be scalar
4190             !!! and not a perl variable or a ref to a perl compenent
4191             !!! see PERLVAR
4192             e.g. 'file,name,2,substit,PERLFUNCX'
4193             call the function substit("name", "file");
4194            
4195             =cut
4196              
4197             $dict{PERLFUNCX} = sub {
4198             my $work1 = shift;
4199             my $name = pop @{ $work1 };
4200             my $nbr_arg = pop @{ $work1 };
4201             my $arg = '';
4202             my $todo;
4203             my $ref_var = peek_my( 3 );
4204             for ( 1 .. $nbr_arg )
4205             {
4206             my $new = pop @{ $work1 };
4207             if ( $new =~ /^[\\$%@]/ )
4208             {
4209             $arg = $arg . ',' . $new;
4210             }
4211             else
4212             {
4213             $arg = $arg . ',"' . $new . '"';
4214             }
4215             }
4216             if ( $arg )
4217             {
4218             $arg =~ s/^,//;
4219             }
4220             if ( $name !~ /::[^:]*$/ )
4221             {
4222             $todo = "main::" . $name . '(' . $arg . ');';
4223             }
4224             else
4225             {
4226             my $before = $`;
4227             eval "require $before";
4228             $todo = $name . '(' . $arg . ');';
4229             }
4230              
4231             my @ret = eval( $todo );
4232             if ( $@ )
4233             {
4234             chomp $@;
4235             $DEBUG = $@;
4236             @ret = ();
4237             }
4238             return \@ret, $nbr_arg + 2, 0;
4239             };
4240              
4241             =head2 xxx name1 PERLFUNC1
4242              
4243             execute the PERL function name1 with the only one parameter xxx
4244             the default name space is "main::"
4245             It is possible tu use a specific name space
4246             the parameter are "stringified"
4247             e.g. 'file,name,CAT,substit,PERLFUNC1'
4248             call the function substit("filename");
4249            
4250             =cut
4251              
4252             $dict{PERLFUNC1} = sub {
4253             my $work1 = shift;
4254             my $name = pop @{ $work1 };
4255             my $arg = pop @{ $work1 };
4256             my $todo;
4257             if ( $name !~ /::[^:]*$/ )
4258             {
4259             $todo = "main::" . $name . '("' . $arg . '");';
4260             }
4261             else
4262             {
4263             my $before = $`;
4264             eval "require $before";
4265             $todo = $name . '("' . $arg . '");';
4266             }
4267             my @ret = eval( $todo );
4268             if ( $@ )
4269             {
4270             chomp $@;
4271             $DEBUG = $@;
4272             @ret = ();
4273             }
4274             return \@ret, 2, 0;
4275              
4276             };
4277              
4278             =head2 xxx nbr name1 PERLVAR
4279              
4280             Return the perl variable.
4281             If the var returned is an array, return each element of the array on the stack
4282             If the var returned is a hash , return a STRUCTURATED LIST
4283             the default name space is "main::"
4284             It is possible tu use a specific name space
4285             the parameter are "stringified"
4286             e.g.1 '{$data},PERLVAR'
4287             call the value of $data;
4288             e.g.2 '{%S}->{extra},PERLVAR'
4289             call the value of $S->{extra};
4290            
4291             =cut
4292              
4293             $dict{PERLVAR} = sub {
4294             my $work1 = shift;
4295             my $name = pop @{ $work1 };
4296             my $name1 = pop @{ $work1 };
4297             $name =~ /^\{([^}]*)\}/;
4298             my $base_name = $1;
4299             my @ret;
4300 15     15   8903 use PadWalker qw(peek_my);
  15         13476  
  15         84907  
4301             my $level = 0 ;
4302             my $ref_var;
4303             while ( ! exists $ref_var->{$base_name} )
4304             {
4305             eval { $ref_var= peek_my( $level++ ) };
4306             if ( $@ )
4307             {
4308             return \@ret, 1, 0;
4309             }
4310             }
4311              
4312             my @all = split /->/, $name;
4313             my $res = __deref__( $ref_var, \@all );
4314             my ($tmp ,undef )= __to_sl__($res,0);
4315             $tmp =~ s/#\s+$/\#/;
4316             $tmp =~ s/^\s+#/\#/;
4317             push @ret, $tmp;
4318            
4319             return \@ret, 1, 0;
4320              
4321             };
4322              
4323             sub __to_sl__
4324             {
4325 8     8   15 my $ref = shift;
4326 8         12 my $dep = shift;
4327              
4328 8         13 my $res;
4329 8 50       25 if ( ref $ref eq 'HASH' )
    100          
4330             {
4331 0         0 $dep++;
4332 0         0 $res .= '#' x $dep . ' ';
4333 0         0 foreach my $key ( keys %$ref )
4334             {
4335 0         0 $res .= $key . ' ' . '|' x $dep . ' ';
4336 0         0 my ( $r, $dep ) = __to_sl__( $ref->{ $key }, $dep );
4337 0         0 $res .= $r . ' ' . '#' x $dep . ' ';
4338             }
4339             }
4340             elsif ( ref $ref eq 'ARRAY' )
4341             {
4342 1         2 $dep++;
4343 1         4 foreach my $val ( @$ref )
4344             {
4345 4         12 my ( $r, $dep ) = __to_sl__( $val, $dep );
4346 4         12 $res .= ' ' . '#' x $dep . ' ' . $r;
4347             }
4348 1         3 $res .= ' ' . '#' x $dep . ' ';
4349             }
4350             else
4351             {
4352 7         10 $res = $ref;
4353             }
4354            
4355 8         23 $res =~ s/\s+##\s+##/ ## #/g;
4356 8         22 return $res, $dep;
4357             }
4358              
4359             sub __deref__
4360             {
4361 7     7   10 my $var_ref = shift;
4362 7         13 my $array_ref = shift;
4363 7         10 my $ret;
4364 7         11 my $ref = shift @{ $array_ref };
  7         15  
4365 7 50       21 if ( ref $var_ref eq 'REF' )
4366             {
4367 0         0 $var_ref = $$var_ref;
4368             }
4369 7 50       58 if ( $ref =~ s/\{|\}//g )
    0          
4370             {
4371 7 100       24 if ( ref $var_ref->{ $ref } eq 'SCALAR')
4372             {
4373 1         4 $ret = ${$var_ref->{ $ref }};
  1         3  
4374             } else {
4375 6         9 $ret = $var_ref->{ $ref };
4376             }
4377             }
4378             elsif ( $ref =~ s/\[|\]//g )
4379             {
4380 0         0 $ret = $var_ref->[$ref];
4381             }
4382 7 100       19 if ( ref $ret eq 'REF' )
4383             {
4384 2         5 $ret = $$ret;
4385             }
4386 7 100       11 if ( scalar @{ $array_ref } )
  7         16  
4387             {
4388 3         15 $ret = __deref__( $ret, $array_ref );
4389             }
4390            
4391 7         39 return $ret;
4392             }
4393              
4394             =head2 a >R
4395              
4396             put 'a' on the return stack
4397            
4398             =cut
4399              
4400             $dict{'>R'} = sub {
4401             my @ret;
4402             my $work1 = shift;
4403             my $val = pop @{ $work1 };
4404             push @ret, $val;
4405             return \@ret, 1, -1;
4406             };
4407              
4408             =head2 R>
4409              
4410             remove first element from the return stack and copy on the normal stack
4411            
4412             =cut
4413              
4414             $dict{'R>'} = sub {
4415             my @ret;
4416             my $work1 = shift;
4417             my $return1 = shift;
4418             my $val;
4419             if ( scalar @{ $return1 } )
4420             {
4421              
4422             push @ret, pop @{ $return1 };
4423             }
4424             return \@ret, 0, 1;
4425             };
4426              
4427             =head2 RL
4428              
4429             return the depth of the return stack
4430            
4431             =cut
4432              
4433             $dict{RL} = sub {
4434             my @ret;
4435             my $work1 = shift;
4436             my $return1 = shift;
4437             push @ret, scalar @{ $return1 };
4438             return \@ret, 0, 0;
4439             };
4440              
4441             =head2 R@
4442              
4443             copy return stack on normal stack
4444            
4445             =cut
4446              
4447             $dict{'R@'} = sub {
4448             my @ret;
4449             my $work1 = shift;
4450             my $return1 = shift;
4451             push @ret, @{ $return1 };
4452             return \@ret, 0, 0;
4453             };
4454              
4455             ########################
4456             # FILE operator
4457             ########################
4458              
4459             =head1 FILE operators ( basic IO )
4460              
4461             =cut
4462              
4463             =head2 file, mode , FH, OPEN
4464              
4465             OPEN a file and keep the filehandle in the variable X
4466             mode could be all combination of :
4467             'r' ( read ),
4468             'w' ( write ),
4469             'c' ( create ),
4470             't' ( truncate ),
4471             'a'( append = seek to end )
4472             =cut
4473              
4474             $dict{OPEN} = sub {
4475             my @ret;
4476             my $work1 = shift;
4477             my $fh_var = pop @{ $work1 };
4478             my $mode = pop @{ $work1 };
4479             my $file = pop @{ $work1 };
4480             my $fh;
4481              
4482             my $type = O_RDONLY;
4483             $type |= O_RDONLY if ( $mode =~ /r/ );
4484             $type |= O_RDWR if ( $mode =~ /w/ );
4485             $type |= O_CREAT if ( $mode =~ /c/ );
4486             $type |= O_TRUNC if ( $mode =~ /t/ );
4487              
4488             sysopen $fh, $file, $type;
4489             seek $fh, 0, 2 if ( $mode =~ /a/ );
4490             $var{ $fh_var } = $fh;
4491             return \@ret, 3, 0;
4492             };
4493              
4494             =head2 file, UNLINK
4495              
4496             UNLINK ( delete ) a file
4497            
4498             =cut
4499              
4500             $dict{UNLINK} = sub {
4501             my @ret;
4502             my $work1 = shift;
4503             my $file = pop @{ $work1 };
4504              
4505            
4506             push @ret , unlink($file);
4507             return \@ret, 1, 0;
4508             };
4509             =head2 FH, STAT
4510              
4511             STAT the file using the handle stored in the var FH ( FH could also be a file path )
4512             return the same content as perl stat. Keep in mind that the indice 0 from the perl array is the 1 fisrt stack level.
4513             To get the size of a file:
4514             /tmp/rpn,STAT,13,8,KEEPR
4515            
4516             =cut
4517              
4518             $dict{STAT} = sub {
4519             my $work1 = shift;
4520             my $fh_var = pop @{ $work1 };
4521             my $fh = $var{ $fh_var };
4522             $fh = $fh_var if ( ref( $fh ) ne 'GLOB' );
4523             my @ret = reverse stat( $fh );
4524             return \@ret, 2, 0;
4525             };
4526              
4527             =head2 OFFSET, WHENCE, FH, SEEK
4528              
4529             SEEK of OFFSET in the file using the handle stored in the var FH
4530             if WHENCE = 0 seek from the beginning of the file
4531             if WHENCE = 1 seek from the current position
4532             if WHENCE = 2 seek from the end of the file ( offset must be < 0 )
4533             ( see perldoc -f seek )
4534            
4535             =cut
4536              
4537             $dict{SEEK} = sub {
4538             my @ret;
4539             my $work1 = shift;
4540             my $fh_var = pop @{ $work1 };
4541             my $whence = pop @{ $work1 };
4542             my $offset = pop @{ $work1 };
4543             my $fh = $var{ $fh_var };
4544             sysseek $fh, $offset, $whence;
4545             return \@ret, 3, 0;
4546             };
4547              
4548             =head2 FH, TELL
4549              
4550             TELL return the position in the file using the handle stored in the var FH
4551            
4552             =cut
4553              
4554             $dict{TELL} = sub {
4555             my @ret;
4556             my $work1 = shift;
4557             my $fh_var = pop @{ $work1 };
4558             my $fh = $var{ $fh_var };
4559             my $tmp = sysseek($fh, 0, 1);
4560             push @ret, $tmp;
4561             return \@ret, 1, 0;
4562             };
4563              
4564             =head2 FH, CLOSE
4565              
4566             CLOSE the file handle stored in the var FH
4567            
4568             =cut
4569              
4570             $dict{CLOSE} = sub {
4571             my @ret;
4572             my $work1 = shift;
4573             my $fh_var = pop @{ $work1 };
4574             close $var{ $fh_var };
4575             delete $var{ $fh_var };
4576             return \@ret, 1, 0;
4577             };
4578              
4579             =head2 N, FH, GETC
4580              
4581             read and put on top of the stack N character from the filedscriptor stored in the variable FH
4582             to do a file slurp:
4583             /tmp/rpn,r,fh,OPEN,sh,STAT,13,6,KEEPR,fh,GETC,fh,CLOSE
4584            
4585             =cut
4586              
4587             $dict{GETC} = sub {
4588             my @ret;
4589             my $work1 = shift;
4590             my $fh_var = pop @{ $work1 };
4591             my $nbr = pop @{ $work1 };
4592             my $buf;
4593             my $fh = $var{ $fh_var };
4594             sysread $fh, $buf, $nbr;
4595             push @ret, $buf;
4596             return \@ret, 2, 0;
4597             };
4598              
4599             =head2 N, FH, GETCS
4600              
4601             read and put on the stack N character from the filedscriptor stored in the variable FH
4602             each character is pushed on the stack ( and then the stack is evalueted )
4603            
4604             =cut
4605              
4606             $dict{GETCS} = sub {
4607             my @ret;
4608             my $work1 = shift;
4609             my $fh_var = pop @{ $work1 };
4610             my $nbr = pop @{ $work1 };
4611             my $fh = $var{ $fh_var };
4612             for ( 1 .. $nbr )
4613             {
4614             my $buf = getc( $var{ $fh_var } );
4615             #sysread $fh, $buf, 1;
4616             push @ret, $buf;
4617             }
4618             return \@ret, 2, 0;
4619             };
4620              
4621             =head2 N, FH, WRITE
4622              
4623             put and delete N element from the stack to the filedscriptor stored in the variable FH
4624            
4625             =cut
4626              
4627             $dict{WRITE} = sub {
4628             my @ret;
4629             my $work1 = shift;
4630             my $fh_var = pop @{ $work1 };
4631             my $nbr = pop @{ $work1 };
4632             my $buf;
4633            
4634             for ( 1 .. $nbr )
4635             {
4636             $buf .= pop @{ $work1 };
4637             }
4638             my $fh = $var{ $fh_var };
4639             syswrite $fh, $buf;
4640             return \@ret, 2 + $nbr, 0;
4641             };
4642              
4643             =head2 N, FH, WRITELINE
4644              
4645             put and delete N element from the stack as a new line for each element to the filedscriptor stored in the variable FH
4646             to flush buffer, use 0,0,FH,SEEK
4647            
4648             =cut
4649              
4650             $dict{WRITELINE} = sub {
4651             my @ret;
4652             my $work1 = shift;
4653             my $fh_var = pop @{ $work1 };
4654             my $nbr = pop @{ $work1 };
4655             my $buf;
4656             my $from = ( 1 + ( $#$work1 ) - $nbr );
4657             $from = $from < 0 ? 0 : $from;
4658             my @TMP = @{ $work1 }[ $from .. ( $#$work1 ) ];
4659             foreach my $tmp ( @TMP )
4660             {
4661             $buf .= "$tmp\n";
4662             }
4663             my $fh = $var{ $fh_var };
4664             syswrite $fh, $buf, length $buf;
4665             return \@ret, 2 + $nbr, 0;
4666             };
4667              
4668             =head2 FH, READLINE
4669              
4670             read and put on the stack a line from the filedscriptor stored in the variable FH
4671            
4672             =cut
4673              
4674             $dict{READLINE} = sub {
4675              
4676             my $work1 = shift;
4677             my $fh_var = pop @{ $work1 };
4678             my $fh = $var{ $fh_var };
4679             my $buf;
4680             my $tmp = '';
4681             while ( $tmp !~ /((\n\r)|\n|\r)/ )
4682             {
4683             last if ( !sysread $fh, $tmp, 1 );
4684             $buf .= $tmp;
4685             }
4686             my @ret;
4687             push @ret, $buf;
4688             return \@ret, 1, 0;
4689             };
4690              
4691             ########################
4692             # loop operators
4693             ########################
4694              
4695             =head1 LOOP and DECISION operators
4696              
4697             =cut
4698              
4699             =head2 a IF xxx THEN
4700              
4701             test the element on top of stack
4702             if == 1 execute 'xxx' block
4703            
4704             The loop is executed always one time
4705              
4706             =cut
4707              
4708             $dict{THEN} = sub {
4709             my @ret;
4710             my $work1 = shift;
4711             my $return1 = shift;
4712             my $b_ref = pop @{ $return1 };
4713             my $a_ref = pop @{ $return1 };
4714             my @pre = @{ $work1 };
4715             my @BEGIN = splice @pre, $a_ref + 1, $b_ref - $a_ref - 1;
4716             my $len = scalar @BEGIN;
4717             my $r = scalar @{ $work1 };
4718             my $i = $r - $len - 2;
4719             my $res = $pre[$i];
4720             # my $res = pop @pre;
4721             pop @pre;
4722              
4723             my $len_d = 2 + $len;
4724              
4725             if ( $res )
4726             {
4727             my @TMP = @pre;
4728             pop @TMP;
4729             push @TMP, @BEGIN;
4730             process( \@TMP );
4731             $len_d = scalar( @pre ) + $len + 1;
4732             @ret = @TMP;
4733             }
4734              
4735             return \@ret, $len_d, 2;
4736             };
4737              
4738             =head2 a IF zzz ELSE xxx THEN
4739              
4740             test the element on top of stack
4741             if == 1 execute 'xxx' block
4742             if != 1 execute 'zzz' block
4743            
4744             The loop is executed always one time
4745              
4746             =cut
4747              
4748             $dict{THENELSE} = sub {
4749             my @ret;
4750             my $work1 = shift;
4751             my $return1 = shift;
4752             my $c_ref = pop @{ $return1 };
4753             my $b_ref = pop @{ $return1 };
4754             my $a_ref = pop @{ $return1 };
4755             my @pre = @{ $work1 };
4756              
4757             my @BEGIN = splice @pre, 0, $a_ref - 1;
4758             @pre = @{ $work1 };
4759             my @THEN = splice @pre, $c_ref + 1, $b_ref - 1;
4760             my @ELSE = splice @pre, scalar( @BEGIN ) + 2;
4761             pop @ELSE;
4762              
4763             my $VAR = $pre[-2];
4764              
4765             my $len_d = scalar( @pre ) + scalar( @BEGIN ) + scalar( @THEN ) + 3;
4766             if ( $VAR )
4767             {
4768             my @TMP = @BEGIN;
4769             push @TMP, $VAR;
4770             push @TMP, 'IF';
4771             push @TMP, @THEN;
4772             push @TMP, 'THEN';
4773             process( \@TMP );
4774             @ret = @TMP;
4775             $len_d = scalar( @THEN ) + scalar( @BEGIN ) + scalar( @ELSE ) + 5;
4776              
4777             if ( scalar( @pre ) == 2 )
4778             {
4779             $len_d++;
4780             }
4781             }
4782             else
4783             {
4784             my @TMP = @BEGIN;
4785             push @TMP, @ELSE;
4786             process( \@TMP );
4787             @ret = @TMP;
4788             $len_d = scalar( @pre ) + scalar( @BEGIN ) + scalar( @ELSE ) + scalar( @THEN ) + 2;
4789             }
4790             return \@ret, $len_d, 3;
4791             };
4792              
4793             =head2 BEGIN xxx WHILE zzz REPEAT
4794              
4795             execute 'xxx' block
4796             test the element on top of stack
4797             if == 0 execute 'zzz' block and branch again at 'BEGIN'
4798             if != 0 end the loop
4799            
4800             The loop is executed always one time
4801              
4802              
4803             =cut
4804              
4805             $dict{REPEAT} = sub {
4806             my @ret;
4807             my $work1 = shift;
4808             my $return1 = shift;
4809             my $c_ref = pop @{ $return1 };
4810             my $b_ref = pop @{ $return1 };
4811             my $a_ref = pop @{ $return1 };
4812             my @pre = @{ $work1 };
4813             my @BEGIN = splice @pre, $a_ref, $b_ref - $a_ref;
4814             my @HEAD = splice @pre, 0, $a_ref;
4815             my $len = scalar( @BEGIN );
4816             @pre = @{ $work1 };
4817             my @WHILE = splice @pre, $b_ref + 1, $c_ref - $b_ref;
4818             my @WHILE2 = @WHILE;
4819             @pre = @{ $work1 };
4820             my @TMP = @HEAD;
4821             my $head = $HEAD[-1];
4822             push @TMP, @BEGIN;
4823             process( \@TMP );
4824             my $res = pop @TMP;
4825             $len += scalar( @WHILE );
4826              
4827             if ( !$res )
4828             {
4829             push @TMP, @WHILE;
4830             process( \@TMP );
4831             push @ret, @TMP;
4832             @BEGIN = splice @pre, $a_ref, $b_ref - $a_ref;
4833             push @ret, 'BEGIN', @BEGIN, 'WHILE', @WHILE2, 'REPEAT';
4834             return \@ret, scalar( @TMP ) + $len + 1, 3;
4835             }
4836             my @BEGIN1 = @BEGIN;
4837             process( \@BEGIN1 );
4838             $res = pop @BEGIN1;
4839             push @ret, @BEGIN1;
4840             return \@ret, scalar( @WHILE2 ) + scalar( @BEGIN ) + 1, 3;
4841             };
4842              
4843             =head2 end start DO,block,LOOP
4844              
4845             process 'block' with iterator from value 'start' until 'end' value,with increment of 1;
4846             The iterator variable is the second value on the stack (start argument)
4847            
4848             =cut
4849              
4850             $dict{LOOP} = sub {
4851             my $work1 = shift;
4852             my $return1 = shift;
4853             my $len = scalar( @{ $work1 } );
4854             my $b_ref = pop @{ $return1 };
4855             my $a_ref = pop @{ $return1 };
4856             my @pre = @{ $work1 };
4857             my @BLOCK = splice @pre, $a_ref + 1, $b_ref - $a_ref;
4858             my @pre1 = @{ $work1 };
4859             my @HEAD = splice @pre1, 0, $a_ref;
4860             pop @pre;
4861             my $start = pop @pre;
4862             my $end = pop @pre;
4863             my $ind = $start;
4864             my @ret;
4865              
4866             if ( $ind <= $end )
4867             {
4868             $var{ _T_ }= $ind;
4869             $ind++;
4870             my @TMP = @pre;
4871             push @TMP, @BLOCK;
4872             process( \@TMP );
4873             @pre = @TMP;
4874             push @pre, $end, $ind, "DO", @BLOCK, "LOOP";
4875             }
4876             return \@pre, $len + 1, 0;
4877             };
4878              
4879             =head2 end start increment DO,block,+LOOP
4880              
4881             process 'block' with iterator from value 'start' untill 'end' value,with increment of 'increment'
4882             This allow rational or negative value
4883             The iterator variable is the second value on the stack (start argument)
4884            
4885             =cut
4886              
4887             $dict{'+LOOP'} = sub {
4888             my $work1 = shift;
4889             my $return1 = shift;
4890             my $len = scalar( @{ $work1 } );
4891             my $b_ref = pop @{ $return1 };
4892             my $a_ref = pop @{ $return1 };
4893             my @pre = @{ $work1 };
4894             my @BLOCK = splice @pre, $a_ref + 1, $b_ref - $a_ref;
4895             my @pre1 = @{ $work1 };
4896             my @HEAD = splice @pre1, 0, $a_ref;
4897             pop @pre;
4898             my $inc = pop @pre;
4899             my $start = pop @pre;
4900             my $end = pop @pre;
4901            
4902             my @TMP1 = @pre;
4903             my $subs_start = scalar( @TMP1 ) - 1;
4904             push @TMP1, @BLOCK;
4905             my $ind = $start;
4906             my @ret;
4907             if ( $inc < 0 )
4908             {
4909             if ( $ind >= $end )
4910             {
4911             $var{ _T_ }= $ind;
4912             $ind += $inc;
4913             @pre = @TMP1;
4914             push @pre, $end, $ind,$inc, "DO", @BLOCK, "+LOOP";
4915             }
4916             }
4917             elsif ( $inc > 0 )
4918             {
4919             if ( $ind <= $end )
4920             {
4921             $var{ _T_ }= $ind;
4922             $ind += $inc;
4923             @pre = @TMP1;
4924             push @pre, $end, $ind,$inc, "DO", @BLOCK, "+LOOP";
4925             }
4926             }
4927             else
4928             {
4929             my @pre = ();
4930             }
4931             return \@pre, $len + 1, 2;
4932             };
4933              
4934             #####################################
4935             # main code
4936             #####################################
4937             sub parse
4938             {
4939 1319     1319 0 1988 my $remainder = shift;
4940 1319         4756 $remainder =~ s/^$separator_in//;
4941 1319         1867 my $before;
4942 1319         1730 my $is_string = 0;
4943 1319         3762 $remainder =~ s/^\s+//;
4944 1319 100       3019 if ( $remainder =~ /^('|")(.*)/ )
4945             {
4946 15         24 $is_string = 1;
4947 15         38 $remainder = $2;
4948 15 50       59 if ( $remainder =~ /^([^\"']*)('|")(.*)/ )
4949             {
4950 15         35 $before = $1;
4951 15         32 $remainder = $3;
4952             }
4953             }
4954             else
4955             {
4956 1304         4173 ( $before, $remainder ) = split /$separator_in/, $remainder, 2;
4957             }
4958 1319         3419 return ( $before, $remainder, $is_string );
4959             }
4960              
4961             sub rpn($)
4962             {
4963 301     301 0 2898668 my $item = shift;
4964 301         678 $DEBUG = '';
4965 301         504 my @stack;
4966 301         883 while ( length $item )
4967             {
4968 1319         2004 my $elem;
4969             my $is_string;
4970 1319         2358 ( $elem, $item, $is_string ) = parse( $item );
4971 1319 100       2860 if ( $is_string )
4972             {
4973 15         46 push @stack, "'" . $elem . "'";
4974             }
4975             else
4976             {
4977 1304         4500 push @stack, $elem;
4978             }
4979             }
4980 301         874 process( \@stack );
4981 301         897 my $ret = join $separator_out, @stack;
4982 301         1018 return $ret;
4983             }
4984              
4985             sub process
4986             {
4987 343     343 0 533 my $stack = shift;
4988 343         969 my $is_block;
4989             my $is_begin;
4990 343         0 my $is_while;
4991 343         0 my $is_do;
4992 343         530 my $is_if=0;
4993 343         756 my $is_else;
4994             my $else;
4995 343         0 my @work;
4996              
4997 343         506 while ( @{ $stack } )
  3423         8538  
4998             {
4999 3080         4468 my $op = shift @{ $stack };
  3080         6023  
5000 3080         4849 my $is_string = 0;
5001 3080         4675 my $tmp_op = $op;
5002 3080         6992 $tmp_op =~ s/^\s+//g;
5003 3080         6343 $tmp_op =~ s/\s+$//g;
5004 3080 100 100     11670 if ( exists( $dict{ $tmp_op } ) || exists( $var{ $tmp_op } ) )
5005             {
5006 651         1305 $op =~ s/^\s+//g;
5007 651         1153 $op =~ s/\s+$//g;
5008             }
5009 3080 50       6774 if ( ( $op =~ /^VARIABLE$/g ) )
5010             {
5011 0         0 push @work, shift @{ $stack };
  0         0  
5012             }
5013 3080 100       6515 if ( $op =~ /^'(.*)'$/ )
5014             {
5015 21         40 $is_string = 1;
5016 21 50       51 unless ( $is_do )
5017             {
5018 21         134 $op =~ s/^'(.*)'$/$1/g;
5019             }
5020             }
5021 3080 100       7574 if ( $op =~ /^;$/g )
5022             {
5023 1         3 $is_block = 0;
5024 1         2 push @return, ( scalar( @work ) );
5025             }
5026 3080 100       7003 if ( $op =~ /^PERL$/g )
5027             {
5028 2         3 $is_block = 0;
5029 2         17 push @return, ( scalar( @work ) );
5030             }
5031 3080 100       5983 if ( $op =~ /^PERLFUNC$/g )
5032             {
5033 1         3 $is_block = 0;
5034 1         3 push @return, ( scalar( @work ) );
5035             }
5036 3080 100       6908 if ( $op =~ /^:$/g )
5037             {
5038 3         7 $is_block = 1;
5039 3         10 push @return, ( scalar( @work ) );
5040 3         9 next;
5041             }
5042 3077 100       5948 if ( !$is_block )
5043             {
5044 3070 100       5875 if ( $op =~ /^BEGIN$/g )
5045             {
5046 5         10 $is_begin = 1;
5047 5         11 push @return, ( scalar( @work ) );
5048 5         17 next;
5049             }
5050 3065 100       6290 if ( ( $op =~ /^WHILE$/g ) )
5051             {
5052 5         8 $is_begin = 0;
5053 5         11 $is_do = 1;
5054 5         21 push @return, ( scalar( @work ) );
5055             }
5056 3065 100 100     6951 if ( $is_do && ( $op =~ /^REPEAT$/g ) )
5057             {
5058 5         9 $is_do = 0;
5059 5         13 push @return, ( scalar( @work ) - 1 );
5060             }
5061 3065 100       7112 if ( $op =~ /^DO$/g )
5062             {
5063 36         63 $is_do = 1;
5064 36         105 push @return, ( scalar( @work ) );
5065             }
5066 3065 100       7019 if ( ( $op =~ /^LOOP|\+LOOP$/g ) )
5067             {
5068 36         56 $is_do = 0;
5069 36         71 push @return, scalar( @work );
5070             }
5071              
5072 3065 100       6045 if ( $op =~ /^IF$/g )
5073             {
5074 5         9 $is_do = 1;
5075 5 50       14 if ( $is_if == 0 )
5076             {
5077 5         11 push @return, ( scalar( @work ) );
5078             }
5079 5         10 $is_if++;
5080             }
5081 3065 100       5786 if ( $op =~ /^ELSE$/g )
5082             {
5083 2 50       7 if ( $is_if == 1 )
5084             {
5085 2         45 $is_else++;
5086 2         7 $else = ( scalar( @work ) );
5087             }
5088             }
5089 3065 100       6185 if ( $op =~ /^THEN$/g )
5090             {
5091 5         25 $is_if--;
5092 5 50       31 if ( $is_if == 0 )
5093             {
5094 5         12 push @return, ( scalar( @work ) );
5095 5         9 $is_do = 0;
5096              
5097 5 100       13 if ( $is_else )
5098             {
5099 2         5 $op = "THENELSE";
5100 2         3 push @return, $else;
5101             }
5102             }
5103             }
5104              
5105             }
5106 3072 100       6586 if ( !$is_string )
5107             {
5108 3051 100 100     13237 if ( $is_do || $is_begin || $is_block )
      100        
5109             {
5110 178         501 push @work, $op;
5111             }
5112             else
5113             {
5114 2873 100       5778 if ( defined( $dict{ $op } ) )
5115             {
5116 441         1520 my @work_stack = @work;
5117 441         964 my @return_stack = @return;
5118 441         1575 my ( $ret, $remove_stack, $remove_return ) = $dict{ $op }( \@work_stack, \@return_stack );
5119 441 50       1143 if ( $remove_return >= 0 )
5120             {
5121 441         1247 for ( 1 .. $remove_return )
5122             {
5123 63         117 pop @return;
5124             }
5125             }
5126             else
5127             {
5128 0         0 my $to_ret = pop @{ $ret };
  0         0  
5129 0         0 push @return, $to_ret;
5130             }
5131 441         890 for ( 1 .. $remove_stack )
5132             {
5133 1394         2484 pop @work;
5134             }
5135 441         635 unshift @{ $stack }, @work, @{ $ret };
  441         748  
  441         1653  
5136 441         1618 undef @work;
5137             }
5138             else
5139             {
5140 2432         6544 push @work, $op;
5141             }
5142             }
5143             }
5144             else
5145             {
5146 21         55 push @work, $op;
5147             }
5148             }
5149 343         557 unshift @{ $stack }, @work;
  343         1171  
5150             }
5151              
5152             =head1 Useful functions for the module (not related to the RPN language)
5153              
5154             =cut
5155              
5156             =head2 rpn_error()
5157              
5158             function which return the debug info from the calculation (like a division by 0)
5159            
5160             =cut
5161              
5162             sub rpn_error
5163             {
5164 0     0 1   return $DEBUG;
5165             }
5166              
5167             =head2 rpn_separator_out( 'sep' )
5168              
5169             function to set a specific separator for the returned stack (default = space)
5170             This is useful when the result of rpn() is use inside another rpn() call
5171            
5172             =cut
5173              
5174             sub rpn_separator_out
5175             {
5176 0     0 1   my $sep = shift;
5177 0 0         $separator_out = $sep if ( $sep ) ;
5178 0           return $separator_out;
5179             }
5180              
5181             =head2 rpn_separator_in( 'sep' )
5182              
5183             function to set a specific separator for the input data (default = ')
5184            
5185            
5186             =cut
5187              
5188             sub rpn_separator_in
5189             {
5190 0     0 1   my $sep = shift;
5191 0 0         $separator_in = $sep if ( $sep );
5192 0           return $separator_in ;
5193             }
5194              
5195             1;
5196              
5197             __END__