Commit 331840f5 authored by Rob Pike's avatar Rob Pike

exp/template: allow declaration of variables only inside control structures.

In simple pipelines the declaration has no scope.
Also document the scope.

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4761044
parent e07c6e6e
......@@ -18,6 +18,9 @@ The input text for a template is UTF-8-encoded text in any format.
"{{" 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
......@@ -150,24 +153,27 @@ Execute.
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.
......
......@@ -696,13 +696,13 @@ func (t *Template) action() (n node) {
}
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 {
......@@ -714,6 +714,9 @@ func (t *Template) pipeline(context string) (pipe *pipeNode) {
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 == "," {
......@@ -750,7 +753,7 @@ func (t *Template) pipeline(context string) (pipe *pipeNode) {
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() {
......@@ -825,7 +828,7 @@ func (t *Template) templateControl() node {
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)
}
......
......@@ -181,8 +181,6 @@ var parseTests = []parseTest{
"[(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,
......@@ -226,6 +224,7 @@ var parseTests = []parseTest{
{"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) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment