mirror of
https://github.com/golang/go
synced 2024-11-25 10:07:56 -07:00
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
This commit is contained in:
parent
e07c6e6ee7
commit
331840f509
@ -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.
|
"{{" and "}}"; all text outside actions is copied to the output unchanged.
|
||||||
Actions may not span newlines.
|
Actions may not span newlines.
|
||||||
|
|
||||||
|
Once constructed, templates and template sets can be executed safely in
|
||||||
|
parallel.
|
||||||
|
|
||||||
Actions
|
Actions
|
||||||
|
|
||||||
Here is the list of actions. "Arguments" and "pipelines" are evaluations of
|
Here is the list of actions. "Arguments" and "pipelines" are evaluations of
|
||||||
@ -150,24 +153,27 @@ Execute.
|
|||||||
|
|
||||||
Variables
|
Variables
|
||||||
|
|
||||||
A pipeline may initialize a single variable to capture the result. The
|
A pipeline inside an "if" or "with" action may initialize a variable to capture
|
||||||
initialization has syntax
|
the result. The initialization has syntax
|
||||||
|
|
||||||
$variable := pipeline
|
$variable := pipeline
|
||||||
|
|
||||||
where $variable is the name of the variable.
|
where $variable is the name of the variable.
|
||||||
|
|
||||||
The one exception is a pipeline in a range action; in ranges, the variable is
|
If a "range" action initializes a variable, the variable is set to the
|
||||||
set to the successive elements of the iteration. Also, a range may declare two
|
successive elements of the iteration. Also, a "range" may declare two
|
||||||
variables, separated by a comma:
|
variables, separated by a comma:
|
||||||
|
|
||||||
$index, $element := pipeline
|
$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
|
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
|
only one variable, it is assigned the element; this is opposite to the
|
||||||
convention in Go range clauses.
|
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,
|
When execution begins, $ is set to the data argument passed to Execute, that is,
|
||||||
to the starting value of dot.
|
to the starting value of dot.
|
||||||
|
|
||||||
|
@ -696,13 +696,13 @@ func (t *Template) action() (n node) {
|
|||||||
}
|
}
|
||||||
t.backup()
|
t.backup()
|
||||||
defer t.popVars(len(t.vars))
|
defer t.popVars(len(t.vars))
|
||||||
return newAction(t.lex.lineNumber(), t.pipeline("command"))
|
return newAction(t.lex.lineNumber(), t.pipeline("command", false))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pipeline:
|
// Pipeline:
|
||||||
// field or command
|
// field or command
|
||||||
// pipeline "|" pipeline
|
// pipeline "|" pipeline
|
||||||
func (t *Template) pipeline(context string) (pipe *pipeNode) {
|
func (t *Template) pipeline(context string, allowDecls bool) (pipe *pipeNode) {
|
||||||
var decl []*variableNode
|
var decl []*variableNode
|
||||||
// Are there declarations?
|
// Are there declarations?
|
||||||
for {
|
for {
|
||||||
@ -714,6 +714,9 @@ func (t *Template) pipeline(context string) (pipe *pipeNode) {
|
|||||||
if len(variable.ident) != 1 {
|
if len(variable.ident) != 1 {
|
||||||
t.errorf("illegal variable in declaration: %s", v.val)
|
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)
|
decl = append(decl, variable)
|
||||||
t.vars = append(t.vars, v.val)
|
t.vars = append(t.vars, v.val)
|
||||||
if next.typ == itemChar && next.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) {
|
func (t *Template) parseControl(context string) (lineNum int, pipe *pipeNode, list, elseList *listNode) {
|
||||||
lineNum = t.lex.lineNumber()
|
lineNum = t.lex.lineNumber()
|
||||||
defer t.popVars(len(t.vars))
|
defer t.popVars(len(t.vars))
|
||||||
pipe = t.pipeline(context)
|
pipe = t.pipeline(context, true)
|
||||||
var next node
|
var next node
|
||||||
list, next = t.itemList(false)
|
list, next = t.itemList(false)
|
||||||
switch next.typ() {
|
switch next.typ() {
|
||||||
@ -825,7 +828,7 @@ func (t *Template) templateControl() node {
|
|||||||
if t.next().typ != itemRightDelim {
|
if t.next().typ != itemRightDelim {
|
||||||
t.backup()
|
t.backup()
|
||||||
defer t.popVars(len(t.vars))
|
defer t.popVars(len(t.vars))
|
||||||
pipe = t.pipeline("template")
|
pipe = t.pipeline("template", false)
|
||||||
}
|
}
|
||||||
return newTemplate(t.lex.lineNumber(), name, pipe)
|
return newTemplate(t.lex.lineNumber(), name, pipe)
|
||||||
}
|
}
|
||||||
|
@ -181,8 +181,6 @@ var parseTests = []parseTest{
|
|||||||
"[(action: [(command: [I=printf S=`%d` N=23])])]"},
|
"[(action: [(command: [I=printf S=`%d` N=23])])]"},
|
||||||
{"pipeline", "{{.X|.Y}}", noError,
|
{"pipeline", "{{.X|.Y}}", noError,
|
||||||
`[(action: [(command: [F=[X]]) (command: [F=[Y]])])]`},
|
`[(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,
|
{"declaration", "{{.X|.Y}}", noError,
|
||||||
`[(action: [(command: [F=[X]]) (command: [F=[Y]])])]`},
|
`[(action: [(command: [F=[X]]) (command: [F=[Y]])])]`},
|
||||||
{"simple if", "{{if .X}}hello{{end}}", noError,
|
{"simple if", "{{if .X}}hello{{end}}", noError,
|
||||||
@ -226,6 +224,7 @@ var parseTests = []parseTest{
|
|||||||
{"invalid punctuation", "{{printf 3, 4}}", hasError, ""},
|
{"invalid punctuation", "{{printf 3, 4}}", hasError, ""},
|
||||||
{"multidecl outside range", "{{with $v, $u := 3}}{{end}}", hasError, ""},
|
{"multidecl outside range", "{{with $v, $u := 3}}{{end}}", hasError, ""},
|
||||||
{"too many decls in range", "{{range $u, $v, $w := 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) {
|
func TestParse(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user