• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 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
5package lex
6
7import (
8	"strings"
9	"testing"
10	"text/scanner"
11)
12
13type lexTest struct {
14	name   string
15	input  string
16	output string
17}
18
19var lexTests = []lexTest{
20	{
21		"empty",
22		"",
23		"",
24	},
25	{
26		"simple",
27		"1 (a)",
28		"1.(.a.)",
29	},
30	{
31		"simple define",
32		lines(
33			"#define A 1234",
34			"A",
35		),
36		"1234.\n",
37	},
38	{
39		"define without value",
40		"#define A",
41		"",
42	},
43	{
44		"macro without arguments",
45		"#define A() 1234\n" + "A()\n",
46		"1234.\n",
47	},
48	{
49		"macro with just parens as body",
50		"#define A () \n" + "A\n",
51		"(.).\n",
52	},
53	{
54		"macro with parens but no arguments",
55		"#define A (x) \n" + "A\n",
56		"(.x.).\n",
57	},
58	{
59		"macro with arguments",
60		"#define A(x, y, z) x+z+y\n" + "A(1, 2, 3)\n",
61		"1.+.3.+.2.\n",
62	},
63	{
64		"argumented macro invoked without arguments",
65		lines(
66			"#define X() foo ",
67			"X()",
68			"X",
69		),
70		"foo.\n.X.\n",
71	},
72	{
73		"multiline macro without arguments",
74		lines(
75			"#define A 1\\",
76			"\t2\\",
77			"\t3",
78			"before",
79			"A",
80			"after",
81		),
82		"before.\n.1.\n.2.\n.3.\n.after.\n",
83	},
84	{
85		"multiline macro with arguments",
86		lines(
87			"#define A(a, b, c) a\\",
88			"\tb\\",
89			"\tc",
90			"before",
91			"A(1, 2, 3)",
92			"after",
93		),
94		"before.\n.1.\n.2.\n.3.\n.after.\n",
95	},
96	{
97		"LOAD macro",
98		lines(
99			"#define LOAD(off, reg) \\",
100			"\tMOVBLZX	(off*4)(R12),	reg \\",
101			"\tADDB	reg,		DX",
102			"",
103			"LOAD(8, AX)",
104		),
105		"\n.\n.MOVBLZX.(.8.*.4.).(.R12.).,.AX.\n.ADDB.AX.,.DX.\n",
106	},
107	{
108		"nested multiline macro",
109		lines(
110			"#define KEYROUND(xmm, load, off, r1, r2, index) \\",
111			"\tMOVBLZX	(BP)(DX*4),	R8 \\",
112			"\tload((off+1), r2) \\",
113			"\tMOVB	R8,		(off*4)(R12) \\",
114			"\tPINSRW	$index, (BP)(R8*4), xmm",
115			"#define LOAD(off, reg) \\",
116			"\tMOVBLZX	(off*4)(R12),	reg \\",
117			"\tADDB	reg,		DX",
118			"KEYROUND(X0, LOAD, 8, AX, BX, 0)",
119		),
120		"\n.MOVBLZX.(.BP.).(.DX.*.4.).,.R8.\n.\n.MOVBLZX.(.(.8.+.1.).*.4.).(.R12.).,.BX.\n.ADDB.BX.,.DX.\n.MOVB.R8.,.(.8.*.4.).(.R12.).\n.PINSRW.$.0.,.(.BP.).(.R8.*.4.).,.X0.\n",
121	},
122	{
123		"taken #ifdef",
124		lines(
125			"#define A",
126			"#ifdef A",
127			"#define B 1234",
128			"#endif",
129			"B",
130		),
131		"1234.\n",
132	},
133	{
134		"not taken #ifdef",
135		lines(
136			"#ifdef A",
137			"#define B 1234",
138			"#endif",
139			"B",
140		),
141		"B.\n",
142	},
143	{
144		"taken #ifdef with else",
145		lines(
146			"#define A",
147			"#ifdef A",
148			"#define B 1234",
149			"#else",
150			"#define B 5678",
151			"#endif",
152			"B",
153		),
154		"1234.\n",
155	},
156	{
157		"not taken #ifdef with else",
158		lines(
159			"#ifdef A",
160			"#define B 1234",
161			"#else",
162			"#define B 5678",
163			"#endif",
164			"B",
165		),
166		"5678.\n",
167	},
168	{
169		"nested taken/taken #ifdef",
170		lines(
171			"#define A",
172			"#define B",
173			"#ifdef A",
174			"#ifdef B",
175			"#define C 1234",
176			"#else",
177			"#define C 5678",
178			"#endif",
179			"#endif",
180			"C",
181		),
182		"1234.\n",
183	},
184	{
185		"nested taken/not-taken #ifdef",
186		lines(
187			"#define A",
188			"#ifdef A",
189			"#ifdef B",
190			"#define C 1234",
191			"#else",
192			"#define C 5678",
193			"#endif",
194			"#endif",
195			"C",
196		),
197		"5678.\n",
198	},
199	{
200		"nested not-taken/would-be-taken #ifdef",
201		lines(
202			"#define B",
203			"#ifdef A",
204			"#ifdef B",
205			"#define C 1234",
206			"#else",
207			"#define C 5678",
208			"#endif",
209			"#endif",
210			"C",
211		),
212		"C.\n",
213	},
214	{
215		"nested not-taken/not-taken #ifdef",
216		lines(
217			"#ifdef A",
218			"#ifdef B",
219			"#define C 1234",
220			"#else",
221			"#define C 5678",
222			"#endif",
223			"#endif",
224			"C",
225		),
226		"C.\n",
227	},
228	{
229		"nested #define",
230		lines(
231			"#define A #define B THIS",
232			"A",
233			"B",
234		),
235		"THIS.\n",
236	},
237	{
238		"nested #define with args",
239		lines(
240			"#define A #define B(x) x",
241			"A",
242			"B(THIS)",
243		),
244		"THIS.\n",
245	},
246	/* This one fails. See comment in Slice.Col.
247	{
248		"nested #define with args",
249		lines(
250			"#define A #define B (x) x",
251			"A",
252			"B(THIS)",
253		),
254		"x.\n",
255	},
256	*/
257}
258
259func TestLex(t *testing.T) {
260	for _, test := range lexTests {
261		input := NewInput(test.name)
262		input.Push(NewTokenizer(test.name, strings.NewReader(test.input), nil))
263		result := drain(input)
264		if result != test.output {
265			t.Errorf("%s: got %q expected %q", test.name, result, test.output)
266		}
267	}
268}
269
270// lines joins the arguments together as complete lines.
271func lines(a ...string) string {
272	return strings.Join(a, "\n") + "\n"
273}
274
275// drain returns a single string representing the processed input tokens.
276func drain(input *Input) string {
277	var buf strings.Builder
278	for {
279		tok := input.Next()
280		if tok == scanner.EOF {
281			return buf.String()
282		}
283		if tok == '#' {
284			continue
285		}
286		if buf.Len() > 0 {
287			buf.WriteByte('.')
288		}
289		buf.WriteString(input.Text())
290	}
291}
292
293type badLexTest struct {
294	input string
295	error string
296}
297
298var badLexTests = []badLexTest{
299	{
300		"3 #define foo bar\n",
301		"'#' must be first item on line",
302	},
303	{
304		"#ifdef foo\nhello",
305		"unclosed #ifdef or #ifndef",
306	},
307	{
308		"#ifndef foo\nhello",
309		"unclosed #ifdef or #ifndef",
310	},
311	{
312		"#ifdef foo\nhello\n#else\nbye",
313		"unclosed #ifdef or #ifndef",
314	},
315	{
316		"#define A() A()\nA()",
317		"recursive macro invocation",
318	},
319	{
320		"#define A a\n#define A a\n",
321		"redefinition of macro",
322	},
323	{
324		"#define A a",
325		"no newline after macro definition",
326	},
327}
328
329func TestBadLex(t *testing.T) {
330	for _, test := range badLexTests {
331		input := NewInput(test.error)
332		input.Push(NewTokenizer(test.error, strings.NewReader(test.input), nil))
333		err := firstError(input)
334		if err == nil {
335			t.Errorf("%s: got no error", test.error)
336			continue
337		}
338		if !strings.Contains(err.Error(), test.error) {
339			t.Errorf("got error %q expected %q", err.Error(), test.error)
340		}
341	}
342}
343
344// firstError returns the first error value triggered by the input.
345func firstError(input *Input) (err error) {
346	panicOnError = true
347	defer func() {
348		panicOnError = false
349		switch e := recover(); e := e.(type) {
350		case nil:
351		case error:
352			err = e
353		default:
354			panic(e)
355		}
356	}()
357
358	for {
359		tok := input.Next()
360		if tok == scanner.EOF {
361			return
362		}
363	}
364}
365