**Description**
The engine works by converting a template (which is a string, or a file) into a Tcl script, and then running it. Each line of text encountered will be returned as is. Exception is text between '''<%''' ... '''%>''' which is treated as [Tcl] code (the [eval] happens in a regular [interp]).
***Why not [expand]?***
[expand] is a simple and snugly for Tcl language, be mounted in [tcllib]. You can use it as quick as flash.
But, that library has a problem, when to really use. We cannot write easily following signs
[ ] { }
[DDG] No! We can easily write
[ ] { }
with [expand] if we use alternative brackets for expansion:
(Tclkit) 24 % package require textutil::expander
1.3.1
(Tclkit) 25 % textutil::expander exp
::exp
(Tclkit) 26 % set string {[] {} <% set x %> }
[] {} <% set x %>
(Tclkit) 27 % set x 65
65
(Tclkit) 28 % exp expand $string {<% %>}
[] {} 65
[kanryu] Oh, I didn't know to be able to change brackets with textutil::expander.
Then, there are restricted cases to use this library, maybe.
***Why not [TemplaTcl]?***
[TemplaTcl] is a powerful and cool template engine.
But now, we cannot use it on Tcl8.4 :(
**tmpl_parser is**
a simple and fast template engine. That still not have function to read a file, create a parser object.
But we can use it instant, only write a command '''require''' or '''source'''.
Simple variable substitution is done with "<%= $''variable'' %>" (e.g., Thanks <%=$count%> accesses!).
Simple expression substitution is done with "<%:''Tcl expression''%>" (e.g., <%:$i+2000%>).
**Usage**
First, let's create a template file (templtest.tmpl):
======
<%
for {set i 0} {$i < 4} {incr i} { %>
<%= $i %>
<%
} %>
<% foreach item $cityList {
%>
<%= $item %>
<% } %>
======
The above template (in [HTML] language) generates a 4-row table and an unordered list, taking values from a ''$cityList'' variable that we're going to define. First let's load the code:
======
source tmpl_parser.tcl
======
reading template file.
======
set fd [open templtest.tmpl r]
set tmpl [read $fd]
close $fd
======
now create a parser instance:
======
set parser [::tmpl_parser::tmpl_parser $tmpl]
======
set the variables used in our template:
set cityList {Ragusa Ravenna Rieti Rimini Rome Rovigo}
and render the template:
puts [eval $parser]
Here's the output that gets produced:
======
1
2
3
4
Ragusa
Ravenna
Rieti
Rimini
Rome
Rovigo
======
**tmpl_parser code**
======
# tmpl_parser.tcl
#
# Tcl embeddedd script parser(a template engine)
#
# This module comverts Tcl embedded scripts into a Tcl normal script(parser),
# after you just have to do eval command for the generated parser.
#
# Copyright (c) 2007 by Kanryu KATO
# licensed on Tcl License.
package require Tcl 8.3
package provide tmpl_parser 0.1
namespace eval ::tmpl_parser {
namespace export tmpl_parser
}
proc ::tmpl_parser::tmpl_parser {tmpl} {
# Tcl enbedded tags
# [[outer <%...inner...%> outer]] <-$tmpl
# [ = ] <-$token
# cd ef hi j <-indexes
set parser { {set _o {}} }
while {[set i [string first %> $tmpl]] != -1} {
set h [expr {$i-1}]
set j [expr {$i+2}]
set token [string range $tmpl 0 $h]
set d [string first <% $token]
set c [expr {$d-1}]
set e [expr {$d+2}]
set f [expr {$d+3}]
# outer
lappend parser [escaped_parse [string range $token 0 $c]]
switch [string index $token $e] {
"=" {
# normal expression (e.g., Thanks <%=$count%> accesses!)
lappend parser [normal_parse [string range $token $f end]]
}
":" {
# numeric expression (e.g., <%:$i+2000%>)
lappend parser [numeric_parse [string range $token $f end]]
}
default {
# embedded Tcl command is passed through listing
lappend parser [string range $token $e end]
}
}
# after "%>"
set tmpl [string range $tmpl $j end]
}
#last outer
lappend parser [escaped_parse $tmpl]
lappend parser {join $_o ""}
return [join $parser "\n"]
}
proc ::tmpl_parser::escaped_parse {str} {
set str [string map {\" \\\" \{ \\\{ \} \\\} \\ \\\\} $str]
return "lappend _o \"$str\""
}
proc ::tmpl_parser::normal_parse {str} {
return "lappend _o $str"
}
proc ::tmpl_parser::numeric_parse {str} {
return "lappend _o [expr $str]"
}
======
----
'''[milarepa] - 2014-07-20 17:26:11'''
I been using tmpl_parser to parse a configuration file. So far it works fine. My problem now is that when I insert a variable like this:
======
<%= $variable_name %>
======
Inside that `$variable_name` there is a plain text that containes another tag to process another variable:
======
<%= $variable_inside_a_variable %>
======
I understand when the parser processes `$variable_name` will print `$variable_inside_a_variable` literally without doing the variable substitution.
Is there a way to do exactly that?
----
'''[milarepa] - 2014-07-20 17:35:05'''
I just go it !!!
Put the following lines to parse the variable once more:
======
set variable_inside_a_variable [eval [::tmpl_parser::tmpl_parser $variable_inside_a_variable]]
======
**Another implementation**
[dbohdan] 2014-08-04: I've reimplemented `tmpl_parser` with `[regexp]` and Tcl's built-in quoting used for code generation (I found the original's approach to quoting a bit of a problem when trying to extend it). In this version `<%= ... %>` can be used for both variable substitution and expressions, which are evaluated a run time; parse-time expression evaluation that the original used `<%: ... %>` for is not present. A new tag pair `<%! ... %>` is introduced: `<%! command %>` translates to code to insert the return value of `command` into the output. The motivation is that it is shorter than saying `<%= [ command ] %>` and eliminates unnecessary expression evaluation.
Caveat: I am not sure what is the oldest Tcl version that will run this implementation; it runs in 8.5.
======
# Convert a template into Tcl code.
proc parse {template} {
set result {}
set regExpr {^(.*?)<%(.*?)%>(.*)$}
set listing "set _output {}\n"
while {[regexp $regExpr $template \
match preceding token template]} {
append listing [list append _output $preceding]\n
switch -exact -- [string index $token 0] {
= {
append listing \
[format {append _output [expr %s]} \
[list [string range $token 1 end]]]
}
! {
append listing \
[format {append _output [%s]} \
[string range $token 1 end]]
}
default {
append listing $token
}
}
append listing \n
}
append listing [list append _output $template]\n
return $listing
}
======
The `
` and `
` examples from "Usage" work as is. What follows is a more in-depth example that comes from [Tclssg]. It employs the new `<%! ... %>` feature and generates HTML for use with [Bootstrap].
======
<%
interp-source default-procs.tcl
%>
<%! format-html-title %>
<%! page-var-get-default headExtra "" %>