Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
G
golang
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
golang
Commits
3761da2d
Commit
3761da2d
authored
Apr 17, 2009
by
Rob Pike
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
document template
R=rsc DELTA=92 (73 added, 0 deleted, 19 changed) OCL=27566 CL=27572
parent
c8f93788
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
91 additions
and
18 deletions
+91
-18
template.go
src/lib/template/template.go
+91
-18
No files found.
src/lib/template/template.go
View file @
3761da2d
...
@@ -2,8 +2,59 @@
...
@@ -2,8 +2,59 @@
// Use of this source code is governed by a BSD-style
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// license that can be found in the LICENSE file.
// Template library. See http://code.google.com/p/json-template/wiki/Reference
/*
// TODO: document this here as well.
Data-driven templates for generating textual output such as
HTML. See
http://code.google.com/p/json-template/wiki/Reference
for full documentation of the template language. A summary:
Templates are executed by applying them to a data structure.
Annotations in the template refer to elements of the data
structure (typically a field of a struct) to control execution
and derive values to be displayed. The template walks the
structure as it executes and the "cursor" @ represents the
value at the current location in the structure.
Data items may be values or pointers; the interface hides the
indirection.
Major constructs ({} are metacharacters; [] marks optional elements):
{# comment }
A one-line comment.
{.section field} XXX [ {.or} YYY ] {.end}
Set @ to the value of the field. It may be an explicit @
to stay at the same point in the data. If the field is nil
or empty, execute YYY; otherwise execute XXX.
{.repeated section field} XXX [ {.alternates with} ZZZ ] [ {.or} YYY ] {.end}
Like .section, but field must be an array or slice. XXX
is executed for each element. If the array is nil or empty,
YYY is executed instead. If the {.alternates with} marker
is present, ZZZ is executed between iterations of XXX.
(TODO(r): .alternates is not yet implemented)
{field}
{field|formatter}
Insert the value of the field into the output. Field is
first looked for in the cursor, as in .section and .repeated.
If it is not found, the search continues in outer sections
until the top level is reached.
If a formatter is specified, it must be named in the formatter
map passed to the template set up routines or in the default
set ("html","str","") and is used to process the data for
output. The formatter function has signature
func(wr io.Write, data interface{}, formatter string)
where wr is the destination for output, data is the field
value, and formatter is its name at the invocation site.
*/
package
template
package
template
import
(
import
(
...
@@ -15,6 +66,7 @@ import (
...
@@ -15,6 +66,7 @@ import (
"template"
;
"template"
;
)
)
// Errors returned during parsing and execution.
var
ErrUnmatchedRDelim
=
os
.
NewError
(
"unmatched closing delimiter"
)
var
ErrUnmatchedRDelim
=
os
.
NewError
(
"unmatched closing delimiter"
)
var
ErrUnmatchedLDelim
=
os
.
NewError
(
"unmatched opening delimiter"
)
var
ErrUnmatchedLDelim
=
os
.
NewError
(
"unmatched opening delimiter"
)
var
ErrBadDirective
=
os
.
NewError
(
"unrecognized directive name"
)
var
ErrBadDirective
=
os
.
NewError
(
"unrecognized directive name"
)
...
@@ -26,7 +78,7 @@ var ErrNoVar = os.NewError("variable name not in struct");
...
@@ -26,7 +78,7 @@ var ErrNoVar = os.NewError("variable name not in struct");
var
ErrBadType
=
os
.
NewError
(
"unsupported type for variable"
);
var
ErrBadType
=
os
.
NewError
(
"unsupported type for variable"
);
var
ErrNotStruct
=
os
.
NewError
(
"driver must be a struct"
)
var
ErrNotStruct
=
os
.
NewError
(
"driver must be a struct"
)
var
ErrNoFormatter
=
os
.
NewError
(
"unknown formatter"
)
var
ErrNoFormatter
=
os
.
NewError
(
"unknown formatter"
)
var
Err
EmptyDelims
=
os
.
NewError
(
"empty
delimiter strings"
)
var
Err
BadDelims
=
os
.
NewError
(
"invalid
delimiter strings"
)
// All the literals are aces.
// All the literals are aces.
var
lbrace
=
[]
byte
{
'{'
}
var
lbrace
=
[]
byte
{
'{'
}
...
@@ -72,6 +124,7 @@ func (st *state) error(err *os.Error, args ...) {
...
@@ -72,6 +124,7 @@ func (st *state) error(err *os.Error, args ...) {
sys
.
Goexit
();
sys
.
Goexit
();
}
}
// Template is the type that represents a template definition.
type
Template
struct
{
type
Template
struct
{
fmap
FormatterMap
;
// formatters for variables
fmap
FormatterMap
;
// formatters for variables
ldelim
,
rdelim
[]
byte
;
// delimiters; default {}
ldelim
,
rdelim
[]
byte
;
// delimiters; default {}
...
@@ -100,11 +153,12 @@ func childTemplate(parent *Template, buf []byte) *Template {
...
@@ -100,11 +153,12 @@ func childTemplate(parent *Template, buf []byte) *Template {
return
t
;
return
t
;
}
}
// Is c a white space character?
func
white
(
c
uint8
)
bool
{
func
white
(
c
uint8
)
bool
{
return
c
==
' '
||
c
==
'\t'
||
c
==
'\r'
||
c
==
'\n'
return
c
==
' '
||
c
==
'\t'
||
c
==
'\r'
||
c
==
'\n'
}
}
//
s
afely, does s[n:n+len(t)] == t?
//
S
afely, does s[n:n+len(t)] == t?
func
equal
(
s
[]
byte
,
n
int
,
t
[]
byte
)
bool
{
func
equal
(
s
[]
byte
,
n
int
,
t
[]
byte
)
bool
{
b
:=
s
[
n
:
len
(
s
)];
b
:=
s
[
n
:
len
(
s
)];
if
len
(
t
)
>
len
(
b
)
{
// not enough space left for a match.
if
len
(
t
)
>
len
(
b
)
{
// not enough space left for a match.
...
@@ -124,7 +178,7 @@ func (t *Template) executeSection(w []string, st *state)
...
@@ -124,7 +178,7 @@ func (t *Template) executeSection(w []string, st *state)
// nextItem returns the next item from the input buffer. If the returned
// nextItem returns the next item from the input buffer. If the returned
// item is empty, we are at EOF. The item will be either a
// item is empty, we are at EOF. The item will be either a
// delimited string or a non-empty string between delimited
// delimited string or a non-empty string between delimited
// strings.
Most t
okens stop at (but include, if plain text) a newline.
// strings.
T
okens stop at (but include, if plain text) a newline.
// Action tokens on a line by themselves drop the white space on
// Action tokens on a line by themselves drop the white space on
// either side, up to and including the newline.
// either side, up to and including the newline.
func
(
t
*
Template
)
nextItem
(
st
*
state
)
[]
byte
{
func
(
t
*
Template
)
nextItem
(
st
*
state
)
[]
byte
{
...
@@ -471,6 +525,8 @@ func (t *Template) writeVariable(st *state, name_formatter string) {
...
@@ -471,6 +525,8 @@ func (t *Template) writeVariable(st *state, name_formatter string) {
panic
(
"notreached"
);
panic
(
"notreached"
);
}
}
// Execute the template. execute, executeSection and executeRepeated
// are mutually recursive.
func
(
t
*
Template
)
execute
(
st
*
state
)
{
func
(
t
*
Template
)
execute
(
st
*
state
)
{
for
{
for
{
item
:=
t
.
nextItem
(
st
);
item
:=
t
.
nextItem
(
st
);
...
@@ -512,12 +568,25 @@ func (t *Template) doParse() {
...
@@ -512,12 +568,25 @@ func (t *Template) doParse() {
// stub for now
// stub for now
}
}
// A valid delimeter must contain no white space and be non-empty.
func
validDelim
(
d
[]
byte
)
bool
{
if
len
(
d
)
==
0
{
return
false
}
for
i
,
c
:=
range
d
{
if
white
(
c
)
{
return
false
}
}
return
true
;
}
// Parse initializes a Template by parsing its definition. The string s contains
// Parse initializes a Template by parsing its definition. The string s contains
// the template text. If any errors occur, it returns the error and line number
// the template text. If any errors occur, it returns the error and line number
// in the text of the erroneous construct.
// in the text of the erroneous construct.
func
(
t
*
Template
)
Parse
(
s
string
)
(
*
os
.
Error
,
int
)
{
func
(
t
*
Template
)
Parse
(
s
string
)
(
err
*
os
.
Error
,
eline
int
)
{
if
len
(
t
.
ldelim
)
==
0
||
len
(
t
.
rdelim
)
==
0
{
if
!
validDelim
(
t
.
ldelim
)
||
!
validDelim
(
t
.
rdelim
)
{
return
Err
Empty
Delims
,
0
return
Err
Bad
Delims
,
0
}
}
t
.
init
(
io
.
StringBytes
(
s
));
t
.
init
(
io
.
StringBytes
(
s
));
ch
:=
make
(
chan
*
os
.
Error
);
ch
:=
make
(
chan
*
os
.
Error
);
...
@@ -525,11 +594,11 @@ func (t *Template) Parse(s string) (*os.Error, int) {
...
@@ -525,11 +594,11 @@ func (t *Template) Parse(s string) (*os.Error, int) {
t
.
doParse
();
t
.
doParse
();
ch
<-
nil
;
// clean return;
ch
<-
nil
;
// clean return;
}();
}();
err
:
=
<-
ch
;
err
=
<-
ch
;
if
err
!=
nil
{
if
err
!=
nil
{
return
err
,
*
t
.
linenum
return
err
,
*
t
.
linenum
}
}
return
nil
,
0
return
}
}
// Execute executes a parsed template on the specified data object,
// Execute executes a parsed template on the specified data object,
...
@@ -557,18 +626,22 @@ func New(fmap FormatterMap) *Template {
...
@@ -557,18 +626,22 @@ func New(fmap FormatterMap) *Template {
}
}
// SetDelims sets the left and right delimiters for operations in the template.
// SetDelims sets the left and right delimiters for operations in the template.
// They are validated during parsing. They could be validated here but it's
// better to keep the routine simple. The delimiters are very rarely invalid
// and Parse has the necessary error-handling interface already.
func
(
t
*
Template
)
SetDelims
(
left
,
right
string
)
{
func
(
t
*
Template
)
SetDelims
(
left
,
right
string
)
{
t
.
ldelim
=
io
.
StringBytes
(
left
);
t
.
ldelim
=
io
.
StringBytes
(
left
);
t
.
rdelim
=
io
.
StringBytes
(
right
);
t
.
rdelim
=
io
.
StringBytes
(
right
);
}
}
// Parse creates a Template with default parameters (such as {} for
// Parse creates a Template with default parameters (such as {} for
// metacharacters). The string s contains the template text and the
// metacharacters). The string s contains the template text while the
// formatter map fmap (which may be nil) defines auxiliary functions
// formatter map fmap, which may be nil, defines auxiliary functions
// for formatting variables. It returns the template, an error report
// for formatting variables. The template is returned. If any errors
// (or nil), and the line number in the text of the erroneous construct.
// occur, err will be non-nil and eline will be the line number in the
func
Parse
(
s
string
,
fmap
FormatterMap
)
(
*
Template
,
*
os
.
Error
,
int
)
{
// text of the erroneous construct.
t
:=
New
(
fmap
);
func
Parse
(
s
string
,
fmap
FormatterMap
)
(
t
*
Template
,
err
*
os
.
Error
,
eline
int
)
{
err
,
line
:=
t
.
Parse
(
s
);
t
=
New
(
fmap
);
return
t
,
err
,
line
err
,
eline
=
t
.
Parse
(
s
);
return
}
}
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