Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
go
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
go
Commits
b8c66429
Commit
b8c66429
authored
Jul 08, 2011
by
Rob Pike
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
exp/template: parse variables and declarations
R=golang-dev, adg CC=golang-dev
https://golang.org/cl/4631099
parent
ee14989e
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
120 additions
and
60 deletions
+120
-60
src/pkg/exp/template/exec.go
src/pkg/exp/template/exec.go
+8
-8
src/pkg/exp/template/parse.go
src/pkg/exp/template/parse.go
+106
-52
src/pkg/exp/template/parse_test.go
src/pkg/exp/template/parse_test.go
+6
-0
No files found.
src/pkg/exp/template/exec.go
View file @
b8c66429
...
...
@@ -82,14 +82,14 @@ func (s *state) walk(data reflect.Value, n node) {
switch
n
:=
n
.
(
type
)
{
case
*
actionNode
:
s
.
line
=
n
.
line
s
.
printValue
(
n
,
s
.
evalPipeline
(
data
,
n
.
pipe
line
))
s
.
printValue
(
n
,
s
.
evalPipeline
(
data
,
n
.
pipe
))
case
*
listNode
:
for
_
,
node
:=
range
n
.
nodes
{
s
.
walk
(
data
,
node
)
}
case
*
ifNode
:
s
.
line
=
n
.
line
s
.
walkIfOrWith
(
nodeIf
,
data
,
n
.
pipe
line
,
n
.
list
,
n
.
elseList
)
s
.
walkIfOrWith
(
nodeIf
,
data
,
n
.
pipe
,
n
.
list
,
n
.
elseList
)
case
*
rangeNode
:
s
.
line
=
n
.
line
s
.
walkRange
(
data
,
n
)
...
...
@@ -102,7 +102,7 @@ func (s *state) walk(data reflect.Value, n node) {
s
.
walkTemplate
(
data
,
n
)
case
*
withNode
:
s
.
line
=
n
.
line
s
.
walkIfOrWith
(
nodeWith
,
data
,
n
.
pipe
line
,
n
.
list
,
n
.
elseList
)
s
.
walkIfOrWith
(
nodeWith
,
data
,
n
.
pipe
,
n
.
list
,
n
.
elseList
)
default
:
s
.
errorf
(
"unknown node: %s"
,
n
)
}
...
...
@@ -110,7 +110,7 @@ func (s *state) walk(data reflect.Value, n node) {
// walkIfOrWith walks an 'if' or 'with' node. The two control structures
// are identical in behavior except that 'with' sets dot.
func
(
s
*
state
)
walkIfOrWith
(
typ
nodeType
,
data
reflect
.
Value
,
pipe
[]
*
command
Node
,
list
,
elseList
*
listNode
)
{
func
(
s
*
state
)
walkIfOrWith
(
typ
nodeType
,
data
reflect
.
Value
,
pipe
*
pipe
Node
,
list
,
elseList
*
listNode
)
{
val
:=
s
.
evalPipeline
(
data
,
pipe
)
truth
,
ok
:=
isTrue
(
val
)
if
!
ok
{
...
...
@@ -152,7 +152,7 @@ func isTrue(val reflect.Value) (truth, ok bool) {
}
func
(
s
*
state
)
walkRange
(
data
reflect
.
Value
,
r
*
rangeNode
)
{
val
:=
s
.
evalPipeline
(
data
,
r
.
pipe
line
)
val
:=
s
.
evalPipeline
(
data
,
r
.
pipe
)
down
:=
s
.
down
(
data
)
switch
val
.
Kind
()
{
case
reflect
.
Array
,
reflect
.
Slice
:
...
...
@@ -188,7 +188,7 @@ func (s *state) walkTemplate(data reflect.Value, t *templateNode) {
if
tmpl
==
nil
{
s
.
errorf
(
"template %q not in set"
,
name
)
}
data
=
s
.
evalPipeline
(
data
,
t
.
pipe
line
)
data
=
s
.
evalPipeline
(
data
,
t
.
pipe
)
newState
:=
*
s
newState
.
tmpl
=
tmpl
newState
.
walk
(
data
,
tmpl
.
root
)
...
...
@@ -198,9 +198,9 @@ func (s *state) walkTemplate(data reflect.Value, t *templateNode) {
// values from the data structure by examining fields, calling methods, and so on.
// The printing of those values happens only through walk functions.
func
(
s
*
state
)
evalPipeline
(
data
reflect
.
Value
,
pipe
[]
*
command
Node
)
reflect
.
Value
{
func
(
s
*
state
)
evalPipeline
(
data
reflect
.
Value
,
pipe
*
pipe
Node
)
reflect
.
Value
{
value
:=
zero
for
_
,
cmd
:=
range
pipe
{
for
_
,
cmd
:=
range
pipe
.
cmds
{
value
=
s
.
evalCommand
(
data
,
cmd
,
value
)
// previous value is this one's final arg.
// If the object has type interface{}, dig down one level to the thing inside.
if
value
.
Kind
()
==
reflect
.
Interface
&&
value
.
Type
()
.
NumMethod
()
==
0
{
...
...
src/pkg/exp/template/parse.go
View file @
b8c66429
...
...
@@ -21,35 +21,41 @@ type Template struct {
root
*
listNode
funcs
map
[
string
]
reflect
.
Value
// Parsing only; cleared after parse.
set
*
Set
lex
*
lexer
token
item
//
token lookahead for parser
havePeek
bool
set
*
Set
lex
*
lexer
token
[
2
]
item
// two-
token lookahead for parser
peekCount
int
}
// next returns the next token.
func
(
t
*
Template
)
next
()
item
{
if
t
.
havePeek
{
t
.
havePeek
=
false
if
t
.
peekCount
>
0
{
t
.
peekCount
--
}
else
{
t
.
token
=
t
.
lex
.
nextItem
()
t
.
token
[
0
]
=
t
.
lex
.
nextItem
()
}
return
t
.
token
return
t
.
token
[
t
.
peekCount
]
}
// backup backs the input stream up one token.
func
(
t
*
Template
)
backup
()
{
t
.
havePeek
=
true
t
.
peekCount
++
}
// backup2 backs the input stream up two tokens
func
(
t
*
Template
)
backup2
(
t1
item
)
{
t
.
token
[
1
]
=
t1
t
.
peekCount
=
2
}
// peek returns but does not consume the next token.
func
(
t
*
Template
)
peek
()
item
{
if
t
.
havePeek
{
return
t
.
token
if
t
.
peekCount
>
0
{
return
t
.
token
[
t
.
peekCount
-
1
]
}
t
.
token
=
t
.
lex
.
nextItem
()
t
.
havePeek
=
true
return
t
.
token
t
.
peekCount
=
1
t
.
token
[
0
]
=
t
.
lex
.
nextItem
()
return
t
.
token
[
0
]
}
// A node is an element in the parse tree. The interface is trivial.
...
...
@@ -76,9 +82,11 @@ const (
nodeIf
nodeList
nodeNumber
nodePipe
nodeRange
nodeString
nodeTemplate
nodeVariable
nodeWith
)
...
...
@@ -122,23 +130,42 @@ func (t *textNode) String() string {
return
fmt
.
Sprintf
(
"(text: %q)"
,
t
.
text
)
}
//
actionNode holds an action (something bounded by delimiters).
type
action
Node
struct
{
//
pipeNode holds a pipeline with optional declaration
type
pipe
Node
struct
{
nodeType
line
int
pipeline
[]
*
commandNode
line
int
decl
*
variableNode
cmds
[]
*
commandNode
}
func
newPipeline
(
line
int
,
decl
*
variableNode
)
*
pipeNode
{
return
&
pipeNode
{
nodeType
:
nodePipe
,
line
:
line
,
decl
:
decl
}
}
func
newAction
(
line
int
,
pipeline
[]
*
commandNode
)
*
actionNode
{
return
&
actionNode
{
nodeType
:
nodeAction
,
line
:
line
,
pipeline
:
pipeline
}
func
(
p
*
pipeNode
)
append
(
command
*
commandNode
)
{
p
.
cmds
=
append
(
p
.
cmds
,
command
)
}
func
(
a
*
actionNode
)
append
(
command
*
commandNode
)
{
a
.
pipeline
=
append
(
a
.
pipeline
,
command
)
func
(
p
*
pipeNode
)
String
()
string
{
if
p
.
decl
!=
nil
{
return
fmt
.
Sprintf
(
"%s := %v"
,
p
.
decl
.
ident
,
p
.
cmds
)
}
return
fmt
.
Sprintf
(
"%v"
,
p
.
cmds
)
}
// actionNode holds an action (something bounded by delimiters).
type
actionNode
struct
{
nodeType
line
int
pipe
*
pipeNode
}
func
newAction
(
line
int
,
pipe
*
pipeNode
)
*
actionNode
{
return
&
actionNode
{
nodeType
:
nodeAction
,
line
:
line
,
pipe
:
pipe
}
}
func
(
a
*
actionNode
)
String
()
string
{
return
fmt
.
Sprintf
(
"(action: %v)"
,
a
.
pipe
line
)
return
fmt
.
Sprintf
(
"(action: %v)"
,
a
.
pipe
)
}
// commandNode holds a command (a pipeline inside an evaluating action).
...
...
@@ -173,6 +200,20 @@ func (i *identifierNode) String() string {
return
fmt
.
Sprintf
(
"I=%s"
,
i
.
ident
)
}
// variableNode holds a variable.
type
variableNode
struct
{
nodeType
ident
string
}
func
newVariable
(
ident
string
)
*
variableNode
{
return
&
variableNode
{
nodeType
:
nodeVariable
,
ident
:
ident
}
}
func
(
v
*
variableNode
)
String
()
string
{
return
fmt
.
Sprintf
(
"V=%s"
,
v
.
ident
)
}
// dotNode holds the special identifier '.'. It is represented by a nil pointer.
type
dotNode
bool
...
...
@@ -370,80 +411,80 @@ func (e *elseNode) String() string {
return
"{{else}}"
}
// ifNode represents an {{if}} action and its commands.
// TODO: what should evaluation look like? is a pipe
line
enough?
// TODO: what should evaluation look like? is a pipe enough?
type
ifNode
struct
{
nodeType
line
int
pipe
line
[]
*
command
Node
pipe
*
pipe
Node
list
*
listNode
elseList
*
listNode
}
func
newIf
(
line
int
,
pipe
line
[]
*
command
Node
,
list
,
elseList
*
listNode
)
*
ifNode
{
return
&
ifNode
{
nodeType
:
nodeIf
,
line
:
line
,
pipe
line
:
pipelin
e
,
list
:
list
,
elseList
:
elseList
}
func
newIf
(
line
int
,
pipe
*
pipe
Node
,
list
,
elseList
*
listNode
)
*
ifNode
{
return
&
ifNode
{
nodeType
:
nodeIf
,
line
:
line
,
pipe
:
pip
e
,
list
:
list
,
elseList
:
elseList
}
}
func
(
i
*
ifNode
)
String
()
string
{
if
i
.
elseList
!=
nil
{
return
fmt
.
Sprintf
(
"({{if %s}} %s {{else}} %s)"
,
i
.
pipe
line
,
i
.
list
,
i
.
elseList
)
return
fmt
.
Sprintf
(
"({{if %s}} %s {{else}} %s)"
,
i
.
pipe
,
i
.
list
,
i
.
elseList
)
}
return
fmt
.
Sprintf
(
"({{if %s}} %s)"
,
i
.
pipe
line
,
i
.
list
)
return
fmt
.
Sprintf
(
"({{if %s}} %s)"
,
i
.
pipe
,
i
.
list
)
}
// rangeNode represents a {{range}} action and its commands.
type
rangeNode
struct
{
nodeType
line
int
pipe
line
[]
*
command
Node
pipe
*
pipe
Node
list
*
listNode
elseList
*
listNode
}
func
newRange
(
line
int
,
pipe
line
[]
*
command
Node
,
list
,
elseList
*
listNode
)
*
rangeNode
{
return
&
rangeNode
{
nodeType
:
nodeRange
,
line
:
line
,
pipe
line
:
pipelin
e
,
list
:
list
,
elseList
:
elseList
}
func
newRange
(
line
int
,
pipe
*
pipe
Node
,
list
,
elseList
*
listNode
)
*
rangeNode
{
return
&
rangeNode
{
nodeType
:
nodeRange
,
line
:
line
,
pipe
:
pip
e
,
list
:
list
,
elseList
:
elseList
}
}
func
(
r
*
rangeNode
)
String
()
string
{
if
r
.
elseList
!=
nil
{
return
fmt
.
Sprintf
(
"({{range %s}} %s {{else}} %s)"
,
r
.
pipe
line
,
r
.
list
,
r
.
elseList
)
return
fmt
.
Sprintf
(
"({{range %s}} %s {{else}} %s)"
,
r
.
pipe
,
r
.
list
,
r
.
elseList
)
}
return
fmt
.
Sprintf
(
"({{range %s}} %s)"
,
r
.
pipe
line
,
r
.
list
)
return
fmt
.
Sprintf
(
"({{range %s}} %s)"
,
r
.
pipe
,
r
.
list
)
}
// templateNode represents a {{template}} action.
type
templateNode
struct
{
nodeType
line
int
name
node
pipe
line
[]
*
command
Node
line
int
name
node
pipe
*
pipe
Node
}
func
newTemplate
(
line
int
,
name
node
,
pipe
line
[]
*
command
Node
)
*
templateNode
{
return
&
templateNode
{
nodeType
:
nodeTemplate
,
line
:
line
,
name
:
name
,
pipe
line
:
pipelin
e
}
func
newTemplate
(
line
int
,
name
node
,
pipe
*
pipe
Node
)
*
templateNode
{
return
&
templateNode
{
nodeType
:
nodeTemplate
,
line
:
line
,
name
:
name
,
pipe
:
pip
e
}
}
func
(
t
*
templateNode
)
String
()
string
{
return
fmt
.
Sprintf
(
"{{template %s %s}}"
,
t
.
name
,
t
.
pipe
line
)
return
fmt
.
Sprintf
(
"{{template %s %s}}"
,
t
.
name
,
t
.
pipe
)
}
// withNode represents a {{with}} action and its commands.
type
withNode
struct
{
nodeType
line
int
pipe
line
[]
*
command
Node
pipe
*
pipe
Node
list
*
listNode
elseList
*
listNode
}
func
newWith
(
line
int
,
pipe
line
[]
*
command
Node
,
list
,
elseList
*
listNode
)
*
withNode
{
return
&
withNode
{
nodeType
:
nodeWith
,
line
:
line
,
pipe
line
:
pipelin
e
,
list
:
list
,
elseList
:
elseList
}
func
newWith
(
line
int
,
pipe
*
pipe
Node
,
list
,
elseList
*
listNode
)
*
withNode
{
return
&
withNode
{
nodeType
:
nodeWith
,
line
:
line
,
pipe
:
pip
e
,
list
:
list
,
elseList
:
elseList
}
}
func
(
w
*
withNode
)
String
()
string
{
if
w
.
elseList
!=
nil
{
return
fmt
.
Sprintf
(
"({{with %s}} %s {{else}} %s)"
,
w
.
pipe
line
,
w
.
list
,
w
.
elseList
)
return
fmt
.
Sprintf
(
"({{with %s}} %s {{else}} %s)"
,
w
.
pipe
,
w
.
list
,
w
.
elseList
)
}
return
fmt
.
Sprintf
(
"({{with %s}} %s)"
,
w
.
pipe
line
,
w
.
list
)
return
fmt
.
Sprintf
(
"({{with %s}} %s)"
,
w
.
pipe
,
w
.
list
)
}
...
...
@@ -631,17 +672,29 @@ func (t *Template) action() (n node) {
// Pipeline:
// field or command
// pipeline "|" pipeline
func
(
t
*
Template
)
pipeline
(
context
string
)
(
pipe
[]
*
commandNode
)
{
func
(
t
*
Template
)
pipeline
(
context
string
)
(
pipe
*
pipeNode
)
{
var
decl
*
variableNode
// Is there a declaration?
if
v
:=
t
.
peek
();
v
.
typ
==
itemVariable
{
t
.
next
()
if
ce
:=
t
.
peek
();
ce
.
typ
==
itemColonEquals
{
t
.
next
()
decl
=
newVariable
(
v
.
val
)
}
else
{
t
.
backup2
(
v
)
}
}
pipe
=
newPipeline
(
t
.
lex
.
lineNumber
(),
decl
)
for
{
switch
token
:=
t
.
next
();
token
.
typ
{
case
itemRightDelim
:
if
len
(
pipe
)
==
0
{
if
len
(
pipe
.
cmds
)
==
0
{
t
.
errorf
(
"missing value for %s"
,
context
)
}
return
case
itemBool
,
itemComplex
,
itemDot
,
itemField
,
itemIdentifier
,
itemNumber
,
itemRawString
,
itemString
:
case
itemBool
,
itemComplex
,
itemDot
,
itemField
,
itemIdentifier
,
item
Variable
,
item
Number
,
itemRawString
,
itemString
:
t
.
backup
()
pipe
=
append
(
pipe
,
t
.
command
())
pipe
.
append
(
t
.
command
())
default
:
t
.
unexpected
(
token
,
context
)
}
...
...
@@ -649,7 +702,7 @@ func (t *Template) pipeline(context string) (pipe []*commandNode) {
return
}
func
(
t
*
Template
)
parseControl
(
context
string
)
(
lineNum
int
,
pipe
[]
*
command
Node
,
list
,
elseList
*
listNode
)
{
func
(
t
*
Template
)
parseControl
(
context
string
)
(
lineNum
int
,
pipe
*
pipe
Node
,
list
,
elseList
*
listNode
)
{
lineNum
=
t
.
lex
.
lineNumber
()
pipe
=
t
.
pipeline
(
context
)
var
next
node
...
...
@@ -732,8 +785,7 @@ func (t *Template) templateControl() node {
default
:
t
.
unexpected
(
token
,
"template invocation"
)
}
pipeline
:=
t
.
pipeline
(
"template"
)
return
newTemplate
(
t
.
lex
.
lineNumber
(),
name
,
pipeline
)
return
newTemplate
(
t
.
lex
.
lineNumber
(),
name
,
t
.
pipeline
(
"template"
))
}
// command:
...
...
@@ -758,6 +810,8 @@ Loop:
cmd
.
append
(
newIdentifier
(
token
.
val
))
case
itemDot
:
cmd
.
append
(
newDot
())
case
itemVariable
:
cmd
.
append
(
newVariable
(
token
.
val
))
case
itemField
:
cmd
.
append
(
newField
(
token
.
val
))
case
itemBool
:
...
...
src/pkg/exp/template/parse_test.go
View file @
b8c66429
...
...
@@ -146,10 +146,16 @@ var parseTests = []parseTest{
`[(action: [(command: [F=[X]])])]`
},
{
"simple command"
,
"{{printf}}"
,
noError
,
`[(action: [(command: [I=printf])])]`
},
{
"variable invocation"
,
"{{$x 23}}"
,
noError
,
"[(action: [(command: [V=$x N=23])])]"
},
{
"multi-word command"
,
"{{printf `%d` 23}}"
,
noError
,
"[(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: $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
,
`[({{if [(command: [F=[X]])]}} [(text: "hello")])]`
},
{
"if with else"
,
"{{if .X}}true{{else}}false{{end}}"
,
noError
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment