Commit c756a195 authored by Rob Pike's avatar Rob Pike

exp/template: boolean constants

R=rsc
CC=golang-dev
https://golang.org/cl/4628073
parent 7e1a3e9f
...@@ -224,7 +224,8 @@ func (s *state) evalArg(data reflect.Value, typ reflect.Type, n node) reflect.Va ...@@ -224,7 +224,8 @@ func (s *state) evalArg(data reflect.Value, typ reflect.Type, n node) reflect.Va
return value return value
} }
switch typ.Kind() { switch typ.Kind() {
// TODO: boolean case reflect.Bool:
return s.evalBool(data, typ, n)
case reflect.String: case reflect.String:
return s.evalString(data, typ, n) return s.evalString(data, typ, n)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
...@@ -240,6 +241,16 @@ func (s *state) evalArg(data reflect.Value, typ reflect.Type, n node) reflect.Va ...@@ -240,6 +241,16 @@ func (s *state) evalArg(data reflect.Value, typ reflect.Type, n node) reflect.Va
panic("not reached") panic("not reached")
} }
func (s *state) evalBool(v reflect.Value, typ reflect.Type, n node) reflect.Value {
if n, ok := n.(*boolNode); ok {
value := reflect.New(typ).Elem()
value.SetBool(n.true)
return value
}
s.errorf("expected bool; found %s", n)
panic("not reached")
}
func (s *state) evalString(v reflect.Value, typ reflect.Type, n node) reflect.Value { func (s *state) evalString(v reflect.Value, typ reflect.Type, n node) reflect.Value {
if n, ok := n.(*stringNode); ok { if n, ok := n.(*stringNode); ok {
value := reflect.New(typ).Elem() value := reflect.New(typ).Elem()
......
...@@ -24,6 +24,7 @@ type T struct { ...@@ -24,6 +24,7 @@ type T struct {
// Slices // Slices
SI []int SI []int
SEmpty []int SEmpty []int
SB []bool
// Maps // Maps
MSI map[string]int MSI map[string]int
MSIEmpty map[string]int MSIEmpty map[string]int
...@@ -63,11 +64,11 @@ func (t *T) MSort(m map[string]int) []string { ...@@ -63,11 +64,11 @@ func (t *T) MSort(m map[string]int) []string {
} }
// EPERM returns a value and an os.Error according to its argument. // EPERM returns a value and an os.Error according to its argument.
func (t *T) EPERM(a int) (int, os.Error) { func (t *T) EPERM(error bool) (bool, os.Error) {
if a == 0 { if error {
return 0, os.EPERM return true, os.EPERM
} }
return a, nil return false, nil
} }
type U struct { type U struct {
...@@ -80,6 +81,7 @@ var tVal = &T{ ...@@ -80,6 +81,7 @@ var tVal = &T{
X: "x", X: "x",
U: &U{"v"}, U: &U{"v"},
SI: []int{3, 4, 5}, SI: []int{3, 4, 5},
SB: []bool{true, false},
MSI: map[string]int{"one": 1, "two": 2, "three": 3}, MSI: map[string]int{"one": 1, "two": 2, "three": 3},
} }
...@@ -106,13 +108,14 @@ var execTests = []execTest{ ...@@ -106,13 +108,14 @@ var execTests = []execTest{
{"range empty no else", "{{range .SEmpty}}-{{.}}-{{end}}", "", tVal, true}, {"range empty no else", "{{range .SEmpty}}-{{.}}-{{end}}", "", tVal, true},
{"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true}, {"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true},
{"range empty else", "{{range .SEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, {"range empty else", "{{range .SEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
{"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true},
{"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true}, {"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true},
{"range map", "{{range .MSI | .MSort}}-{{.}}-{{end}}", "-one--three--two-", tVal, true}, {"range map", "{{range .MSI | .MSort}}-{{.}}-{{end}}", "-one--three--two-", tVal, true},
{"range empty map no else", "{{range .MSIEmpty}}-{{.}}-{{end}}", "", tVal, true}, {"range empty map no else", "{{range .MSIEmpty}}-{{.}}-{{end}}", "", tVal, true},
{"range map else", "{{range .MSI | .MSort}}-{{.}}-{{else}}EMPTY{{end}}", "-one--three--two-", tVal, true}, {"range map else", "{{range .MSI | .MSort}}-{{.}}-{{else}}EMPTY{{end}}", "-one--three--two-", tVal, true},
{"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, {"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
{"error method, no error", "{{.EPERM 1}}", "1", tVal, true}, {"error method, error", "{{.EPERM true}}", "", tVal, false},
{"error method, error", "{{.EPERM 0}}", "1", tVal, false}, {"error method, no error", "{{.EPERM false}}", "false", tVal, true},
} }
func TestExecute(t *testing.T) { func TestExecute(t *testing.T) {
...@@ -147,7 +150,7 @@ func TestExecute(t *testing.T) { ...@@ -147,7 +150,7 @@ func TestExecute(t *testing.T) {
func TestExecuteError(t *testing.T) { func TestExecuteError(t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
tmpl := New("error") tmpl := New("error")
err := tmpl.Parse("{{.EPERM 0}}") err := tmpl.Parse("{{.EPERM true}}")
if err != nil { if err != nil {
t.Fatalf("parse error: %s", err) t.Fatalf("parse error: %s", err)
} }
...@@ -155,6 +158,6 @@ func TestExecuteError(t *testing.T) { ...@@ -155,6 +158,6 @@ func TestExecuteError(t *testing.T) {
if err == nil { if err == nil {
t.Errorf("expected error; got none") t.Errorf("expected error; got none")
} else if !strings.Contains(err.String(), os.EPERM.String()) { } else if !strings.Contains(err.String(), os.EPERM.String()) {
t.Errorf("expected os.EPERM; got %s %s", err) t.Errorf("expected os.EPERM; got %s", err)
} }
} }
...@@ -35,6 +35,7 @@ type itemType int ...@@ -35,6 +35,7 @@ type itemType int
const ( const (
itemError itemType = iota // error occurred; value is text of error itemError itemType = iota // error occurred; value is text of error
itemBool // boolean constant
itemDot // the cursor, spelled '.'. itemDot // the cursor, spelled '.'.
itemEOF itemEOF
itemElse // else keyword itemElse // else keyword
...@@ -55,6 +56,7 @@ const ( ...@@ -55,6 +56,7 @@ const (
// Make the types prettyprint. // Make the types prettyprint.
var itemName = map[itemType]string{ var itemName = map[itemType]string{
itemError: "error", itemError: "error",
itemBool: "bool",
itemDot: ".", itemDot: ".",
itemEOF: "EOF", itemEOF: "EOF",
itemElse: "else", itemElse: "else",
...@@ -284,6 +286,8 @@ Loop: ...@@ -284,6 +286,8 @@ Loop:
l.emit(key[word]) l.emit(key[word])
case word[0] == '.': case word[0] == '.':
l.emit(itemField) l.emit(itemField)
case word == "true", word == "false":
l.emit(itemBool)
default: default:
l.emit(itemIdentifier) l.emit(itemIdentifier)
} }
......
...@@ -46,6 +46,13 @@ var lexTests = []lexTest{ ...@@ -46,6 +46,13 @@ var lexTests = []lexTest{
tRight, tRight,
tEOF, tEOF,
}}, }},
{"bools", "{{true false}}", []item{
tLeft,
{itemBool, "true"},
{itemBool, "false"},
tRight,
tEOF,
}},
{"dot", "{{.}}", []item{ {"dot", "{{.}}", []item{
tLeft, tLeft,
{itemDot, "."}, {itemDot, "."},
......
...@@ -198,6 +198,23 @@ func (f *fieldNode) String() string { ...@@ -198,6 +198,23 @@ func (f *fieldNode) String() string {
return fmt.Sprintf("F=%s", f.ident) return fmt.Sprintf("F=%s", f.ident)
} }
// boolNode holds a boolean constant.
type boolNode struct {
nodeType
true bool
}
func newBool(true bool) *boolNode {
return &boolNode{nodeType: nodeString, true: true}
}
func (b *boolNode) String() string {
if b.true {
return fmt.Sprintf("B=true")
}
return fmt.Sprintf("B=false")
}
// numberNode holds a number, signed or unsigned, integer, floating, or imaginary. // numberNode holds a number, signed or unsigned, integer, floating, or imaginary.
// The value is parsed and stored under all the types that can represent the value. // The value is parsed and stored under all the types that can represent the value.
// This simulates in a small amount of code the behavior of Go's ideal constants. // This simulates in a small amount of code the behavior of Go's ideal constants.
...@@ -534,6 +551,8 @@ Loop: ...@@ -534,6 +551,8 @@ Loop:
cmd.append(newDot()) cmd.append(newDot())
case itemField: case itemField:
cmd.append(newField(token.val)) cmd.append(newField(token.val))
case itemBool:
cmd.append(newBool(token.val == "true"))
case itemNumber: case itemNumber:
if len(cmd.args) == 0 { if len(cmd.args) == 0 {
t.errorf("command cannot be %q", token.val) t.errorf("command cannot be %q", token.val)
......
...@@ -151,6 +151,8 @@ var parseTests = []parseTest{ ...@@ -151,6 +151,8 @@ var parseTests = []parseTest{
`[({{range [(command: [F=[X]]) (command: [F=[M]])]}} [(text: "true")] {{else}} [(text: "false")])]`}, `[({{range [(command: [F=[X]]) (command: [F=[M]])]}} [(text: "true")] {{else}} [(text: "false")])]`},
{"range []int", "{{range .SI}}{{.}}{{end}}", noError, {"range []int", "{{range .SI}}{{.}}{{end}}", noError,
`[({{range [(command: [F=[SI]])]}} [(action: [(command: [{{<.>}}])])])]`}, `[({{range [(command: [F=[SI]])]}} [(action: [(command: [{{<.>}}])])])]`},
{"constants", "{{range .SI 1 -3.2i true false }}{{end}}", noError,
`[({{range [(command: [F=[SI] N=1 N=-3.2i B=true B=false])]}} [])]`},
// Errors. // Errors.
{"unclosed action", "hello{{range", hasError, ""}, {"unclosed action", "hello{{range", hasError, ""},
{"missing end", "hello{{range .x}}", hasError, ""}, {"missing end", "hello{{range .x}}", hasError, ""},
......
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