Commit e21d981a authored by Russ Cox's avatar Russ Cox

add type in not-found error messages.

delay indirection so that values passed to
formatters preserve pointer-ness.

R=r
OCL=27410
CL=27414
parent ff12f2ef
...@@ -268,17 +268,22 @@ func (t *Template) analyze(item []byte, st *state) (tok int, w []string) { ...@@ -268,17 +268,22 @@ func (t *Template) analyze(item []byte, st *state) (tok int, w []string) {
} }
// If the data for this template is a struct, find the named variable. // If the data for this template is a struct, find the named variable.
func (st *state) findVar(s string) (int, int) { // The special name "@" denotes the current data.
typ, ok := st.data.Type().(reflect.StructType); func (st *state) findVar(s string) reflect.Value {
if s == "@" {
return st.data
}
data := reflect.Indirect(st.data);
typ, ok := data.Type().(reflect.StructType);
if ok { if ok {
for i := 0; i < typ.Len(); i++ { for i := 0; i < typ.Len(); i++ {
name, ftyp, tag, offset := typ.Field(i); name, ftyp, tag, offset := typ.Field(i);
if name == s { if name == s {
return i, ftyp.Kind() return data.(reflect.StructValue).Field(i)
} }
} }
} }
return -1, -1 return nil
} }
// Is there no data to look at? // Is there no data to look at?
...@@ -301,18 +306,13 @@ func (t *Template) executeRepeated(w []string, st *state) { ...@@ -301,18 +306,13 @@ func (t *Template) executeRepeated(w []string, st *state) {
if w[1] != "section" { if w[1] != "section" {
st.error(ErrSyntax, `: .repeated must have "section"`) st.error(ErrSyntax, `: .repeated must have "section"`)
} }
// Find driver array/struct for this section. It must be in the current struct. // Find driver array/struct for this section. It must be in the current struct.
// The special name "@" leaves us at this level. field := st.findVar(w[2]);
var field reflect.Value; if field == nil {
if w[2] == "@" { st.error(ErrNoVar, ": .repeated ", w[2], " in ", reflect.Indirect(st.data).Type());
field = st.data
} else {
i, kind := st.findVar(w[1]);
if i < 0 {
st.error(ErrNoVar, ": ", w[2]);
}
field = reflect.Indirect(st.data.(reflect.StructValue).Field(i));
} }
// Must be an array/slice // Must be an array/slice
if field != nil && field.Kind() != reflect.ArrayKind { if field != nil && field.Kind() != reflect.ArrayKind {
st.error(ErrBadType, " in .repeated: ", w[2], " ", field.Type().String()); st.error(ErrBadType, " in .repeated: ", w[2], " ", field.Type().String());
...@@ -349,24 +349,17 @@ Loop: ...@@ -349,24 +349,17 @@ Loop:
array := field.(reflect.ArrayValue); array := field.(reflect.ArrayValue);
for i := 0; i < array.Len(); i++ { for i := 0; i < array.Len(); i++ {
tmp := childTemplate(t, t.buf[start:end]); tmp := childTemplate(t, t.buf[start:end]);
tmp.execute(&state{st, st.errorchan, reflect.Indirect(array.Elem(i)), st.wr}); tmp.execute(&state{st, st.errorchan, array.Elem(i), st.wr});
} }
} }
} }
// Execute a ".section" // Execute a ".section"
func (t *Template) executeSection(w []string, st *state) { func (t *Template) executeSection(w []string, st *state) {
// Find driver array/struct for this section. It must be in the current struct. // Find driver data for this section. It must be in the current struct.
// The special name "@" leaves us at this level. field := st.findVar(w[1]);
var field reflect.Value; if field == nil {
if w[1] == "@" { st.error(ErrNoVar, ": .section ", w[1], " in ", reflect.Indirect(st.data).Type());
field = st.data
} else {
i, kind := st.findVar(w[1]);
if i < 0 {
st.error(ErrNoVar, ": ", w[1]);
}
field = st.data.(reflect.StructValue).Field(i);
} }
// Scan section, remembering slice of text we must execute. // Scan section, remembering slice of text we must execute.
orFound := false; orFound := false;
...@@ -424,14 +417,14 @@ Loop: ...@@ -424,14 +417,14 @@ Loop:
// Look up a variable, up through the parent if necessary. // Look up a variable, up through the parent if necessary.
func (t *Template) varValue(name string, st *state) reflect.Value { func (t *Template) varValue(name string, st *state) reflect.Value {
i, kind := st.findVar(name); field := st.findVar(name);
if i < 0 { if field == nil {
if st.parent == nil { if st.parent == nil {
st.error(ErrNoVar, ": ", name) st.error(ErrNoVar, ": ", name)
} }
return t.varValue(name, st.parent); return t.varValue(name, st.parent);
} }
return st.data.(reflect.StructValue).Field(i); return field;
} }
// Evaluate a variable, looking up through the parent if necessary. // Evaluate a variable, looking up through the parent if necessary.
...@@ -517,12 +510,8 @@ func Parse(s string, fmap FormatterMap) (*Template, *os.Error, int) { ...@@ -517,12 +510,8 @@ func Parse(s string, fmap FormatterMap) (*Template, *os.Error, int) {
} }
func (t *Template) Execute(data interface{}, wr io.Write) *os.Error { func (t *Template) Execute(data interface{}, wr io.Write) *os.Error {
// Extract the driver struct. // Extract the driver data.
val := reflect.Indirect(reflect.NewValue(data)); val := reflect.NewValue(data);
sval, ok1 := val.(reflect.StructValue);
if !ok1 {
return ErrNotStruct
}
ch := make(chan *os.Error); ch := make(chan *os.Error);
go func() { go func() {
t.execute(&state{nil, ch, val, wr}); t.execute(&state{nil, ch, val, wr});
......
...@@ -192,13 +192,15 @@ func TestAll(t *testing.T) { ...@@ -192,13 +192,15 @@ func TestAll(t *testing.T) {
} }
} }
func TestBadDriverType(t *testing.T) { func TestStringDriverType(t *testing.T) {
tmpl, err, line := Parse("hi", nil); tmpl, err, line := Parse("template: {@}", nil);
if err != nil { if err != nil {
t.Error("unexpected parse error:", err) t.Error("unexpected parse error:", err)
} }
err = tmpl.Execute("hi", nil); var b io.ByteBuffer;
if err == nil { err = tmpl.Execute("hello", &b);
t.Error("failed to detect string as driver type") s := string(b.Data());
if s != "template: hello" {
t.Errorf("failed passing string as data: expected %q got %q", "template: hello", s)
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment