use quoted-printable encoding for html mail
[bse.git] / site / cgi-bin / modules / Squirrel / Template / Expr.pm
CommitLineData
4697e5c3
TC
1package Squirrel::Template::Expr;
2use strict;
3
46296d79 4our $VERSION = "1.012";
ac507437
TC
5
6package Squirrel::Template::Expr::Eval;
7use Scalar::Util ();
f17f9f79
TC
8use Squirrel::Template::Expr::WrapScalar;
9use Squirrel::Template::Expr::WrapHash;
10use Squirrel::Template::Expr::WrapArray;
11use Squirrel::Template::Expr::WrapCode;
12use Squirrel::Template::Expr::WrapClass;
13
ac507437 14use constant TMPL => 0;
a96f9b25 15use constant ACTS => 1;
ac507437
TC
16
17sub new {
a96f9b25 18 my ($class, $templater, $acts) = @_;
ac507437 19
a96f9b25 20 return bless [ $templater, $acts ], $class;
ac507437
TC
21}
22
23sub _wrapped {
24 my ($self, $val) = @_;
25
26 if (ref($val)) {
27 if (Scalar::Util::blessed($val)) {
28 return $val;
29 }
30 else {
31 my $type = Scalar::Util::reftype($val);
32 if ($type eq "ARRAY") {
a96f9b25 33 return Squirrel::Template::Expr::WrapArray->new($val, $self->[TMPL]);
ac507437
TC
34 }
35 elsif ($type eq "HASH") {
a96f9b25 36 return Squirrel::Template::Expr::WrapHash->new($val, $self->[TMPL]);
ac507437
TC
37 }
38 elsif ($type eq "CODE") {
a96f9b25 39 return Squirrel::Template::Expr::WrapCode->new($val, $self->[TMPL]);
ac507437
TC
40 }
41 }
42 }
43 else {
a96f9b25 44 return Squirrel::Template::Expr::WrapScalar->new($val, $self->[TMPL], $self->[ACTS]);
ac507437
TC
45 }
46}
47
48sub _process_var {
49 return $_[0][TMPL]->get_var($_[1][1]);
50}
51
52sub _process_add {
53 return $_[0]->process($_[1][1]) + $_[0]->process($_[1][2]);
54}
55
56sub _process_subtract {
57 return $_[0]->process($_[1][1]) - $_[0]->process($_[1][2]);
58}
59
60sub _process_mult {
61 return $_[0]->process($_[1][1]) * $_[0]->process($_[1][2]);
62}
63
64sub _process_fdiv {
65 return $_[0]->process($_[1][1]) / $_[0]->process($_[1][2]);
66}
67
68sub _process_div {
69 return int($_[0]->process($_[1][1]) / $_[0]->process($_[1][2]));
70}
71
72sub _process_mod {
73 return $_[0]->process($_[1][1]) % $_[0]->process($_[1][2]);
74}
75
76# string relops
77sub _process_eq {
78 return $_[0]->process($_[1][1]) eq $_[0]->process($_[1][2]);
79}
80
81sub _process_ne {
82 return $_[0]->process($_[1][1]) ne $_[0]->process($_[1][2]);
83}
84
85sub _process_gt {
86 return $_[0]->process($_[1][1]) gt $_[0]->process($_[1][2]);
87}
88
89sub _process_lt {
90 return $_[0]->process($_[1][1]) lt $_[0]->process($_[1][2]);
91}
92
93sub _process_ge {
94 return $_[0]->process($_[1][1]) ge $_[0]->process($_[1][2]);
95}
96
97sub _process_le {
98 return $_[0]->process($_[1][1]) le $_[0]->process($_[1][2]);
99}
100
101# number relops
102sub _process_neq {
103 return $_[0]->process($_[1][1]) == $_[0]->process($_[1][2]);
104}
105
106sub _process_nne {
107 return $_[0]->process($_[1][1]) != $_[0]->process($_[1][2]);
108}
109
110sub _process_ngt {
111 return $_[0]->process($_[1][1]) > $_[0]->process($_[1][2]);
112}
113
114sub _process_nlt {
115 return $_[0]->process($_[1][1]) < $_[0]->process($_[1][2]);
116}
117
118sub _process_nge {
119 return $_[0]->process($_[1][1]) >= $_[0]->process($_[1][2]);
120}
121
122sub _process_nle {
123 return $_[0]->process($_[1][1]) <= $_[0]->process($_[1][2]);
124}
125
126sub _process_match {
127 return $_[0]->process($_[1][1]) =~ $_[0]->process($_[1][2]);
128}
129
130sub _process_notmatch {
131 return $_[0]->process($_[1][1]) !~ $_[0]->process($_[1][2]);
132}
133
134sub _process_cond {
135 return $_[0]->process($_[1][1]) ? $_[0]->process($_[1][2]) : $_[0]->process($_[1][3]);
136}
137
138sub _process_uminus {
139 return - ($_[0]->process($_[1][1]));
140}
141
142sub _process_concat {
143 return $_[0]->process($_[1][1]) . $_[0]->process($_[1][2]);
144}
145
146sub _process_const {
147 return $_[1][1];
148}
149
dd94dd8e
TC
150sub _do_call {
151 my ($self, $val, $args, $method, $ctx) = @_;
b12ea476 152
b12ea476
TC
153 if (Scalar::Util::blessed($val)
154 && !$val->isa("Squirrel::Template::Expr::WrapBase")) {
ac507437
TC
155 $val->can($method)
156 or die [ error => "No such method $method" ];
157 if ($val->can("restricted_method")) {
158 $val->restricted_method($method)
159 and die [ error => "method $method is restricted" ];
160 }
161 return $ctx && $ctx eq 'LIST' ? $val->$method(@$args)
162 : scalar($val->$method(@$args));
163 }
164 else {
165 my $wrapped = $self->_wrapped($val);
166 return $wrapped->call($method, $args, $ctx);
167 }
168}
169
dd94dd8e
TC
170sub _process_call {
171 my ($self, $node, $ctx) = @_;
172
173 $ctx ||= "";
174
175 my $val = $self->process($node->[2]);
176 my $args = $self->process_list($node->[3]);
177 my $method = $node->[1];
178
179 return $self->_do_call($val, $args, $method, $ctx);
180}
181
182sub _process_callvar {
183 my ($self, $node, $ctx) = @_;
184
185 $ctx ||= "";
186
187 my $val = $self->process($node->[2]);
188 my $args = $self->process_list($node->[3]);
189 my $method = $self->[TMPL]->get_var($node->[1]);
190
191 return $self->_do_call($val, $args, $method, $ctx);
192}
193
194sub _process_funccall {
195 my ($self, $node, $ctx) = @_;
196
197 $ctx ||= "";
198 my $code = $self->process($node->[1]);
199 my $args = $self->process_list($node->[2]);
200
201 if (Scalar::Util::reftype($code) eq "CODE") {
202 return $ctx eq "LIST" ? $code->(@$args) : scalar($code->(@$args));
203 }
204 else {
205 die [ error => "can't call non code as a function" ];
206 }
207}
208
ac507437
TC
209sub _process_list {
210 my ($self, $node) = @_;
211
212 return $self->process_list($node->[1], 'LIST');
213}
214
215sub _process_range {
216 my ($self, $node, $ctx) = @_;
217
218 my $start = $self->process($node->[1]);
219 my $end = $self->process($node->[2]);
220
221 return $ctx eq 'LIST' ? ( $start .. $end ) : [ $start .. $end ];
222}
223
f17f9f79
TC
224sub _process_hash {
225 my ($self, $node) = @_;
226
227 my %result;
228 for my $pair (@{$node->[1]}) {
229 my $key = $self->process($pair->[0]);
230 my $value = $self->process($pair->[1]);
231 $result{$key} = $value;
232 }
233
234 return \%result;
235}
236
ac507437
TC
237sub _process_subscript {
238 my ($self, $node) = @_;
239
240 my $list = $self->process($node->[1]);
241 my $index = $self->process($node->[2]);
242 Scalar::Util::blessed($list)
243 and die [ error => "Cannot subscript an object" ];
244 my $type = Scalar::Util::reftype($list);
245 if ($type eq "HASH") {
246 return $list->{$index};
247 }
248 elsif ($type eq "ARRAY") {
249 return $list->[$index];
250 }
251 else {
252 die [ error => "Cannot subscript a $type" ];
253 }
254}
255
256sub _process_not {
257 return !$_[0]->process($_[1][1]);
258}
259
260sub _process_or {
261 return $_[0]->process($_[1][1]) || $_[0]->process($_[1][2]);
262}
263
264sub _process_and {
265 return $_[0]->process($_[1][1]) && $_[0]->process($_[1][2]);
266}
267
268sub process {
269 my ($self, $node, $ctx) = @_;
270
271 my $method = "_process_$node->[0]";
272 $self->can($method) or die "No handler for $node->[0]";
273 return $self->$method($node, $ctx);
274}
275
276sub process_list {
277 my ($self, $list) = @_;
278
279 return [ map $self->process($_, 'LIST'), @$list ];
280}
4697e5c3
TC
281
282package Squirrel::Template::Expr::Parser;
283
284sub new {
285 my ($class) = @_;
286
287 return bless {}, $class;
288}
289
290sub parse {
291 my ($self, $text) = @_;
292
293 my $tokenizer = Squirrel::Template::Expr::Tokenizer->new($text);
c507244d
TC
294 my $result = $self->_parse_expr($tokenizer);
295
296 my $last = $tokenizer->get;
297 unless ($last->[0] eq 'eof') {
298 die [ error => "Expected eof but found $last->[0]" ];
299 }
300
301 return $result;
4697e5c3
TC
302}
303
231cecfb
TC
304sub parse_tokens {
305 my ($self, $tokenizer) = @_;
306
307 return $self->_parse_expr($tokenizer);
308}
309
4697e5c3
TC
310sub _parse_expr {
311 my ($self, $tok) = @_;
312
ac507437 313 return $self->_parse_cond($tok);
4697e5c3
TC
314}
315
316my %ops =
317 (
ac507437
TC
318 "op+" => "add",
319 "op-" => "subtract",
320 "op*" => "mult",
321 "op/" => "fdiv",
4697e5c3
TC
322 "div" => "div",
323 "mod" => "mod",
324 "op_" => "concat",
ac507437
TC
325
326 "opeq" => "eq",
327 "opne" => "ne",
328 "oplt" => "lt",
329 "opgt" => "gt",
330 "ople" => "le",
331 "opge" => "ge",
332
333 "op==" => "neq",
334 "op!=" => "nne",
335 "op<" => "nlt",
336 "op>" => "ngt",
337 "op<=" => "nle",
338 "op>=" => "nge",
339 'op=~' => "match",
340 'op!~' => "notmatch",
4697e5c3
TC
341 );
342
ac507437
TC
343sub _parse_cond {
344 my ($self, $tok) = @_;
345
346 my $result = $self->_parse_or($tok);
347 if ($tok->peektype eq 'op?') {
348 $tok->get;
349 my $true = $self->_parse_or($tok);
350 my $colon = $tok->get;
351 $colon->[0] eq 'op:'
46296d79 352 or die [ error => "Expected : for ? : operator but found $colon->[0]" ];
4742ac21 353 my $false = $self->_parse_cond($tok);
ac507437
TC
354
355 $result = [ cond => $result, $true, $false ];
356 }
357
358 return $result;
359}
360
4697e5c3
TC
361sub _parse_or {
362 my ($self, $tok) = @_;
363
364 my $result = $self->_parse_and($tok);
365 while ($tok->peektype eq 'or') {
366 my $op = $tok->get;
367 my $other = $self->_parse_and($tok);
368 $result = [ or => $result, $other ];
369 }
370
371 return $result;
372}
373
374sub _parse_and {
375 my ($self, $tok) = @_;
376
377 my $result = $self->_parse_rel($tok);
378 while ($tok->peektype eq 'and') {
379 my $op = $tok->get;
380 my $other = $self->_parse_rel($tok);
381 $result = [ and => $result, $other ];
382 }
383
384 return $result;
385}
386
387my %relops = map {; "op$_" => 1 } qw(eq ne gt lt ge le == != < > >= <= =~ !~);
388
389sub _parse_rel {
390 my ($self, $tok) = @_;
391
392 my $result = $self->_parse_additive($tok);
393 my $nexttype = $tok->peektype;
394 while ($relops{$nexttype}) {
395 my $op = $tok->get;
396 my $other = $self->_parse_additive($tok);
ac507437 397 $result = [ $ops{$nexttype}, $result, $other ];
4697e5c3
TC
398 $nexttype = $tok->peektype;
399 }
400 return $result;
401}
402
403sub _parse_additive {
404 my ($self, $tok) = @_;
405
406 my $result = $self->_parse_mult($tok);
407 my $nexttype = $tok->peektype;
408 while ($nexttype eq 'op+' || $nexttype eq 'op-' || $nexttype eq 'op_') {
409 my $op = $tok->get;
410 my $other = $self->_parse_mult($tok);
411 $result = [ $ops{$nexttype}, $result, $other ];
412 $nexttype = $tok->peektype;
413 }
414 return $result;
415}
416
417sub _parse_mult {
418 my ($self, $tok) = @_;
419
420 my $result = $self->_parse_prefix($tok);
421 my $nexttype = $tok->peektype;
422 while ($nexttype eq 'op*' || $nexttype eq 'op/'
423 || $nexttype eq 'div' || $nexttype eq 'mod') {
424 my $op = $tok->get;
425 my $other = $self->_parse_prefix($tok);
426 $result = [ $ops{$op->[0]}, $result, $other ];
427 $nexttype = $tok->peektype;
428 }
429 return $result;
430}
431
432sub _parse_prefix {
433 my ($self, $tok) = @_;
434
435 my $nexttype = $tok->peektype('TERM');
961939b5 436 if ($nexttype eq 'op-') {
4697e5c3
TC
437 $tok->get;
438 return [ uminus => $self->_parse_prefix($tok) ];
439 }
440 elsif ($nexttype eq 'op+') {
441 $tok->get;
442 return $self->_parse_prefix($tok);
443 }
444 elsif ($nexttype eq 'op!' || $nexttype eq 'opnot') {
445 $tok->get;
446 return [ not => $self->_parse_prefix($tok) ];
447 }
448 else {
449 return $self->_parse_call($tok);
450 }
451}
452
453sub _parse_list {
454 my ($self, $tok) = @_;
455
4742ac21 456 $tok->peektype("TERM") eq 'op)'
4697e5c3
TC
457 and return [];
458
459 my @list;
460 push @list, $self->_parse_expr($tok);
461 my $peek = $tok->peektype;
462 while ($peek eq 'op,' || $peek eq '..') {
463 $tok->get;
464 if ($peek eq '..') {
465 my $start = pop @list;
466 $start->[0] ne 'range'
467 or die [ error => "Can't use a range as the start of a range" ];
468 my $end = $self->_parse_expr($tok);
469 push @list, [ range => $start, $end ];
470 }
471 else {
472 push @list, $self->_parse_expr($tok);
473 }
474 $peek = $tok->peektype;
475 }
476
477 return \@list;
478}
479
480sub _parse_paren_list {
481 my ($self, $tok, $what) = @_;
482
483 my $open = $tok->get;
484 $open->[0] eq 'op('
485 or die [ error => "Expected '(' for $what but found $open->[0]" ];
486 my $list = $self->_parse_list($tok);
487 my $close = $tok->get;
488 $close->[0] eq 'op)'
489 or die [ error => "Expected ')' for $what but found $close->[0]" ];
490
491 return $list;
492}
493
494sub _parse_call {
495 my ($self, $tok) = @_;
496
497 my $result = $self->_parse_postfix($tok);
498 my $next = $tok->peektype;
dd94dd8e 499 while ($next eq 'op.' || $next eq 'op[' || $next eq 'op(') {
4697e5c3
TC
500 if ($next eq 'op.') {
501 $tok->get;
502 my $name = $tok->get;
dd94dd8e
TC
503 if ($name->[0] eq "id") {
504 my $list = [];
505 if ($tok->peektype eq 'op(') {
506 $list = $self->_parse_paren_list($tok, "method");
507 }
508 $result = [ call => $name->[2], $result, $list ];
509 }
510 elsif ($name->[0] eq 'op$') {
511 # get the real name
512 $name = $tok->get;
513 $name->[0] eq 'id'
46296d79 514 or die [ error => "Expected an identifier after .\$ but found $name->[1]" ];
dd94dd8e
TC
515 my $list = [];
516 if ($tok->peektype eq 'op(') {
517 $list = $self->_parse_paren_list($tok, "method");
518 }
519 $result = [ callvar => $name->[2], $result, $list ];
520 }
521 else {
46296d79 522 die [ error => "Expected a method name or \$var after '.' but found $name->[1]" ];
4697e5c3 523 }
4697e5c3
TC
524 }
525 elsif ($next eq 'op[') {
526 $tok->get;
527 my $index = $self->_parse_expr($tok);
528 my $close = $tok->get;
529 $close->[0] eq 'op]'
46296d79 530 or die [ error => "Expected closing ']' but got $close->[0]" ];
4697e5c3
TC
531 $result = [ subscript => $result, $index ];
532 }
dd94dd8e
TC
533 elsif ($next eq 'op(') {
534 my $args = $self->_parse_paren_list($tok, "call");
535 $result = [ funccall => $result, $args ];
536 }
4697e5c3
TC
537 $next = $tok->peektype;
538 }
539
540 return $result;
541}
542
543sub _parse_postfix {
544 my ($self, $tok) = @_;
545
546 return $self->_parse_primary($tok);
547}
548
549sub _parse_primary {
550 my ($self, $tok) = @_;
551
552 my $t = $tok->get('TERM');
961939b5
TC
553 if ($t->[0] eq 'op(') {
554 my $r = $self->_parse_expr($tok);
555 my $close = $tok->get;
556 unless ($close->[0] eq 'op)') {
557 die [ error => "Expected ')' but found $close->[0]" ];
558 }
559 return $r;
560 }
561 elsif ($t->[0] eq 'str' || $t->[0] eq 'num') {
4697e5c3
TC
562 return [ const => $t->[2] ];
563 }
ac507437
TC
564 elsif ($t->[0] eq 're') {
565 my $str = $t->[2];
566 my $opts = $t->[3];
567 my $sub = eval "sub { my \$str = shift; qr/\$str/$opts; }";
568 my $re;
569 $sub and $re = eval { $sub->($str) };
570 $re
571 or die [ error => "Cannot compile /$t->[2]/$opts: $@" ];
572 return [ const => $re ];
573 }
4697e5c3
TC
574 elsif ($t->[0] eq 'id') {
575 return [ var => $t->[2] ];
576 }
577 elsif ($t->[0] eq 'op[') {
39fbca4a
TC
578 my $list = [];
579 if ($tok->peektype ne 'op]') {
580 $list = $self->_parse_list($tok);
581 }
4697e5c3
TC
582 my $close = $tok->get;
583 $close->[0] eq 'op]'
46296d79 584 or die [ error => "Expected list end ']' but got $close->[0]" ];
4697e5c3
TC
585 return [ list => $list ];
586 }
f17f9f79
TC
587 elsif ($t->[0] eq 'op{') {
588 my @pairs;
589 if ($tok->peektype eq 'op}') {
590 $tok->get; # discard '}'
591 }
592 else {
593 my $next;
594 do {
595 my $key = $self->_parse_additive($tok);
596 my $colon = $tok->get;
597 $colon->[0] eq 'op:'
598 or die [ error => "Expected : in hash but found $colon->[1]" ];
599 my $value = $self->_parse_expr($tok);
600 push @pairs, [ $key, $value ];
601 } while ($next = $tok->get and $next->[0] eq 'op,');
602 $next->[0] eq 'op}'
603 or die [ error => "Expected , or } but found $tok->[1]" ];
604 }
605
606 return [ hash => \@pairs ];
607 }
4697e5c3
TC
608 elsif ($t->[0] eq 're') {
609 return [ re => $t->[2], $t->[3] ];
610 }
611 else {
612 die [ error => "Expected term but got $t->[0]" ];
613 }
614}
615
616package Squirrel::Template::Expr::Tokenizer;
617
618use constant TEXT => 0;
619use constant QUEUE => 1;
620
621sub new {
622 my ($class, $text) = @_;
623
624 return bless [ $text, [] ], $class;
625}
626
627my %escapes =
628 (
629 n => "\n",
630 "\\" => "\\",
631 t => "\t",
632 '"' => '"',
633 );
634
635sub get {
636 my ($self, $want) = @_;
637
638 my $queue = $self->[QUEUE];
639 @$queue
640 and return shift @$queue;
641 length $self->[TEXT]
642 or return;
643
644 $want ||= '';
645
646 if ($want ne 'TERM' &&
a97045d4 647 $self->[TEXT] =~ s/\A(\s*(div\b|mod\b|\.\.|and\b|or\b)\s*)//) {
4697e5c3
TC
648 push @$queue, [ $2 => $1 ];
649 }
650 elsif ($self->[TEXT] =~ s/\A(\s*(0x[0-9A-Fa-f]+)\s*)//) {
651 push @$queue, [ num => $1, oct $2 ];
652 }
653 elsif ($self->[TEXT] =~ s/\A(\s*(0b[01]+)\s*)//) {
654 push @$queue, [ num => $1, oct $2 ];
655 }
656 elsif ($self->[TEXT] =~ s/\A(\s*0o([0-7]+)\s*)//) {
657 push @$queue, [ num => $1, oct $2 ];
658 }
659 elsif ($self->[TEXT] =~ s/\A(\s*((?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)(?:[Ee][+-]?[0-9]+)?)\s*)//) {
660 push @$queue, [ num => $1, $2 ];
661 }
662 elsif ($want eq 'TERM' &&
663 $self->[TEXT] =~ s!\A(\s*/((?:[^/\\]|\\.)+)/([ismx]*\s)?\s*)!!) {
664 push @$queue, [ re => $1, $2, $3 || "" ];
665 }
66a07e7c 666 elsif ($self->[TEXT] =~ s/\A(\s*(not\b|eq\b|ne\b|le\b|lt\b|ge\b|gt\b|<=|>=|[!=]\=|\=\~|!~|[_\?:,\[\]\(\)<>=!.*\/+\{\};\$-])\s*)//) {
4697e5c3
TC
667 push @$queue, [ "op$2" => $1 ];
668 }
669 elsif ($self->[TEXT] =~ s/\A(\s*([A-Za-z_][a-zA-Z_0-9]*)\s*)//) {
670 push @$queue, [ id => $1, $2 ];
671 }
672 elsif ($self->[TEXT] =~ s/\A(\s*\"((?:[^"\\]|\\["\\nt]|\\x[0-9a-fA-F]{2}|\\x\{[0-9a-fA-F]+\}|\\N\{[A-Za-z0-9 ]+\})*)\"\s*)//) {
673 my $orig = $1;
674 my $str = _process_escapes($2);
675 push @$queue, [ str => $1, $str ];
676 }
677 elsif ($self->[TEXT] =~ s/\A(\s*\'([^\']*)\'\s*)//) {
678 push @$queue, [ str => $1, $2 ];
679 }
680 else {
681 die [ error => "Unknown token '$self->[TEXT]'" ];
682 }
683
684 unless (length $self->[TEXT]) {
685 push @$queue, [ eof => "" ];
686 }
687
688 return shift @$queue;
689}
690
691sub unget {
692 my ($self, $tok) = @_;
693
694 unshift @{$self->[QUEUE]}, $tok;
695}
696
697sub peek {
698 my ($self, $what) = @_;
699
700 unless (@{$self->[QUEUE]}) {
701 my $t = $self->get($what)
702 or return;
703 unshift @{$self->[QUEUE]}, $t;
704 }
705
706 return $self->[QUEUE][0];
707}
708
709sub peektype {
710 my ($self, $what) = @_;
711
712 return $self->peek($what)->[0];
713}
714
715sub _process_escapes {
716 my ($str) = @_;
717
718 $str =~
719 s(
720 \\([nt\\\"])
721 |
722 \\x\{([0-9A-Fa-f]+)\}
723 |
724 \\x([0-9A-Fa-f]{2})
725 |
726 \\N\{([A-Za-z0-9\ ]+)\}
727 )(
728 $1 ? $escapes{$1} :
729 $2 ? chr(hex($2)) :
730 $3 ? chr(hex($3)) :
731 _vianame($4)
732 )gex;
733
734 return $str;
735}
736
737my $charnames_loaded;
738sub _vianame {
739 my ($name, $errors) = @_;
740
741 require charnames;
742 my $code = charnames::vianame($name);
743 unless (defined $code) {
744 die [ error => "Unknown \\N name '$name'" ];
745 }
746 return chr($code);
747}
748
7491;
6c291231
TC
750
751__END__
752
753=head1 NAME
754
755Squirrel::Template::Expr - expression handling for Squirrel::Template
756
757=head1 SYNOPSIS
758
759 # code that uses it
760 my $parser = Squirrel::Template::Expr::Parser->new;
761
762 my $expr = $parser->parse($expr_text);
763
764 my $tokens = Squirrel::Template::Expr::Tokenizer->new($expr_text);
765
766 my $expr = $parser->parse_tokens($tokenizer);
767 # and possibly process more tokens here
768
769 my $eval = Squirrel::Template::Expr::Parser->new($templater);
770
771 my $value = $eval->process($expr);
772 my $value = $eval->process($expr, "LIST");
773
774 my $arrayref = $eval->process(\@exprs);
775
776 # Expressions
777
778 <:= somevalue + 10 :>
779 <:.if somevalue == 10 :>
780
781=head1 DESCRIPTION
782
783Squirrel::Template::Expr provides expression parsing and evaluation
784for newer style tags for L<Squirrel::Template>.
785
786=head1 EXPRESSION SYNTAX
787
788=head2 Operators
789
790Listed highest precedence first.
791
792=over
793
794=item *
795
796C<<[ I<list> ]>>, C<<{ I<key>:I<value>, ... }>>, literals
797
798C<<[ I<list> ]>> allows you to build lists objects. Within C<[ ... ]>
799you can use the C<..> operator to produce a list of numerically or
800alphabetically ascending values per Perl's magic increment.
801
802eg.
803
804 [ "a", "c" .. "z" ]
805 [ 1 .. 10 ]
806
807Method calls within C<<[ ... ]>> are done in perl's list context.
808
809C<<{ ... }>> allows you to build hash objects.
810
811eg.
812
813 { "somekey":somevariable, somekeyinvar:"somevalue" }
814
815See L</Literals> for literals
816
817=item *
818
819method calls - methods are called as:
820
821 object.method;
822
823or
824
825 object.method(arguments)
826
827and may be chained.
828
829Virtual methods are defined for hashes, arrays and scalars, see
830L<Squirrel::Template::Expr::WrapHash>,
831L<Squirrel::Template::Expr::WrapArray>,
832L<Squirrel::Template::Expr::WrapScalar>,
833L<Squirrel::Template::Expr::WrapCode> and
834L<Squirrel::Template::Expr::WrapClass>.
835
836=item *
837
dd94dd8e
TC
838function calls - functions are called as:
839
840 somevar();
841
842or
843
844 somevar(arguments);
845
846or any other expression that doesn't look like a method call:
847
848 somehash.get["foo"]();
849
850=item *
851
6c291231
TC
852unary -, unary +, unary !, unary not
853
854=item *
855
856* / div mod - simple arithmetic operators. C<div> returns the integer
857portion of dividing the first operand by the second. C<mod> returns
858the remainder of integer division.
859
860=item *
861
862+ - _ - arithmetic addition and subtraction. C<_> does string
863concatenation.
864
865=item *
866
867eq ne le lt ge gt == != > < >= <= =~ !~ - relational operators as per
868Perl.
869
870=item *
871
872and - boolean and, with shortcut.
873
874=item *
875
876or - boolean or, with shortcut.
877
878=item *
879
880Conditional (C<< I<cond> ? I<true> : I<false> >>) - return the value
881of I<true> or I<false> depending on I<cond>.
882
883=back
884
885=head2 Literals
886
887Numbers can be represented in several formats:
888
889=over
890
891=item *
892
893simple decimal - C<100>, C<3.14159>, C<1e10>.
894
895=item *
896
897hex - C<0x64>
898
899=item *
900
901octal - C<0o144>
902
903=item *
904
905binary - C<0b1100100>
906
907=back
908
909Strings can be either " or ' delimited.
910
911Simple quote delimited strings allow no escaping, and may not contain
912single quotes. The contents are treated literally.
913
914Double quoted strings allow escaping as follows:
915
916=over
917
918=item *
919
920Any of C<\">, C<\n>, C<\\>, C<\t> are treated as in C or perl,
921replaced with double quote, newline, backslash or tab respectively.
922
923=item *
924
925C<<\x{I<hex-digits>}>> is replaced with the unicode code-point
926indicated by the hex number.
927
928=item *
929
930C<< \xI<hex-digit>I<hex-digit> >> is replaced by the unicode
931code-point indicated by the 2-digit hex number.
932
933=item *
934
935C<< \N{ I<unicode-character-name> } >> is replaced by the unicode
936character named.
937
938=back
939
940=head1 Squirrel::Template::Expr::Parser
941
942Squirrel::Template::Expr::Parser provides parsing for expressions.
943
944=head1 Methods
945
946=over
947
948=item new()
949
950Create a new parser object.
951
952=item parse($text)
953
954Parse C<$text> as an expression. Parsing must reach the end of the
955text or an exception will be thrown.
956
957=item parse_tokens($tokenizer)
958
959Process tokens from C<$tokenizer>, a
960L</Squirrel::Template::Expr::Tokenizer> object. The caller can call
961these method several times with the same C<$tokenizer> to parse
962components of a statement, and should ensure the eof token is visible
963after the final component.
964
965=back
966
967=head1 Squirrel::Template::Expr::Tokenizer
968
969Split text into tokens. Token parsing is occasionally context
970sensitive.
971
972=head2 Methods
973
974=over
975
976=item new($text)
977
978Create a new tokenizer for parsing C<$text>.
979
980=item get()
981
982=item get($context)
983
984Retrieve a token from the stream, consuming it. If a term is expected
985$context should be set to C<'TERM'>.
986
987=item unget()
988
989Push a token back into the stream.
990
991=item peek()
992
993=item peek($context)
994
995Retrieve the next token from the stream without consuming it.
996
997=item peektype()
998
999=item peektype($context)
1000
1001Retrieve the type of the next token from the stream without consuming
1002it.
1003
1004=back
1005
1006=head1 Squirrel::Template::Expr::Eval
1007
1008Used to evaluate an expression returned by
1009Squirrel::Template::Expr::parse().
1010
1011=head2 Methods
1012
1013=over
1014
1015=item new($templater)
1016
1017Create a new evaluator. C<$templater> should be a
1018L<Squirrel::Template> object.
1019
1020=back
1021
1022=head1 SEE ALSO
1023
1024L<Squirrel::Template>
1025
1026=head1 AUTHOR
1027
1028Tony Cook <tony@develop-help.com>
1029
1030=cut