File Coverage

blib/lib/Text/PORE/Node/If.pm
Criterion Covered Total %
statement 98 101 97.0
branch 29 32 90.6
condition 3 9 33.3
subroutine 8 8 100.0
pod 0 4 0.0
total 138 154 89.6


line stmt bran cond sub pod time code
1             # IfNode -- if-then-else construct
2             # condition (expressionNode ref): condition expression
3             # if_body (array ref): template to be executed on true (Node stack)
4             # else_body (array ref): templates to be executed on false (Node stack)
5             #
6             package Text::PORE::Node::If;
7            
8 1     1   100 use Text::PORE::Node::Attr;
  1         5  
  1         34  
9 1     1   5 use Text::PORE::Node;
  1         2  
  1         19  
10 1     1   912 use English;
  1         1033  
  1         7  
11 1     1   752 use strict;
  1         3  
  1         3412  
12            
13             @Text::PORE::Node::If::ISA = qw(Text::PORE::Node::Attr);
14            
15             sub new {
16 14     14 0 109 my $type = shift;
17 14         21 my $lineno = shift;
18 14         16 my $attrs = shift;
19 14         17 my $then = shift;
20 14         19 my $else = shift;
21            
22 14   33     89 my $self = bless {}, ref($type) || $type;
23            
24 14         61 $self = $self->SUPER::new($lineno, 'if', $attrs);
25            
26 14         31 $self->{'if_body'} = $then;
27 14   33     74 $self->{'else_body'} = $else || new Text::PORE::Node($lineno);;
28            
29 14   33     82 bless $self, ref($type) || $type;
30             }
31            
32             sub traverse {
33 17     17 0 21 my $self = shift;
34 17         18 my $globals = shift;
35            
36 17         45 my $context = $globals->GetAttribute('_context');
37 17         25 my $result;
38             my $return;
39            
40 17         40 $result = $self->evaluate($globals);
41            
42 17 50       55 $self->output("[IF:$self->{'lineno'}]") if $self->getDebug();
43            
44             # note - in other places, we load the error messages directly,
45             # but the syntax here would just get too confusing
46 17 100       75 $return = $self->{$result ? 'if_body' : 'else_body'}->
47             traverse($globals);
48            
49 17         44 $self->error($return);
50            
51 17         49 return $self->errorDump();
52             }
53            
54             sub evaluate {
55 17     17 0 18 my $self = shift;
56 17         17 my $globals = shift;
57            
58 17         20 my $expr;
59             my $return;
60            
61             # parse the condition into a perl expression
62 17         33 $expr = $self->format_expr_for_perl($globals);
63             #print STDERR "[$expr]\n";
64             # evaluate the expression
65 17         1129 $return = eval ($expr);
66             #print STDERR "return = [$return]\n";
67            
68 17 50       59 if ($EVAL_ERROR) {
69 0         0 $self->error("Expression evaluation error:\n".
70             "\tMessage: [$EVAL_ERROR]\n".
71             "\tExpression: [$expr]\n".
72             "\tContext: [".
73             $globals->GetAttribute('_context')."]\n");
74             }
75            
76 17         37 $return;
77             }
78            
79             sub format_expr_for_perl {
80 17     17 0 17 my $self = shift;
81 17         18 my $globals = shift;
82            
83 17         41 my ($expr) = $self->{'attrs'}{'cond'};
84 17         19 my (@expr_list);
85            
86             # Tokenize expression and check for correctness
87            
88             LOOP:
89 17         36 while ($expr) {
90 93         218 $expr =~ s/^\s+//;
91            
92 93         111 $_ = $expr;
93            
94             SWITCH: {
95 93 100       80 s/^([\'\"])(([^\\]|\\.)*?)\1// && do { # Match constant
  93         275  
96 25         42 push @expr_list, $MATCH;
97 25         35 last SWITCH;
98             };
99 68 100       197 s/^([\.\w]+)// && do { # Match attribute
100 38         69 push @expr_list, $MATCH;
101 38         52 last SWITCH;
102             };
103 30 100       74 s/^(>=|<=|!=)// && do { # Match two-char operator
104 3         6 push @expr_list, $MATCH;
105 3         5 last SWITCH;
106             };
107 27 100       91 s/^[<>()+\-*\/\%]// && do { # Match one-char operator
108             # < > ( ) + - * / %
109 26         49 push @expr_list, $MATCH;
110 26         33 last SWITCH;
111             };
112 1 50       5 s/^=// && do { # Match equals
113 1         3 push @expr_list, "==";
114 1         2 last SWITCH;
115             };
116 0         0 $self->error("Expression syntax error at [$expr]");
117 0         0 last LOOP;
118             }
119            
120 93         228 $expr = $_;
121             }
122            
123            
124             # Replace expression elements to translate to Perl
125             # TODO - should combine this switch and the last for efficiency
126            
127 17         16 my $i;
128 17         18 my $case_insensitive = 0;
129 17         18 my $lc;
130            
131 17         42 for ($i=0; $i <= $#expr_list; $i++) {
132            
133             # NOTE - we want BOTH $_ and $lc for the following switch
134             # for purposes of efficiency
135 93         110 $_ = $expr_list[$i];
136 93         110 $lc = lc($_);
137            
138             # NOTE - arithmetic and inequality operators fall through
139             SWITCH: {
140 93 100       91 ($lc eq 'and') && do { # And
  93         157  
141 2         5 $expr_list[$i] = '&&'; last SWITCH;
  2         6  
142             };
143 91 100       146 ($lc eq 'or') && do { # Or
144 2         3 $expr_list[$i] = '||'; last SWITCH;
  2         6  
145             };
146 89 100       153 ($lc eq 'not') && do { # Not
147 2         4 $expr_list[$i] = '!'; last SWITCH;
  2         5  
148             };
149 87 100       124 ($lc eq 'eq') && do { # Case-insensitive stirng equality
150 5         9 $expr_list[$i] = '=~';
151 5         6 $case_insensitive = 1;
152 5         14 last SWITCH;
153             };
154 82 100       174 ($lc eq 'eqs') && do { # Case-sensitive string equality
155 6         9 $expr_list[$i] = 'eq'; last SWITCH;
  6         16  
156             };
157 76 100       198 /^([\'\"])(.*)\1$/ && do { # Match constant
158 25 100       61 my $str = ($case_insensitive ? "/^$2\$\/i" : $MATCH);
159 25         28 $expr_list[$i] = $str;
160 25         23 $case_insensitive = 0;
161 25         72 last SWITCH;
162             };
163 51 100       198 /^([\.\w]+)$/i && do { # Slots
164 21         34 my $attr = $MATCH;
165            
166             ### Modified by Zhengrong Tang to escape non-word chars.
167             ### Otherwise, eval() would generate warning messages
168             ### when slot value contains $, @, %, etc.
169 21         62 my $slot_value = $self->retrieveSlot($globals, $attr);
170 21         70 $slot_value =~ s/(\W)/\\$1/g; # escape all non-word chars
171            
172 21         41 $expr_list[$i] = "qq(". $slot_value . ")";
173 21         65 last SWITCH;
174             };
175            
176             }
177             }
178            
179             # Put expression back together into one string
180            
181 17         42 $expr = join(" ", @expr_list);
182 17         47 return $expr;
183             }
184            
185            
186             1;