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
effddcad
Commit
effddcad
authored
Apr 13, 2010
by
Rob Pike
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
template: use panic/recover to simplify internal error handling.
R=rsc CC=golang-dev
https://golang.org/cl/824049
parent
cd242fb4
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
32 additions
and
50 deletions
+32
-50
template.go
src/pkg/template/template.go
+32
-50
No files found.
src/pkg/template/template.go
View file @
effddcad
...
@@ -69,7 +69,6 @@ import (
...
@@ -69,7 +69,6 @@ import (
"io/ioutil"
"io/ioutil"
"os"
"os"
"reflect"
"reflect"
"runtime"
"strings"
"strings"
)
)
...
@@ -153,11 +152,10 @@ type repeatedElement struct {
...
@@ -153,11 +152,10 @@ type repeatedElement struct {
type
Template
struct
{
type
Template
struct
{
fmap
FormatterMap
// formatters for variables
fmap
FormatterMap
// formatters for variables
// Used during parsing:
// Used during parsing:
ldelim
,
rdelim
[]
byte
// delimiters; default {}
ldelim
,
rdelim
[]
byte
// delimiters; default {}
buf
[]
byte
// input text to process
buf
[]
byte
// input text to process
p
int
// position in buf
p
int
// position in buf
linenum
int
// position in input
linenum
int
// position in input
error
os
.
Error
// error during parsing (only)
// Parsed results:
// Parsed results:
elems
*
vector
.
Vector
elems
*
vector
.
Vector
}
}
...
@@ -169,11 +167,10 @@ type state struct {
...
@@ -169,11 +167,10 @@ type state struct {
parent
*
state
// parent in hierarchy
parent
*
state
// parent in hierarchy
data
reflect
.
Value
// the driver data for this section etc.
data
reflect
.
Value
// the driver data for this section etc.
wr
io
.
Writer
// where to send output
wr
io
.
Writer
// where to send output
errors
chan
os
.
Error
// for reporting errors during execute
}
}
func
(
parent
*
state
)
clone
(
data
reflect
.
Value
)
*
state
{
func
(
parent
*
state
)
clone
(
data
reflect
.
Value
)
*
state
{
return
&
state
{
parent
,
data
,
parent
.
wr
,
parent
.
errors
}
return
&
state
{
parent
,
data
,
parent
.
wr
}
}
}
// New creates a new template with the specified formatter map (which
// New creates a new template with the specified formatter map (which
...
@@ -189,14 +186,13 @@ func New(fmap FormatterMap) *Template {
...
@@ -189,14 +186,13 @@ func New(fmap FormatterMap) *Template {
// Report error and stop executing. The line number must be provided explicitly.
// Report error and stop executing. The line number must be provided explicitly.
func
(
t
*
Template
)
execError
(
st
*
state
,
line
int
,
err
string
,
args
...
interface
{})
{
func
(
t
*
Template
)
execError
(
st
*
state
,
line
int
,
err
string
,
args
...
interface
{})
{
st
.
errors
<-
&
Error
{
line
,
fmt
.
Sprintf
(
err
,
args
)}
panic
(
&
Error
{
line
,
fmt
.
Sprintf
(
err
,
args
)})
runtime
.
Goexit
()
}
}
// Report error,
save in Template
to terminate parsing.
// Report error,
panic
to terminate parsing.
// The line number comes from the template state.
// The line number comes from the template state.
func
(
t
*
Template
)
parseError
(
err
string
,
args
...
interface
{})
{
func
(
t
*
Template
)
parseError
(
err
string
,
args
...
interface
{})
{
t
.
error
=
&
Error
{
t
.
linenum
,
fmt
.
Sprintf
(
err
,
args
)}
panic
(
&
Error
{
t
.
linenum
,
fmt
.
Sprintf
(
err
,
args
)})
}
}
// -- Lexical analysis
// -- Lexical analysis
...
@@ -427,9 +423,6 @@ func (t *Template) newVariable(name_formatter string) (v *variableElement) {
...
@@ -427,9 +423,6 @@ func (t *Template) newVariable(name_formatter string) (v *variableElement) {
// Otherwise return its details.
// Otherwise return its details.
func
(
t
*
Template
)
parseSimple
(
item
[]
byte
)
(
done
bool
,
tok
int
,
w
[]
string
)
{
func
(
t
*
Template
)
parseSimple
(
item
[]
byte
)
(
done
bool
,
tok
int
,
w
[]
string
)
{
tok
,
w
=
t
.
analyze
(
item
)
tok
,
w
=
t
.
analyze
(
item
)
if
t
.
error
!=
nil
{
return
}
done
=
true
// assume for simplicity
done
=
true
// assume for simplicity
switch
tok
{
switch
tok
{
case
tokComment
:
case
tokComment
:
...
@@ -449,7 +442,6 @@ func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) {
...
@@ -449,7 +442,6 @@ func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) {
t
.
elems
.
Push
(
&
literalElement
{
tab
})
t
.
elems
.
Push
(
&
literalElement
{
tab
})
default
:
default
:
t
.
parseError
(
"internal error: unknown literal: %s"
,
w
[
0
])
t
.
parseError
(
"internal error: unknown literal: %s"
,
w
[
0
])
return
}
}
return
return
case
tokVariable
:
case
tokVariable
:
...
@@ -472,19 +464,13 @@ func (t *Template) parseRepeated(words []string) *repeatedElement {
...
@@ -472,19 +464,13 @@ func (t *Template) parseRepeated(words []string) *repeatedElement {
r
.
altstart
=
-
1
r
.
altstart
=
-
1
r
.
altend
=
-
1
r
.
altend
=
-
1
Loop
:
Loop
:
for
t
.
error
==
nil
{
for
{
item
:=
t
.
nextItem
()
item
:=
t
.
nextItem
()
if
t
.
error
!=
nil
{
break
}
if
len
(
item
)
==
0
{
if
len
(
item
)
==
0
{
t
.
parseError
(
"missing .end for .repeated section"
)
t
.
parseError
(
"missing .end for .repeated section"
)
break
break
}
}
done
,
tok
,
w
:=
t
.
parseSimple
(
item
)
done
,
tok
,
w
:=
t
.
parseSimple
(
item
)
if
t
.
error
!=
nil
{
break
}
if
done
{
if
done
{
continue
continue
}
}
...
@@ -517,9 +503,6 @@ Loop:
...
@@ -517,9 +503,6 @@ Loop:
break
Loop
break
Loop
}
}
}
}
if
t
.
error
!=
nil
{
return
nil
}
if
r
.
altend
<
0
{
if
r
.
altend
<
0
{
r
.
altend
=
t
.
elems
.
Len
()
r
.
altend
=
t
.
elems
.
Len
()
}
}
...
@@ -536,19 +519,13 @@ func (t *Template) parseSection(words []string) *sectionElement {
...
@@ -536,19 +519,13 @@ func (t *Template) parseSection(words []string) *sectionElement {
s
.
start
=
t
.
elems
.
Len
()
s
.
start
=
t
.
elems
.
Len
()
s
.
or
=
-
1
s
.
or
=
-
1
Loop
:
Loop
:
for
t
.
error
==
nil
{
for
{
item
:=
t
.
nextItem
()
item
:=
t
.
nextItem
()
if
t
.
error
!=
nil
{
break
}
if
len
(
item
)
==
0
{
if
len
(
item
)
==
0
{
t
.
parseError
(
"missing .end for .section"
)
t
.
parseError
(
"missing .end for .section"
)
break
break
}
}
done
,
tok
,
w
:=
t
.
parseSimple
(
item
)
done
,
tok
,
w
:=
t
.
parseSimple
(
item
)
if
t
.
error
!=
nil
{
break
}
if
done
{
if
done
{
continue
continue
}
}
...
@@ -571,19 +548,13 @@ Loop:
...
@@ -571,19 +548,13 @@ Loop:
t
.
parseError
(
"internal error: unknown section item: %s"
,
item
)
t
.
parseError
(
"internal error: unknown section item: %s"
,
item
)
}
}
}
}
if
t
.
error
!=
nil
{
return
nil
}
s
.
end
=
t
.
elems
.
Len
()
s
.
end
=
t
.
elems
.
Len
()
return
s
return
s
}
}
func
(
t
*
Template
)
parse
()
{
func
(
t
*
Template
)
parse
()
{
for
t
.
error
==
nil
{
for
{
item
:=
t
.
nextItem
()
item
:=
t
.
nextItem
()
if
t
.
error
!=
nil
{
break
}
if
len
(
item
)
==
0
{
if
len
(
item
)
==
0
{
break
break
}
}
...
@@ -909,37 +880,48 @@ func validDelim(d []byte) bool {
...
@@ -909,37 +880,48 @@ func validDelim(d []byte) bool {
return
true
return
true
}
}
// checkError is a deferred function to turn a panic with type *Error into a plain error return.
// Other panics are unexpected and so are re-enabled.
func
checkError
(
error
*
os
.
Error
)
{
if
v
:=
recover
();
v
!=
nil
{
if
e
,
ok
:=
v
.
(
*
Error
);
ok
{
*
error
=
e
}
else
{
// runtime errors should crash
panic
(
v
)
}
}
}
// -- Public interface
// -- Public interface
// Parse initializes a Template by parsing its definition. The string
// Parse initializes a Template by parsing its definition. The string
// s contains the template text. If any errors occur, Parse returns
// s contains the template text. If any errors occur, Parse returns
// the error.
// the error.
func
(
t
*
Template
)
Parse
(
s
string
)
os
.
Error
{
func
(
t
*
Template
)
Parse
(
s
string
)
(
err
os
.
Error
)
{
if
t
.
elems
==
nil
{
if
t
.
elems
==
nil
{
return
&
Error
{
1
,
"template not allocated with New"
}
return
&
Error
{
1
,
"template not allocated with New"
}
}
}
if
!
validDelim
(
t
.
ldelim
)
||
!
validDelim
(
t
.
rdelim
)
{
if
!
validDelim
(
t
.
ldelim
)
||
!
validDelim
(
t
.
rdelim
)
{
return
&
Error
{
1
,
fmt
.
Sprintf
(
"bad delimiter strings %q %q"
,
t
.
ldelim
,
t
.
rdelim
)}
return
&
Error
{
1
,
fmt
.
Sprintf
(
"bad delimiter strings %q %q"
,
t
.
ldelim
,
t
.
rdelim
)}
}
}
defer
checkError
(
&
err
)
t
.
buf
=
[]
byte
(
s
)
t
.
buf
=
[]
byte
(
s
)
t
.
p
=
0
t
.
p
=
0
t
.
linenum
=
1
t
.
linenum
=
1
t
.
parse
()
t
.
parse
()
return
t
.
error
return
nil
}
}
// Execute applies a parsed template to the specified data object,
// Execute applies a parsed template to the specified data object,
// generating output to wr.
// generating output to wr.
func
(
t
*
Template
)
Execute
(
data
interface
{},
wr
io
.
Writer
)
os
.
Error
{
func
(
t
*
Template
)
Execute
(
data
interface
{},
wr
io
.
Writer
)
(
err
os
.
Error
)
{
// Extract the driver data.
// Extract the driver data.
val
:=
reflect
.
NewValue
(
data
)
val
:=
reflect
.
NewValue
(
data
)
errors
:=
make
(
chan
os
.
Error
)
defer
checkError
(
&
err
)
go
func
()
{
t
.
p
=
0
t
.
p
=
0
t
.
execute
(
0
,
t
.
elems
.
Len
(),
&
state
{
nil
,
val
,
wr
})
t
.
execute
(
0
,
t
.
elems
.
Len
(),
&
state
{
nil
,
val
,
wr
,
errors
})
return
nil
errors
<-
nil
// clean return;
}()
return
<-
errors
}
}
// SetDelims sets the left and right delimiters for operations in the
// SetDelims sets the left and right delimiters for operations in the
...
...
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