19
tEOF = item{itemEOF, ""}
20
tLeft = item{itemLeftDelim, "{{"}
21
tRight = item{itemRightDelim, "}}"}
22
tRange = item{itemRange, "range"}
23
tPipe = item{itemPipe, "|"}
24
tFor = item{itemIdentifier, "for"}
25
tQuote = item{itemString, `"abc \n\t\" "`}
61
tEOF = item{itemEOF, 0, ""}
62
tFor = item{itemIdentifier, 0, "for"}
63
tLeft = item{itemLeftDelim, 0, "{{"}
64
tLpar = item{itemLeftParen, 0, "("}
65
tPipe = item{itemPipe, 0, "|"}
66
tQuote = item{itemString, 0, `"abc \n\t\" "`}
67
tRange = item{itemRange, 0, "range"}
68
tRight = item{itemRightDelim, 0, "}}"}
69
tRpar = item{itemRightParen, 0, ")"}
70
tSpace = item{itemSpace, 0, " "}
26
71
raw = "`" + `abc\n\t\" ` + "`"
27
tRawQuote = item{itemRawString, raw}
72
tRawQuote = item{itemRawString, 0, raw}
30
75
var lexTests = []lexTest{
31
76
{"empty", "", []item{tEOF}},
32
{"spaces", " \t\n", []item{{itemText, " \t\n"}, tEOF}},
33
{"text", `now is the time`, []item{{itemText, "now is the time"}, tEOF}},
77
{"spaces", " \t\n", []item{{itemText, 0, " \t\n"}, tEOF}},
78
{"text", `now is the time`, []item{{itemText, 0, "now is the time"}, tEOF}},
34
79
{"text with comment", "hello-{{/* this is a comment */}}-world", []item{
39
{"punctuation", "{{,@%}}", []item{
80
{itemText, 0, "hello-"},
81
{itemText, 0, "-world"},
84
{"punctuation", "{{,@% }}", []item{
93
{"parens", "{{((3))}}", []item{
47
103
{"empty action", `{{}}`, []item{tLeft, tRight, tEOF}},
48
{"for", `{{for }}`, []item{tLeft, tFor, tRight, tEOF}},
104
{"for", `{{for}}`, []item{tLeft, tFor, tRight, tEOF}},
49
105
{"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}},
50
106
{"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, tRight, tEOF}},
51
107
{"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{
56
{itemNumber, "-7.2i"},
58
{itemNumber, "+1.2e-4"},
60
{itemComplex, "1+2i"},
109
{itemNumber, 0, "1"},
111
{itemNumber, 0, "02"},
113
{itemNumber, 0, "0x14"},
115
{itemNumber, 0, "-7.2i"},
117
{itemNumber, 0, "1e3"},
119
{itemNumber, 0, "+1.2e-4"},
121
{itemNumber, 0, "4.2i"},
123
{itemComplex, 0, "1+2i"},
64
127
{"characters", `{{'a' '\n' '\'' '\\' '\u00FF' '\xFF' '本'}}`, []item{
66
{itemCharConstant, `'a'`},
67
{itemCharConstant, `'\n'`},
68
{itemCharConstant, `'\''`},
69
{itemCharConstant, `'\\'`},
70
{itemCharConstant, `'\u00FF'`},
71
{itemCharConstant, `'\xFF'`},
72
{itemCharConstant, `'本'`},
129
{itemCharConstant, 0, `'a'`},
131
{itemCharConstant, 0, `'\n'`},
133
{itemCharConstant, 0, `'\''`},
135
{itemCharConstant, 0, `'\\'`},
137
{itemCharConstant, 0, `'\u00FF'`},
139
{itemCharConstant, 0, `'\xFF'`},
141
{itemCharConstant, 0, `'本'`},
76
145
{"bools", "{{true false}}", []item{
147
{itemBool, 0, "true"},
149
{itemBool, 0, "false"},
83
153
{"dot", "{{.}}", []item{
89
{"dots", "{{.x . .2 .x.y}}", []item{
159
{"nil", "{{nil}}", []item{
165
{"dots", "{{.x . .2 .x.y.z}}", []item{
167
{itemField, 0, ".x"},
171
{itemNumber, 0, ".2"},
173
{itemField, 0, ".x"},
174
{itemField, 0, ".y"},
175
{itemField, 0, ".z"},
98
179
{"keywords", "{{range if else end with}}", []item{
100
{itemRange, "range"},
181
{itemRange, 0, "range"},
185
{itemElse, 0, "else"},
189
{itemWith, 0, "with"},
108
193
{"variables", "{{$c := printf $ $hello $23 $ $var.Field .Method}}", []item{
110
{itemVariable, "$c"},
111
{itemColonEquals, ":="},
112
{itemIdentifier, "printf"},
114
{itemVariable, "$hello"},
115
{itemVariable, "$23"},
117
{itemVariable, "$var.Field"},
118
{itemField, ".Method"},
195
{itemVariable, 0, "$c"},
197
{itemColonEquals, 0, ":="},
199
{itemIdentifier, 0, "printf"},
201
{itemVariable, 0, "$"},
203
{itemVariable, 0, "$hello"},
205
{itemVariable, 0, "$23"},
207
{itemVariable, 0, "$"},
209
{itemVariable, 0, "$var"},
210
{itemField, 0, ".Field"},
212
{itemField, 0, ".Method"},
216
{"variable invocation", "{{$x 23}}", []item{
218
{itemVariable, 0, "$x"},
220
{itemNumber, 0, "23"},
122
224
{"pipeline", `intro {{echo hi 1.2 |noargs|args 1 "hi"}} outro`, []item{
123
{itemText, "intro "},
225
{itemText, 0, "intro "},
125
{itemIdentifier, "echo"},
126
{itemIdentifier, "hi"},
129
{itemIdentifier, "noargs"},
131
{itemIdentifier, "args"},
133
{itemString, `"hi"`},
227
{itemIdentifier, 0, "echo"},
229
{itemIdentifier, 0, "hi"},
231
{itemNumber, 0, "1.2"},
234
{itemIdentifier, 0, "noargs"},
236
{itemIdentifier, 0, "args"},
238
{itemNumber, 0, "1"},
240
{itemString, 0, `"hi"`},
135
{itemText, " outro"},
242
{itemText, 0, " outro"},
138
245
{"declaration", "{{$v := 3}}", []item{
140
{itemVariable, "$v"},
141
{itemColonEquals, ":="},
247
{itemVariable, 0, "$v"},
249
{itemColonEquals, 0, ":="},
251
{itemNumber, 0, "3"},
146
255
{"2 declarations", "{{$v , $w := 3}}", []item{
148
{itemVariable, "$v"},
150
{itemVariable, "$w"},
151
{itemColonEquals, ":="},
257
{itemVariable, 0, "$v"},
261
{itemVariable, 0, "$w"},
263
{itemColonEquals, 0, ":="},
265
{itemNumber, 0, "3"},
269
{"field of parenthesized expression", "{{(.X).Y}}", []item{
272
{itemField, 0, ".X"},
274
{itemField, 0, ".Y"},
157
279
{"badchar", "#{{\x01}}", []item{
160
{itemError, "unrecognized character in action: U+0001"},
282
{itemError, 0, "unrecognized character in action: U+0001"},
162
284
{"unclosed action", "{{\n}}", []item{
164
{itemError, "unclosed action"},
286
{itemError, 0, "unclosed action"},
166
288
{"EOF in action", "{{range", []item{
169
{itemError, "unclosed action"},
291
{itemError, 0, "unclosed action"},
171
293
{"unclosed quote", "{{\"\n\"}}", []item{
173
{itemError, "unterminated quoted string"},
295
{itemError, 0, "unterminated quoted string"},
175
297
{"unclosed raw quote", "{{`xx\n`}}", []item{
177
{itemError, "unterminated raw quoted string"},
299
{itemError, 0, "unterminated raw quoted string"},
179
301
{"unclosed char constant", "{{'\n}}", []item{
181
{itemError, "unterminated character constant"},
303
{itemError, 0, "unterminated character constant"},
183
305
{"bad number", "{{3k}}", []item{
185
{itemError, `bad number syntax: "3k"`},
307
{itemError, 0, `bad number syntax: "3k"`},
309
{"unclosed paren", "{{(3}}", []item{
312
{itemNumber, 0, "3"},
313
{itemError, 0, `unclosed left paren`},
315
{"extra right paren", "{{3)}}", []item{
317
{itemNumber, 0, "3"},
319
{itemError, 0, `unexpected right paren U+0029 ')'`},
226
382
var lexDelimTests = []lexTest{
227
383
{"punctuation", "$$,@%{{}}@@", []item{
239
395
{"empty action", `$$@@`, []item{tLeftDelim, tRightDelim, tEOF}},
240
{"for", `$$for @@`, []item{tLeftDelim, tFor, tRightDelim, tEOF}},
396
{"for", `$$for@@`, []item{tLeftDelim, tFor, tRightDelim, tEOF}},
241
397
{"quote", `$$"abc \n\t\" "@@`, []item{tLeftDelim, tQuote, tRightDelim, tEOF}},
242
398
{"raw quote", "$$" + raw + "@@", []item{tLeftDelim, tRawQuote, tRightDelim, tEOF}},
246
tLeftDelim = item{itemLeftDelim, "$$"}
247
tRightDelim = item{itemRightDelim, "@@"}
402
tLeftDelim = item{itemLeftDelim, 0, "$$"}
403
tRightDelim = item{itemRightDelim, 0, "@@"}
250
406
func TestDelims(t *testing.T) {
251
407
for _, test := range lexDelimTests {
252
408
items := collect(&test, "$$", "@@")
253
if !reflect.DeepEqual(items, test.items) {
254
t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
409
if !equal(items, test.items, false) {
410
t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
415
var lexPosTests = []lexTest{
416
{"empty", "", []item{tEOF}},
417
{"punctuation", "{{,@%#}}", []item{
418
{itemLeftDelim, 0, "{{"},
423
{itemRightDelim, 6, "}}"},
426
{"sample", "0123{{hello}}xyz", []item{
427
{itemText, 0, "0123"},
428
{itemLeftDelim, 4, "{{"},
429
{itemIdentifier, 6, "hello"},
430
{itemRightDelim, 11, "}}"},
431
{itemText, 13, "xyz"},
436
// The other tests don't check position, to make the test cases easier to construct.
438
func TestPos(t *testing.T) {
439
for _, test := range lexPosTests {
440
items := collect(&test, "", "")
441
if !equal(items, test.items, true) {
442
t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
443
if len(items) == len(test.items) {
444
// Detailed print; avoid item.String() to expose the position value.
445
for i := range items {
446
if !equal(items[i:i+1], test.items[i:i+1], true) {
449
t.Errorf("\t#%d: got {%v %d %q} expected {%v %d %q}", i, i1.typ, i1.pos, i1.val, i2.typ, i2.pos, i2.val)