mirror of
https://github.com/golang/go
synced 2024-11-25 13:57:57 -07:00
exp/template: allow range actions to declare a key and element variable.
{{range $key, $element := pipeline}} This CL is smaller than it looks due to some rearrangement and renaming. R=rsc, r CC=golang-dev https://golang.org/cl/4709047
This commit is contained in:
parent
aec89c4c40
commit
fc1f0bd5e9
@ -155,9 +155,18 @@ initialization has syntax
|
|||||||
|
|
||||||
$variable := pipeline
|
$variable := pipeline
|
||||||
|
|
||||||
where $variable is the name of the variable. The one exception is a pipeline in
|
where $variable is the name of the variable.
|
||||||
a range action; in ranges, the variable is set to the successive elements of the
|
|
||||||
iteration.
|
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
|
||||||
|
variables, separated by a comma:
|
||||||
|
|
||||||
|
$index, $element := pipeline
|
||||||
|
|
||||||
|
In this 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.
|
||||||
|
|
||||||
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.
|
||||||
|
@ -46,9 +46,9 @@ func (s *state) pop(mark int) {
|
|||||||
s.vars = s.vars[0:mark]
|
s.vars = s.vars[0:mark]
|
||||||
}
|
}
|
||||||
|
|
||||||
// setTop overwrites the top variable on the stack. Used by range iterations.
|
// setVar overwrites the top-nth variable on the stack. Used by range iterations.
|
||||||
func (s *state) setTop(value reflect.Value) {
|
func (s *state) setVar(n int, value reflect.Value) {
|
||||||
s.vars[len(s.vars)-1].value = value
|
s.vars[len(s.vars)-n].value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// varValue returns the value of the named variable.
|
// varValue returns the value of the named variable.
|
||||||
@ -191,9 +191,13 @@ func (s *state) walkRange(dot reflect.Value, r *rangeNode) {
|
|||||||
}
|
}
|
||||||
for i := 0; i < val.Len(); i++ {
|
for i := 0; i < val.Len(); i++ {
|
||||||
elem := val.Index(i)
|
elem := val.Index(i)
|
||||||
// Set $x to the element rather than the slice.
|
// Set top var (lexically the second if there are two) to the element.
|
||||||
if r.pipe.decl != nil {
|
if len(r.pipe.decl) > 0 {
|
||||||
s.setTop(elem)
|
s.setVar(1, elem)
|
||||||
|
}
|
||||||
|
// Set next var (lexically the first if there are two) to the index.
|
||||||
|
if len(r.pipe.decl) > 1 {
|
||||||
|
s.setVar(2, reflect.ValueOf(i))
|
||||||
}
|
}
|
||||||
s.walk(elem, r.list)
|
s.walk(elem, r.list)
|
||||||
}
|
}
|
||||||
@ -204,9 +208,13 @@ func (s *state) walkRange(dot reflect.Value, r *rangeNode) {
|
|||||||
}
|
}
|
||||||
for _, key := range val.MapKeys() {
|
for _, key := range val.MapKeys() {
|
||||||
elem := val.MapIndex(key)
|
elem := val.MapIndex(key)
|
||||||
// Set $x to the key rather than the map.
|
// Set top var (lexically the second if there are two) to the element.
|
||||||
if r.pipe.decl != nil {
|
if len(r.pipe.decl) > 0 {
|
||||||
s.setTop(elem)
|
s.setVar(1, elem)
|
||||||
|
}
|
||||||
|
// Set next var (lexically the first if there are two) to the key.
|
||||||
|
if len(r.pipe.decl) > 1 {
|
||||||
|
s.setVar(2, key)
|
||||||
}
|
}
|
||||||
s.walk(elem, r.list)
|
s.walk(elem, r.list)
|
||||||
}
|
}
|
||||||
@ -255,8 +263,8 @@ func (s *state) evalPipeline(dot reflect.Value, pipe *pipeNode) (value reflect.V
|
|||||||
value = reflect.ValueOf(value.Interface()) // lovely!
|
value = reflect.ValueOf(value.Interface()) // lovely!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pipe.decl != nil {
|
for _, variable := range pipe.decl {
|
||||||
s.push(pipe.decl.ident[0], value)
|
s.push(variable.ident[0], value)
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
@ -195,14 +195,8 @@ var execTests = []execTest{
|
|||||||
|
|
||||||
// Variables.
|
// Variables.
|
||||||
{"$ int", "{{$}}", "123", 123, true},
|
{"$ int", "{{$}}", "123", 123, true},
|
||||||
{"with $x int", "{{with $x := .I}}{{$x}}{{end}}", "17", tVal, true},
|
|
||||||
{"range $x SI", "{{range $x := .SI}}<{{$x}}>{{end}}", "<3><4><5>", tVal, true},
|
|
||||||
{"range $x PSI", "{{range $x := .PSI}}<{{$x}}>{{end}}", "<21><22><23>", tVal, true},
|
|
||||||
{"if $x with $y int", "{{if $x := true}}{{with $y := .I}}{{$x}},{{$y}}{{end}}{{end}}", "true,17", tVal, true},
|
|
||||||
{"if $x with $x int", "{{if $x := true}}{{with $x := .I}}{{$x}},{{end}}{{$x}}{{end}}", "17,true", tVal, true},
|
|
||||||
{"$.I", "{{$.I}}", "17", tVal, true},
|
{"$.I", "{{$.I}}", "17", tVal, true},
|
||||||
{"$.U.V", "{{$.U.V}}", "v", tVal, true},
|
{"$.U.V", "{{$.U.V}}", "v", tVal, true},
|
||||||
{"with $x struct.U.V", "{{with $x := $}}{{$.U.V}}{{end}}", "v", tVal, true},
|
|
||||||
|
|
||||||
// Pointers.
|
// Pointers.
|
||||||
{"*int", "{{.PI}}", "23", tVal, true},
|
{"*int", "{{.PI}}", "23", tVal, true},
|
||||||
@ -253,6 +247,8 @@ var execTests = []execTest{
|
|||||||
{"if slice", "{{if .SI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true},
|
{"if slice", "{{if .SI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true},
|
||||||
{"if emptymap", "{{if .MSIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
|
{"if emptymap", "{{if .MSIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
|
||||||
{"if map", "{{if .MSI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true},
|
{"if map", "{{if .MSI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true},
|
||||||
|
{"if $x with $y int", "{{if $x := true}}{{with $y := .I}}{{$x}},{{$y}}{{end}}{{end}}", "true,17", tVal, true},
|
||||||
|
{"if $x with $x int", "{{if $x := true}}{{with $x := .I}}{{$x}},{{end}}{{$x}}{{end}}", "17,true", tVal, true},
|
||||||
|
|
||||||
// Print etc.
|
// Print etc.
|
||||||
{"print", `{{print "hello, print"}}`, "hello, print", tVal, true},
|
{"print", `{{print "hello, print"}}`, "hello, print", tVal, true},
|
||||||
@ -312,6 +308,8 @@ var execTests = []execTest{
|
|||||||
{"with emptymap", "{{with .MSIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
|
{"with emptymap", "{{with .MSIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
|
||||||
{"with map", "{{with .MSIone}}{{.}}{{else}}EMPTY{{end}}", "map[one:1]", tVal, true},
|
{"with map", "{{with .MSIone}}{{.}}{{else}}EMPTY{{end}}", "map[one:1]", tVal, true},
|
||||||
{"with empty interface, struct field", "{{with .Empty4}}{{.V}}{{end}}", "v", tVal, true},
|
{"with empty interface, struct field", "{{with .Empty4}}{{.V}}{{end}}", "v", tVal, true},
|
||||||
|
{"with $x int", "{{with $x := .I}}{{$x}}{{end}}", "17", tVal, true},
|
||||||
|
{"with $x struct.U.V", "{{with $x := $}}{{$.U.V}}{{end}}", "v", tVal, true},
|
||||||
|
|
||||||
// Range.
|
// Range.
|
||||||
{"range []int", "{{range .SI}}-{{.}}-{{end}}", "-3--4--5-", tVal, true},
|
{"range []int", "{{range .SI}}-{{.}}-{{end}}", "-3--4--5-", tVal, true},
|
||||||
@ -325,6 +323,11 @@ var execTests = []execTest{
|
|||||||
{"range map else", "{{range .MSI | .MSort}}-{{.}}-{{else}}EMPTY{{end}}", "-one--three--two-", tVal, true},
|
{"range map else", "{{range .MSI | .MSort}}-{{.}}-{{else}}EMPTY{{end}}", "-one--three--two-", tVal, true},
|
||||||
{"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
|
{"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
|
||||||
{"range empty interface", "{{range .Empty3}}-{{.}}-{{else}}EMPTY{{end}}", "-7--8-", tVal, true},
|
{"range empty interface", "{{range .Empty3}}-{{.}}-{{else}}EMPTY{{end}}", "-7--8-", tVal, true},
|
||||||
|
{"range $x SI", "{{range $x := .SI}}<{{$x}}>{{end}}", "<3><4><5>", tVal, true},
|
||||||
|
{"range $x $y SI", "{{range $x, $y := .SI}}<{{$x}}={{$y}}>{{end}}", "<0=3><1=4><2=5>", tVal, true},
|
||||||
|
{"range $x MSIone", "{{range $x := .MSIone}}<{{$x}}>{{end}}", "<1>", tVal, true},
|
||||||
|
{"range $x $y MSIone", "{{range $x, $y := .MSIone}}<{{$x}}={{$y}}>{{end}}", "<one=1>", tVal, true},
|
||||||
|
{"range $x PSI", "{{range $x := .PSI}}<{{$x}}>{{end}}", "<21><22><23>", tVal, true},
|
||||||
|
|
||||||
// Cute examples.
|
// Cute examples.
|
||||||
{"or as if true", `{{or .SI "slice is empty"}}`, "[3 4 5]", tVal, true},
|
{"or as if true", `{{or .SI "slice is empty"}}`, "[3 4 5]", tVal, true},
|
||||||
|
@ -37,7 +37,8 @@ type itemType int
|
|||||||
const (
|
const (
|
||||||
itemError itemType = iota // error occurred; value is text of error
|
itemError itemType = iota // error occurred; value is text of error
|
||||||
itemBool // boolean constant
|
itemBool // boolean constant
|
||||||
itemChar // character constant
|
itemChar // printable ASCII character; grab bag for comma etc.
|
||||||
|
itemCharConstant // character constant
|
||||||
itemComplex // complex constant (1+2i); imaginary is just a number
|
itemComplex // complex constant (1+2i); imaginary is just a number
|
||||||
itemColonEquals // colon-equals (':=') introducing a declaration
|
itemColonEquals // colon-equals (':=') introducing a declaration
|
||||||
itemEOF
|
itemEOF
|
||||||
@ -68,6 +69,7 @@ var itemName = map[itemType]string{
|
|||||||
itemError: "error",
|
itemError: "error",
|
||||||
itemBool: "bool",
|
itemBool: "bool",
|
||||||
itemChar: "char",
|
itemChar: "char",
|
||||||
|
itemCharConstant: "charconst",
|
||||||
itemComplex: "complex",
|
itemComplex: "complex",
|
||||||
itemColonEquals: ":=",
|
itemColonEquals: ":=",
|
||||||
itemEOF: "EOF",
|
itemEOF: "EOF",
|
||||||
@ -315,6 +317,9 @@ func lexInsideAction(l *lexer) stateFn {
|
|||||||
case isAlphaNumeric(r):
|
case isAlphaNumeric(r):
|
||||||
l.backup()
|
l.backup()
|
||||||
return lexIdentifier
|
return lexIdentifier
|
||||||
|
case r <= unicode.MaxASCII && unicode.IsPrint(r):
|
||||||
|
l.emit(itemChar)
|
||||||
|
return lexInsideAction
|
||||||
default:
|
default:
|
||||||
return l.errorf("unrecognized character in action: %#U", r)
|
return l.errorf("unrecognized character in action: %#U", r)
|
||||||
}
|
}
|
||||||
@ -369,7 +374,7 @@ Loop:
|
|||||||
break Loop
|
break Loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
l.emit(itemChar)
|
l.emit(itemCharConstant)
|
||||||
return lexInsideAction
|
return lexInsideAction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,14 @@ var lexTests = []lexTest{
|
|||||||
{itemText, "-world"},
|
{itemText, "-world"},
|
||||||
tEOF,
|
tEOF,
|
||||||
}},
|
}},
|
||||||
|
{"punctuation", "{{,@%}}", []item{
|
||||||
|
tLeft,
|
||||||
|
{itemChar, ","},
|
||||||
|
{itemChar, "@"},
|
||||||
|
{itemChar, "%"},
|
||||||
|
tRight,
|
||||||
|
tEOF,
|
||||||
|
}},
|
||||||
{"empty action", `{{}}`, []item{tLeft, tRight, tEOF}},
|
{"empty action", `{{}}`, []item{tLeft, tRight, tEOF}},
|
||||||
{"for", `{{for }}`, []item{tLeft, tFor, tRight, tEOF}},
|
{"for", `{{for }}`, []item{tLeft, tFor, tRight, tEOF}},
|
||||||
{"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}},
|
{"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}},
|
||||||
@ -55,13 +63,13 @@ var lexTests = []lexTest{
|
|||||||
}},
|
}},
|
||||||
{"characters", `{{'a' '\n' '\'' '\\' '\u00FF' '\xFF' '本'}}`, []item{
|
{"characters", `{{'a' '\n' '\'' '\\' '\u00FF' '\xFF' '本'}}`, []item{
|
||||||
tLeft,
|
tLeft,
|
||||||
{itemChar, `'a'`},
|
{itemCharConstant, `'a'`},
|
||||||
{itemChar, `'\n'`},
|
{itemCharConstant, `'\n'`},
|
||||||
{itemChar, `'\''`},
|
{itemCharConstant, `'\''`},
|
||||||
{itemChar, `'\\'`},
|
{itemCharConstant, `'\\'`},
|
||||||
{itemChar, `'\u00FF'`},
|
{itemCharConstant, `'\u00FF'`},
|
||||||
{itemChar, `'\xFF'`},
|
{itemCharConstant, `'\xFF'`},
|
||||||
{itemChar, `'本'`},
|
{itemCharConstant, `'本'`},
|
||||||
tRight,
|
tRight,
|
||||||
tEOF,
|
tEOF,
|
||||||
}},
|
}},
|
||||||
@ -127,11 +135,29 @@ var lexTests = []lexTest{
|
|||||||
{itemText, " outro"},
|
{itemText, " outro"},
|
||||||
tEOF,
|
tEOF,
|
||||||
}},
|
}},
|
||||||
|
{"declaration", "{{$v := 3}}", []item{
|
||||||
|
tLeft,
|
||||||
|
{itemVariable, "$v"},
|
||||||
|
{itemColonEquals, ":="},
|
||||||
|
{itemNumber, "3"},
|
||||||
|
tRight,
|
||||||
|
tEOF,
|
||||||
|
}},
|
||||||
|
{"2 declarations", "{{$v , $w := 3}}", []item{
|
||||||
|
tLeft,
|
||||||
|
{itemVariable, "$v"},
|
||||||
|
{itemChar, ","},
|
||||||
|
{itemVariable, "$w"},
|
||||||
|
{itemColonEquals, ":="},
|
||||||
|
{itemNumber, "3"},
|
||||||
|
tRight,
|
||||||
|
tEOF,
|
||||||
|
}},
|
||||||
// errors
|
// errors
|
||||||
{"badchar", "#{{#}}", []item{
|
{"badchar", "#{{\x01}}", []item{
|
||||||
{itemText, "#"},
|
{itemText, "#"},
|
||||||
tLeft,
|
tLeft,
|
||||||
{itemError, "unrecognized character in action: U+0023 '#'"},
|
{itemError, "unrecognized character in action: U+0001"},
|
||||||
}},
|
}},
|
||||||
{"unclosed action", "{{\n}}", []item{
|
{"unclosed action", "{{\n}}", []item{
|
||||||
tLeft,
|
tLeft,
|
||||||
|
@ -140,11 +140,11 @@ func (t *textNode) String() string {
|
|||||||
type pipeNode struct {
|
type pipeNode struct {
|
||||||
nodeType
|
nodeType
|
||||||
line int
|
line int
|
||||||
decl *variableNode
|
decl []*variableNode
|
||||||
cmds []*commandNode
|
cmds []*commandNode
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPipeline(line int, decl *variableNode) *pipeNode {
|
func newPipeline(line int, decl []*variableNode) *pipeNode {
|
||||||
return &pipeNode{nodeType: nodePipe, line: line, decl: decl}
|
return &pipeNode{nodeType: nodePipe, line: line, decl: decl}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +154,7 @@ func (p *pipeNode) append(command *commandNode) {
|
|||||||
|
|
||||||
func (p *pipeNode) String() string {
|
func (p *pipeNode) String() string {
|
||||||
if p.decl != nil {
|
if p.decl != nil {
|
||||||
return fmt.Sprintf("%s := %v", p.decl.ident, p.cmds)
|
return fmt.Sprintf("%v := %v", p.decl, p.cmds)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%v", p.cmds)
|
return fmt.Sprintf("%v", p.cmds)
|
||||||
}
|
}
|
||||||
@ -287,7 +287,7 @@ type numberNode struct {
|
|||||||
func newNumber(text string, typ itemType) (*numberNode, os.Error) {
|
func newNumber(text string, typ itemType) (*numberNode, os.Error) {
|
||||||
n := &numberNode{nodeType: nodeNumber, text: text}
|
n := &numberNode{nodeType: nodeNumber, text: text}
|
||||||
switch typ {
|
switch typ {
|
||||||
case itemChar:
|
case itemCharConstant:
|
||||||
rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0])
|
rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -704,21 +704,31 @@ func (t *Template) action() (n node) {
|
|||||||
// field or command
|
// field or command
|
||||||
// pipeline "|" pipeline
|
// pipeline "|" pipeline
|
||||||
func (t *Template) pipeline(context string) (pipe *pipeNode) {
|
func (t *Template) pipeline(context string) (pipe *pipeNode) {
|
||||||
var decl *variableNode
|
var decl []*variableNode
|
||||||
// Is there a declaration?
|
// Are there declarations?
|
||||||
|
for {
|
||||||
if v := t.peek(); v.typ == itemVariable {
|
if v := t.peek(); v.typ == itemVariable {
|
||||||
t.next()
|
t.next()
|
||||||
if ce := t.peek(); ce.typ == itemColonEquals {
|
if next := t.peek(); next.typ == itemColonEquals || next.typ == itemChar {
|
||||||
t.next()
|
t.next()
|
||||||
decl = newVariable(v.val)
|
variable := newVariable(v.val)
|
||||||
if len(decl.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)
|
||||||
}
|
}
|
||||||
|
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 context == "range" && len(decl) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.errorf("too many declarations in %s", context)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
t.backup2(v)
|
t.backup2(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
pipe = newPipeline(t.lex.lineNumber(), decl)
|
pipe = newPipeline(t.lex.lineNumber(), decl)
|
||||||
for {
|
for {
|
||||||
switch token := t.next(); token.typ {
|
switch token := t.next(); token.typ {
|
||||||
@ -727,7 +737,8 @@ func (t *Template) pipeline(context string) (pipe *pipeNode) {
|
|||||||
t.errorf("missing value for %s", context)
|
t.errorf("missing value for %s", context)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case itemBool, itemChar, itemComplex, itemDot, itemField, itemIdentifier, itemVariable, itemNumber, itemRawString, itemString:
|
case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier,
|
||||||
|
itemVariable, itemNumber, itemRawString, itemString:
|
||||||
t.backup()
|
t.backup()
|
||||||
pipe.append(t.command())
|
pipe.append(t.command())
|
||||||
default:
|
default:
|
||||||
@ -848,7 +859,7 @@ Loop:
|
|||||||
cmd.append(newField(token.val))
|
cmd.append(newField(token.val))
|
||||||
case itemBool:
|
case itemBool:
|
||||||
cmd.append(newBool(token.val == "true"))
|
cmd.append(newBool(token.val == "true"))
|
||||||
case itemChar, itemComplex, itemNumber:
|
case itemCharConstant, itemComplex, itemNumber:
|
||||||
number, err := newNumber(token.val, token.typ)
|
number, err := newNumber(token.val, token.typ)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.error(err)
|
t.error(err)
|
||||||
|
@ -77,7 +77,7 @@ func TestNumberParse(t *testing.T) {
|
|||||||
var c complex128
|
var c complex128
|
||||||
typ := itemNumber
|
typ := itemNumber
|
||||||
if test.text[0] == '\'' {
|
if test.text[0] == '\'' {
|
||||||
typ = itemChar
|
typ = itemCharConstant
|
||||||
} else {
|
} else {
|
||||||
_, err := fmt.Sscan(test.text, &c)
|
_, err := fmt.Sscan(test.text, &c)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -174,7 +174,7 @@ var parseTests = []parseTest{
|
|||||||
{"$ invocation", "{{$}}", noError,
|
{"$ invocation", "{{$}}", noError,
|
||||||
"[(action: [(command: [V=[$]])])]"},
|
"[(action: [(command: [V=[$]])])]"},
|
||||||
{"variable invocation", "{{with $x := 3}}{{$x 23}}{{end}}", noError,
|
{"variable invocation", "{{with $x := 3}}{{$x 23}}{{end}}", noError,
|
||||||
"[({{with [$x] := [(command: [N=3])]}} [(action: [(command: [V=[$x] N=23])])])]"},
|
"[({{with [V=[$x]] := [(command: [N=3])]}} [(action: [(command: [V=[$x] N=23])])])]"},
|
||||||
{"variable with fields", "{{$.I}}", noError,
|
{"variable with fields", "{{$.I}}", noError,
|
||||||
"[(action: [(command: [V=[$ I]])])]"},
|
"[(action: [(command: [V=[$ I]])])]"},
|
||||||
{"multi-word command", "{{printf `%d` 23}}", noError,
|
{"multi-word command", "{{printf `%d` 23}}", noError,
|
||||||
@ -182,7 +182,7 @@ var parseTests = []parseTest{
|
|||||||
{"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,
|
{"pipeline with decl", "{{$x := .X|.Y}}", noError,
|
||||||
`[(action: [$x] := [(command: [F=[X]]) (command: [F=[Y]])])]`},
|
`[(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,
|
||||||
@ -223,6 +223,9 @@ var parseTests = []parseTest{
|
|||||||
{"declare with field", "{{with $x.Y := 4}}{{end}}", hasError, ""},
|
{"declare with field", "{{with $x.Y := 4}}{{end}}", hasError, ""},
|
||||||
{"template with field ref", "{{template .X}}", hasError, ""},
|
{"template with field ref", "{{template .X}}", hasError, ""},
|
||||||
{"template with var", "{{template $v}}", hasError, ""},
|
{"template with var", "{{template $v}}", hasError, ""},
|
||||||
|
{"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, ""},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParse(t *testing.T) {
|
func TestParse(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user