Change next_proto member type.
[chromium-blink-merge.git] / tools / gn / parser_unittest.cc
blob4ebefb20f89d323ada9b0fcb0804eec853606fd6
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include <iostream>
6 #include <sstream>
8 #include "testing/gtest/include/gtest/gtest.h"
9 #include "tools/gn/input_file.h"
10 #include "tools/gn/parser.h"
11 #include "tools/gn/tokenizer.h"
13 namespace {
15 bool GetTokens(const InputFile* input, std::vector<Token>* result) {
16 result->clear();
17 Err err;
18 *result = Tokenizer::Tokenize(input, &err);
19 return !err.has_error();
22 void DoParserPrintTest(const char* input, const char* expected) {
23 std::vector<Token> tokens;
24 InputFile input_file(SourceFile("/test"));
25 input_file.SetContents(input);
26 ASSERT_TRUE(GetTokens(&input_file, &tokens));
28 Err err;
29 scoped_ptr<ParseNode> result = Parser::Parse(tokens, &err);
30 if (!result)
31 err.PrintToStdout();
32 ASSERT_TRUE(result);
34 std::ostringstream collector;
35 result->Print(collector, 0);
37 EXPECT_EQ(expected, collector.str());
40 void DoExpressionPrintTest(const char* input, const char* expected) {
41 std::vector<Token> tokens;
42 InputFile input_file(SourceFile("/test"));
43 input_file.SetContents(input);
44 ASSERT_TRUE(GetTokens(&input_file, &tokens));
46 Err err;
47 scoped_ptr<ParseNode> result = Parser::ParseExpression(tokens, &err);
48 ASSERT_TRUE(result);
50 std::ostringstream collector;
51 result->Print(collector, 0);
53 EXPECT_EQ(expected, collector.str());
56 // Expects the tokenizer or parser to identify an error at the given line and
57 // character.
58 void DoParserErrorTest(const char* input, int err_line, int err_char) {
59 InputFile input_file(SourceFile("/test"));
60 input_file.SetContents(input);
62 Err err;
63 std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
64 if (!err.has_error()) {
65 scoped_ptr<ParseNode> result = Parser::Parse(tokens, &err);
66 ASSERT_FALSE(result);
67 ASSERT_TRUE(err.has_error());
70 EXPECT_EQ(err_line, err.location().line_number());
71 EXPECT_EQ(err_char, err.location().char_offset());
74 // Expects the tokenizer or parser to identify an error at the given line and
75 // character.
76 void DoExpressionErrorTest(const char* input, int err_line, int err_char) {
77 InputFile input_file(SourceFile("/test"));
78 input_file.SetContents(input);
80 Err err;
81 std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
82 if (!err.has_error()) {
83 scoped_ptr<ParseNode> result = Parser::ParseExpression(tokens, &err);
84 ASSERT_FALSE(result);
85 ASSERT_TRUE(err.has_error());
88 EXPECT_EQ(err_line, err.location().line_number());
89 EXPECT_EQ(err_char, err.location().char_offset());
92 } // namespace
94 TEST(Parser, Literal) {
95 DoExpressionPrintTest("5", "LITERAL(5)\n");
96 DoExpressionPrintTest("\"stuff\"", "LITERAL(\"stuff\")\n");
99 TEST(Parser, BinaryOp) {
100 // TODO(scottmg): The tokenizer is dumb, and treats "5-1" as two integers,
101 // not a binary operator between two positive integers.
102 DoExpressionPrintTest("5 - 1",
103 "BINARY(-)\n"
104 " LITERAL(5)\n"
105 " LITERAL(1)\n");
106 DoExpressionPrintTest("5+1",
107 "BINARY(+)\n"
108 " LITERAL(5)\n"
109 " LITERAL(1)\n");
110 DoExpressionPrintTest("5 - 1 - 2",
111 "BINARY(-)\n"
112 " BINARY(-)\n"
113 " LITERAL(5)\n"
114 " LITERAL(1)\n"
115 " LITERAL(2)\n");
118 TEST(Parser, FunctionCall) {
119 DoExpressionPrintTest("foo()",
120 "FUNCTION(foo)\n"
121 " LIST\n");
122 DoExpressionPrintTest("blah(1, 2)",
123 "FUNCTION(blah)\n"
124 " LIST\n"
125 " LITERAL(1)\n"
126 " LITERAL(2)\n");
127 DoExpressionErrorTest("foo(1, 2,)", 1, 10);
128 DoExpressionErrorTest("foo(1 2)", 1, 7);
131 TEST(Parser, ParenExpression) {
132 const char* input = "(foo(1)) + (a + (b - c) + d)";
133 const char* expected =
134 "BINARY(+)\n"
135 " FUNCTION(foo)\n"
136 " LIST\n"
137 " LITERAL(1)\n"
138 " BINARY(+)\n"
139 " BINARY(+)\n"
140 " IDENTIFIER(a)\n"
141 " BINARY(-)\n"
142 " IDENTIFIER(b)\n"
143 " IDENTIFIER(c)\n"
144 " IDENTIFIER(d)\n";
145 DoExpressionPrintTest(input, expected);
146 DoExpressionErrorTest("(a +", 1, 4);
149 TEST(Parser, OrderOfOperationsLeftAssociative) {
150 const char* input = "5 - 1 - 2\n";
151 const char* expected =
152 "BINARY(-)\n"
153 " BINARY(-)\n"
154 " LITERAL(5)\n"
155 " LITERAL(1)\n"
156 " LITERAL(2)\n";
157 DoExpressionPrintTest(input, expected);
160 TEST(Parser, OrderOfOperationsEqualityBoolean) {
161 const char* input =
162 "if (a == \"b\" && is_stuff) {\n"
163 " print(\"hai\")\n"
164 "}\n";
165 const char* expected =
166 "BLOCK\n"
167 " CONDITION\n"
168 " BINARY(&&)\n"
169 " BINARY(==)\n"
170 " IDENTIFIER(a)\n"
171 " LITERAL(\"b\")\n"
172 " IDENTIFIER(is_stuff)\n"
173 " BLOCK\n"
174 " FUNCTION(print)\n"
175 " LIST\n"
176 " LITERAL(\"hai\")\n";
177 DoParserPrintTest(input, expected);
180 TEST(Parser, UnaryOp) {
181 DoExpressionPrintTest("!foo",
182 "UNARY(!)\n"
183 " IDENTIFIER(foo)\n");
186 TEST(Parser, List) {
187 DoExpressionPrintTest("[]", "LIST\n");
188 DoExpressionPrintTest("[1,asd,]",
189 "LIST\n"
190 " LITERAL(1)\n"
191 " IDENTIFIER(asd)\n");
192 DoExpressionPrintTest("[1, 2+3 - foo]",
193 "LIST\n"
194 " LITERAL(1)\n"
195 " BINARY(-)\n"
196 " BINARY(+)\n"
197 " LITERAL(2)\n"
198 " LITERAL(3)\n"
199 " IDENTIFIER(foo)\n");
200 DoExpressionPrintTest("[1,\n2,\n 3,\n 4]",
201 "LIST\n"
202 " LITERAL(1)\n"
203 " LITERAL(2)\n"
204 " LITERAL(3)\n"
205 " LITERAL(4)\n");
207 DoExpressionErrorTest("[a, 2+,]", 1, 6);
208 DoExpressionErrorTest("[,]", 1, 2);
209 DoExpressionErrorTest("[a,,]", 1, 4);
212 TEST(Parser, Assignment) {
213 DoParserPrintTest("a=2",
214 "BLOCK\n"
215 " BINARY(=)\n"
216 " IDENTIFIER(a)\n"
217 " LITERAL(2)\n");
220 TEST(Parser, Accessor) {
221 // Accessor indexing.
222 DoParserPrintTest("a=b[c+2]",
223 "BLOCK\n"
224 " BINARY(=)\n"
225 " IDENTIFIER(a)\n"
226 " ACCESSOR\n"
227 " b\n" // AccessorNode is a bit weird in that it holds
228 // a Token, not a ParseNode for the base.
229 " BINARY(+)\n"
230 " IDENTIFIER(c)\n"
231 " LITERAL(2)\n");
232 DoParserErrorTest("a = b[1][0]", 1, 5);
234 // Member accessors.
235 DoParserPrintTest("a=b.c+2",
236 "BLOCK\n"
237 " BINARY(=)\n"
238 " IDENTIFIER(a)\n"
239 " BINARY(+)\n"
240 " ACCESSOR\n"
241 " b\n"
242 " IDENTIFIER(c)\n"
243 " LITERAL(2)\n");
244 DoParserErrorTest("a = b.c.d", 1, 6); // Can't nest accessors (currently).
245 DoParserErrorTest("a.b = 5", 1, 1); // Can't assign to accessors (currently).
248 TEST(Parser, Condition) {
249 DoParserPrintTest("if(1) { a = 2 }",
250 "BLOCK\n"
251 " CONDITION\n"
252 " LITERAL(1)\n"
253 " BLOCK\n"
254 " BINARY(=)\n"
255 " IDENTIFIER(a)\n"
256 " LITERAL(2)\n");
258 DoParserPrintTest("if(1) { a = 2 } else if (0) { a = 3 } else { a = 4 }",
259 "BLOCK\n"
260 " CONDITION\n"
261 " LITERAL(1)\n"
262 " BLOCK\n"
263 " BINARY(=)\n"
264 " IDENTIFIER(a)\n"
265 " LITERAL(2)\n"
266 " CONDITION\n"
267 " LITERAL(0)\n"
268 " BLOCK\n"
269 " BINARY(=)\n"
270 " IDENTIFIER(a)\n"
271 " LITERAL(3)\n"
272 " BLOCK\n"
273 " BINARY(=)\n"
274 " IDENTIFIER(a)\n"
275 " LITERAL(4)\n");
278 TEST(Parser, OnlyCallAndAssignInBody) {
279 DoParserErrorTest("[]", 1, 2);
280 DoParserErrorTest("3 + 4", 1, 5);
281 DoParserErrorTest("6 - 7", 1, 5);
282 DoParserErrorTest("if (1) { 5 } else { print(4) }", 1, 12);
285 TEST(Parser, NoAssignmentInCondition) {
286 DoParserErrorTest("if (a=2) {}", 1, 5);
289 TEST(Parser, CompleteFunction) {
290 const char* input =
291 "cc_test(\"foo\") {\n"
292 " sources = [\n"
293 " \"foo.cc\",\n"
294 " \"foo.h\"\n"
295 " ]\n"
296 " dependencies = [\n"
297 " \"base\"\n"
298 " ]\n"
299 "}\n";
300 const char* expected =
301 "BLOCK\n"
302 " FUNCTION(cc_test)\n"
303 " LIST\n"
304 " LITERAL(\"foo\")\n"
305 " BLOCK\n"
306 " BINARY(=)\n"
307 " IDENTIFIER(sources)\n"
308 " LIST\n"
309 " LITERAL(\"foo.cc\")\n"
310 " LITERAL(\"foo.h\")\n"
311 " BINARY(=)\n"
312 " IDENTIFIER(dependencies)\n"
313 " LIST\n"
314 " LITERAL(\"base\")\n";
315 DoParserPrintTest(input, expected);
318 TEST(Parser, FunctionWithConditional) {
319 const char* input =
320 "cc_test(\"foo\") {\n"
321 " sources = [\"foo.cc\"]\n"
322 " if (OS == \"mac\") {\n"
323 " sources += \"bar.cc\"\n"
324 " } else if (OS == \"win\") {\n"
325 " sources -= [\"asd.cc\", \"foo.cc\"]\n"
326 " } else {\n"
327 " dependencies += [\"bar.cc\"]\n"
328 " }\n"
329 "}\n";
330 const char* expected =
331 "BLOCK\n"
332 " FUNCTION(cc_test)\n"
333 " LIST\n"
334 " LITERAL(\"foo\")\n"
335 " BLOCK\n"
336 " BINARY(=)\n"
337 " IDENTIFIER(sources)\n"
338 " LIST\n"
339 " LITERAL(\"foo.cc\")\n"
340 " CONDITION\n"
341 " BINARY(==)\n"
342 " IDENTIFIER(OS)\n"
343 " LITERAL(\"mac\")\n"
344 " BLOCK\n"
345 " BINARY(+=)\n"
346 " IDENTIFIER(sources)\n"
347 " LITERAL(\"bar.cc\")\n"
348 " CONDITION\n"
349 " BINARY(==)\n"
350 " IDENTIFIER(OS)\n"
351 " LITERAL(\"win\")\n"
352 " BLOCK\n"
353 " BINARY(-=)\n"
354 " IDENTIFIER(sources)\n"
355 " LIST\n"
356 " LITERAL(\"asd.cc\")\n"
357 " LITERAL(\"foo.cc\")\n"
358 " BLOCK\n"
359 " BINARY(+=)\n"
360 " IDENTIFIER(dependencies)\n"
361 " LIST\n"
362 " LITERAL(\"bar.cc\")\n";
363 DoParserPrintTest(input, expected);
366 TEST(Parser, NestedBlocks) {
367 const char* input = "{cc_test(\"foo\") {{foo=1}\n{}}}";
368 const char* expected =
369 "BLOCK\n"
370 " BLOCK\n"
371 " FUNCTION(cc_test)\n"
372 " LIST\n"
373 " LITERAL(\"foo\")\n"
374 " BLOCK\n"
375 " BLOCK\n"
376 " BINARY(=)\n"
377 " IDENTIFIER(foo)\n"
378 " LITERAL(1)\n"
379 " BLOCK\n";
380 DoParserPrintTest(input, expected);
381 const char* input_with_newline = "{cc_test(\"foo\") {{foo=1}\n{}}}";
382 DoParserPrintTest(input_with_newline, expected);
385 TEST(Parser, UnterminatedBlock) {
386 DoParserErrorTest("stuff() {", 1, 9);
389 TEST(Parser, BadlyTerminatedNumber) {
390 DoParserErrorTest("1234z", 1, 5);
393 TEST(Parser, NewlinesInUnusualPlaces) {
394 DoParserPrintTest(
395 "if\n"
396 "(\n"
397 "a\n"
398 ")\n"
399 "{\n"
400 "}\n",
401 "BLOCK\n"
402 " CONDITION\n"
403 " IDENTIFIER(a)\n"
404 " BLOCK\n");
407 TEST(Parser, NewlinesInUnusualPlaces2) {
408 DoParserPrintTest(
409 "a\n=\n2\n",
410 "BLOCK\n"
411 " BINARY(=)\n"
412 " IDENTIFIER(a)\n"
413 " LITERAL(2)\n");
414 DoParserPrintTest(
415 "x =\ny if\n(1\n) {}",
416 "BLOCK\n"
417 " BINARY(=)\n"
418 " IDENTIFIER(x)\n"
419 " IDENTIFIER(y)\n"
420 " CONDITION\n"
421 " LITERAL(1)\n"
422 " BLOCK\n");
423 DoParserPrintTest(
424 "x = 3\n+2",
425 "BLOCK\n"
426 " BINARY(=)\n"
427 " IDENTIFIER(x)\n"
428 " BINARY(+)\n"
429 " LITERAL(3)\n"
430 " LITERAL(2)\n"
434 TEST(Parser, NewlineBeforeSubscript) {
435 const char* input = "a = b[1]";
436 const char* input_with_newline = "a = b\n[1]";
437 const char* expected =
438 "BLOCK\n"
439 " BINARY(=)\n"
440 " IDENTIFIER(a)\n"
441 " ACCESSOR\n"
442 " b\n"
443 " LITERAL(1)\n";
444 DoParserPrintTest(
445 input,
446 expected);
447 DoParserPrintTest(
448 input_with_newline,
449 expected);
452 TEST(Parser, SequenceOfExpressions) {
453 DoParserPrintTest(
454 "a = 1 b = 2",
455 "BLOCK\n"
456 " BINARY(=)\n"
457 " IDENTIFIER(a)\n"
458 " LITERAL(1)\n"
459 " BINARY(=)\n"
460 " IDENTIFIER(b)\n"
461 " LITERAL(2)\n");
464 TEST(Parser, BlockAfterFunction) {
465 const char* input = "func(\"stuff\") {\n}";
466 // TODO(scottmg): Do we really want these to mean different things?
467 const char* input_with_newline = "func(\"stuff\")\n{\n}";
468 const char* expected =
469 "BLOCK\n"
470 " FUNCTION(func)\n"
471 " LIST\n"
472 " LITERAL(\"stuff\")\n"
473 " BLOCK\n";
474 DoParserPrintTest(input, expected);
475 DoParserPrintTest(input_with_newline, expected);
478 TEST(Parser, LongExpression) {
479 const char* input = "a = b + c && d || e";
480 const char* expected =
481 "BLOCK\n"
482 " BINARY(=)\n"
483 " IDENTIFIER(a)\n"
484 " BINARY(||)\n"
485 " BINARY(&&)\n"
486 " BINARY(+)\n"
487 " IDENTIFIER(b)\n"
488 " IDENTIFIER(c)\n"
489 " IDENTIFIER(d)\n"
490 " IDENTIFIER(e)\n";
491 DoParserPrintTest(input, expected);
494 TEST(Parser, CommentsStandalone) {
495 const char* input =
496 "# Toplevel comment.\n"
497 "\n"
498 "executable(\"wee\") {}\n";
499 const char* expected =
500 "BLOCK\n"
501 " BLOCK_COMMENT(# Toplevel comment.)\n"
502 " FUNCTION(executable)\n"
503 " LIST\n"
504 " LITERAL(\"wee\")\n"
505 " BLOCK\n";
506 DoParserPrintTest(input, expected);
509 TEST(Parser, CommentsStandaloneEof) {
510 const char* input =
511 "executable(\"wee\") {}\n"
512 "# EOF comment.\n";
513 const char* expected =
514 "BLOCK\n"
515 " +AFTER_COMMENT(\"# EOF comment.\")\n"
516 " FUNCTION(executable)\n"
517 " LIST\n"
518 " LITERAL(\"wee\")\n"
519 " BLOCK\n";
520 DoParserPrintTest(input, expected);
523 TEST(Parser, CommentsLineAttached) {
524 const char* input =
525 "executable(\"wee\") {\n"
526 " # Some sources.\n"
527 " sources = [\n"
528 " \"stuff.cc\",\n"
529 " \"things.cc\",\n"
530 " # This file is special or something.\n"
531 " \"another.cc\",\n"
532 " ]\n"
533 "}\n";
534 const char* expected =
535 "BLOCK\n"
536 " FUNCTION(executable)\n"
537 " LIST\n"
538 " LITERAL(\"wee\")\n"
539 " BLOCK\n"
540 " BINARY(=)\n"
541 " +BEFORE_COMMENT(\"# Some sources.\")\n"
542 " IDENTIFIER(sources)\n"
543 " LIST\n"
544 " LITERAL(\"stuff.cc\")\n"
545 " LITERAL(\"things.cc\")\n"
546 " LITERAL(\"another.cc\")\n"
547 " +BEFORE_COMMENT(\"# This file is special or something.\")\n";
548 DoParserPrintTest(input, expected);
551 TEST(Parser, CommentsSuffix) {
552 const char* input =
553 "executable(\"wee\") { # This is some stuff.\n"
554 "sources = [ \"a.cc\" # And another comment here.\n"
555 "] }";
556 const char* expected =
557 "BLOCK\n"
558 " FUNCTION(executable)\n"
559 " LIST\n"
560 " LITERAL(\"wee\")\n"
561 " END())\n"
562 " +SUFFIX_COMMENT(\"# This is some stuff.\")\n"
563 " BLOCK\n"
564 " BINARY(=)\n"
565 " IDENTIFIER(sources)\n"
566 " LIST\n"
567 " LITERAL(\"a.cc\")\n"
568 " +SUFFIX_COMMENT(\"# And another comment here.\")\n";
569 DoParserPrintTest(input, expected);
572 TEST(Parser, CommentsSuffixDifferentLine) {
573 const char* input =
574 "executable(\"wee\") {\n"
575 " sources = [ \"a\",\n"
576 " \"b\" ] # Comment\n"
577 "}\n";
578 const char* expected =
579 "BLOCK\n"
580 " FUNCTION(executable)\n"
581 " LIST\n"
582 " LITERAL(\"wee\")\n"
583 " BLOCK\n"
584 " BINARY(=)\n"
585 " IDENTIFIER(sources)\n"
586 " LIST\n"
587 " LITERAL(\"a\")\n"
588 " LITERAL(\"b\")\n"
589 " END(])\n"
590 " +SUFFIX_COMMENT(\"# Comment\")\n";
591 DoParserPrintTest(input, expected);
594 TEST(Parser, CommentsSuffixMultiple) {
595 const char* input =
596 "executable(\"wee\") {\n"
597 " sources = [\n"
598 " \"a\", # This is a comment,\n"
599 " # and some more,\n" // Note that this is aligned with above.
600 " # then the end.\n"
601 " ]\n"
602 "}\n";
603 const char* expected =
604 "BLOCK\n"
605 " FUNCTION(executable)\n"
606 " LIST\n"
607 " LITERAL(\"wee\")\n"
608 " BLOCK\n"
609 " BINARY(=)\n"
610 " IDENTIFIER(sources)\n"
611 " LIST\n"
612 " LITERAL(\"a\")\n"
613 " +SUFFIX_COMMENT(\"# This is a comment,\")\n"
614 " +SUFFIX_COMMENT(\"# and some more,\")\n"
615 " +SUFFIX_COMMENT(\"# then the end.\")\n";
616 DoParserPrintTest(input, expected);
619 TEST(Parser, CommentsConnectedInList) {
620 const char* input =
621 "defines = [\n"
622 "\n"
623 " # Connected comment.\n"
624 " \"WEE\",\n"
625 " \"BLORPY\",\n"
626 "]\n";
627 const char* expected =
628 "BLOCK\n"
629 " BINARY(=)\n"
630 " IDENTIFIER(defines)\n"
631 " LIST\n"
632 " LITERAL(\"WEE\")\n"
633 " +BEFORE_COMMENT(\"# Connected comment.\")\n"
634 " LITERAL(\"BLORPY\")\n";
635 DoParserPrintTest(input, expected);
638 TEST(Parser, CommentsAtEndOfBlock) {
639 const char* input =
640 "if (is_win) {\n"
641 " sources = [\"a.cc\"]\n"
642 " # Some comment at end.\n"
643 "}\n";
644 const char* expected =
645 "BLOCK\n"
646 " CONDITION\n"
647 " IDENTIFIER(is_win)\n"
648 " BLOCK\n"
649 " BINARY(=)\n"
650 " IDENTIFIER(sources)\n"
651 " LIST\n"
652 " LITERAL(\"a.cc\")\n"
653 " END(})\n"
654 " +BEFORE_COMMENT(\"# Some comment at end.\")\n";
655 DoParserPrintTest(input, expected);
658 // TODO(scottmg): I could be convinced this is incorrect. It's not clear to me
659 // which thing this comment is intended to be attached to.
660 TEST(Parser, CommentsEndOfBlockSingleLine) {
661 const char* input =
662 "defines = [ # EOL defines.\n"
663 "]\n";
664 const char* expected =
665 "BLOCK\n"
666 " BINARY(=)\n"
667 " IDENTIFIER(defines)\n"
668 " +SUFFIX_COMMENT(\"# EOL defines.\")\n"
669 " LIST\n";
670 DoParserPrintTest(input, expected);
673 TEST(Parser, HangingIf) {
674 DoParserErrorTest("if", 1, 1);
677 TEST(Parser, NegatingList) {
678 DoParserErrorTest("executable(\"wee\") { sources =- [ \"foo.cc\" ] }", 1, 30);