Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

moved things over from the November project

  • Loading branch information...
commit bb20f94e60512f431f20aec8697a6efaaaf1b796 0 parents
@masak masak authored
51 Makefile.PL
@@ -0,0 +1,51 @@
+#!/usr/bin/perl
+$|++;
+
+use strict;
+use warnings;
+
+use File::Spec;
+use File::HomeDir;
+
+my $parrot_dir = $ENV{PARROT_DIR}
+ or die 'Please set $PARROT_DIR (see README).'."\n";
+
+$ENV{PERL6LIB}
+ or die 'Please set $PERL6LIB (see README).'."\n";
+
+if ( ! -d $parrot_dir ) {
+ print STDERR "Not a directory $parrot_dir, exiting...\n";
+ exit 1;
+} elsif ( ! -x File::Spec->catfile( $parrot_dir, 'parrot' )) {
+ print STDERR "Couldn't find parrot executable in $parrot_dir, "
+ . "have you compiled?";
+ exit 1;
+}
+
+my @infiles = map { $_.'.in' } qw< Makefile test_wiki.sh wiki.sh >;
+
+my %replacements = (
+ PARROT_DIR => $parrot_dir,
+);
+
+if ( !-e 'lib/Test.pm' ) {
+ !system("ln -s $parrot_dir/languages/rakudo/Test.pm lib/") or die @!;
+ print "Symlinked Test.pm from the Rakudo directory \n";
+}
+
+for my $infile (@infiles) {
+ if ((my $outfile = $infile) =~ s/\.in$//g) {
+ open my $IN, '<', $infile or die "Couldn't open $infile, $!, $?";
+ open my $OUT, '>', $outfile or die "Couldn't open $outfile, $!, $?";
+ while (my $line = <$IN>) {
+ while ( $line =~ /<(.*?)>/g ) {
+ my $repl = $1;
+ if (exists $replacements{$repl}) {
+ $line =~ s/<$repl>/$replacements{$repl}/g;
+ }
+ }
+ print $OUT $line;
+ }
+ print "Created $outfile \n";
+ }
+}
17 Makefile.in
@@ -0,0 +1,17 @@
+PARROT_DIR=<PARROT_DIR>
+
+SOURCES=lib/HTML/Template/Grammar.pm lib/HTML/Template.pm
+
+PIRS=$(SOURCES:.pm=.pir)
+
+all: $(PIRS)
+
+%.pir: %.pm
+ $(PARROT_DIR)/parrot $(PARROT_DIR)/languages/rakudo/perl6.pbc --target=pir --output=$@ $<
+
+clean:
+ rm -f $(PIRS)
+
+test: all
+ prove -e '$(PARROT_DIR)/parrot $(PARROT_DIR)/languages/rakudo/perl6.pbc'\
+ -r --nocolor t/
137 lib/HTML/Template.pm
@@ -0,0 +1,137 @@
+class HTML::Template;
+
+use Text::Escape;
+use HTML::Template::Grammar;
+
+has $!in;
+has %!params;
+has %!meta;
+
+method from_string( Str $in ) {
+ return self.new(in => $in);
+}
+
+method from_file($file_path) {
+ return self.from_string( slurp($file_path) );
+}
+
+method param( Pair $param ) {
+ %!params{$param.key} = $param.value;
+}
+
+method with_params( Hash %params ) {
+ %!params = %params;
+ return self;
+}
+
+method output() {
+ return self.substitute( self.parse, %!params );
+}
+
+method parse( $in? ) {
+ # TODO: If used .parse fall tests, fix it!
+ ($in || $!in) ~~ /<HTML::Template::Grammar::TOP>/;
+ die("No match") unless $/;
+ return $/<HTML::Template::Grammar::TOP><contents>;
+}
+
+method substitute( $contents, %params ) {
+ my $output = ~$contents<plaintext>;
+
+ for ($contents<chunk> // ()) -> $chunk {
+
+ if $chunk<directive><insertion> -> $i {
+ my $key = ~$i<attributes><name>;
+
+ my $value;
+ if (defined %params{$key}) {
+ $value = %params{$key};
+ } else {
+ $value = %!params{$key};
+ }
+
+ # RAKUDO: Scalar type not implemented yet
+ warn "Param $key is a { $value.WHAT }" unless $value ~~ Str | Int;
+
+ if $i<attributes><escape> {
+ my $et = ~$i<attributes><escape>[0];
+ # RAKUDO: Segfault here :(
+ #$value = escape($value, $et);
+ if $et eq 'HTML' {
+ $value = escape($value, 'HTML');
+ }
+ elsif $et eq 'URL' | 'URI' {
+ $value = escape($value, 'URL');
+ }
+
+ }
+ $output ~= ~$value;
+ }
+ elsif $chunk<directive><if_statement> -> $if {
+ my $cond;
+ if $if<attributes><name><lctrls> -> $lc {
+ if %!meta<loops><current> -> $c {
+ if $lc<lc_last> {
+ $cond = ?($c<elems> == $c<iteration>);
+ }
+ elsif $lc<lc_first> {
+ $cond = ?($c<iteration> == 1);
+ }
+ }
+ }
+ else {
+ $cond = %params{~$if<attributes><name>};
+ }
+
+ if $cond {
+ $output ~= self.substitute(
+ $if<contents>,
+ %params
+ );
+ }
+ elsif $if<else> {
+ $output ~= self.substitute(
+ $if<else>[0],
+ %params
+ );
+ }
+ }
+ elsif $chunk<directive><for_statement> -> $for {
+ my $key = ~$for<attributes><name><val>;
+
+ my $iterations = %params{$key};
+
+ # RAKUDO: Rakudo doesn't understand autovivification of multiple
+ # hash indices %!meta<loops><current> = $key; [perl #61740]
+ %!meta<loops> = {} unless defined %!meta<loops>;
+
+ # that will fail on nested same-named loops... hm
+ %!meta<loops>{$key} = {elems => $iterations.elems, iteration => 0};
+ %!meta<loops><current> = %!meta<loops>{$key};
+
+ for $iterations.values -> $iteration {
+ %!meta<loops>{$key}<iteration>++;
+ $output ~= self.substitute(
+ $for<contents>,
+ $iteration
+ );
+ }
+ }
+ elsif $chunk<directive><include> {
+ my $file = ~$chunk<directive><include><attributes><name><val>;
+ %params<TMPL_PATH> = '' if not defined %params<TMPL_PATH>;
+ $file = %params<TMPL_PATH> ~ $file;
+ if $file ~~ :e {
+ $output ~= self.substitute(
+ self.parse( slurp($file) ),
+ %params
+ );
+ }
+ }
+
+ $output ~= ~$chunk<plaintext>;
+ }
+ return $output;
+}
+
+# vim:ft=perl6
48 lib/HTML/Template/Grammar.pm
@@ -0,0 +1,48 @@
+use v6;
+# RAKUDO: It is uncertain whether a 'grammar' keyword can start a file, just
+# like 'class' can.
+
+grammar HTML::Template::Grammar {
+ regex TOP { ^ <contents> $ };
+
+ regex contents { <plaintext> <chunk>* };
+ regex chunk { <directive> <plaintext> };
+ regex plaintext { [ <!before '<TMPL_' ><!before '</TMPL_' >. ]* };
+
+ token directive {
+ | <insertion>
+ | <if_statement>
+ | <for_statement>
+ | <include>
+ };
+
+ regex insertion {
+ <.tag_start> 'VAR' <attributes> '>'
+ };
+
+ regex if_statement {
+ <.tag_start> 'IF' <attributes> '>'
+ <contents>
+ [ '<TMPL_ELSE>' <else=contents> ]?
+ '</TMPL_IF>'
+ };
+
+ regex for_statement {
+ <.tag_start> [ 'FOR' | 'LOOP' ] <attributes> '>'
+ <contents>
+ '</TMPL_' [ 'FOR' | 'LOOP' ] '>'
+ };
+
+ regex include {
+ <.tag_start> 'INCLUDE' <attributes> '>'
+ };
+
+ token tag_start { '<TMPL_' };
+ token attributes { \s+ 'NAME='? <name> [\s+ 'ESCAPE=' <escape> ]? };
+ token name { $<val>=\w+ | <lctrls> | [<.qq> $<val>=[ <[ 0..9 '/._' \- \\ ] +alpha>* ] <.qq>] };
+ regex qq { '"' };
+ token lctrls { <lc_last> | <lc_first> };
+ regex lc_last { '!LAST' };
+ regex lc_first { '!FIRST' };
+ token escape { 'NONE' | 'HTML' | 'URL' | 'URI' | 'JS' | 'JAVASCRIPT' };
+};
166 t/01.t
@@ -0,0 +1,166 @@
+use v6;
+
+use Test;
+plan 28;
+
+use HTML::Template;
+
+my @inputs_that_should_parse =
+ [ 'foo', {},
+ 'foo', 'plain text' ],
+
+ [ qq|<body>plain <a href="link">link</a>\n text</body>|, {},
+ qq|<body>plain <a href="link">link</a>\n text</body>|, 'simple HTML' ],
+
+ [ 'pre<TMPL_VAR NAME=BAR>post', { 'BAR' => 50 },
+ 'pre50post', 'simple variable insertion' ],
+
+ [ 'pre<title><TMPL_VAR NAME=BAR></title>post', { 'BAR' => 50 },
+ 'pre<title>50</title>post', 'simple variable insertion in html' ],
+
+ [ 'pre<TMPL_VAR BAR ESCAPE=NONE>post', { 'BAR' => 'YaY' },
+ 'preYaYpost', 'variable insertion with NONE escape' ],
+
+ [ 'pre<TMPL_VAR BAR ESCAPE=HTML>post', { 'BAR' => '<' },
+ 'pre&lt;post', 'variable insertion with HTML escape' ],
+
+ [ 'pre<TMPL_VAR BAR ESCAPE=URL>post', { 'BAR' => ' ' },
+ 'pre+%20+post', 'variable insertion with URL escape' ],
+
+ [ 'pre<TMPL_VAR BAR ESCAPE=URI>post', { 'BAR' => ' ' },
+ 'pre+%20+post', 'variable insertion with URI escape' ],
+
+ [ 'pre<TMPL_VAR NAME=BAR>between<TMPL_VAR NAME=BAZ>post',
+ { 'BAR' => '!', 'BAZ' => '!!' },
+ 'pre!between!!post', 'two variable insertions' ],
+
+ # 10
+ [ 'pre<TMPL_IF NAME=FOO>bar</TMPL_IF>post', { 'FOO' => 1 },
+ 'prebarpost', 'true condition' ],
+
+ [ 'pre<TMPL_IF NAME=FOO>bar</TMPL_IF>post', { 'FOO' => 0 },
+ 'prepost', 'false condition (because the parameter was false)' ],
+
+ [ 'pre<TMPL_IF NAME=FOO>bar</TMPL_IF>post', {},
+ 'prepost', 'false condition (because the parameter was not declared)' ],
+
+ [ 'pre<TMPL_FOR NAME=BLUBB>[<TMPL_VAR FOO>]</TMPL_FOR>post',
+ { 'BLUBB' => [ { 'FOO' => 'a' }, { 'FOO' => 'b' }, { 'FOO' => 'c' } ] },
+ 'pre[a][b][c]post', 'a simple for loop' ],
+
+ [ 'pre<TMPL_IF NAME=BLUBB><TMPL_FOR NAME=BLUBB>[<TMPL_VAR FOO>]</TMPL_FOR></TMPL_IF>post',
+ { 'BLUBB' => [ { 'FOO' => 'a' }, { 'FOO' => 'b' }, { 'FOO' => 'c' } ] },
+ 'pre[a][b][c]post', 'a simple for loop in if' ],
+
+ [ 'pre<TMPL_FOR NAME=BLUBB>[<TMPL_VAR FOO>]</TMPL_FOR>post',
+ { 'BLUBB' => [] },
+ 'prepost', 'an empty for loop' ],
+
+ # this doesn't work in p5 version by default, but it's useful for us,
+ # and it DWIMs
+ [ 'pre<TMPL_FOR NAME=FOO>[<TMPL_VAR a><TMPL_VAR BAR>]</TMPL_FOR>post',
+ { FOO => [{a => 1}, {a => 2}], BAR => 'Y' },
+ 'pre[1Y][2Y]post', 'an empty for loop' ],
+
+ [ '<TMPL_IF NAME=FOO>a<TMPL_IF NAME=BAR>b</TMPL_IF>c</TMPL_IF>',
+ { 'FOO' => 1 },
+ 'ac',
+ 'nested if directives, inner one false' ],
+
+ [ '<TMPL_IF NAME=FOO>a<TMPL_IF NAME=BAR>b</TMPL_IF>c</TMPL_IF>',
+ { 'FOO' => 1, BAR => 1 },
+ 'abc',
+ 'nested if directives, all true' ],
+
+ [ '<TMPL_IF NAME=FOO>a<TMPL_VAR NAME=FOO>c</TMPL_IF>',
+ { 'FOO' => 'YaY' },
+ 'aYaYc',
+ 'if derictives and insertion directive' ],
+
+ # 20
+ [
+ '<TMPL_FOR FOO><TMPL_IF BAR><TMPL_VAR BAR></TMPL_IF></TMPL_FOR>',
+ { FOO => [ { 'BAR' => '1' }, {}, { 'BAR' => '3' } ] },
+ '13',
+ 'an if inside a for, with the condition set only sometimes' ],
+
+ [ '<TMPL_FOR FOO>[<TMPL_FOR BAR><TMPL_VAR VAL></TMPL_FOR>]</TMPL_FOR>',
+ { 'FOO' => [ { 'BAR' => [ map { { 'VAL' => "a$_" } }, 1..3 ] },
+ { 'BAR' => [ map { { 'VAL' => "b$_" } }, 1..4 ] },
+ { 'BAR' => [ map { { 'VAL' => "c$_" } }, 1..2 ] } ] },
+ '[a1a2a3][b1b2b3b4][c1c2]',
+ 'nested for loops' ],
+
+ [ 'pre <TMPL_IF A>a<TMPL_ELSE>b</TMPL_IF>c<TMPL_VAR D> post',
+ { 'A' => 1, 'D' => 'd' },
+ 'pre acd post',
+ 'true if/else followed by a variable insertion' ],
+
+ [ 'pre <TMPL_IF A>a<TMPL_ELSE>b</TMPL_IF>c<TMPL_VAR D> post',
+ { 'A' => 0, 'D' => 'd' },
+ 'pre bcd post',
+ 'false (but defined) if/else followed by a variable insertion' ],
+
+ [ 'pre <TMPL_IF A>a<TMPL_ELSE>b</TMPL_IF>c<TMPL_VAR d> post',
+ { 'd' => 'd' },
+ 'pre bcd post',
+ 'false (undefined) if/else followed by a variable insertion' ],
+
+ [ 'pre<TMPL_INCLUDE NAME="t/test-templates/2.tmpl">post',
+ { 'FOO' => 'bar' },
+ "pre<h1>bar</h1>\npost",
+ 'include template' ],
+
+ [ '<TMPL_LOOP NAME=FOO>:)</TMPL_LOOP>',
+ { 'FOO' => [ { :BAR } ] },
+ ":)",
+ 'we can use TMPL_LOOP as TMPL_FOR' ],
+;
+
+my @inputs_that_should_not_parse = (
+ [ 'pre<TMPL_IF NAME=YUCK>no tmpl_if',
+ { 'YUCK' => 1 },
+ 'an if directive without a closing tag' ],
+);
+
+for @inputs_that_should_parse -> $test {
+ # RAKUDO: List assignment not implemented yet
+ my $input = $test[0];
+ my $parameters = $test[1];
+ my $expected_output = $test[2];
+ my $description = $test[3];
+
+ # RAKUDO: Break this line with long dots.
+ my $actual_output
+ = HTML::Template.from_string($input).with_params(
+ $parameters).output();
+
+ is( $actual_output, $expected_output, $description );
+}
+
+for @inputs_that_should_not_parse -> $test {
+ # RAKUDO: List assignment not implemented yet
+ my $input = $test[0];
+ my $parameters = $test[1];
+ my $description = $test[2];
+
+ dies_ok( { HTML::Template.from_string($input).with_params(
+ $parameters).output() }, $description );
+}
+
+my $output = HTML::Template.from_file( 't/test-templates/1.tmpl' ).with_params(
+ { 'TITLE' => 'Mmm, pie' } ).output();
+
+is( $output, (join "\n",
+ '<html>',
+ ' <head>',
+ ' <title>Mmm, pie</title>',
+ ' </head>',
+ ' <body>',
+ ' <h1>Mmm, pie</h1>',
+ ' </body>',
+ '</html>',
+ ''),
+ 'reading from file' );
+
+# vim:ft=perl6
61 t/08-include.t
@@ -0,0 +1,61 @@
+use v6;
+
+use Test;
+plan 4;
+
+use HTML::Template;
+our $*WARNINGS = 1;
+
+my @tests =
+ [ 't/test-templates/a.tmpl',
+ {},
+ "AAA\n",
+ 'no include'],
+
+ [ 't/test-templates/b.tmpl',
+ {},
+ "BBB\nCCC\n\n",
+ 'include one file'],
+
+ [ 't/test-templates/bb.tmpl',
+ {},
+ "BBB\nCCC\n\n",
+ 'include one file'],
+
+# [ 't/test-templates/bbb.tmpl',
+# {},
+# "BBB\nCCC\n\n",
+# 'include one file'], # TODO is "" really required around the file name?
+
+# [ 't/test-templates/d.tmpl',
+# {},
+# "BBB\nCCC\n\n",
+# 'recursive include'], # TODO should not blow up...
+
+# [ 't/test-templates/e.tmpl',
+# {},
+# "EEE",
+# 'missing include file'], # TODO needs warning or exception
+
+ [ 't/test-templates/page.tmpl',
+ { HEADER => "head", FOOTER => "foot"},
+ "head\n\nbody\nfoot\n\n",
+ 'include two files'],
+;
+
+for @tests -> $t {
+ my $file = $t[0];
+ my $parameters = $t[1];
+ my $expected_output = $t[2];
+ my $description = $t[3];
+
+ #diag $file;
+ my $actual_output
+ #= HTML::Template.from_file($file).output();
+ = HTML::Template.from_file($file).with_params(
+ $parameters).output();
+ #diag "'$actual_output'";
+ is( $actual_output, $expected_output, $description );
+}
+
+
28 t/09-loop-control.t
@@ -0,0 +1,28 @@
+use v6;
+
+use Test;
+plan 2;
+
+use HTML::Template;
+
+{
+ my $data = (FOO => [ {IN => 'a'}, {IN => 'b'}, {IN => 'c'} ]);
+ my $out = HTML::Template.from_string(
+ '<TMPL_LOOP NAME=FOO><TMPL_VAR NAME=IN>'
+ ~ '<TMPL_IF NAME=!LAST>.<TMPL_ELSE>, </TMPL_IF></TMPL_LOOP>'
+ ).with_params(hash $data).output;
+ my $expected = 'a, b, c.';
+ is($out, $expected, '!LAST returns true on the last iteration');
+}
+
+{
+ my $data = (FOO => [ {IN => 'a'}, {IN => 'b'}, {IN => 'c'} ]);
+ my $out = HTML::Template.from_string(
+ '<TMPL_LOOP NAME=FOO><TMPL_VAR NAME=IN>'
+ ~ '<TMPL_IF NAME=!FIRST>:<TMPL_ELSE>-</TMPL_IF></TMPL_LOOP>'
+ ).with_params(hash $data).output;
+ my $expected = 'a:b-c-';
+ is($out, $expected, '!FIRST returns true on the first iteration');
+}
+
+# vim:ft=perl6
Please sign in to comment.
Something went wrong with that request. Please try again.