Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(75)

Side by Side Diff: tools/gn/parser.cc

Issue 21114002: Add initial prototype for the GN meta-buildsystem. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: add owners and readme Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tools/gn/parser.h ('k') | tools/gn/parser_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 "tools/gn/parser.h"
6
7 #include "base/logging.h"
8 #include "tools/gn/functions.h"
9 #include "tools/gn/operators.h"
10 #include "tools/gn/token.h"
11
12 namespace {
13
14 // Returns true if the two tokens are on the same line. We assume they're in
15 // the same file.
16 bool IsSameLine(const Token& a, const Token& b) {
17 DCHECK(a.location().file() == b.location().file());
18 return a.location().line_number() == b.location().line_number();
19 }
20
21 } // namespace
22
23 Parser::Parser(const std::vector<Token>& tokens, Err* err)
24 : tokens_(tokens),
25 err_(err),
26 cur_(0) {
27 }
28
29 Parser::~Parser() {
30 }
31
32 // static
33 scoped_ptr<ParseNode> Parser::Parse(const std::vector<Token>& tokens,
34 Err* err) {
35 Parser p(tokens, err);
36 return p.ParseBlock(false).PassAs<ParseNode>();
37 }
38
39 // static
40 scoped_ptr<ParseNode> Parser::ParseExpression(const std::vector<Token>& tokens,
41 Err* err) {
42 Parser p(tokens, err);
43 return p.ParseExpression().Pass();
44 }
45
46 bool Parser::IsToken(Token::Type type, char* str) const {
47 if (at_end())
48 return false;
49 return cur_token().type() == type || cur_token().value() == str;
50 }
51
52 scoped_ptr<AccessorNode> Parser::ParseAccessor() {
53 scoped_ptr<AccessorNode> accessor(new AccessorNode);
54
55 DCHECK(cur_token().type() == Token::IDENTIFIER);
56 accessor->set_base(cur_token());
57 cur_++; // Skip identifier.
58 cur_++; // Skip "[" (we know this exists because the existance of this
59 // token is how the caller knows it's an accessor.
60
61 if (at_end()) {
62 *err_ = MakeEOFError("Got EOF when looking for list index.");
63 return scoped_ptr<AccessorNode>();
64 }
65
66 // Get the expression.
67 scoped_ptr<ParseNode> expr = ParseExpression().Pass();
68 if (has_error())
69 return scoped_ptr<AccessorNode>();
70 if (at_end()) {
71 *err_ = MakeEOFError("Got EOF when looking for list accessor ]");
72 return scoped_ptr<AccessorNode>();
73 }
74 accessor->set_index(expr.Pass());
75
76 // Skip over "]"
77 if (!cur_token().IsScoperEqualTo("]")) {
78 *err_ = Err(cur_token(), "Expecting ]",
79 "You started a list access but didn't terminate it, and instead "
80 "I fould this\nstupid thing.");
81 return scoped_ptr<AccessorNode>();
82 }
83 cur_++;
84
85 return accessor.Pass();
86 }
87
88 // Blocks at the file scope don't need {} so we have the option to ignore
89 // them. When need_braces is set, we'll expect a begin an end brace.
90 //
91 // block := "{" block_contents "}"
92 // block_contents := (expression | conditional | block)*
93 scoped_ptr<BlockNode> Parser::ParseBlock(bool need_braces) {
94 scoped_ptr<BlockNode> block(new BlockNode(true));
95
96 // Eat initial { if necessary.
97 const Token* opening_curly_brace;
98 if (need_braces) {
99 if (at_end()) {
100 *err_ = MakeEOFError("Got EOF when looking for { for block.",
101 "It should have been after here.");
102 return scoped_ptr<BlockNode>();
103 } else if(!IsScopeBeginScoper(cur_token())) {
104 *err_ = Err(cur_token(), "Expecting { instead of this thing.",
105 "THOU SHALT USE CURLY BRACES FOR ALL BLOCKS.");
106 return scoped_ptr<BlockNode>();
107 }
108 opening_curly_brace = &cur_token();
109 block->set_begin_token(opening_curly_brace);
110 cur_++;
111 }
112
113 // Loop until EOF or end brace found.
114 while (!at_end() && !IsScopeEndScoper(cur_token())) {
115 if (cur_token().IsIdentifierEqualTo("if")) {
116 // Conditional.
117 block->append_statement(ParseCondition().PassAs<ParseNode>());
118 } else if (IsScopeBeginScoper(cur_token())) {
119 // Nested block.
120 block->append_statement(ParseBlock(true).PassAs<ParseNode>());
121 } else {
122 // Everything else is an expression.
123 block->append_statement(ParseExpression().PassAs<ParseNode>());
124 }
125 if (has_error())
126 return scoped_ptr<BlockNode>();
127 }
128
129 // Eat the ending "}" if necessary.
130 if (need_braces) {
131 if (at_end() || !IsScopeEndScoper(cur_token())) {
132 *err_ = Err(*opening_curly_brace, "Expecting }",
133 "I ran headlong into the end of the file looking for the "
134 "closing brace\ncorresponding to this one.");
135 return scoped_ptr<BlockNode>();
136 }
137 block->set_end_token(&cur_token());
138 cur_++; // Skip past "}".
139 }
140
141 return block.Pass();
142 }
143
144 // conditional := "if (" expression ")" block [else_conditional]
145 // else_conditional := ("else" block) | ("else" conditional)
146 scoped_ptr<ConditionNode> Parser::ParseCondition() {
147 scoped_ptr<ConditionNode> cond(new ConditionNode);
148
149 // Skip past "if".
150 const Token& if_token = cur_token();
151 cond->set_if_token(if_token);
152 DCHECK(if_token.IsIdentifierEqualTo("if"));
153 cur_++;
154
155 if (at_end() || !IsFunctionCallArgBeginScoper(cur_token())) {
156 *err_ = Err(if_token, "Expecting \"(\" after \"if\"",
157 "Did you think this was Python or something?");
158 return scoped_ptr<ConditionNode>();
159 }
160
161 // Skip over (.
162 const Token& open_paren_token = cur_token();
163 cur_++;
164 if (at_end()) {
165 *err_ = Err(if_token, "Unexpected EOF inside if condition");
166 return scoped_ptr<ConditionNode>();
167 }
168
169 // Condition inside ().
170 cond->set_condition(ParseExpression().Pass());
171 if (has_error())
172 return scoped_ptr<ConditionNode>();
173
174 if (at_end() || !IsFunctionCallArgEndScoper(cur_token())) {
175 *err_ = Err(open_paren_token, "Expecting \")\" for \"if\" condition",
176 "You didn't finish the thought you started here.");
177 return scoped_ptr<ConditionNode>();
178 }
179 cur_++; // Skip over )
180
181 // Contents of {}.
182 cond->set_if_true(ParseBlock(true).Pass());
183 if (has_error())
184 return scoped_ptr<ConditionNode>();
185
186 // Optional "else" at the end.
187 if (!at_end() && cur_token().IsIdentifierEqualTo("else")) {
188 cur_++;
189
190 // The else may be followed by an if or a block.
191 if (at_end()) {
192 *err_ = MakeEOFError("Ran into end of file after \"else\".",
193 "else, WHAT?!?!?");
194 return scoped_ptr<ConditionNode>();
195 }
196 if (cur_token().IsIdentifierEqualTo("if")) {
197 // "else if() {"
198 cond->set_if_false(ParseCondition().PassAs<ParseNode>());
199 } else if (IsScopeBeginScoper(cur_token())) {
200 // "else {"
201 cond->set_if_false(ParseBlock(true).PassAs<ParseNode>());
202 } else {
203 // else <anything else>
204 *err_ = Err(cur_token(), "Expected \"if\" or \"{\" after \"else\".",
205 "This is neither of those things.");
206 return scoped_ptr<ConditionNode>();
207 }
208 }
209
210 if (has_error())
211 return scoped_ptr<ConditionNode>();
212 return cond.Pass();
213 }
214
215 // expression := paren_expression | accessor | identifier | literal |
216 // funccall | unary_expression | binary_expression
217 //
218 // accessor := identifier <non-newline-whitespace>* "[" expression "]"
219 //
220 // The "non-newline-whitespace is used to differentiate between this case:
221 // a[1]
222 // and this one:
223 // a
224 // [1]
225 // The second one is kind of stupid (since it does nothing with the values)
226 // but is still legal.
227 scoped_ptr<ParseNode> Parser::ParseExpression() {
228 scoped_ptr<ParseNode> expr = ParseExpressionExceptBinaryOperators();
229 if (has_error())
230 return scoped_ptr<ParseNode>();
231
232 // That may have hit EOF, in which case we can't have any binary operators.
233 if (at_end())
234 return expr.Pass();
235
236 // TODO(brettw) handle operator precidence!
237 // Gobble up all subsequent expressions as long as there are binary
238 // operators.
239
240 if (IsBinaryOperator(cur_token())) {
241 scoped_ptr<BinaryOpNode> binary_op(new BinaryOpNode);
242 binary_op->set_left(expr.Pass());
243 const Token& operator_token = cur_token();
244 binary_op->set_op(operator_token);
245 cur_++;
246 if (at_end()) {
247 *err_ = Err(operator_token, "Unexpected EOF in expression.",
248 "I was looking for the right-hand-side of this operator.");
249 return scoped_ptr<ParseNode>();
250 }
251 binary_op->set_right(ParseExpression().Pass());
252 if (has_error())
253 return scoped_ptr<ParseNode>();
254 return binary_op.PassAs<ParseNode>();
255 }
256
257 return expr.Pass();
258 }
259
260
261 // This internal one does not handle binary operators, since it requires
262 // looking at the "next" thing. The regular ParseExpression above handles it.
263 scoped_ptr<ParseNode> Parser::ParseExpressionExceptBinaryOperators() {
264 if (at_end())
265 return scoped_ptr<ParseNode>();
266
267 const Token& token = cur_token();
268
269 // Unary expression.
270 if (IsUnaryOperator(token))
271 return ParseUnaryOp().PassAs<ParseNode>();
272
273 // Parenthesized expressions.
274 if (token.IsScoperEqualTo("("))
275 return ParseParenExpression();
276
277 // Function calls.
278 if (token.type() == Token::IDENTIFIER) {
279 if (has_next_token() && IsFunctionCallArgBeginScoper(next_token()))
280 return ParseFunctionCall().PassAs<ParseNode>();
281 }
282
283 // Lists.
284 if (token.IsScoperEqualTo("[")) {
285 return ParseList(Token(Location(), Token::SCOPER, "["),
286 Token(Location(), Token::SCOPER, "]")).PassAs<ParseNode>();
287 }
288
289 // Literals.
290 if (token.type() == Token::STRING || token.type() == Token::INTEGER) {
291 cur_++;
292 return scoped_ptr<ParseNode>(new LiteralNode(token));
293 }
294
295 // Accessors.
296 if (token.type() == Token::IDENTIFIER &&
297 has_next_token() && next_token().IsScoperEqualTo("[") &&
298 IsSameLine(token, next_token())) {
299 return ParseAccessor().PassAs<ParseNode>();
300 }
301
302 // Identifiers.
303 if (token.type() == Token::IDENTIFIER) {
304 cur_++;
305 return scoped_ptr<ParseNode>(new IdentifierNode(token));
306 }
307
308 // Handle errors.
309 if (token.type() == Token::SEPARATOR) {
310 *err_ = Err(token, "Unexpected comma.",
311 "You can't put a comma here, it must be in list separating "
312 "complete\nthoughts.");
313 } else if (IsScopeBeginScoper(token)) {
314 *err_ = Err(token, "Unexpected token.",
315 "You can't put a \"{\" scope here, it must be in a block.");
316 } else {
317 *err_ = Err(token, "Unexpected token.",
318 "I was really hoping for something else here and you let me down .");
319 }
320 return scoped_ptr<ParseNode>();
321 }
322
323 // function_call := identifier "(" list_contents ")"
324 // [<non-newline-whitespace>* block]
325 scoped_ptr<FunctionCallNode> Parser::ParseFunctionCall() {
326 scoped_ptr<FunctionCallNode> func(new FunctionCallNode);
327
328 const Token& function_token = cur_token();
329 func->set_function(function_token);
330
331 // This function should only get called when we know we have a function,
332 // which only happens when there is a paren following the name. Skip past it.
333 DCHECK(has_next_token());
334 cur_++; // Skip past function name to (.
335 const Token& open_paren_token = cur_token();
336 DCHECK(IsFunctionCallArgBeginScoper(open_paren_token));
337
338 if (at_end()) {
339 *err_ = Err(open_paren_token, "Unexpected EOF for function call.",
340 "You didn't finish the thought you started here.");
341 return scoped_ptr<FunctionCallNode>();
342 }
343
344 // Arguments.
345 func->set_args(ParseList(Token(Location(), Token::SCOPER, "("),
346 Token(Location(), Token::SCOPER, ")")));
347 if (has_error())
348 return scoped_ptr<FunctionCallNode>();
349
350 // Optional {} after function call for certain functions. The "{" must be on
351 // the same line as the ")" to disambiguate the case of a function followed
352 // by a random block just used for scoping purposes.
353 if (!at_end() && IsScopeBeginScoper(cur_token())) {
354 const Token& args_end_token = tokens_[cur_ - 1];
355 DCHECK(args_end_token.IsScoperEqualTo(")"));
356 if (IsSameLine(args_end_token, cur_token()))
357 func->set_block(ParseBlock(true).Pass());
358 }
359
360 if (has_error())
361 return scoped_ptr<FunctionCallNode>();
362 return func.Pass();
363 }
364
365 // list := "[" expression* "]"
366 // list_contents := [(expression ",")* expression [","]]
367 //
368 // The list_contents is also used in function calls surrounded by parens, so
369 // this function takes the tokens that are expected to surround the list.
370 scoped_ptr<ListNode> Parser::ParseList(const Token& expected_begin,
371 const Token& expected_end) {
372 scoped_ptr<ListNode> list(new ListNode);
373
374 const Token& open_bracket_token = cur_token();
375 list->set_begin_token(open_bracket_token);
376 cur_++; // Skip "[" or "(".
377
378 bool need_separator = false;
379 while(true) {
380 if (at_end()) {
381 *err_ = Err(open_bracket_token, "EOF found when parsing list.",
382 "I expected a \"" + expected_end.value().as_string() +
383 "\" corresponding to this one.");
384 return scoped_ptr<ListNode>();
385 }
386 if (cur_token().type() == expected_end.type() &&
387 cur_token().value() == expected_end.value()) {
388 list->set_end_token(cur_token());
389 cur_++;
390 break;
391 }
392
393 if (need_separator) {
394 DCHECK(!list->contents().empty());
395 LocationRange prev_item_range =
396 list->contents().at(list->contents().size() - 1)->GetRange();
397 *err_ = Err(prev_item_range.end(),
398 "Need comma separating items in list.",
399 "You probably need a comma after this thingy.");
400 err_->AppendRange(prev_item_range);
401 return scoped_ptr<ListNode>();
402 }
403 scoped_ptr<ParseNode> expr = ParseExpression().Pass();
404 if (has_error())
405 return scoped_ptr<ListNode>();
406 list->append_item(expr.Pass());
407
408 need_separator = true;
409 if (!at_end()) {
410 // Skip over the separator, marking that we found it.
411 if (cur_token().type() == Token::SEPARATOR) {
412 cur_++;
413 need_separator = false;
414 }
415 }
416 }
417 return list.Pass();
418 }
419
420 // paren_expression := "(" expression ")"
421 scoped_ptr<ParseNode> Parser::ParseParenExpression() {
422 const Token& open_paren_token = cur_token();
423 cur_++; // Skip over (
424
425 scoped_ptr<ParseNode> ret = ParseExpression();
426 if (has_error())
427 return scoped_ptr<ParseNode>();
428
429 if (at_end()) {
430 *err_ = Err(open_paren_token, "EOF found when parsing expression.",
431 "I was looking for a \")\" corresponding to this one.");
432 return scoped_ptr<ParseNode>();
433 }
434 if (!cur_token().IsScoperEqualTo(")")) {
435 *err_ = Err(open_paren_token, "Expected \")\" for expression",
436 "I was looking for a \")\" corresponding to this one.");
437 return scoped_ptr<ParseNode>();
438 }
439 cur_++; // Skip over )
440 return ret.Pass();
441 }
442
443 // unary_expression := "!" expression
444 scoped_ptr<UnaryOpNode> Parser::ParseUnaryOp() {
445 scoped_ptr<UnaryOpNode> unary(new UnaryOpNode);
446
447 DCHECK(!at_end() && IsUnaryOperator(cur_token()));
448 const Token& op_token = cur_token();
449 unary->set_op(op_token);
450 cur_++;
451
452 if (at_end()) {
453 *err_ = Err(op_token, "Expected expression.",
454 "This operator needs something to operate on.");
455 return scoped_ptr<UnaryOpNode>();
456 }
457 unary->set_operand(ParseExpression().Pass());
458 if (has_error())
459 return scoped_ptr<UnaryOpNode>();
460 return unary.Pass();
461 }
462
463 Err Parser::MakeEOFError(const std::string& message,
464 const std::string& help) const {
465 if (tokens_.empty())
466 return Err(Location(NULL, 1, 1), message, help);
467
468 const Token& last = tokens_[tokens_.size() - 1];
469 return Err(last, message, help);
470 }
OLDNEW
« no previous file with comments | « tools/gn/parser.h ('k') | tools/gn/parser_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698