summaryrefslogtreecommitdiffstats
path: root/tools/gn/parser.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tools/gn/parser.cc')
-rw-r--r--tools/gn/parser.cc470
1 files changed, 0 insertions, 470 deletions
diff --git a/tools/gn/parser.cc b/tools/gn/parser.cc
deleted file mode 100644
index 385aa34..0000000
--- a/tools/gn/parser.cc
+++ /dev/null
@@ -1,470 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "tools/gn/parser.h"
-
-#include "base/logging.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/operators.h"
-#include "tools/gn/token.h"
-
-namespace {
-
-// Returns true if the two tokens are on the same line. We assume they're in
-// the same file.
-bool IsSameLine(const Token& a, const Token& b) {
- DCHECK(a.location().file() == b.location().file());
- return a.location().line_number() == b.location().line_number();
-}
-
-} // namespace
-
-Parser::Parser(const std::vector<Token>& tokens, Err* err)
- : tokens_(tokens),
- err_(err),
- cur_(0) {
-}
-
-Parser::~Parser() {
-}
-
-// static
-scoped_ptr<ParseNode> Parser::Parse(const std::vector<Token>& tokens,
- Err* err) {
- Parser p(tokens, err);
- return p.ParseBlock(false).PassAs<ParseNode>();
-}
-
-// static
-scoped_ptr<ParseNode> Parser::ParseExpression(const std::vector<Token>& tokens,
- Err* err) {
- Parser p(tokens, err);
- return p.ParseExpression().Pass();
-}
-
-bool Parser::IsToken(Token::Type type, char* str) const {
- if (at_end())
- return false;
- return cur_token().type() == type || cur_token().value() == str;
-}
-
-scoped_ptr<AccessorNode> Parser::ParseAccessor() {
- scoped_ptr<AccessorNode> accessor(new AccessorNode);
-
- DCHECK(cur_token().type() == Token::IDENTIFIER);
- accessor->set_base(cur_token());
- cur_++; // Skip identifier.
- cur_++; // Skip "[" (we know this exists because the existance of this
- // token is how the caller knows it's an accessor.
-
- if (at_end()) {
- *err_ = MakeEOFError("Got EOF when looking for list index.");
- return scoped_ptr<AccessorNode>();
- }
-
- // Get the expression.
- scoped_ptr<ParseNode> expr = ParseExpression().Pass();
- if (has_error())
- return scoped_ptr<AccessorNode>();
- if (at_end()) {
- *err_ = MakeEOFError("Got EOF when looking for list accessor ]");
- return scoped_ptr<AccessorNode>();
- }
- accessor->set_index(expr.Pass());
-
- // Skip over "]"
- if (!cur_token().IsScoperEqualTo("]")) {
- *err_ = Err(cur_token(), "Expecting ]",
- "You started a list access but didn't terminate it, and instead "
- "I fould this\nstupid thing.");
- return scoped_ptr<AccessorNode>();
- }
- cur_++;
-
- return accessor.Pass();
-}
-
-// Blocks at the file scope don't need {} so we have the option to ignore
-// them. When need_braces is set, we'll expect a begin an end brace.
-//
-// block := "{" block_contents "}"
-// block_contents := (expression | conditional | block)*
-scoped_ptr<BlockNode> Parser::ParseBlock(bool need_braces) {
- scoped_ptr<BlockNode> block(new BlockNode(true));
-
- // Eat initial { if necessary.
- const Token* opening_curly_brace;
- if (need_braces) {
- if (at_end()) {
- *err_ = MakeEOFError("Got EOF when looking for { for block.",
- "It should have been after here.");
- return scoped_ptr<BlockNode>();
- } else if(!IsScopeBeginScoper(cur_token())) {
- *err_ = Err(cur_token(), "Expecting { instead of this thing.",
- "THOU SHALT USE CURLY BRACES FOR ALL BLOCKS.");
- return scoped_ptr<BlockNode>();
- }
- opening_curly_brace = &cur_token();
- block->set_begin_token(opening_curly_brace);
- cur_++;
- }
-
- // Loop until EOF or end brace found.
- while (!at_end() && !IsScopeEndScoper(cur_token())) {
- if (cur_token().IsIdentifierEqualTo("if")) {
- // Conditional.
- block->append_statement(ParseCondition().PassAs<ParseNode>());
- } else if (IsScopeBeginScoper(cur_token())) {
- // Nested block.
- block->append_statement(ParseBlock(true).PassAs<ParseNode>());
- } else {
- // Everything else is an expression.
- block->append_statement(ParseExpression().PassAs<ParseNode>());
- }
- if (has_error())
- return scoped_ptr<BlockNode>();
- }
-
- // Eat the ending "}" if necessary.
- if (need_braces) {
- if (at_end() || !IsScopeEndScoper(cur_token())) {
- *err_ = Err(*opening_curly_brace, "Expecting }",
- "I ran headlong into the end of the file looking for the "
- "closing brace\ncorresponding to this one.");
- return scoped_ptr<BlockNode>();
- }
- block->set_end_token(&cur_token());
- cur_++; // Skip past "}".
- }
-
- return block.Pass();
-}
-
-// conditional := "if (" expression ")" block [else_conditional]
-// else_conditional := ("else" block) | ("else" conditional)
-scoped_ptr<ConditionNode> Parser::ParseCondition() {
- scoped_ptr<ConditionNode> cond(new ConditionNode);
-
- // Skip past "if".
- const Token& if_token = cur_token();
- cond->set_if_token(if_token);
- DCHECK(if_token.IsIdentifierEqualTo("if"));
- cur_++;
-
- if (at_end() || !IsFunctionCallArgBeginScoper(cur_token())) {
- *err_ = Err(if_token, "Expecting \"(\" after \"if\"",
- "Did you think this was Python or something?");
- return scoped_ptr<ConditionNode>();
- }
-
- // Skip over (.
- const Token& open_paren_token = cur_token();
- cur_++;
- if (at_end()) {
- *err_ = Err(if_token, "Unexpected EOF inside if condition");
- return scoped_ptr<ConditionNode>();
- }
-
- // Condition inside ().
- cond->set_condition(ParseExpression().Pass());
- if (has_error())
- return scoped_ptr<ConditionNode>();
-
- if (at_end() || !IsFunctionCallArgEndScoper(cur_token())) {
- *err_ = Err(open_paren_token, "Expecting \")\" for \"if\" condition",
- "You didn't finish the thought you started here.");
- return scoped_ptr<ConditionNode>();
- }
- cur_++; // Skip over )
-
- // Contents of {}.
- cond->set_if_true(ParseBlock(true).Pass());
- if (has_error())
- return scoped_ptr<ConditionNode>();
-
- // Optional "else" at the end.
- if (!at_end() && cur_token().IsIdentifierEqualTo("else")) {
- cur_++;
-
- // The else may be followed by an if or a block.
- if (at_end()) {
- *err_ = MakeEOFError("Ran into end of file after \"else\".",
- "else, WHAT?!?!?");
- return scoped_ptr<ConditionNode>();
- }
- if (cur_token().IsIdentifierEqualTo("if")) {
- // "else if() {"
- cond->set_if_false(ParseCondition().PassAs<ParseNode>());
- } else if (IsScopeBeginScoper(cur_token())) {
- // "else {"
- cond->set_if_false(ParseBlock(true).PassAs<ParseNode>());
- } else {
- // else <anything else>
- *err_ = Err(cur_token(), "Expected \"if\" or \"{\" after \"else\".",
- "This is neither of those things.");
- return scoped_ptr<ConditionNode>();
- }
- }
-
- if (has_error())
- return scoped_ptr<ConditionNode>();
- return cond.Pass();
-}
-
-// expression := paren_expression | accessor | identifier | literal |
-// funccall | unary_expression | binary_expression
-//
-// accessor := identifier <non-newline-whitespace>* "[" expression "]"
-//
-// The "non-newline-whitespace is used to differentiate between this case:
-// a[1]
-// and this one:
-// a
-// [1]
-// The second one is kind of stupid (since it does nothing with the values)
-// but is still legal.
-scoped_ptr<ParseNode> Parser::ParseExpression() {
- scoped_ptr<ParseNode> expr = ParseExpressionExceptBinaryOperators();
- if (has_error())
- return scoped_ptr<ParseNode>();
-
- // That may have hit EOF, in which case we can't have any binary operators.
- if (at_end())
- return expr.Pass();
-
- // TODO(brettw) handle operator precidence!
- // Gobble up all subsequent expressions as long as there are binary
- // operators.
-
- if (IsBinaryOperator(cur_token())) {
- scoped_ptr<BinaryOpNode> binary_op(new BinaryOpNode);
- binary_op->set_left(expr.Pass());
- const Token& operator_token = cur_token();
- binary_op->set_op(operator_token);
- cur_++;
- if (at_end()) {
- *err_ = Err(operator_token, "Unexpected EOF in expression.",
- "I was looking for the right-hand-side of this operator.");
- return scoped_ptr<ParseNode>();
- }
- binary_op->set_right(ParseExpression().Pass());
- if (has_error())
- return scoped_ptr<ParseNode>();
- return binary_op.PassAs<ParseNode>();
- }
-
- return expr.Pass();
-}
-
-
-// This internal one does not handle binary operators, since it requires
-// looking at the "next" thing. The regular ParseExpression above handles it.
-scoped_ptr<ParseNode> Parser::ParseExpressionExceptBinaryOperators() {
- if (at_end())
- return scoped_ptr<ParseNode>();
-
- const Token& token = cur_token();
-
- // Unary expression.
- if (IsUnaryOperator(token))
- return ParseUnaryOp().PassAs<ParseNode>();
-
- // Parenthesized expressions.
- if (token.IsScoperEqualTo("("))
- return ParseParenExpression();
-
- // Function calls.
- if (token.type() == Token::IDENTIFIER) {
- if (has_next_token() && IsFunctionCallArgBeginScoper(next_token()))
- return ParseFunctionCall().PassAs<ParseNode>();
- }
-
- // Lists.
- if (token.IsScoperEqualTo("[")) {
- return ParseList(Token(Location(), Token::SCOPER, "["),
- Token(Location(), Token::SCOPER, "]")).PassAs<ParseNode>();
- }
-
- // Literals.
- if (token.type() == Token::STRING || token.type() == Token::INTEGER) {
- cur_++;
- return scoped_ptr<ParseNode>(new LiteralNode(token));
- }
-
- // Accessors.
- if (token.type() == Token::IDENTIFIER &&
- has_next_token() && next_token().IsScoperEqualTo("[") &&
- IsSameLine(token, next_token())) {
- return ParseAccessor().PassAs<ParseNode>();
- }
-
- // Identifiers.
- if (token.type() == Token::IDENTIFIER) {
- cur_++;
- return scoped_ptr<ParseNode>(new IdentifierNode(token));
- }
-
- // Handle errors.
- if (token.type() == Token::SEPARATOR) {
- *err_ = Err(token, "Unexpected comma.",
- "You can't put a comma here, it must be in list separating "
- "complete\nthoughts.");
- } else if (IsScopeBeginScoper(token)) {
- *err_ = Err(token, "Unexpected token.",
- "You can't put a \"{\" scope here, it must be in a block.");
- } else {
- *err_ = Err(token, "Unexpected token.",
- "I was really hoping for something else here and you let me down.");
- }
- return scoped_ptr<ParseNode>();
-}
-
-// function_call := identifier "(" list_contents ")"
-// [<non-newline-whitespace>* block]
-scoped_ptr<FunctionCallNode> Parser::ParseFunctionCall() {
- scoped_ptr<FunctionCallNode> func(new FunctionCallNode);
-
- const Token& function_token = cur_token();
- func->set_function(function_token);
-
- // This function should only get called when we know we have a function,
- // which only happens when there is a paren following the name. Skip past it.
- DCHECK(has_next_token());
- cur_++; // Skip past function name to (.
- const Token& open_paren_token = cur_token();
- DCHECK(IsFunctionCallArgBeginScoper(open_paren_token));
-
- if (at_end()) {
- *err_ = Err(open_paren_token, "Unexpected EOF for function call.",
- "You didn't finish the thought you started here.");
- return scoped_ptr<FunctionCallNode>();
- }
-
- // Arguments.
- func->set_args(ParseList(Token(Location(), Token::SCOPER, "("),
- Token(Location(), Token::SCOPER, ")")));
- if (has_error())
- return scoped_ptr<FunctionCallNode>();
-
- // Optional {} after function call for certain functions. The "{" must be on
- // the same line as the ")" to disambiguate the case of a function followed
- // by a random block just used for scoping purposes.
- if (!at_end() && IsScopeBeginScoper(cur_token())) {
- const Token& args_end_token = tokens_[cur_ - 1];
- DCHECK(args_end_token.IsScoperEqualTo(")"));
- if (IsSameLine(args_end_token, cur_token()))
- func->set_block(ParseBlock(true).Pass());
- }
-
- if (has_error())
- return scoped_ptr<FunctionCallNode>();
- return func.Pass();
-}
-
-// list := "[" expression* "]"
-// list_contents := [(expression ",")* expression [","]]
-//
-// The list_contents is also used in function calls surrounded by parens, so
-// this function takes the tokens that are expected to surround the list.
-scoped_ptr<ListNode> Parser::ParseList(const Token& expected_begin,
- const Token& expected_end) {
- scoped_ptr<ListNode> list(new ListNode);
-
- const Token& open_bracket_token = cur_token();
- list->set_begin_token(open_bracket_token);
- cur_++; // Skip "[" or "(".
-
- bool need_separator = false;
- while(true) {
- if (at_end()) {
- *err_ = Err(open_bracket_token, "EOF found when parsing list.",
- "I expected a \"" + expected_end.value().as_string() +
- "\" corresponding to this one.");
- return scoped_ptr<ListNode>();
- }
- if (cur_token().type() == expected_end.type() &&
- cur_token().value() == expected_end.value()) {
- list->set_end_token(cur_token());
- cur_++;
- break;
- }
-
- if (need_separator) {
- DCHECK(!list->contents().empty());
- LocationRange prev_item_range =
- list->contents().at(list->contents().size() - 1)->GetRange();
- *err_ = Err(prev_item_range.end(),
- "Need comma separating items in list.",
- "You probably need a comma after this thingy.");
- err_->AppendRange(prev_item_range);
- return scoped_ptr<ListNode>();
- }
- scoped_ptr<ParseNode> expr = ParseExpression().Pass();
- if (has_error())
- return scoped_ptr<ListNode>();
- list->append_item(expr.Pass());
-
- need_separator = true;
- if (!at_end()) {
- // Skip over the separator, marking that we found it.
- if (cur_token().type() == Token::SEPARATOR) {
- cur_++;
- need_separator = false;
- }
- }
- }
- return list.Pass();
-}
-
-// paren_expression := "(" expression ")"
-scoped_ptr<ParseNode> Parser::ParseParenExpression() {
- const Token& open_paren_token = cur_token();
- cur_++; // Skip over (
-
- scoped_ptr<ParseNode> ret = ParseExpression();
- if (has_error())
- return scoped_ptr<ParseNode>();
-
- if (at_end()) {
- *err_ = Err(open_paren_token, "EOF found when parsing expression.",
- "I was looking for a \")\" corresponding to this one.");
- return scoped_ptr<ParseNode>();
- }
- if (!cur_token().IsScoperEqualTo(")")) {
- *err_ = Err(open_paren_token, "Expected \")\" for expression",
- "I was looking for a \")\" corresponding to this one.");
- return scoped_ptr<ParseNode>();
- }
- cur_++; // Skip over )
- return ret.Pass();
-}
-
-// unary_expression := "!" expression
-scoped_ptr<UnaryOpNode> Parser::ParseUnaryOp() {
- scoped_ptr<UnaryOpNode> unary(new UnaryOpNode);
-
- DCHECK(!at_end() && IsUnaryOperator(cur_token()));
- const Token& op_token = cur_token();
- unary->set_op(op_token);
- cur_++;
-
- if (at_end()) {
- *err_ = Err(op_token, "Expected expression.",
- "This operator needs something to operate on.");
- return scoped_ptr<UnaryOpNode>();
- }
- unary->set_operand(ParseExpression().Pass());
- if (has_error())
- return scoped_ptr<UnaryOpNode>();
- return unary.Pass();
-}
-
-Err Parser::MakeEOFError(const std::string& message,
- const std::string& help) const {
- if (tokens_.empty())
- return Err(Location(NULL, 1, 1), message, help);
-
- const Token& last = tokens_[tokens_.size() - 1];
- return Err(last, message, help);
-}