#!/usr/bin/perl
# A really simple template engine
-# (c) 2004 Martin Mares <mj@ucw.cz>
+# (c) 2004--2008 Martin Mares <mj@ucw.cz>
package UCW::Temple;
use warnings;
no strict 'vars';
+require Exporter;
+our $VERSION = 1.0;
+our @ISA = qw(Exporter);
+our @EXPORT = qw(out);
+our @EXPORT_OK = qw();
+
use IO::File;
+our $arguments;
our @cond;
-our $temp;
+
+sub out {
+ $T::out_func->(@_);
+ return;
+}
+
+our %deps = ();
+
+sub add_depend($) {
+ $deps{$_[0]} = 1;
+}
sub get_nested($$$) {
my ($r,$left,$right) = @_;
sub eval_if_ok($) {
if ($cond[0] > 0) {
my $x = shift;
- my $res = eval $x;
+ my $res = eval "package T; $x";
return $res unless $@;
die "Error evaluating $x: $@";
} else {
}
# print "Cond stack: @cond\n";
} else {
- eval_if_ok("$f($a)");
+ my $res = eval_if_ok("$f $a");
+ out $res if defined $res;
}
}
pos $t = 0;
for(;;) {
if ($t =~ /\G([^\@]+)/cgs) {
- print $1 if $cond[0] > 0;
+ out $1 if $cond[0] > 0;
} elsif ($t =~ /\G\@\s*\n/cgs) {
# @ at end of line is ignored and eats the end of line
} elsif ($t =~ /\G\@#[^\n]*\n/cgs) {
# a comment, which is ignored
} elsif ($t =~ /\G\@\@/cgs) {
- print "\@";
+ out "\@" if $cond[0] > 0;
} elsif ($t =~ /\G\@{/cgs) {
my $x = get_nested(\$t, "{", "}");
- print eval_if_ok($x);
+ out eval_if_ok($x);
} elsif ($t =~ /\G\@\[/cgs) {
my $x = get_nested(\$t, '\[', '\]');
$x =~ s/^\[//;
- $x =~ s/$\]//;
+ $x =~ s/\]$//;
eval_if_ok($x);
+ } elsif ($t =~ /\G\@\(/cgs) {
+ my $x = get_nested(\$t, '\(', '\)');
+ $x =~ s/^\(//;
+ $x =~ s/\)$//;
+ out (defined($arguments->{$x}) ? $arguments->{$x} : "");
} elsif ($t =~ /\G\@(\w+)\(/cgs) {
my $func = $1;
my $args = get_nested(\$t, '(', ')');
} elsif ($t =~ /\G\@(\w+)([^\n]*)\n/cgs) {
eval_func($1, "($2)");
} elsif ($t =~ /\G\@(\$\w+)/cgs) {
- print eval_if_ok($1);
+ out eval_if_ok($1);
} elsif ($t =~ /\G(\@[^\n]*)/cgs) {
die "Unknown control sequence $1";
} elsif ($t =~ /\G$/cgs) {
sub parse_file($) {
my ($name) = @_;
+ add_depend($name);
my $fh = new IO::File $name;
die "Unable to open $name: $!" unless defined $fh;
my $text;
parse_string($text);
}
-sub start()
+sub start(;$)
{
+ $arguments = $_[0];
@cond = (1);
}
$#cond and die "Unterminated \@if (depth $#cond)";
}
-sub process_file($) {
- start();
- parse_file($_[0]);
+sub process_file($;$) {
+ my ($name, $args) = @_;
+ start($args);
+ parse_file($name);
finish();
}
-sub process_string($) {
- start();
- parse_string($_[0]);
+sub process_string($;$) {
+ my ($string, $args) = @_;
+ start($args);
+ parse_string($string);
finish();
}
-### Commands available from the templates
+### Perl commands embedded in the templates are evaluated in this package ###
+
+package T;
+
+import UCW::Temple;
+
+our $temp;
+our $out_func = sub { print @_; };
sub include {
my $fn = shift @_;
while (@_) {
my $v = shift @_;
$temp = shift @_;
- eval_if_ok("\$$v = \$temp");
+ UCW::Temple::eval_if_ok("\$$v = \$temp");
}
- parse_file($fn);
+ UCW::Temple::parse_file($fn);
+ return;
}
sub load {
my $f = shift @_;
- (!defined $f || @_) and die "\@load requires only one argument";
+ (!defined($f) || @_) and die "\@load requires only one argument";
+ UCW::Temple::add_depend($f);
require $f;
+ return;
}
1;