"{{" and "}}"; all text outside actions is copied to the output unchanged.
Actions may not span newlines.
+Once constructed, templates and template sets can be executed safely in
+parallel.
+
Actions
Here is the list of actions. "Arguments" and "pipelines" are evaluations of
Variables
-A pipeline may initialize a single variable to capture the result. The
-initialization has syntax
+A pipeline inside an "if" or "with" action may initialize a variable to capture
+the result. The initialization has syntax
$variable := pipeline
where $variable is the name of the variable.
-The one exception is a pipeline in a range action; in ranges, the variable is
-set to the successive elements of the iteration. Also, a range may declare two
+If a "range" action initializes a variable, the variable is set to the
+successive elements of the iteration. Also, a "range" may declare two
variables, separated by a comma:
$index, $element := pipeline
-In this case $index and $element are set to the successive values of the
+in which case $index and $element are set to the successive values of the
array/slice index or map key and element, respectively. Note that if there is
only one variable, it is assigned the element; this is opposite to the
convention in Go range clauses.
+A variable's scope extends to the "end" action of the control structure
+declaring it.
+
When execution begins, $ is set to the data argument passed to Execute, that is,
to the starting value of dot.
}
t.backup()
defer t.popVars(len(t.vars))
- return newAction(t.lex.lineNumber(), t.pipeline("command"))
+ return newAction(t.lex.lineNumber(), t.pipeline("command", false))
}
// Pipeline:
// field or command
// pipeline "|" pipeline
-func (t *Template) pipeline(context string) (pipe *pipeNode) {
+func (t *Template) pipeline(context string, allowDecls bool) (pipe *pipeNode) {
var decl []*variableNode
// Are there declarations?
for {
if len(variable.ident) != 1 {
t.errorf("illegal variable in declaration: %s", v.val)
}
+ if !allowDecls {
+ t.errorf("variable %q declared but cannot be referenced", v.val)
+ }
decl = append(decl, variable)
t.vars = append(t.vars, v.val)
if next.typ == itemChar && next.val == "," {
func (t *Template) parseControl(context string) (lineNum int, pipe *pipeNode, list, elseList *listNode) {
lineNum = t.lex.lineNumber()
defer t.popVars(len(t.vars))
- pipe = t.pipeline(context)
+ pipe = t.pipeline(context, true)
var next node
list, next = t.itemList(false)
switch next.typ() {
if t.next().typ != itemRightDelim {
t.backup()
defer t.popVars(len(t.vars))
- pipe = t.pipeline("template")
+ pipe = t.pipeline("template", false)
}
return newTemplate(t.lex.lineNumber(), name, pipe)
}
"[(action: [(command: [I=printf S=`%d` N=23])])]"},
{"pipeline", "{{.X|.Y}}", noError,
`[(action: [(command: [F=[X]]) (command: [F=[Y]])])]`},
- {"pipeline with decl", "{{$x := .X|.Y}}", noError,
- `[(action: [V=[$x]] := [(command: [F=[X]]) (command: [F=[Y]])])]`},
{"declaration", "{{.X|.Y}}", noError,
`[(action: [(command: [F=[X]]) (command: [F=[Y]])])]`},
{"simple if", "{{if .X}}hello{{end}}", noError,
{"invalid punctuation", "{{printf 3, 4}}", hasError, ""},
{"multidecl outside range", "{{with $v, $u := 3}}{{end}}", hasError, ""},
{"too many decls in range", "{{range $u, $v, $w := 3}}{{end}}", hasError, ""},
+ {"useless declaration", "{{$x := .X|.Y}}", hasError, ""},
}
func TestParse(t *testing.T) {