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