diff options
Diffstat (limited to 'web/server/h2o/libh2o/misc/picotemplate')
-rw-r--r-- | web/server/h2o/libh2o/misc/picotemplate/README.mkdn | 42 | ||||
-rwxr-xr-x | web/server/h2o/libh2o/misc/picotemplate/picotemplate.pl | 172 |
2 files changed, 214 insertions, 0 deletions
diff --git a/web/server/h2o/libh2o/misc/picotemplate/README.mkdn b/web/server/h2o/libh2o/misc/picotemplate/README.mkdn new file mode 100644 index 000000000..266f4bfa5 --- /dev/null +++ b/web/server/h2o/libh2o/misc/picotemplate/README.mkdn @@ -0,0 +1,42 @@ +PICOTEMPLATE - a tiny template engine for embedded HTML +======================================================= + +Picotemplate is a tiny template engine (preprocessor) designed to embed HTML (or other types of document) within the source code of any programming language. + +There are often cases where you would like to generate HTML within a program. In such cases, the easiest way would be to use a DSL (domain-specific language) to embed the HTML in the source code and preprocess it. Picotemplate is a preprocesser designed for such an use-case. + +As of now, Perl, C++, JavaScript, and JSX are the supported languages. + +THE LOGIC +--------- + +* lines starting with "?" are considered as embedded document +* <?= ?> within embedded document are considered as expressions (that return strings) +* the output is accumulated to variable named "_" (or $output in case of perl) +* filenames starting with an underscore (_) will be preprocessed, and the name of the output file will be the same omitting the leading underscore (e.g. _foo.cc will be preproccessed and converted to foo.cc) + +EXAMPLE +------- + +The following code (in _foo.cc) will be preprocessed and converted to a function (in foo.cc) that returns an function building an HTML snippet. + +``` +std::string unordered_list(const std::vector<std::string>& strs) +{ + std::string _; // output is accumulated to _ +?<ul> + for (std::vector<std::string>::const_iterator i = strs.begin(); + i != strs.end(); + ++i) { +?<li><?= escapeHTML(*i) ?></li> + } +?</ul> + return _; +} +``` + +To preprocess a source file, simply run the command with the name of the source file to preprocess. The following exmaple preprocesses _foo.cc_ (template files should start with an underscore) and produces _foo.cc_. + +``` +$ picotemplate.pl _foo.cc +``` diff --git a/web/server/h2o/libh2o/misc/picotemplate/picotemplate.pl b/web/server/h2o/libh2o/misc/picotemplate/picotemplate.pl new file mode 100755 index 000000000..c49bf858f --- /dev/null +++ b/web/server/h2o/libh2o/misc/picotemplate/picotemplate.pl @@ -0,0 +1,172 @@ +#! /usr/bin/perl + +# Copyright (c) 2012-2014 Kazuho Oku +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +use strict; +use warnings; +use Getopt::Long; + +my ($opt_conf, $opt_help); +GetOptions( + "conf=s" => \$opt_conf, + help => \$opt_help, +) or exit 1; + +if ($opt_help) { + print << "EOT"; +Usage: + $0 [--conf=<script-file>] <src-file> + +Description: + Applies necessary conversions against <src-file> (the name should start + with an underscore) and emits the result. The result is stored in a file + which is given the same name as <src-file> omitting the preceeding + underscore. + +Options: + --conf=<script-file> if set, reads configuration from <script-file>, + otherwise the configuration is taken from the + extension of <src-file> + +EOT + exit 0; +} + +my $srcfn = shift @ARGV; +die "no filename" + unless $srcfn; + +our ($push_expr, $push_void_expr, $push_str); + +if ($opt_conf) { + require $opt_conf; +} else { + ($push_expr, $push_void_expr, $push_str) = convfunc($srcfn); +} + +$srcfn =~ m{^(.*/)_([^/]+)$} + or die "filename should start with an underscore"; +my $dstfn = "$1$2"; + +open my $srcfh, "<", $srcfn + or die "failed to open file:$srcfn"; +open my $dstfh, ">", $dstfn + or die "failed to open file:$dstfn"; + +my $pending = ''; +my $flush_pending = sub { + return if $pending eq ''; + print $dstfh $push_str->($pending), map { "\n" } @{[ $pending =~ /\n/g ]}; + $pending = ''; +}; +while (my $line = <$srcfh>) { + if ($line =~ /^\?/) { + my $markup = $'; + for my $token (split /(<\?[=!].*?\?>)/, $markup) { + if ($token =~ /^\<\?([=!])\s*(.*?)\s*\?>$/) { + $flush_pending->(); + if ($1 eq '=') { + print $dstfh $push_expr->($2); + } else { + print $dstfh $push_void_expr->($2); + } + } elsif ($token ne "") { + $pending .= $token; + } + } + } else { + $flush_pending->(); + print $dstfh "$line"; + } +} +$flush_pending->(); + +close $srcfh; +close $dstfh; + +sub convfunc { + my $srcfn = shift; + if ($srcfn =~ /\.pl$/) { + # perl + return ( + # push_expr function + sub { + my $src = shift; + return qq{\$_ = ($src); s/\\n\$//; \$output .= \$_;}; + }, + # push_void_expr function + sub { + my $src = shift; + return qq{do { $src };}; + }, + # push_str function + sub { + my $str = shift; + $str = quotemeta $str; + return qq{\$output .= "$str";}; + }, + ); + } elsif ($srcfn =~ /\.(?:cc|cpp|cxx)$/) { + # C++ + return ( + # push_expr function + sub { + my $src = shift; + return qq^{std::string _r; _r += ($src); if (! _r.empty() && *(_r.end() - 1) == '\\n') _r.erase(_r.size() - 1); _ += _r;}^; + }, + # push_void_expr function + sub { + my $src = shift; + return qq{{std::string::size_type __picotemplate_l = _.size(); { $src; } if (__picotemplate_l != _.size() && *(_.end() - 1) == '\\n') _.erase(_.size() - 1);}}; + }, + # push_str function + sub { + my $str = shift; + $str =~ s/([\\'"])/\\$1/gs; + $str =~ s/\n/\\n/gs; + return qq{_ += ("$str");}; + }, + ); + } elsif ($srcfn =~ /\.jsx?$/) { + # JavaScript or JSX + return ( + # push_expr function + sub { + my $src = shift; + return qq{_ += ($src).replace(/\\n\$/, "");}; + }, + # push_void_expr function + sub { + my $src = shift; + return qq{{ $src; }}; + }, + # push_str function + sub { + my $str = shift; + $str =~ s/([\\'"])/\\$1/gs; + $str =~ s/\n/\\n/gs; + return qq{_ += "$str";}; + }, + ); + } else { + die "unknown filetype!"; + } +} |