File Coverage

lib/Parse/RPN.pm
Criterion Covered Total %
statement 169 195 86.6
branch 60 76 78.9
condition 12 14 85.7
subroutine 14 18 77.7
pod 3 7 42.8
total 258 310 83.2


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