blob: f57dab8b23b2fbb788fba502de4774a5bc4a5550 [file] [log] [blame]
Rob Pikecd7826e2011-06-23 09:27:28 +10001// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Rob Pikec66917d2011-08-09 15:42:53 +10005package parse
Rob Pikecd7826e2011-06-23 09:27:28 +10006
7import (
Rob Pike64228e32011-07-06 17:46:36 +10008 "flag"
Rob Pikecd7826e2011-06-23 09:27:28 +10009 "fmt"
Rob Pikec66917d2011-08-09 15:42:53 +100010 "reflect"
Rob Pikecd7826e2011-06-23 09:27:28 +100011 "testing"
12)
13
Rob Pike64228e32011-07-06 17:46:36 +100014var debug = flag.Bool("debug", false, "show the errors produced by the tests")
Rob Pikecd7826e2011-06-23 09:27:28 +100015
16type numberTest struct {
17 text string
18 isInt bool
19 isUint bool
20 isFloat bool
Rob Pike13f88972011-07-04 15:15:47 +100021 isComplex bool
Rob Pikecd7826e2011-06-23 09:27:28 +100022 int64
23 uint64
24 float64
Rob Pike13f88972011-07-04 15:15:47 +100025 complex128
Rob Pikecd7826e2011-06-23 09:27:28 +100026}
27
28var numberTests = []numberTest{
29 // basics
Rob Pike13f88972011-07-04 15:15:47 +100030 {"0", true, true, true, false, 0, 0, 0, 0},
31 {"-0", true, true, true, false, 0, 0, 0, 0}, // check that -0 is a uint.
32 {"73", true, true, true, false, 73, 73, 73, 0},
Rob Pikebf9531f2011-07-11 11:46:22 +100033 {"073", true, true, true, false, 073, 073, 073, 0},
34 {"0x73", true, true, true, false, 0x73, 0x73, 0x73, 0},
Rob Pike13f88972011-07-04 15:15:47 +100035 {"-73", true, false, true, false, -73, 0, -73, 0},
36 {"+73", true, false, true, false, 73, 0, 73, 0},
37 {"100", true, true, true, false, 100, 100, 100, 0},
38 {"1e9", true, true, true, false, 1e9, 1e9, 1e9, 0},
39 {"-1e9", true, false, true, false, -1e9, 0, -1e9, 0},
40 {"-1.2", false, false, true, false, 0, 0, -1.2, 0},
41 {"1e19", false, true, true, false, 0, 1e19, 1e19, 0},
42 {"-1e19", false, false, true, false, 0, 0, -1e19, 0},
43 {"4i", false, false, false, true, 0, 0, 0, 4i},
44 {"-1.2+4.2i", false, false, false, true, 0, 0, 0, -1.2 + 4.2i},
Rob Pikebf9531f2011-07-11 11:46:22 +100045 {"073i", false, false, false, true, 0, 0, 0, 73i}, // not octal!
Rob Pike13f88972011-07-04 15:15:47 +100046 // complex with 0 imaginary are float (and maybe integer)
47 {"0i", true, true, true, true, 0, 0, 0, 0},
48 {"-1.2+0i", false, false, true, true, 0, 0, -1.2, -1.2},
49 {"-12+0i", true, false, true, true, -12, 0, -12, -12},
50 {"13+0i", true, true, true, true, 13, 13, 13, 13},
Rob Pikecd7826e2011-06-23 09:27:28 +100051 // funny bases
Rob Pike13f88972011-07-04 15:15:47 +100052 {"0123", true, true, true, false, 0123, 0123, 0123, 0},
53 {"-0x0", true, true, true, false, 0, 0, 0, 0},
54 {"0xdeadbeef", true, true, true, false, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0},
Rob Pikebf9531f2011-07-11 11:46:22 +100055 // character constants
56 {`'a'`, true, true, true, false, 'a', 'a', 'a', 0},
57 {`'\n'`, true, true, true, false, '\n', '\n', '\n', 0},
58 {`'\\'`, true, true, true, false, '\\', '\\', '\\', 0},
59 {`'\''`, true, true, true, false, '\'', '\'', '\'', 0},
60 {`'\xFF'`, true, true, true, false, 0xFF, 0xFF, 0xFF, 0},
61 {`'パ'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0},
62 {`'\u30d1'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0},
63 {`'\U000030d1'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0},
Rob Pikecd7826e2011-06-23 09:27:28 +100064 // some broken syntax
65 {text: "+-2"},
66 {text: "0x123."},
67 {text: "1e."},
68 {text: "0xi."},
Rob Pike13f88972011-07-04 15:15:47 +100069 {text: "1+2."},
Rob Pikebf9531f2011-07-11 11:46:22 +100070 {text: "'x"},
71 {text: "'xx'"},
Rob Pikecd7826e2011-06-23 09:27:28 +100072}
73
74func TestNumberParse(t *testing.T) {
75 for _, test := range numberTests {
Rob Pike13f88972011-07-04 15:15:47 +100076 // If fmt.Sscan thinks it's complex, it's complex. We can't trust the output
77 // because imaginary comes out as a number.
78 var c complex128
Rob Pikebf9531f2011-07-11 11:46:22 +100079 typ := itemNumber
80 if test.text[0] == '\'' {
Rob Pikefc1f0bd2011-07-14 13:15:55 +100081 typ = itemCharConstant
Rob Pikebf9531f2011-07-11 11:46:22 +100082 } else {
83 _, err := fmt.Sscan(test.text, &c)
84 if err == nil {
85 typ = itemComplex
86 }
87 }
88 n, err := newNumber(test.text, typ)
Rob Pike13f88972011-07-04 15:15:47 +100089 ok := test.isInt || test.isUint || test.isFloat || test.isComplex
Rob Pikecd7826e2011-06-23 09:27:28 +100090 if ok && err != nil {
Rob Pikebf9531f2011-07-11 11:46:22 +100091 t.Errorf("unexpected error for %q: %s", test.text, err)
Rob Pikecd7826e2011-06-23 09:27:28 +100092 continue
93 }
94 if !ok && err == nil {
95 t.Errorf("expected error for %q", test.text)
96 continue
97 }
98 if !ok {
Rob Pikebf9531f2011-07-11 11:46:22 +100099 if *debug {
100 fmt.Printf("%s\n\t%s\n", test.text, err)
101 }
Rob Pikecd7826e2011-06-23 09:27:28 +1000102 continue
103 }
Rob Pikec66917d2011-08-09 15:42:53 +1000104 if n.IsComplex != test.isComplex {
Rob Pike13f88972011-07-04 15:15:47 +1000105 t.Errorf("complex incorrect for %q; should be %t", test.text, test.isComplex)
Rob Pikecd7826e2011-06-23 09:27:28 +1000106 }
107 if test.isInt {
Rob Pikec66917d2011-08-09 15:42:53 +1000108 if !n.IsInt {
Rob Pikecd7826e2011-06-23 09:27:28 +1000109 t.Errorf("expected integer for %q", test.text)
110 }
Rob Pikec66917d2011-08-09 15:42:53 +1000111 if n.Int64 != test.int64 {
112 t.Errorf("int64 for %q should be %d Is %d", test.text, test.int64, n.Int64)
Rob Pikecd7826e2011-06-23 09:27:28 +1000113 }
Rob Pikec66917d2011-08-09 15:42:53 +1000114 } else if n.IsInt {
Rob Pikecd7826e2011-06-23 09:27:28 +1000115 t.Errorf("did not expect integer for %q", test.text)
116 }
117 if test.isUint {
Rob Pikec66917d2011-08-09 15:42:53 +1000118 if !n.IsUint {
Rob Pikecd7826e2011-06-23 09:27:28 +1000119 t.Errorf("expected unsigned integer for %q", test.text)
120 }
Rob Pikec66917d2011-08-09 15:42:53 +1000121 if n.Uint64 != test.uint64 {
122 t.Errorf("uint64 for %q should be %d Is %d", test.text, test.uint64, n.Uint64)
Rob Pikecd7826e2011-06-23 09:27:28 +1000123 }
Rob Pikec66917d2011-08-09 15:42:53 +1000124 } else if n.IsUint {
Rob Pikecd7826e2011-06-23 09:27:28 +1000125 t.Errorf("did not expect unsigned integer for %q", test.text)
126 }
127 if test.isFloat {
Rob Pikec66917d2011-08-09 15:42:53 +1000128 if !n.IsFloat {
Rob Pikecd7826e2011-06-23 09:27:28 +1000129 t.Errorf("expected float for %q", test.text)
130 }
Rob Pikec66917d2011-08-09 15:42:53 +1000131 if n.Float64 != test.float64 {
132 t.Errorf("float64 for %q should be %g Is %g", test.text, test.float64, n.Float64)
Rob Pikecd7826e2011-06-23 09:27:28 +1000133 }
Rob Pikec66917d2011-08-09 15:42:53 +1000134 } else if n.IsFloat {
Rob Pikecd7826e2011-06-23 09:27:28 +1000135 t.Errorf("did not expect float for %q", test.text)
136 }
Rob Pike13f88972011-07-04 15:15:47 +1000137 if test.isComplex {
Rob Pikec66917d2011-08-09 15:42:53 +1000138 if !n.IsComplex {
Rob Pike13f88972011-07-04 15:15:47 +1000139 t.Errorf("expected complex for %q", test.text)
140 }
Rob Pikec66917d2011-08-09 15:42:53 +1000141 if n.Complex128 != test.complex128 {
142 t.Errorf("complex128 for %q should be %g Is %g", test.text, test.complex128, n.Complex128)
Rob Pike13f88972011-07-04 15:15:47 +1000143 }
Rob Pikec66917d2011-08-09 15:42:53 +1000144 } else if n.IsComplex {
Rob Pike13f88972011-07-04 15:15:47 +1000145 t.Errorf("did not expect complex for %q", test.text)
146 }
Rob Pikecd7826e2011-06-23 09:27:28 +1000147 }
148}
149
Rob Pikecd7826e2011-06-23 09:27:28 +1000150type parseTest struct {
151 name string
152 input string
153 ok bool
154 result string
155}
156
157const (
158 noError = true
159 hasError = false
160)
161
162var parseTests = []parseTest{
163 {"empty", "", noError,
164 `[]`},
Rob Pikec66917d2011-08-09 15:42:53 +1000165 {"comment", "{{/*\n\n\n*/}}", noError,
166 `[]`},
Rob Pikecd7826e2011-06-23 09:27:28 +1000167 {"spaces", " \t\n", noError,
168 `[(text: " \t\n")]`},
169 {"text", "some text", noError,
170 `[(text: "some text")]`},
Rob Pike8d538c62011-07-07 10:56:33 +1000171 {"emptyAction", "{{}}", hasError,
Rob Pikecd7826e2011-06-23 09:27:28 +1000172 `[(action: [])]`},
Rob Pike81592c22011-06-28 23:04:08 +1000173 {"field", "{{.X}}", noError,
174 `[(action: [(command: [F=[X]])])]`},
Rob Pike5b165822011-07-05 16:02:34 +1000175 {"simple command", "{{printf}}", noError,
176 `[(action: [(command: [I=printf])])]`},
Rob Pikee7030e72011-07-11 10:01:15 +1000177 {"$ invocation", "{{$}}", noError,
Rob Pike7b79b3b2011-07-11 15:23:38 +1000178 "[(action: [(command: [V=[$]])])]"},
Rob Pikee7030e72011-07-11 10:01:15 +1000179 {"variable invocation", "{{with $x := 3}}{{$x 23}}{{end}}", noError,
Rob Pikefc1f0bd2011-07-14 13:15:55 +1000180 "[({{with [V=[$x]] := [(command: [N=3])]}} [(action: [(command: [V=[$x] N=23])])])]"},
Rob Pike7b79b3b2011-07-11 15:23:38 +1000181 {"variable with fields", "{{$.I}}", noError,
182 "[(action: [(command: [V=[$ I]])])]"},
Rob Pike5b165822011-07-05 16:02:34 +1000183 {"multi-word command", "{{printf `%d` 23}}", noError,
184 "[(action: [(command: [I=printf S=`%d` N=23])])]"},
185 {"pipeline", "{{.X|.Y}}", noError,
186 `[(action: [(command: [F=[X]]) (command: [F=[Y]])])]`},
Rob Pikec7057012011-07-17 13:31:59 +1000187 {"pipeline with decl", "{{$x := .X|.Y}}", noError,
188 `[(action: [V=[$x]] := [(command: [F=[X]]) (command: [F=[Y]])])]`},
Rob Pikeb8c66422011-07-08 17:54:16 +1000189 {"declaration", "{{.X|.Y}}", noError,
190 `[(action: [(command: [F=[X]]) (command: [F=[Y]])])]`},
Rob Pike13f88972011-07-04 15:15:47 +1000191 {"simple if", "{{if .X}}hello{{end}}", noError,
192 `[({{if [(command: [F=[X]])]}} [(text: "hello")])]`},
193 {"if with else", "{{if .X}}true{{else}}false{{end}}", noError,
194 `[({{if [(command: [F=[X]])]}} [(text: "true")] {{else}} [(text: "false")])]`},
Rob Pike81592c22011-06-28 23:04:08 +1000195 {"simple range", "{{range .X}}hello{{end}}", noError,
196 `[({{range [(command: [F=[X]])]}} [(text: "hello")])]`},
197 {"chained field range", "{{range .X.Y.Z}}hello{{end}}", noError,
198 `[({{range [(command: [F=[X Y Z]])]}} [(text: "hello")])]`},
199 {"nested range", "{{range .X}}hello{{range .Y}}goodbye{{end}}{{end}}", noError,
200 `[({{range [(command: [F=[X]])]}} [(text: "hello")({{range [(command: [F=[Y]])]}} [(text: "goodbye")])])]`},
201 {"range with else", "{{range .X}}true{{else}}false{{end}}", noError,
202 `[({{range [(command: [F=[X]])]}} [(text: "true")] {{else}} [(text: "false")])]`},
203 {"range over pipeline", "{{range .X|.M}}true{{else}}false{{end}}", noError,
204 `[({{range [(command: [F=[X]]) (command: [F=[M]])]}} [(text: "true")] {{else}} [(text: "false")])]`},
205 {"range []int", "{{range .SI}}{{.}}{{end}}", noError,
206 `[({{range [(command: [F=[SI]])]}} [(action: [(command: [{{<.>}}])])])]`},
Rob Pikebf9531f2011-07-11 11:46:22 +1000207 {"constants", "{{range .SI 1 -3.2i true false 'a'}}{{end}}", noError,
208 `[({{range [(command: [F=[SI] N=1 N=-3.2i B=true B=false N='a'])]}} [])]`},
Rob Pike3987b912011-07-10 07:32:01 +1000209 {"template", "{{template `x`}}", noError,
Rob Pike7aa1a1a2011-07-13 15:58:31 +1000210 `[{{template "x"}}]`},
211 {"template with arg", "{{template `x` .Y}}", noError,
212 `[{{template "x" [(command: [F=[Y]])]}}]`},
Rob Pike13f88972011-07-04 15:15:47 +1000213 {"with", "{{with .X}}hello{{end}}", noError,
214 `[({{with [(command: [F=[X]])]}} [(text: "hello")])]`},
215 {"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError,
216 `[({{with [(command: [F=[X]])]}} [(text: "hello")] {{else}} [(text: "goodbye")])]`},
Rob Pikecd7826e2011-06-23 09:27:28 +1000217 // Errors.
218 {"unclosed action", "hello{{range", hasError, ""},
Rob Pikee7030e72011-07-11 10:01:15 +1000219 {"unmatched end", "{{end}}", hasError, ""},
Rob Pikecd7826e2011-06-23 09:27:28 +1000220 {"missing end", "hello{{range .x}}", hasError, ""},
221 {"missing end after else", "hello{{range .x}}{{else}}", hasError, ""},
Rob Pike5b165822011-07-05 16:02:34 +1000222 {"undefined function", "hello{{undefined}}", hasError, ""},
Rob Pikee7030e72011-07-11 10:01:15 +1000223 {"undefined variable", "{{$x}}", hasError, ""},
224 {"variable undefined after end", "{{with $x := 4}}{{end}}{{$x}}", hasError, ""},
Rob Pike41efecf2011-07-13 13:21:18 +1000225 {"variable undefined in template", "{{template $v}}", hasError, ""},
Rob Pike7b79b3b2011-07-11 15:23:38 +1000226 {"declare with field", "{{with $x.Y := 4}}{{end}}", hasError, ""},
Rob Pike7aa1a1a2011-07-13 15:58:31 +1000227 {"template with field ref", "{{template .X}}", hasError, ""},
228 {"template with var", "{{template $v}}", hasError, ""},
Rob Pikefc1f0bd2011-07-14 13:15:55 +1000229 {"invalid punctuation", "{{printf 3, 4}}", hasError, ""},
230 {"multidecl outside range", "{{with $v, $u := 3}}{{end}}", hasError, ""},
231 {"too many decls in range", "{{range $u, $v, $w := 3}}{{end}}", hasError, ""},
Rob Pikecd7826e2011-06-23 09:27:28 +1000232}
233
Rob Pikec66917d2011-08-09 15:42:53 +1000234var builtins = map[string]reflect.Value{
235 "printf": reflect.ValueOf(fmt.Sprintf),
236}
237
Rob Pikecd7826e2011-06-23 09:27:28 +1000238func TestParse(t *testing.T) {
239 for _, test := range parseTests {
Rob Pikec66917d2011-08-09 15:42:53 +1000240 tmpl, err := New(test.name).Parse(test.input, builtins)
Rob Pikecd7826e2011-06-23 09:27:28 +1000241 switch {
242 case err == nil && !test.ok:
243 t.Errorf("%q: expected error; got none", test.name)
244 continue
245 case err != nil && test.ok:
246 t.Errorf("%q: unexpected error: %v", test.name, err)
247 continue
248 case err != nil && !test.ok:
249 // expected error, got one
Rob Pike64228e32011-07-06 17:46:36 +1000250 if *debug {
Rob Pikecd7826e2011-06-23 09:27:28 +1000251 fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
252 }
253 continue
254 }
Rob Pikec66917d2011-08-09 15:42:53 +1000255 result := tmpl.Root.String()
Rob Pikecd7826e2011-06-23 09:27:28 +1000256 if result != test.result {
257 t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.result)
258 }
259 }
260}