var ErrNoVar = os.NewError("variable name not in struct");
var ErrBadType = os.NewError("unsupported type for variable");
var ErrNotStruct = os.NewError("driver must be a struct")
+var ErrNoFormatter = os.NewError("unknown formatter")
// All the literals are aces.
var lbrace = []byte{ '{' }
Variable;
)
+// FormatterMap is the type describing the mapping from formatter
+// names to the functions that implement them.
+type FormatterMap map[string] func(reflect.Value) string
+
type template struct {
errorchan chan *os.Error; // for erroring out
linenum *int; // shared by all templates derived from this one
parent *template;
data reflect.Value; // the driver data for this section etc.
+ fmap FormatterMap; // formatters for variables
buf []byte; // input text to process
p int; // position in buf
wr io.Write; // where to send output
}
// Create a top-level template
-func newTemplate(ch chan *os.Error, linenum *int, buf []byte, data reflect.Value, wr io.Write) *template {
+func newTemplate(ch chan *os.Error, linenum *int, buf []byte, data reflect.Value, fmap FormatterMap, wr io.Write) *template {
t := new(template);
t.errorchan = ch;
t.linenum = linenum;
t.data = data;
t.buf = buf;
t.p = 0;
+ t.fmap = fmap;
t.wr = wr;
return t;
}
// Create a template deriving from its parent
func childTemplate(parent *template, buf []byte, data reflect.Value) *template {
- t := newTemplate(parent.errorchan, parent.linenum, buf, data, parent.wr);
+ t := newTemplate(parent.errorchan, parent.linenum, buf, data, parent.fmap, parent.wr);
t.parent = parent;
return t;
}
return c == ' ' || c == '\t' || c == '\n'
}
-// Data items can be values or pointers to values. This function hides the pointer.
-func indirect(v reflect.Value) reflect.Value {
- if v.Kind() == reflect.PtrKind {
- p := v.(reflect.PtrValue);
- if p.Get() == nil {
- return nil
- }
- v = p.Sub()
- }
- return v
-}
-
func (t *template) execute()
func (t *template) executeSection(w []string)
// Is there no data to look at?
func empty(v reflect.Value, indirect_ok bool) bool {
- v = indirect(v);
+ v = reflect.Indirect(v);
if v == nil {
return true
}
if i < 0 {
t.error(ErrNoVar, ": ", w[2]);
}
- field = indirect(t.data.(reflect.StructValue).Field(i));
+ field = reflect.Indirect(t.data.(reflect.StructValue).Field(i));
}
// Must be an array/slice
if field != nil && field.Kind() != reflect.ArrayKind {
if field != nil {
array := field.(reflect.ArrayValue);
for i := 0; i < array.Len(); i++ {
- elem := indirect(array.Elem(i));
+ elem := reflect.Indirect(array.Elem(i));
tmp := childTemplate(t, t.buf[start:end], elem);
tmp.execute();
}
tmp.execute();
}
-// Evalute a variable, looking up through the parent if necessary.
-// TODO: add formatting outputters
-func (t *template) evalVariable(s string) string {
- i, kind := t.findVar(s);
+// Look up a variable, up through the parent if necessary.
+func (t *template) varValue(name string) reflect.Value {
+ i, kind := t.findVar(name);
if i < 0 {
if t.parent == nil {
- t.error(ErrNoVar, ": ", s)
+ t.error(ErrNoVar, ": ", name)
}
- return t.parent.evalVariable(s);
+ return t.parent.varValue(name);
+ }
+ return t.data.(reflect.StructValue).Field(i);
+}
+
+// Evalute a variable, looking up through the parent if necessary.
+// If it has a formatter attached ({var|formatter}) run that too.
+func (t *template) evalVariable(name_formatter string) string {
+ name := name_formatter;
+ formatter := "";
+ bar := strings.Index(name_formatter, "|");
+ if bar >= 0 {
+ name = name_formatter[0:bar];
+ formatter = name_formatter[bar+1:len(name_formatter)];
+ }
+ val := t.varValue(name);
+ if fn, ok := t.fmap[formatter]; ok {
+ return fn(val)
+ }
+ if formatter == "" {
+ return fmt.Sprint(val.Interface())
}
- return fmt.Sprint(t.data.(reflect.StructValue).Field(i).Interface());
+ t.error(ErrNoFormatter, ": ", formatter);
+ panic("notreached");
}
func (t *template) execute() {
}
}
-func Execute(s string, data interface{}, wr io.Write) *os.Error {
+func Execute(s string, data interface{}, fmap FormatterMap, wr io.Write) *os.Error {
// Extract the driver struct.
- val := indirect(reflect.NewValue(data));
+ val := reflect.Indirect(reflect.NewValue(data));
sval, ok1 := val.(reflect.StructValue);
if !ok1 {
return ErrNotStruct
}
ch := make(chan *os.Error);
var linenum int;
- t := newTemplate(ch, &linenum, io.StringBytes(s), val, wr);
+ t := newTemplate(ch, &linenum, io.StringBytes(s), val, fmap, wr);
go func() {
t.execute();
ch <- nil; // clean return;
"fmt";
"io";
"os";
+ "reflect";
"template";
"testing";
)
var t1 = T{ "ItemNumber1", "ValueNumber1" }
var t2 = T{ "ItemNumber2", "ValueNumber2" }
+func uppercase(v reflect.Value) string {
+ s := reflect.Indirect(v).(reflect.StringValue).Get();
+ t := "";
+ for i := 0; i < len(s); i++ {
+ c := s[i];
+ if 'a' <= c && c <= 'z' {
+ c = c + 'A' - 'a'
+ }
+ t += string(c);
+ }
+ return t;
+}
+
+func plus1(v reflect.Value) string {
+ i := reflect.Indirect(v).(reflect.IntValue).Get();
+ return fmt.Sprint(i + 1);
+}
+
+var formatters = FormatterMap {
+ "uppercase" : uppercase,
+ "+1" : plus1,
+}
+
var tests = []*Test {
// Simple
&Test{ "", "" },
"ItemNumber1=ValueNumber1\n"
"ItemNumber2=ValueNumber2\n"
},
+
+ // Formatters
+ &Test{
+ "{.section pdata }\n"
+ "{header|uppercase}={integer|+1}\n"
+ "{.end}\n",
+
+ "HEADER=78\n"
+ },
}
func TestAll(t *testing.T) {
var buf io.ByteBuffer;
for i, test := range tests {
buf.Reset();
- err := Execute(test.in, s, &buf);
+ err := Execute(test.in, s, formatters, &buf);
if err != nil {
t.Error("unexpected error:", err)
}
}
}
-/*
-func TestParser(t *testing.T) {
- t1 := &T{ "ItemNumber1", "ValueNumber1" };
- t2 := &T{ "ItemNumber2", "ValueNumber2" };
- a := []*T{ t1, t2 };
- s := &S{ "Header", 77, a };
- err := Execute(
- "{#hello world}\n"
- "some text: {.meta-left}{.space}{.meta-right}\n"
- "{.meta-left}\n"
- "{.meta-right}\n"
- "{.section data }\n"
- "some text for the section\n"
- "{header} for iteration number {integer}\n"
- " {.repeated section @}\n"
- "repeated section: {value1}={value2}\n"
- " {.end}\n"
- "{.or}\n"
- "This appears only if there is no data\n"
- "{.end }\n"
- "this is the end\n"
- , s, os.Stdout);
- if err != nil {
- t.Error(err)
- }
-}
-*/
-
func TestBadDriverType(t *testing.T) {
- err := Execute("hi", "hello", os.Stdout);
+ err := Execute("hi", "hello", nil, os.Stdout);
if err == nil {
t.Error("failed to detect string as driver type")
}