• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include <sstream>
6 
7 #include "gn/input_file.h"
8 #include "gn/parser.h"
9 #include "gn/tokenizer.h"
10 #include "util/test/test.h"
11 
12 namespace {
13 
GetTokens(const InputFile * input,std::vector<Token> * result)14 bool GetTokens(const InputFile* input, std::vector<Token>* result) {
15   result->clear();
16   Err err;
17   *result = Tokenizer::Tokenize(input, &err);
18   return !err.has_error();
19 }
20 
DoParserPrintTest(const char * input,const char * expected)21 void DoParserPrintTest(const char* input, const char* expected) {
22   std::vector<Token> tokens;
23   InputFile input_file(SourceFile("/test"));
24   input_file.SetContents(input);
25   ASSERT_TRUE(GetTokens(&input_file, &tokens));
26 
27   Err err;
28   std::unique_ptr<ParseNode> result = Parser::Parse(tokens, &err);
29   if (!result)
30     err.PrintToStdout();
31   ASSERT_TRUE(result);
32 
33   std::ostringstream collector;
34   RenderToText(result->GetJSONNode(), 0, collector);
35 
36   EXPECT_EQ(expected, collector.str());
37 }
38 
DoExpressionPrintTest(const char * input,const char * expected)39 void DoExpressionPrintTest(const char* input, const char* expected) {
40   std::vector<Token> tokens;
41   InputFile input_file(SourceFile("/test"));
42   input_file.SetContents(input);
43   ASSERT_TRUE(GetTokens(&input_file, &tokens));
44 
45   Err err;
46   std::unique_ptr<ParseNode> result = Parser::ParseExpression(tokens, &err);
47   ASSERT_TRUE(result);
48 
49   std::ostringstream collector;
50   RenderToText(result->GetJSONNode(), 0, collector);
51 
52   EXPECT_EQ(expected, collector.str());
53 }
54 
55 // Expects the tokenizer or parser to identify an error at the given line and
56 // character.
DoParserErrorTest(const char * input,int err_line,int err_char)57 void DoParserErrorTest(const char* input, int err_line, int err_char) {
58   InputFile input_file(SourceFile("/test"));
59   input_file.SetContents(input);
60 
61   Err err;
62   std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
63   if (!err.has_error()) {
64     std::unique_ptr<ParseNode> result = Parser::Parse(tokens, &err);
65     ASSERT_FALSE(result);
66     ASSERT_TRUE(err.has_error());
67   }
68 
69   EXPECT_EQ(err_line, err.location().line_number());
70   EXPECT_EQ(err_char, err.location().column_number());
71 }
72 
73 // Expects the tokenizer or parser to identify an error at the given line and
74 // character.
DoExpressionErrorTest(const char * input,int err_line,int err_char)75 void DoExpressionErrorTest(const char* input, int err_line, int err_char) {
76   InputFile input_file(SourceFile("/test"));
77   input_file.SetContents(input);
78 
79   Err err;
80   std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
81   if (!err.has_error()) {
82     std::unique_ptr<ParseNode> result = Parser::ParseExpression(tokens, &err);
83     ASSERT_FALSE(result);
84     ASSERT_TRUE(err.has_error());
85   }
86 
87   EXPECT_EQ(err_line, err.location().line_number());
88   EXPECT_EQ(err_char, err.location().column_number());
89 }
90 
91 }  // namespace
92 
TEST(Parser,Literal)93 TEST(Parser, Literal) {
94   DoExpressionPrintTest("5", "LITERAL(5)\n");
95   DoExpressionPrintTest("\"stuff\"", "LITERAL(\"stuff\")\n");
96 }
97 
TEST(Parser,BinaryOp)98 TEST(Parser, BinaryOp) {
99   // TODO(scottmg): The tokenizer is dumb, and treats "5-1" as two integers,
100   // not a binary operator between two positive integers.
101   DoExpressionPrintTest("5 - 1",
102                         "BINARY(-)\n"
103                         " LITERAL(5)\n"
104                         " LITERAL(1)\n");
105   DoExpressionPrintTest("5+1",
106                         "BINARY(+)\n"
107                         " LITERAL(5)\n"
108                         " LITERAL(1)\n");
109   DoExpressionPrintTest("5 - 1 - 2",
110                         "BINARY(-)\n"
111                         " BINARY(-)\n"
112                         "  LITERAL(5)\n"
113                         "  LITERAL(1)\n"
114                         " LITERAL(2)\n");
115 }
116 
TEST(Parser,FunctionCall)117 TEST(Parser, FunctionCall) {
118   DoExpressionPrintTest("foo()",
119                         "FUNCTION(foo)\n"
120                         " LIST\n");
121   DoExpressionPrintTest("blah(1, 2)",
122                         "FUNCTION(blah)\n"
123                         " LIST\n"
124                         "  LITERAL(1)\n"
125                         "  LITERAL(2)\n");
126   DoExpressionErrorTest("foo(1, 2,)", 1, 10);
127   DoExpressionErrorTest("foo(1 2)", 1, 7);
128 }
129 
TEST(Parser,ParenExpression)130 TEST(Parser, ParenExpression) {
131   const char* input = "(foo(1)) + (a + (b - c) + d)";
132   const char* expected =
133       "BINARY(+)\n"
134       " FUNCTION(foo)\n"
135       "  LIST\n"
136       "   LITERAL(1)\n"
137       " BINARY(+)\n"
138       "  BINARY(+)\n"
139       "   IDENTIFIER(a)\n"
140       "   BINARY(-)\n"
141       "    IDENTIFIER(b)\n"
142       "    IDENTIFIER(c)\n"
143       "  IDENTIFIER(d)\n";
144   DoExpressionPrintTest(input, expected);
145   DoExpressionErrorTest("(a +", 1, 4);
146 }
147 
TEST(Parser,OrderOfOperationsLeftAssociative)148 TEST(Parser, OrderOfOperationsLeftAssociative) {
149   const char* input = "5 - 1 - 2\n";
150   const char* expected =
151       "BINARY(-)\n"
152       " BINARY(-)\n"
153       "  LITERAL(5)\n"
154       "  LITERAL(1)\n"
155       " LITERAL(2)\n";
156   DoExpressionPrintTest(input, expected);
157 }
158 
TEST(Parser,OrderOfOperationsEqualityBoolean)159 TEST(Parser, OrderOfOperationsEqualityBoolean) {
160   const char* input =
161       "if (a == \"b\" && is_stuff) {\n"
162       "  print(\"hai\")\n"
163       "}\n";
164   const char* expected =
165       "BLOCK\n"
166       " CONDITION\n"
167       "  BINARY(&&)\n"
168       "   BINARY(==)\n"
169       "    IDENTIFIER(a)\n"
170       "    LITERAL(\"b\")\n"
171       "   IDENTIFIER(is_stuff)\n"
172       "  BLOCK\n"
173       "   FUNCTION(print)\n"
174       "    LIST\n"
175       "     LITERAL(\"hai\")\n";
176   DoParserPrintTest(input, expected);
177 }
178 
TEST(Parser,UnaryOp)179 TEST(Parser, UnaryOp) {
180   DoExpressionPrintTest("!foo",
181                         "UNARY(!)\n"
182                         " IDENTIFIER(foo)\n");
183 
184   // No contents for binary operator.
185   DoExpressionErrorTest("a = !", 1, 5);
186 }
187 
TEST(Parser,List)188 TEST(Parser, List) {
189   DoExpressionPrintTest("[]", "LIST\n");
190   DoExpressionPrintTest("[1,asd,]",
191                         "LIST\n"
192                         " LITERAL(1)\n"
193                         " IDENTIFIER(asd)\n");
194   DoExpressionPrintTest("[1, 2+3 - foo]",
195                         "LIST\n"
196                         " LITERAL(1)\n"
197                         " BINARY(-)\n"
198                         "  BINARY(+)\n"
199                         "   LITERAL(2)\n"
200                         "   LITERAL(3)\n"
201                         "  IDENTIFIER(foo)\n");
202   DoExpressionPrintTest("[1,\n2,\n 3,\n  4]",
203                         "LIST\n"
204                         " LITERAL(1)\n"
205                         " LITERAL(2)\n"
206                         " LITERAL(3)\n"
207                         " LITERAL(4)\n");
208 
209   DoExpressionErrorTest("[a, 2+,]", 1, 7);
210   DoExpressionErrorTest("[,]", 1, 2);
211   DoExpressionErrorTest("[a,,]", 1, 4);
212 }
213 
TEST(Parser,Assignment)214 TEST(Parser, Assignment) {
215   DoParserPrintTest("a=2",
216                     "BLOCK\n"
217                     " BINARY(=)\n"
218                     "  IDENTIFIER(a)\n"
219                     "  LITERAL(2)\n");
220 
221   DoExpressionErrorTest("a = ", 1, 3);
222 }
223 
TEST(Parser,Accessor)224 TEST(Parser, Accessor) {
225   // Accessor indexing.
226   DoParserPrintTest("a=b[c+2]",
227                     "BLOCK\n"
228                     " BINARY(=)\n"
229                     "  IDENTIFIER(a)\n"
230                     "  ACCESSOR\n"
231                     "   b\n"  // AccessorNode is a bit weird in that it holds
232                               // a Token, not a ParseNode for the base.
233                     "   BINARY(+)\n"
234                     "    IDENTIFIER(c)\n"
235                     "    LITERAL(2)\n");
236   DoParserErrorTest("a = b[1][0]", 1, 5);
237 
238   // Member accessors.
239   DoParserPrintTest("a=b.c+2",
240                     "BLOCK\n"
241                     " BINARY(=)\n"
242                     "  IDENTIFIER(a)\n"
243                     "  BINARY(+)\n"
244                     "   ACCESSOR\n"
245                     "    b\n"
246                     "    IDENTIFIER(c)\n"
247                     "   LITERAL(2)\n");
248   DoParserPrintTest("a.b = 5",
249                     "BLOCK\n"
250                     " BINARY(=)\n"
251                     "  ACCESSOR\n"
252                     "   a\n"
253                     "   IDENTIFIER(b)\n"
254                     "  LITERAL(5)\n");
255   DoParserErrorTest("a = b.c.d", 1, 6);  // Can't nest accessors (currently).
256 
257   // Error at the bad dot in the RHS, not the + operator (crbug.com/472038).
258   DoParserErrorTest("foo(a + b.c.d)", 1, 10);
259 }
260 
TEST(Parser,Condition)261 TEST(Parser, Condition) {
262   DoParserPrintTest("if(1) { a = 2 }",
263                     "BLOCK\n"
264                     " CONDITION\n"
265                     "  LITERAL(1)\n"
266                     "  BLOCK\n"
267                     "   BINARY(=)\n"
268                     "    IDENTIFIER(a)\n"
269                     "    LITERAL(2)\n");
270 
271   DoParserPrintTest("if(1) { a = 2 } else if (0) { a = 3 } else { a = 4 }",
272                     "BLOCK\n"
273                     " CONDITION\n"
274                     "  LITERAL(1)\n"
275                     "  BLOCK\n"
276                     "   BINARY(=)\n"
277                     "    IDENTIFIER(a)\n"
278                     "    LITERAL(2)\n"
279                     "  CONDITION\n"
280                     "   LITERAL(0)\n"
281                     "   BLOCK\n"
282                     "    BINARY(=)\n"
283                     "     IDENTIFIER(a)\n"
284                     "     LITERAL(3)\n"
285                     "   BLOCK\n"
286                     "    BINARY(=)\n"
287                     "     IDENTIFIER(a)\n"
288                     "     LITERAL(4)\n");
289 }
290 
TEST(Parser,OnlyCallAndAssignInBody)291 TEST(Parser, OnlyCallAndAssignInBody) {
292   DoParserErrorTest("[]", 1, 2);
293   DoParserErrorTest("3 + 4", 1, 5);
294   DoParserErrorTest("6 - 7", 1, 5);
295   DoParserErrorTest("if (1) { 5 } else { print(4) }", 1, 12);
296 }
297 
TEST(Parser,NoAssignmentInCondition)298 TEST(Parser, NoAssignmentInCondition) {
299   DoParserErrorTest("if (a=2) {}", 1, 5);
300 }
301 
TEST(Parser,CompleteFunction)302 TEST(Parser, CompleteFunction) {
303   const char* input =
304       "cc_test(\"foo\") {\n"
305       "  sources = [\n"
306       "    \"foo.cc\",\n"
307       "    \"foo.h\"\n"
308       "  ]\n"
309       "  dependencies = [\n"
310       "    \"base\"\n"
311       "  ]\n"
312       "}\n";
313   const char* expected =
314       "BLOCK\n"
315       " FUNCTION(cc_test)\n"
316       "  LIST\n"
317       "   LITERAL(\"foo\")\n"
318       "  BLOCK\n"
319       "   BINARY(=)\n"
320       "    IDENTIFIER(sources)\n"
321       "    LIST\n"
322       "     LITERAL(\"foo.cc\")\n"
323       "     LITERAL(\"foo.h\")\n"
324       "   BINARY(=)\n"
325       "    IDENTIFIER(dependencies)\n"
326       "    LIST\n"
327       "     LITERAL(\"base\")\n";
328   DoParserPrintTest(input, expected);
329 }
330 
TEST(Parser,FunctionWithConditional)331 TEST(Parser, FunctionWithConditional) {
332   const char* input =
333       "cc_test(\"foo\") {\n"
334       "  sources = [\"foo.cc\"]\n"
335       "  if (OS == \"mac\") {\n"
336       "    sources += \"bar.cc\"\n"
337       "  } else if (OS == \"win\") {\n"
338       "    sources -= [\"asd.cc\", \"foo.cc\"]\n"
339       "  } else {\n"
340       "    dependencies += [\"bar.cc\"]\n"
341       "  }\n"
342       "}\n";
343   const char* expected =
344       "BLOCK\n"
345       " FUNCTION(cc_test)\n"
346       "  LIST\n"
347       "   LITERAL(\"foo\")\n"
348       "  BLOCK\n"
349       "   BINARY(=)\n"
350       "    IDENTIFIER(sources)\n"
351       "    LIST\n"
352       "     LITERAL(\"foo.cc\")\n"
353       "   CONDITION\n"
354       "    BINARY(==)\n"
355       "     IDENTIFIER(OS)\n"
356       "     LITERAL(\"mac\")\n"
357       "    BLOCK\n"
358       "     BINARY(+=)\n"
359       "      IDENTIFIER(sources)\n"
360       "      LITERAL(\"bar.cc\")\n"
361       "    CONDITION\n"
362       "     BINARY(==)\n"
363       "      IDENTIFIER(OS)\n"
364       "      LITERAL(\"win\")\n"
365       "     BLOCK\n"
366       "      BINARY(-=)\n"
367       "       IDENTIFIER(sources)\n"
368       "       LIST\n"
369       "        LITERAL(\"asd.cc\")\n"
370       "        LITERAL(\"foo.cc\")\n"
371       "     BLOCK\n"
372       "      BINARY(+=)\n"
373       "       IDENTIFIER(dependencies)\n"
374       "       LIST\n"
375       "        LITERAL(\"bar.cc\")\n";
376   DoParserPrintTest(input, expected);
377 }
378 
TEST(Parser,UnterminatedBlock)379 TEST(Parser, UnterminatedBlock) {
380   DoParserErrorTest("stuff() {", 1, 9);
381 }
382 
TEST(Parser,BadlyTerminatedNumber)383 TEST(Parser, BadlyTerminatedNumber) {
384   DoParserErrorTest("1234z", 1, 5);
385 }
386 
TEST(Parser,NewlinesInUnusualPlaces)387 TEST(Parser, NewlinesInUnusualPlaces) {
388   DoParserPrintTest(
389       "if\n"
390       "(\n"
391       "a\n"
392       ")\n"
393       "{\n"
394       "}\n",
395       "BLOCK\n"
396       " CONDITION\n"
397       "  IDENTIFIER(a)\n"
398       "  BLOCK\n");
399 }
400 
TEST(Parser,NewlinesInUnusualPlaces2)401 TEST(Parser, NewlinesInUnusualPlaces2) {
402   DoParserPrintTest("a\n=\n2\n",
403                     "BLOCK\n"
404                     " BINARY(=)\n"
405                     "  IDENTIFIER(a)\n"
406                     "  LITERAL(2)\n");
407   DoParserPrintTest("x =\ny if\n(1\n) {}",
408                     "BLOCK\n"
409                     " BINARY(=)\n"
410                     "  IDENTIFIER(x)\n"
411                     "  IDENTIFIER(y)\n"
412                     " CONDITION\n"
413                     "  LITERAL(1)\n"
414                     "  BLOCK\n");
415   DoParserPrintTest("x = 3\n+2",
416                     "BLOCK\n"
417                     " BINARY(=)\n"
418                     "  IDENTIFIER(x)\n"
419                     "  BINARY(+)\n"
420                     "   LITERAL(3)\n"
421                     "   LITERAL(2)\n");
422 }
423 
TEST(Parser,NewlineBeforeSubscript)424 TEST(Parser, NewlineBeforeSubscript) {
425   const char* input = "a = b[1]";
426   const char* input_with_newline = "a = b\n[1]";
427   const char* expected =
428       "BLOCK\n"
429       " BINARY(=)\n"
430       "  IDENTIFIER(a)\n"
431       "  ACCESSOR\n"
432       "   b\n"
433       "   LITERAL(1)\n";
434   DoParserPrintTest(input, expected);
435   DoParserPrintTest(input_with_newline, expected);
436 }
437 
TEST(Parser,SequenceOfExpressions)438 TEST(Parser, SequenceOfExpressions) {
439   DoParserPrintTest("a = 1 b = 2",
440                     "BLOCK\n"
441                     " BINARY(=)\n"
442                     "  IDENTIFIER(a)\n"
443                     "  LITERAL(1)\n"
444                     " BINARY(=)\n"
445                     "  IDENTIFIER(b)\n"
446                     "  LITERAL(2)\n");
447 }
448 
TEST(Parser,BlockAfterFunction)449 TEST(Parser, BlockAfterFunction) {
450   const char* input = "func(\"stuff\") {\n}";
451   // TODO(scottmg): Do we really want these to mean different things?
452   const char* input_with_newline = "func(\"stuff\")\n{\n}";
453   const char* expected =
454       "BLOCK\n"
455       " FUNCTION(func)\n"
456       "  LIST\n"
457       "   LITERAL(\"stuff\")\n"
458       "  BLOCK\n";
459   DoParserPrintTest(input, expected);
460   DoParserPrintTest(input_with_newline, expected);
461 }
462 
TEST(Parser,LongExpression)463 TEST(Parser, LongExpression) {
464   const char* input = "a = b + c && d || e";
465   const char* expected =
466       "BLOCK\n"
467       " BINARY(=)\n"
468       "  IDENTIFIER(a)\n"
469       "  BINARY(||)\n"
470       "   BINARY(&&)\n"
471       "    BINARY(+)\n"
472       "     IDENTIFIER(b)\n"
473       "     IDENTIFIER(c)\n"
474       "    IDENTIFIER(d)\n"
475       "   IDENTIFIER(e)\n";
476   DoParserPrintTest(input, expected);
477 }
478 
TEST(Parser,CommentsStandalone)479 TEST(Parser, CommentsStandalone) {
480   const char* input =
481       "# Toplevel comment.\n"
482       "\n"
483       "executable(\"wee\") {}\n";
484   const char* expected =
485       "BLOCK\n"
486       " BLOCK_COMMENT(# Toplevel comment.)\n"
487       " FUNCTION(executable)\n"
488       "  LIST\n"
489       "   LITERAL(\"wee\")\n"
490       "  BLOCK\n";
491   DoParserPrintTest(input, expected);
492 }
493 
TEST(Parser,CommentsStandaloneEof)494 TEST(Parser, CommentsStandaloneEof) {
495   const char* input =
496       "executable(\"wee\") {}\n"
497       "# EOF comment.\n";
498   const char* expected =
499       "BLOCK\n"
500       " +AFTER_COMMENT(\"# EOF comment.\")\n"
501       " FUNCTION(executable)\n"
502       "  LIST\n"
503       "   LITERAL(\"wee\")\n"
504       "  BLOCK\n";
505   DoParserPrintTest(input, expected);
506 }
507 
TEST(Parser,CommentsLineAttached)508 TEST(Parser, CommentsLineAttached) {
509   const char* input =
510       "executable(\"wee\") {\n"
511       "  # Some sources.\n"
512       "  sources = [\n"
513       "    \"stuff.cc\",\n"
514       "    \"things.cc\",\n"
515       "    # This file is special or something.\n"
516       "    \"another.cc\",\n"
517       "  ]\n"
518       "}\n";
519   const char* expected =
520       "BLOCK\n"
521       " FUNCTION(executable)\n"
522       "  LIST\n"
523       "   LITERAL(\"wee\")\n"
524       "  BLOCK\n"
525       "   BINARY(=)\n"
526       "    +BEFORE_COMMENT(\"# Some sources.\")\n"
527       "    IDENTIFIER(sources)\n"
528       "    LIST\n"
529       "     LITERAL(\"stuff.cc\")\n"
530       "     LITERAL(\"things.cc\")\n"
531       "     LITERAL(\"another.cc\")\n"
532       "      +BEFORE_COMMENT(\"# This file is special or something.\")\n";
533   DoParserPrintTest(input, expected);
534 }
535 
TEST(Parser,CommentsSuffix)536 TEST(Parser, CommentsSuffix) {
537   const char* input =
538       "executable(\"wee\") { # This is some stuff.\n"
539       "sources = [ \"a.cc\" # And another comment here.\n"
540       "] }";
541   const char* expected =
542       "BLOCK\n"
543       " FUNCTION(executable)\n"
544       "  LIST\n"
545       "   LITERAL(\"wee\")\n"
546       "   END())\n"
547       "    +SUFFIX_COMMENT(\"# This is some stuff.\")\n"
548       "  BLOCK\n"
549       "   BINARY(=)\n"
550       "    IDENTIFIER(sources)\n"
551       "    LIST\n"
552       "     LITERAL(\"a.cc\")\n"
553       "      +SUFFIX_COMMENT(\"# And another comment here.\")\n";
554   DoParserPrintTest(input, expected);
555 }
556 
TEST(Parser,CommentsSuffixDifferentLine)557 TEST(Parser, CommentsSuffixDifferentLine) {
558   const char* input =
559       "executable(\"wee\") {\n"
560       "  sources = [ \"a\",\n"
561       "      \"b\" ] # Comment\n"
562       "}\n";
563   const char* expected =
564       "BLOCK\n"
565       " FUNCTION(executable)\n"
566       "  LIST\n"
567       "   LITERAL(\"wee\")\n"
568       "  BLOCK\n"
569       "   BINARY(=)\n"
570       "    IDENTIFIER(sources)\n"
571       "    LIST\n"
572       "     LITERAL(\"a\")\n"
573       "     LITERAL(\"b\")\n"
574       "     END(])\n"
575       "      +SUFFIX_COMMENT(\"# Comment\")\n";
576   DoParserPrintTest(input, expected);
577 }
578 
TEST(Parser,CommentsSuffixMultiple)579 TEST(Parser, CommentsSuffixMultiple) {
580   const char* input =
581       "executable(\"wee\") {\n"
582       "  sources = [\n"
583       "    \"a\",  # This is a comment,\n"
584       "          # and some more,\n"  // Note that this is aligned with above.
585       "          # then the end.\n"
586       "  ]\n"
587       "}\n";
588   const char* expected =
589       "BLOCK\n"
590       " FUNCTION(executable)\n"
591       "  LIST\n"
592       "   LITERAL(\"wee\")\n"
593       "  BLOCK\n"
594       "   BINARY(=)\n"
595       "    IDENTIFIER(sources)\n"
596       "    LIST\n"
597       "     LITERAL(\"a\")\n"
598       "      +SUFFIX_COMMENT(\"# This is a comment,\")\n"
599       "      +SUFFIX_COMMENT(\"# and some more,\")\n"
600       "      +SUFFIX_COMMENT(\"# then the end.\")\n";
601   DoParserPrintTest(input, expected);
602 }
603 
TEST(Parser,CommentsConnectedInList)604 TEST(Parser, CommentsConnectedInList) {
605   const char* input =
606       "defines = [\n"
607       "\n"
608       "  # Connected comment.\n"
609       "  \"WEE\",\n"
610       "  \"BLORPY\",\n"
611       "]\n";
612   const char* expected =
613       "BLOCK\n"
614       " BINARY(=)\n"
615       "  IDENTIFIER(defines)\n"
616       "  LIST\n"
617       "   LITERAL(\"WEE\")\n"
618       "    +BEFORE_COMMENT(\"# Connected comment.\")\n"
619       "   LITERAL(\"BLORPY\")\n";
620   DoParserPrintTest(input, expected);
621 }
622 
TEST(Parser,CommentsAtEndOfBlock)623 TEST(Parser, CommentsAtEndOfBlock) {
624   const char* input =
625       "if (is_win) {\n"
626       "  sources = [\"a.cc\"]\n"
627       "  # Some comment at end.\n"
628       "}\n";
629   const char* expected =
630       "BLOCK\n"
631       " CONDITION\n"
632       "  IDENTIFIER(is_win)\n"
633       "  BLOCK\n"
634       "   BINARY(=)\n"
635       "    IDENTIFIER(sources)\n"
636       "    LIST\n"
637       "     LITERAL(\"a.cc\")\n"
638       "   END(})\n"
639       "    +BEFORE_COMMENT(\"# Some comment at end.\")\n";
640   DoParserPrintTest(input, expected);
641 }
642 
643 // TODO(scottmg): I could be convinced this is incorrect. It's not clear to me
644 // which thing this comment is intended to be attached to.
TEST(Parser,CommentsEndOfBlockSingleLine)645 TEST(Parser, CommentsEndOfBlockSingleLine) {
646   const char* input =
647       "defines = [ # EOL defines.\n"
648       "]\n";
649   const char* expected =
650       "BLOCK\n"
651       " BINARY(=)\n"
652       "  IDENTIFIER(defines)\n"
653       "   +SUFFIX_COMMENT(\"# EOL defines.\")\n"
654       "  LIST\n";
655   DoParserPrintTest(input, expected);
656 }
657 
TEST(Parser,HangingIf)658 TEST(Parser, HangingIf) {
659   DoParserErrorTest("if", 1, 1);
660 }
661 
TEST(Parser,NegatingList)662 TEST(Parser, NegatingList) {
663   DoParserErrorTest("executable(\"wee\") { sources =- [ \"foo.cc\" ] }", 1, 30);
664 }
665 
TEST(Parser,ConditionNoBracesIf)666 TEST(Parser, ConditionNoBracesIf) {
667   DoParserErrorTest(
668       "if (true)\n"
669       "  foreach(foo, []) {}\n"
670       "else {\n"
671       "  foreach(bar, []) {}\n"
672       "}\n",
673       2, 3);
674 }
675 
TEST(Parser,ConditionNoBracesElse)676 TEST(Parser, ConditionNoBracesElse) {
677   DoParserErrorTest(
678       "if (true) {\n"
679       "  foreach(foo, []) {}\n"
680       "} else\n"
681       "  foreach(bar, []) {}\n",
682       4, 3);
683 }
684 
TEST(Parser,ConditionNoBracesElseIf)685 TEST(Parser, ConditionNoBracesElseIf) {
686   DoParserErrorTest(
687       "if (true) {\n"
688       "  foreach(foo, []) {}\n"
689       "} else if (true)\n"
690       "  foreach(bar, []) {}\n",
691       4, 3);
692 }
693 
694 // Disallow standalone {} for introducing new scopes. These are ambiguous with
695 // target declarations (e.g. is:
696 //   foo("bar") {}
697 // a function with an associated block, or a standalone function with a
698 // freestanding block.
TEST(Parser,StandaloneBlock)699 TEST(Parser, StandaloneBlock) {
700   // The error is reported at the end of the block when nothing is done
701   // with it. If we had said "a = { ..." then it would have been OK.
702   DoParserErrorTest(
703       "if (true) {\n"
704       "}\n"
705       "{\n"
706       "  assert(false)\n"
707       "}\n",
708       5, 1);
709 }
710 
TEST(Parser,BlockValues)711 TEST(Parser, BlockValues) {
712   const char* input =
713       "print({a = 1 b = 2}, 3)\n"
714       "a = { b = \"asd\" }";
715   const char* expected =
716       "BLOCK\n"
717       " FUNCTION(print)\n"
718       "  LIST\n"
719       "   BLOCK\n"
720       "    BINARY(=)\n"
721       "     IDENTIFIER(a)\n"
722       "     LITERAL(1)\n"
723       "    BINARY(=)\n"
724       "     IDENTIFIER(b)\n"
725       "     LITERAL(2)\n"
726       "   LITERAL(3)\n"
727       " BINARY(=)\n"
728       "  IDENTIFIER(a)\n"
729       "  BLOCK\n"
730       "   BINARY(=)\n"
731       "    IDENTIFIER(b)\n"
732       "    LITERAL(\"asd\")\n";
733   DoParserPrintTest(input, expected);
734 }
735