Permalink
Browse files

Insert, include and process statements.

  • Loading branch information...
1 parent 6c06e70 commit b9a31d6243277e2ba6b440ee49c0e3ac4f5718c9 Timothy Totten committed Jul 18, 2012
View
35 README.md
@@ -4,46 +4,62 @@ Inspired by Template Toolkit from Perl 5,
Template6 is a simple template engine designed to be
a content-neutral template language.
-Eventually, I intend for this to be as powerful as
-Template Toolkit, but for now I'm keeping it simple
-and leaving it capable of easily adding new functionality.
-
I also intend to borrow features and ideas from
my own Flower and Garden projects.
+This project does not intend to create an exact clone of
+Template Toolkit. Some features from TT are not planned for
+inclusion, and likewise, some feature will be included that
+are not in TT. Not all features will work the same either.
+
## Currently implemented features
-* get and set statements, including implicit versions.
+* GET and SET statements, including implicit versions.
* [% get varname %]
* [% varname %]
* [% set varname = value %]
* [% varname = value %]
-* for statement.
+* FOR statement.
This replaces the FOREACH statement in TT2.
It can be used in one of four ways:
+
* [% for listname as itemname %]
* [% for listname -> itemname %]
* [% for itemname in listname %]
* [% for itemname = listname %]
+
If used with Hashes, you'll need to query the .key or .value accessors.
-* if/elsif/else/unless statements.
+* IF/ELSIF/ELSE/UNLESS statements.
These are very simplistic at the moment, but work for basic tests.
* Querying nested data structures using a simple dot operator syntax.
-* call and default statements.
+* CALL and DEFAULT statements.
+
+## Differences with Template Toolkit
+
+ * You should use explicit quotes, including in INSERT/INCLUDE/PROCESS directives.
+ * All statement directives are case insensitive.
+ * There are no plans for the INTERPOLATE option/style.
+ * Anything not yet implemented (see TODO below.)
+
+## Caveats
+
+ * Whitespace control is not implemented, so some things are fairly odd. See TODO.
+ * A lot of little nit-picky stuff, likely related to the whitespace issue.
## TODO
### Short Term Goals
- * insert, include, process and wrapper statements
+ * WRAPPER statement
* block statements
* given/when statements
+ * Add 'absolute' and 'relative' options to Template6::Provider::File
### Medium Term Goals
@@ -54,6 +70,7 @@ my own Flower and Garden projects.
### Long Term Goals
+ * Variable interpolation (in strings, variable names, etc.)
* Capture of directive output
* Directive comments
* Side-effect notation
View
42 lib/Template6/Context.pm6
@@ -6,9 +6,9 @@ use Template6::Parser;
use Template6::Stash;
use Template6::Provider::File;
-has $.service;
-has $.parser;
-has $.stash;
+has $.service; ## The parent Service object.
+has $.parser; ## Our Parser object.
+has $.stash is rw; ## Our Stash object.
has %.blocks is rw; ## Currently known blocks.
has @.block-cache; ## Known block tree (for nested contexts.)
@@ -60,33 +60,39 @@ method set-extension ($ext) {
}
}
-method get-template ($name) {
- my $shortname = $name;
- if %.blocks.exists($name) {
- return %.blocks{$name};
- }
- for @.block-cache -> $known-blocks {
- if $known-blocks.exists($name) {
- return $known-blocks{$name};
- }
- }
+method get-template-text ($name is copy) {
my $prefix;
my @providers;
- if $shortname ~~ s/^(\w+)'::'// {
+ if $name ~~ s/^(\w+)'::'// {
$prefix = $0;
}
if $prefix.defined && %!providers.exists{$prefix} {
@providers = %!providers{$prefix};
}
else {
- @providers = %!providers.values;
+ @providers = @(%!providers.values);
}
my $template;
for @providers -> $provider {
- $template = $provider.fetch($shortname);
+ $template = $provider.fetch($name);
if $template.defined { last; }
}
+ return $template;
+}
+
+method get-template-block ($name) {
+ if %.blocks.exists($name) {
+ return %.blocks{$name};
+ }
+ for @.block-cache -> $known-blocks {
+ if $known-blocks.exists($name) {
+ return $known-blocks{$name};
+ }
+ }
+
+ my $template = self.get-template-text($name);
+
## If we have a template, store it.
if $template.defined {
if ($template !~~ Callable) {
@@ -100,7 +106,7 @@ method get-template ($name) {
method process ($name, :$localise=False, *%params) {
my $template = $name;
if $template ~~ Str {
- $template = self.get-template($template);
+ $template = self.get-template-block($template);
if !$template.defined { die "Invalid template '$name'"; }
}
if $localise {
@@ -121,7 +127,7 @@ method process ($name, :$localise=False, *%params) {
}
method localise (*%params) {
- $.stash = $.stash.make-clone(%params);
+ $.stash = $.stash.make-clone(|%params);
}
method delocalise {
View
77 lib/Template6/Parser.pm6
@@ -2,7 +2,72 @@ use v6;
class Template6::Parser;
-has @.keywords = 'eq', 'ne', 'lt', 'gt', 'gte', 'lte';
+has @!keywords = 'eq', 'ne', 'lt', 'gt', 'gte', 'lte';
+has $.context;
+
+## Incomplete method, supply the $localline which must define a variable called $template
+method !parse-template (@defs is copy, $localline)
+{
+ my $return = '';
+ my $parsing-templates = True;
+ my @templates;
+
+ while $parsing-templates && @defs.elems {
+ @templates.push: @defs.shift;
+ if @defs.elems && @defs[0] eq '+' {
+ @defs.shift;
+ }
+ else {
+ $parsing-templates = False;
+ }
+ }
+
+ $return ~= "my \%localdata;\n";
+ while @defs.elems >= 3 {
+ my $name = @defs.shift;
+ my $op = @defs.shift;
+ my $value = @defs.shift;
+ if ($value ~~ /\"(.*?)\"|\'(.*?)\'/) {
+ my $string = ~$0;
+ $return ~= "\%localdata<$name> = '$string';\n";
+ }
+ elsif ($value ~~ /^\d+[\.\d+]?$/) {
+ my $number = +$value;
+ $return ~= "\%localdata<$name> = $number;\n";
+ }
+ else {
+ $return ~= "\%localdata<$name> = \$stash.get('$value');\n";
+ }
+ }
+
+ for @templates -> $template is rw {
+ $template ~~ s/^[\"|\']//;
+ $template ~~ s/[\"|\']$//;
+ $return ~= "\{\n my \$tfile = \$stash.get('$template');\n";
+ $return ~= $localline;
+ $return ~= "if \$template.defined \{ \$output ~= \$template; \}\n";
+ $return ~= "\}\n";
+ }
+ return $return;
+}
+
+method parse-insert (*@defs)
+{
+ my $localline = 'my $template = $context.get-template-text($tfile);'~"\n";
+ return self!parse-template(@defs, $localline);
+}
+
+method parse-include (*@defs)
+{
+ my $localline = 'my $template = $context.process($tfile, :localise, |%localdata);'~"\n";
+ return self!parse-template(@defs, $localline);
+}
+
+method parse-process (*@defs)
+{
+ my $localline = 'my $template = $context.process($tfile, |%localdata);'~"\n";
+ return self!parse-template(@defs, $localline);
+}
method parse-get ($name) {
return "\$output ~= \$stash.get('$name');";
@@ -15,8 +80,8 @@ method parse-call ($name) {
method parse-set (:$default, *@values is copy) {
my $return = '';
while @values.elems >= 3 {
- my $name = @values.shift;
- my $op = @values.shift;
+ my $name = @values.shift;
+ my $op = @values.shift;
my $value = @values.shift;
if $default {
$return ~= "if ! \$stash.get('$name', :strict) \{\n";
@@ -63,7 +128,7 @@ method parse-for ($left, $op, $right) {
method !parse-conditional ($name, @stmts is copy) {
for @stmts -> $stmt is rw {
- if @.keywords.grep($stmt) { next; }
+ if @!keywords.grep($stmt) { next; }
if $stmt ~~ /^\d+$/ { next; }
$stmt ~~ s/^(\w+)$/\$stash.get('$0')/;
}
@@ -86,6 +151,10 @@ method parse-elsif (*@stmts) {
return $function;
}
+method parse-elseif (*@stmts) {
+ return self.parse-elsif(|@stmts);
+}
+
method parse-else {
return "\n\}\nelse \{\n";
}
View
40 t/05-includes.t
@@ -0,0 +1,40 @@
+use v6;
+
+BEGIN { @*INC.unshift: './lib'; }
+
+use Test;
+use Template6;
+
+plan 4;
+
+my $t6 = Template6.new;
+$t6.add-path: 't/templates';
+
+my $wanted = "<html>
+<head>
+<title>Hello World</title>
+</head>
+<body>
+<h1>Hello World</h1>
+</body>
+</html>
+";
+
+is $t6.process('insert'), $wanted, 'INSERT statement';
+is $t6.process('include', :name<World>), $wanted, 'INCLUDE statement';
+is $t6.process('include2'), $wanted, 'INCLUDE statement with local template data';
+
+$wanted = "<html>
+<head>
+<title>Hello World</title>
+</head>
+<body>
+<h1>Hello Universe</h1>
+<h2>That's right, it changed to Universe</h2>
+</body>
+</html>
+";
+
+
+is $t6.process('process', :name<World>), $wanted, 'PROCESS statement';
+
View
8 t/templates/include.tt
@@ -0,0 +1,8 @@
+<html>
+<head>
+<title>Hello [% name %]</title>
+</head>
+<body>
+[% INCLUDE "included" %]
+</body>
+</html>
View
8 t/templates/include2.tt
@@ -0,0 +1,8 @@
+<html>
+<head>
+<title>Hello World</title>
+</head>
+<body>
+[% INCLUDE "included" name = "World" %]
+</body>
+</html>
View
2 t/templates/included.tt
@@ -0,0 +1,2 @@
+
+<h1>Hello [% name %]</h1>
View
8 t/templates/insert.tt
@@ -0,0 +1,8 @@
+<html>
+<head>
+<title>Hello World</title>
+</head>
+<body>
+[% INSERT "inserted" %]
+</body>
+</html>
View
2 t/templates/inserted.tt
@@ -0,0 +1,2 @@
+
+<h1>Hello World</h1>
View
9 t/templates/process.tt
@@ -0,0 +1,9 @@
+<html>
+<head>
+<title>Hello [% name %]</title>
+</head>
+<body>
+[% PROCESS "processed" %]
+<h2>That's right, it changed to [% name %]</h2>
+</body>
+</html>
View
2 t/templates/processed.tt
@@ -0,0 +1,2 @@
+[% name = "Universe" %]
+<h1>Hello [% name %]</h1>

0 comments on commit b9a31d6

Please sign in to comment.