File Coverage

blib/lib/HCKit/Template.pm
Criterion Covered Total %
statement 40 435 9.2
branch 0 154 0.0
condition 1 44 2.2
subroutine 7 60 11.6
pod 8 49 16.3
total 56 742 7.5


line stmt bran cond sub pod time code
1             package HCKit::Template;
2              
3 1     1   17943 use strict;
  1         3  
  1         42  
4 1     1   6 use warnings;
  1         2  
  1         45  
5              
6             require Exporter;
7 1     1   5 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         370  
  1         39066  
8             @ISA = qw(Exporter);
9              
10             # Items to export into callers namespace by default. Note: do not export
11             # names by default without a very good reason. Use EXPORT_OK instead.
12             # Do not simply export all your public functions/methods/constants.
13              
14             # This allows declaration use HCKit::Template ':all';
15             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
16             # will save memory.
17             %EXPORT_TAGS = ();
18             @EXPORT_OK = ();
19             @EXPORT = ();
20              
21             $VERSION = '0.02';
22              
23             package HCKit::Template::Rule;
24              
25             sub new {
26 0     0   0 my ($class, $body) = @_;
27 0         0 my $ref = {};
28 0         0 $ref->{body} = $body;
29 0         0 $ref->{args} = [];
30 0   0     0 return bless $ref, ref($class)||$class;
31             }
32              
33             sub body {
34 0     0   0 my ($self) = @_;
35 0         0 return $self->{body};
36             }
37              
38             sub append {
39 0     0   0 my ($self, $newbody) = @_;
40 0         0 $self->{body} .= $newbody;
41             }
42              
43             sub set_args {
44 0     0   0 my ($self, @arg) = @_;
45 0         0 $self->{args} = \@arg;
46             }
47              
48             sub get_args {
49 0     0   0 my ($self) = @_;
50 0         0 return @{$self->{args}};
  0         0  
51             }
52              
53             package HCKit::Template;
54              
55             sub new {
56 1     1 1 11 my $class = shift;
57 1         2 my $ref = {};
58 1         3 $ref->{env} = {};
59 1         3 $ref->{fun} = {};
60 1         3 $ref->{op} = {};
61 1         2 $ref->{delayed} = {}; # names of delayed pre-precess fun
62 1         5 setup($ref);
63 1   33     8 bless $ref, ref($class)||$class;
64             }
65              
66             sub rewrite_file {
67 0     0 1 0 my ($self, $file) = @_;
68 0         0 return $self->_rewrite_file($file, $self->{env});
69             }
70              
71             sub rewrite_string {
72 0     0 1 0 my ($self, $string) = @_;
73 0         0 return $self->_rewrite_string($string, $self->{env});
74             }
75              
76             sub process_file {
77 0     0 1 0 my ($self, @file) = @_;
78 0         0 foreach my $f (@file){
79 0         0 $self->_include_file($f, $self->{env});
80             }
81             }
82              
83             sub get_var {
84 0     0 1 0 my ($self, $key) = @_;
85 0         0 return $self->{env}{$key};
86             }
87              
88             sub set_var {
89 0     0 1 0 my ($self, $key, $val) = @_;
90 0         0 $self->{env}{$key} = $val;
91             }
92              
93             # utilities ###########################################################
94              
95             sub add_delayed {
96 0     0 0 0 my ($self, $name) = @_;
97 0         0 $self->{delayed}->{$name} = 1;
98             }
99              
100             sub is_delayed {
101 0     0 0 0 my ($self, $name) = @_;
102 0         0 return $self->{delayed}->{$name};
103             }
104              
105             sub _rewrite_file {
106 0     0   0 my ($self, $file, $env) = @_;
107 0         0 my ($tmpl, $rule, $data) = read_src($file);
108 0         0 $self->parse_rule($rule, $env);
109 0         0 $self->parse_data($data, $env);
110 0         0 return $self->rewrite($tmpl, $env);
111             }
112              
113             sub _include_file {
114 0     0   0 my ($self, $file, $env) = @_;
115 0         0 my ($tmpl, $rule, $data) = read_src($file);
116 0         0 $self->parse_rule($rule, $env);
117 0         0 $self->parse_data($data, $env);
118             }
119              
120             sub _rewrite_string {
121 0     0   0 my ($self, $string, $env) = @_;
122 0         0 my ($tmpl, $rule, $data) = read_src_from_string($string);
123 0         0 $self->parse_rule($rule, $env);
124 0         0 $self->parse_data($data, $env);
125 0         0 return $self->rewrite($tmpl, $env);
126             }
127              
128             sub read_src {
129 0     0 0 0 my ($file) = @_;
130 0         0 local *FILE;
131 0         0 my @x; # ($tmpl, $rule, $data);
132 0         0 my $mode = 0;
133 0 0       0 open(FILE, $file) || die "can't open $file: $!";
134 0         0 while(){
135 0 0       0 if( index($_, '---RULE---') >= 0 ){
    0          
136 0         0 $mode = 1;
137             }
138             elsif( index($_, '---DATA---') >= 0 ){
139 0         0 $mode = 2;
140             }
141             else{
142 0         0 $x[$mode] .= $_;
143             }
144             }
145 0         0 close(FILE);
146 0         0 return @x;
147             }
148              
149             sub read_src_from_string {
150 0     0 0 0 my ($str) = @_;
151 0         0 my @lines = split /\n/, $str;
152 0         0 my @x; # ($tmpl, $rule, $data);
153 0         0 my $mode = 0;
154 0         0 foreach (@lines){
155 0 0       0 if( index($_, '---RULE---') >= 0 ){
    0          
156 0         0 $mode = 1;
157             }
158             elsif( index($_, '---DATA---') >= 0 ){
159 0         0 $mode = 2;
160             }
161             else{
162 0         0 $x[$mode] .= $_;
163             }
164             }
165 0         0 return @x;
166             }
167              
168             sub parse_rule {
169 0     0 0 0 my ($self, $rule, $env) = @_;
170 0   0     0 $rule ||= "";
171 0         0 while( $rule =~
172             /
173             <([\w-]+)(\s[^>]+)?>(.*?)<\/\1> |
174             <\*(.*?)\*> |
175             (<\*--.*?--\*>)
176             /gsx ){
177 0         0 my ($sym, $opt, $val, $cmd, $comm) = ($1,$2,$3,$4,$5);
178 0 0       0 if( $comm ){ next }
  0         0  
179 0 0       0 if( $cmd ){
180 0 0       0 if( $cmd =~ /^\?(.*?)\?$/ ){
181 0         0 my $instr = trim($1);
182 0 0       0 if( $instr =~ /^include\s+(\S+)$/ ){
183 0         0 $self->_include_file($1, $env);
184 0         0 next;
185             }
186 0         0 die "invalid instruction $cmd";
187             }
188 0         0 $self->eval_var_raw($cmd, $env);
189 0         0 next;
190             }
191 0         0 my $append = 0;
192 0         0 my @args;
193 0 0       0 $opt = "" if !defined($opt);
194 0         0 foreach(split " ", $opt){
195 0 0 0     0 if( $_ eq 'append' || $_ eq '+' ){
    0          
    0          
    0          
    0          
196 0         0 $append = 1;
197             }
198             elsif( $_ eq 'trim' ){
199 0         0 $val = trim($val);
200             }
201             elsif( $_ eq 'chomp' ){
202 0         0 $val =~ s/\s+$//;
203             }
204             elsif( $_ eq 'remove-white' ){
205 0         0 $val = trim($val);
206 0         0 $val =~ s/>\s+
207             }
208             elsif( /args=(.*)/ ){
209 0         0 @args = split /,/, $1;
210             }
211             }
212 0         0 my $prev = $env->{$sym};
213 0 0       0 if( $prev ){
214 0 0       0 ref($prev) eq "HCKit::Template::Rule"
215             || die "$sym already defined";
216 0 0       0 if( $append ){
217 0 0       0 if( @args ){
218 0         0 die "cannot specify args in appending rule";
219             }
220 0         0 $prev->append($val);
221             }
222             else{
223 0         0 $env->{$sym} = HCKit::Template::Rule->new($val);
224             }
225             }
226             else{
227 0         0 $env->{$sym} = HCKit::Template::Rule->new($val);
228 0 0       0 if( @args ){
229 0         0 $env->{$sym}->set_args(@args);
230             }
231             }
232             }
233             }
234              
235             sub parse_data {
236 0     0 0 0 my ($self, $data, $env) = @_;
237 0   0     0 $data ||= "";
238 0         0 while( $data =~
239             /<([\w-]+)>(.*?)<\/\1>|<\*(.*?)\*>|(<--.*?-->)/gs ){
240 0         0 my ($sym, $body, $cmd, $comm) = ($1, $2, trim($3), $4);
241 0 0       0 if( $comm ){ next }
  0         0  
242 0 0       0 if( $sym ){
243 0         0 my $val = $self->parse_data_body($body, $env);
244 0         0 extend_data($sym, $val, $env);
245             }
246             else{
247 0 0       0 if( $cmd =~ /^\?(.*?)\?$/ ){
248 0         0 my $instr = trim($1);
249 0 0       0 if( $instr =~ /^include\s+(\S+)$/ ){
250 0         0 $self->_include_file($1, $env);
251 0         0 next;
252             }
253 0         0 die "invalid instruction $cmd";
254             }
255 0         0 $self->eval_var_raw($cmd, $env);
256             }
257             }
258             }
259              
260             sub parse_data_body {
261 0     0 0 0 my ($self, $body, $env) = @_;
262 0         0 my %hash;
263             my $text;
264 0         0 my $last = 0;
265 0         0 while( $body =~
266             /(<([\w-]+)>(.*?)<\/\2> |
267             <\*(.*?)\*> |
268             ()
269             )/gsx ){
270 0         0 my ($match, $sym, $val, $cmd, $cdata) = ($1,$2,$3,$4,$5);
271 0         0 my $len = length($match);
272 0         0 my $pre = substr($body, $last, pos($body)-$len-$last);
273 0         0 $text .= $pre;
274 0         0 $last = pos($body);
275 0 0       0 if( $sym ){
    0          
276 0         0 my $sub = $self->parse_data_body($val, $env);
277 0         0 extend_data($sym, $sub, \%hash);
278             }
279             elsif( $cdata ){
280 0         0 $cdata =~ s/^
281 0         0 $cdata =~ s/\]\]>$//;
282 0         0 $text .= $cdata;
283             }
284             else{
285 0         0 my ($key, $aux) =
286             $cmd =~ /\s*([\w:.-]+)\s*(.*)/;
287 0         0 my $val = $self->eval_var($key, $aux, $env);
288 0 0       0 if( ref($val) eq "HASH" ){
289 0         0 while( my ($sym, $sub) = each %$val ){
290 0         0 extend_data($sym, $sub, \%hash);
291             }
292             }
293 0         0 else{ $text .= $val }
294             }
295             }
296 0 0       0 if( $last < length($body) ){
297 0         0 $text .= substr($body, $last);
298             }
299 0 0       0 return %hash ? \%hash : $text;
300             }
301              
302             sub extend_data {
303 0     0 0 0 my ($key, $val, $env) = @_;
304 0 0       0 if( defined($env->{$key}) ){
305 0 0       0 if( ref($env->{$key}) eq 'ARRAY' ){
306 0         0 push @{$env->{$key}}, $val;
  0         0  
307             }
308             else{
309 0         0 $env->{$key} = [$env->{$key}, $val];
310             }
311             }
312             else{
313 0         0 $env->{$key} = $val;
314             }
315             }
316              
317             sub rewrite {
318 0     0 0 0 my ($self, $tmpl, $env) = @_;
319 0 0       0 $tmpl = "" if !defined($tmpl);
320 0         0 my $last = 0;
321 0         0 my $output = "";
322 0         0 while( $tmpl =~
323             /(
324             <\*\s*([\w:.-]+)\s*(.*?)\*> |
325             <\&\s*([\w:.-]+(?:@\w+)?)\s*(.*?)\&>(.*?)<\&\s*\/\4\s*\&> |
326             <\{\s*([\w:.-]+(?:@\w+)?)\s*(.*?)\}>(.*?)<\{\s*\/\7\s*\}>
327             )/gxs ){
328 0         0 my ($match, $var, $varaux, $fun, $funaux, $funarg,
329             $loop, $loopaux, $loopbody) =
330             ($1,$2,$3,$4,$5,$6,$7,$8,$9);
331 0         0 my $len = length($match);
332 0         0 my $pre = substr($tmpl, $last, pos($tmpl)-$len-$last);
333 0         0 $output .= $pre;
334 0         0 $last = pos($tmpl);
335 0 0       0 if( $var ){
    0          
    0          
336 0         0 $output .= $self->eval_var($var, $varaux, $env);
337             }
338             elsif( $fun ){
339 0         0 $fun =~ s/@.*//;
340 0         0 $output .= $self->eval_fun($fun, $funaux, $funarg, $env);
341             }
342             elsif( $loop ){
343 0         0 $loop =~ s/@.*//;
344 0         0 $output .= $self->eval_block($loop, $loopaux, $loopbody, $env);
345             }
346             }
347 0 0       0 if( $last < length($tmpl) ){
348 0         0 $output .= substr($tmpl, $last);
349             }
350 0         0 return $output;
351             }
352              
353             sub eval_var_raw {
354 0     0 0 0 my ($self, $text, $env) = @_;
355 0         0 my ($key, $aux) = $text =~ /\s*([\w:.-]+)\s*(.*)/;
356 0         0 return $self->eval_var($key, $aux, $env);
357             }
358              
359             sub eval_var {
360 0     0 0 0 my ($self, $key, $aux, $env) = @_;
361 0         0 $self->eval_construct($key, $aux, { __NEXT__ => $env });
362             }
363              
364             sub eval_fun {
365 0     0 0 0 my ($self, $key, $aux, $funarg, $env) = @_;
366 0         0 my $newenv = { __NEXT__ => $env };
367 0         0 $self->parse_funarg($funarg, $newenv);
368 0         0 $self->eval_construct($key, $aux, $newenv);
369             }
370              
371             sub eval_block {
372 0     0 0 0 my ($self, $key, $aux, $body, $env) = @_;
373 0         0 my $newenv = { __NEXT__ => $env, __BODY__ => $body };
374 0         0 $self->eval_construct($key, $aux, $newenv);
375             }
376              
377             sub eval_construct {
378 0     0 0 0 my ($self, $key, $aux, $env) = @_;
379 0         0 my ($pre, $post) = parse_aux($aux);
380 0         0 my $stack = [];
381 0 0       0 if( $self->is_delayed($key) ){
382 0         0 $stack = $pre;
383             }
384             else{
385 0         0 $self->process_tokens($stack, $env, @$pre);
386             }
387 0         0 my $val = $self->eval_with_fun($key, $stack, $env);
388 0         0 $stack = [$val];
389 0         0 $self->process_tokens($stack, $env, @$post);
390 0         0 return $stack->[0];
391             }
392              
393             sub eval_data_construct {
394 0     0 0 0 my ($self, $key, $aux, $env) = @_;
395 0         0 my ($pre, $post) = parse_aux($aux);
396 0         0 my $stack = [];
397 0 0       0 if( $self->is_delayed($key) ){
398 0         0 $stack = $pre;
399             }
400             else{
401 0         0 $self->process_tokens($stack, $env, @$pre);
402             }
403 0         0 my $val = $self->eval_with_fun($key, $stack, $env);
404 0         0 $stack = [$val];
405 0         0 $self->process_tokens($stack, $env, @$post);
406 0         0 return $stack->[0];
407             }
408              
409             sub process_para {
410 0     0 0 0 my ($rule, $stack, $env) = @_;
411 0         0 my @args = $rule->get_args();
412 0 0       0 if( @args ){
413 0         0 foreach my $p (@args){
414 0 0       0 if( exists $env->{$p} ){ next }
  0         0  
415 0         0 $env->{$p} = shift @$stack;
416             }
417             }
418             }
419              
420             sub parse_aux {
421 0     0 0 0 my ($aux) = @_;
422 0 0       0 $aux = "" if !defined($aux);
423 0         0 my $pre = [];
424 0         0 my $post = [];
425 0         0 my $ref = $pre;
426 0         0 while( $aux =~
427             /(
428             [\w:-]+=\"[^\"]*\" |
429             [\w:-]+=\'[^\']*\' |
430             [\w:-]+=[^\'\"\;\s]+ |
431             \"[^\"]*\" |
432             \'[^\']*\' |
433             [^\s=;]+ |
434             ;
435             )/gsx ){
436 0 0       0 if( $1 eq ';' ){ $ref = $post; next }
  0         0  
  0         0  
437 0         0 push @$ref, $1;
438             }
439 0         0 return ($pre, $post);
440             }
441              
442             sub parse_funarg {
443 0     0 0 0 my ($self, $funarg, $env) = @_;
444 0   0     0 $funarg ||= "";
445 0         0 while( $funarg =~ /<([\w-]+)>(.*?)<\/\1>/gs ){
446 0         0 my ($key, $val) = ($1, $2);
447 0         0 $env->{$key} = $self->rewrite($val, $env);
448             }
449             }
450              
451             sub process_tokens {
452 0     0 0 0 my ($self, $stack, $env, @tok) = @_;
453 0         0 my $i;
454 0         0 for($i=0;$i<=$#tok;$i++){
455 0         0 my $t = $tok[$i];
456 0 0       0 if( $t =~ /^([\w:-]+)=\"([^\"]*)\"$/ ){
    0          
    0          
    0          
    0          
    0          
    0          
    0          
457 0         0 $env->{$1} = $2;
458             }
459             elsif( $t =~ /^([\w:-]+)+=\'([^\']*)\'$/ ){
460 0         0 $env->{$1} = $2;
461             }
462             elsif( $t =~ /^([\w:-]+)=([^\'\"\;\s]+)$/ ){
463 0         0 $env->{$1} = var_lookup($2, $env);
464             }
465             elsif( $t =~ /^\"(.*)\"$/ ){
466 0         0 push @$stack, $1;
467             }
468             elsif( $t =~ /^\'(.*)\'$/ ){
469 0         0 push @$stack, $1;
470             }
471             elsif( $t eq "as" ){
472 0         0 push @$stack, $tok[++$i];
473             }
474             elsif( $t =~ /^(\d+|\d+\.\d+)$/ ){
475 0         0 push @$stack, $1;
476             }
477             elsif( $t =~ /^(\d+)\.\.(\d+)$/ ){
478 0         0 push @$stack, [$1..$2];
479             }
480             else{
481 0         0 $self->eval_with_op($t, $stack, $env);
482             }
483             }
484             }
485              
486             sub lookup {
487 0     0 1 0 my ($sym, $env) = @_;
488 0         0 while( $env ){
489 0 0       0 if( defined($env->{$sym}) ){
490 0         0 return $env->{$sym};
491             }
492 0         0 $env = $env->{'__NEXT__'};
493             }
494 0         0 return undef;
495             }
496              
497             sub var_lookup {
498 0     0 0 0 my ($var, $env) = @_;
499 0         0 my @tok = split /\./, $var;
500 0         0 my $first = shift @tok;
501 0         0 my $val = lookup($first, $env);
502 0         0 foreach my $i (@tok){
503 0 0       0 unless( ref($val) eq 'HASH' ){
504 0         0 return "";
505             }
506 0         0 $val = $val->{$i};
507             }
508 0         0 return $val;
509             }
510              
511             sub eval_with_op {
512 0     0 0 0 my ($self, $expr, $stack, $env) = @_;
513 0         0 my $val = var_lookup($expr, $env);
514 0 0       0 if( defined($val) ){
515 0 0       0 unless( ref($val) ){
516 0         0 $val = $self->rewrite($val, $env);
517             }
518 0         0 push @$stack, $val;
519             }
520             else{
521 0         0 my $op = $self->{op}->{$expr};
522 0 0       0 if( $op ){
523 0         0 &{$op}($self, $stack, $env);
  0         0  
524             }
525             else{
526 0         0 push @$stack, "";
527             }
528             }
529             }
530              
531             sub eval_with_fun {
532 0     0 0 0 my ($self, $expr, $stack, $env) = @_;
533 0         0 my $val = var_lookup($expr, $env);
534 0 0       0 if( defined($val) ){
535 0 0       0 if( ref($val) eq "HCKit::Template::Rule" ){
    0          
536 0         0 process_para($val, $stack, $env);
537 0         0 return $self->rewrite($val->body(), $env);
538             }
539             elsif( ref($val) ){
540 0         0 return $val;
541             }
542             else{
543 0         0 return $val;
544             }
545             }
546             else{
547 0         0 my $fun = $self->{fun}->{$expr};
548 0 0       0 if( $fun ){
549 0         0 return &{$fun}($self, $stack, $env);
  0         0  
550             }
551             }
552 0         0 return "";
553             }
554              
555             # fun ###############################################################
556              
557             # fun_foreach
558             # stack: LIST [IDENT]
559             # switches:
560             # foreach:sep=SEP
561             # foreach:toggle=INIT
562              
563             sub fun_foreach {
564 0     0 0 0 my ($self, $stack, $env) = @_;
565 0         0 my $body = $env->{__BODY__};
566 0         0 my ($ident, $list);
567 0         0 my $top = pop @$stack;
568              
569 0 0       0 if( ref($top) eq "ARRAY" ){
570 0         0 $ident = "iter";
571 0         0 $list = $top;
572             }
573             else{
574 0         0 $ident = $top;
575 0         0 $list = pop @$stack;
576             }
577 0         0 my $output = "";
578 0         0 my $join = $env->{'foreach:sep'};
579 0         0 my $n = 0;
580 0         0 my $toggle = 0;
581 0 0       0 if( defined($env->{'foreach:toggle'}) ){
582 0         0 $toggle = 1;
583 0         0 $env->{toggle} = $env->{'foreach:toggle'};
584             }
585 0 0       0 unless( ref($list) eq "ARRAY" ){
586 0         0 $list = [$list];
587             }
588 0         0 foreach my $e (@$list){
589 0 0 0     0 if( $join && $n++ > 0 ){
590 0         0 $output .= $join;
591             }
592 0         0 $env->{$ident} = $e;
593 0 0       0 if( $toggle ){
594 0 0       0 $env->{toggle} = $env->{toggle} ? 0 : 1;
595             }
596 0         0 my $tmp = $self->rewrite($body, $env);
597 0         0 $output .= $tmp;
598             }
599 0         0 return $output;
600             }
601              
602             sub fun_if {
603 0     0 0 0 my ($self, $stack, $env) = @_;
604 0         0 my ($test) = @$stack;
605 0 0       0 if( $test ){
606 0         0 my $body = $env->{__BODY__};
607 0         0 return $self->rewrite($body, $env);
608             }
609 0         0 else{ return ""; }
610             }
611              
612             sub fun_include {
613 0     0 0 0 my ($self, $stack, $env) = @_;
614 0         0 my $prev_env = $env->{__NEXT__};
615 0         0 foreach my $f (@$stack){
616 0         0 $self->_include_file($f, $prev_env);
617             }
618 0         0 return "";
619             }
620              
621             sub fun_set {
622 0     0 0 0 my ($self, $stack, $env) = @_; # <* set ident val *>
623 0         0 my $key = shift @$stack;
624 0 0       0 $key =~ /^[\w.-]+$/ || die "invalid identifier in set: $key";
625 0         0 my $ns = [];
626 0         0 $self->process_tokens($ns, $env, @$stack);
627 0         0 my $prev = $env->{__NEXT__};
628 0         0 $prev->{$key} = $ns->[0];
629 0         0 return "";
630             }
631              
632             sub fun_default {
633 0     0 0 0 my ($self, $stack, $env) = @_; # <* default ident val *>
634 0         0 my $key = shift @$stack;
635 0 0       0 $key =~ /^[\w-]+$/ || die "invalid identifier in set: $key";
636 0         0 my $bind = lookup($key, $env);
637 0 0       0 if( defined $bind ){ return }
  0         0  
638 0         0 my $ns = [];
639 0         0 $self->process_tokens($ns, $env, @$stack);
640 0         0 my $prev = $env->{__NEXT__};
641 0         0 $prev->{$key} = $ns->[0];
642 0         0 return "";
643             }
644              
645             sub setup_fun {
646 1     1 0 2 my ($ref) = @_;
647 1         3 my $env = $ref->{fun};
648 1         4 $env->{foreach} = \&fun_foreach;
649 1         3 $env->{if} = \&fun_if;
650 1         4 $env->{include} = \&fun_include;
651 1         3 $env->{set} = \&fun_set;
652 1         3 $ref->{delayed}->{set} = 1;
653 1         8 $env->{default} = \&fun_default;
654 1         5 $ref->{delayed}->{default} = 1;
655             }
656              
657             sub op_trim {
658 0     0 0 0 my ($self, $stack, $env) = @_;
659 0         0 my $s = pop @$stack;
660 0         0 push @$stack, trim($s);
661             }
662              
663             sub op_list_remove_last {
664 0     0 0 0 my ($self, $stack, $env) = @_;
665 0         0 my $orig = pop @$stack;
666 0         0 my @list = @$orig;
667 0         0 pop @list;
668 0         0 push @$stack, \@list;
669             }
670              
671             sub op_list_last {
672 0     0 0 0 my ($self, $stack, $env) = @_;
673 0         0 my $orig = pop @$stack;
674 0         0 my $last = pop @$orig;
675 0         0 push @$stack, $last;
676             }
677              
678             sub op_not {
679 0     0 0 0 my ($self, $stack, $env) = @_;
680 0         0 my $arg = pop @$stack;
681 0 0       0 push @$stack, ((!$arg) ? 1 : 0);
682             }
683              
684             sub op_eq {
685 0     0 0 0 my ($self, $stack, $env) = @_;
686 0         0 my $right = pop @$stack;
687 0         0 my $left = pop @$stack;
688 0 0       0 push @$stack, (($left eq $right) ? 1 : 0);
689             }
690              
691             sub op_or {
692 0     0 0 0 my ($self, $stack, $env) = @_;
693 0   0     0 my $right = pop @$stack || 0;
694 0   0     0 my $left = pop @$stack || 0;
695 0 0 0     0 push @$stack, (($left || $right) ? 1 : 0);
696             }
697              
698             sub op_and {
699 0     0 0 0 my ($self, $stack, $env) = @_;
700 0   0     0 my $right = pop @$stack || 0;
701 0   0     0 my $left = pop @$stack || 0;
702 0 0 0     0 push @$stack, (($left && $right) ? 1 : 0);
703             }
704              
705             sub op_concat {
706 0     0 0 0 my ($self, $stack, $env) = @_;
707 0         0 my $right = pop @$stack;
708 0         0 my $left = pop @$stack;
709 0         0 push @$stack, ($left . $right);
710             }
711              
712             sub op_lookup {
713 0     0 0 0 my ($self, $stack, $env) = @_;
714 0   0     0 my $attr = pop @$stack || return "";
715 0   0     0 my $hash = pop @$stack || return "";
716 0         0 foreach my $a (split /\./, $attr){
717 0 0 0     0 if( $hash && ref($hash) eq "HASH" && exists($hash->{$a}) ){
      0        
718 0         0 $hash = $hash->{$a};
719             }
720 0         0 else{ return "" }
721             }
722 0         0 push @$stack, $hash;
723             }
724              
725             sub setup_op {
726 1     1 0 3 my ($self) = @_;
727 1         2 my $env = $self->{op};
728 1         3 $env->{trim} = \&op_trim;
729 1         3 $env->{'list-remove-last'} = \&op_list_remove_last;
730 1         3 $env->{'list-last'} = \&op_list_last;
731 1         3 $env->{'not'} = \&op_not;
732 1         3 $env->{'eq'} = \&op_eq;
733 1         4 $env->{'or'} = \&op_or;
734 1         3 $env->{'and'} = \&op_and;
735 1         4 $env->{'lookup'} = \&op_lookup;
736 1         4 $env->{'concat'} = \&op_concat;
737             }
738              
739             sub setup {
740 1     1 0 3 my ($ref) = @_;
741 1         5 setup_fun($ref);
742 1         4 setup_op($ref);
743             }
744              
745             # utilities ##########################################################
746              
747             sub file_content {
748 0     0 0   my ($file) = @_;
749 0           local *FILE;
750 0           local $/;
751 0           $/ = undef;
752 0 0         open(FILE, $file) || die "can't open $file: $!";
753 0           my $c = ;
754 0           close(FILE);
755 0           return $c;
756             }
757              
758             sub trim {
759 0     0 1   my ($str) = @_;
760 0   0       $str ||= "";
761 0           $str =~ s/^\s+//;
762 0           $str =~ s/\s+$//;
763 0           return $str;
764             }
765              
766             sub debug_env {
767 0     0 0   my ($env, $prefix) = @_;
768 0 0         if( ref($env) eq "ARRAY" ){
    0          
769 0           foreach(@$env){
770 0           debug_env($_, $prefix);
771             }
772             }
773             elsif( ref($env) eq "HASH" ){
774 0           while( my($key, $val) = each %$env ){
775 0           print " " x $prefix, "<$key>\n";
776 0           debug_env($val, $prefix+2);
777 0           print "\n";
778 0           print " " x $prefix, "\n";
779             }
780             }
781             else{
782 0           print " " x $prefix, $env;
783             }
784             }
785              
786             1;
787             __END__