diff options
| author | Dan Willemsen <dwillemsen@google.com> | 2020-06-29 22:33:05 -0700 |
|---|---|---|
| committer | Dan Willemsen <dwillemsen@google.com> | 2020-06-29 22:33:05 -0700 |
| commit | 5125da301ca0a1680c26b5d9f574dc174fdfa98c (patch) | |
| tree | db93815340f8e87080791cf7bc93e135234e9387 /src/parser.cc | |
| parent | e184ff147273cfd25d37692dba4be72eca571e9f (diff) | |
| parent | 06a798c4db92ec3a3a6b3a4b52026f002002affa (diff) | |
| download | platform_build_kati-master.tar.gz platform_build_kati-master.tar.bz2 platform_build_kati-master.zip | |
* aosp/upstream:
Support git submodules in version.cc generation.
Link ckati against jemalloc if host machine runs Linux.
Support building in a git worktree
Fix flaky test
Switch to a golang-based test runner
Refactor source tree into directories
Test: treehugger
Change-Id: I6cca6108531c4c32e3848bf6b532a99aef0f06c2
Diffstat (limited to 'src/parser.cc')
| -rw-r--r-- | src/parser.cc | 625 |
1 files changed, 625 insertions, 0 deletions
diff --git a/src/parser.cc b/src/parser.cc new file mode 100644 index 0000000..4980f77 --- /dev/null +++ b/src/parser.cc @@ -0,0 +1,625 @@ +// Copyright 2015 Google Inc. All rights reserved +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build ignore + +#include "parser.h" + +#include <stack> +#include <unordered_map> + +#include "expr.h" +#include "file.h" +#include "loc.h" +#include "log.h" +#include "stats.h" +#include "stmt.h" +#include "string_piece.h" +#include "strutil.h" + +enum struct ParserState { + NOT_AFTER_RULE = 0, + AFTER_RULE, + MAYBE_AFTER_RULE, +}; + +class Parser { + struct IfState { + IfStmt* stmt; + bool is_in_else; + int num_nest; + }; + + typedef void (Parser::*DirectiveHandler)(StringPiece line, + StringPiece directive); + typedef unordered_map<StringPiece, DirectiveHandler> DirectiveMap; + + public: + Parser(StringPiece buf, const char* filename, vector<Stmt*>* stmts) + : buf_(buf), + state_(ParserState::NOT_AFTER_RULE), + stmts_(stmts), + out_stmts_(stmts), + num_define_nest_(0), + num_if_nest_(0), + loc_(filename, 0), + fixed_lineno_(false) {} + + Parser(StringPiece buf, const Loc& loc, vector<Stmt*>* stmts) + : buf_(buf), + state_(ParserState::NOT_AFTER_RULE), + stmts_(stmts), + out_stmts_(stmts), + num_if_nest_(0), + loc_(loc), + fixed_lineno_(true) {} + + ~Parser() {} + + void Parse() { + l_ = 0; + + for (l_ = 0; l_ < buf_.size();) { + size_t lf_cnt = 0; + size_t e = FindEndOfLine(&lf_cnt); + if (!fixed_lineno_) + loc_.lineno++; + StringPiece line(buf_.data() + l_, e - l_); + if (line.get(line.size() - 1) == '\r') + line.remove_suffix(1); + orig_line_with_directives_ = line; + ParseLine(line); + if (!fixed_lineno_) + loc_.lineno += lf_cnt - 1; + if (e == buf_.size()) + break; + + l_ = e + 1; + } + + if (!if_stack_.empty()) + ERROR_LOC(Loc(loc_.filename, loc_.lineno + 1), "*** missing `endif'."); + if (!define_name_.empty()) + ERROR_LOC(Loc(loc_.filename, define_start_line_), + "*** missing `endef', unterminated `define'."); + } + + static void Init() { + make_directives_ = new DirectiveMap; + (*make_directives_)["include"] = &Parser::ParseInclude; + (*make_directives_)["-include"] = &Parser::ParseInclude; + (*make_directives_)["sinclude"] = &Parser::ParseInclude; + (*make_directives_)["define"] = &Parser::ParseDefine; + (*make_directives_)["ifdef"] = &Parser::ParseIfdef; + (*make_directives_)["ifndef"] = &Parser::ParseIfdef; + (*make_directives_)["ifeq"] = &Parser::ParseIfeq; + (*make_directives_)["ifneq"] = &Parser::ParseIfeq; + (*make_directives_)["else"] = &Parser::ParseElse; + (*make_directives_)["endif"] = &Parser::ParseEndif; + (*make_directives_)["override"] = &Parser::ParseOverride; + (*make_directives_)["export"] = &Parser::ParseExport; + (*make_directives_)["unexport"] = &Parser::ParseUnexport; + + else_if_directives_ = new DirectiveMap; + (*else_if_directives_)["ifdef"] = &Parser::ParseIfdef; + (*else_if_directives_)["ifndef"] = &Parser::ParseIfdef; + (*else_if_directives_)["ifeq"] = &Parser::ParseIfeq; + (*else_if_directives_)["ifneq"] = &Parser::ParseIfeq; + + assign_directives_ = new DirectiveMap; + (*assign_directives_)["define"] = &Parser::ParseDefine; + (*assign_directives_)["export"] = &Parser::ParseExport; + (*assign_directives_)["override"] = &Parser::ParseOverride; + + shortest_directive_len_ = 9999; + longest_directive_len_ = 0; + for (auto p : *make_directives_) { + size_t len = p.first.size(); + shortest_directive_len_ = min(len, shortest_directive_len_); + longest_directive_len_ = max(len, longest_directive_len_); + } + } + + static void Quit() { delete make_directives_; } + + void set_state(ParserState st) { state_ = st; } + + static vector<ParseErrorStmt*> parse_errors; + + private: + void Error(const string& msg) { + ParseErrorStmt* stmt = new ParseErrorStmt(); + stmt->set_loc(loc_); + stmt->msg = msg; + out_stmts_->push_back(stmt); + parse_errors.push_back(stmt); + } + + size_t FindEndOfLine(size_t* lf_cnt) { + return ::FindEndOfLine(buf_, l_, lf_cnt); + } + + Value* ParseExpr(StringPiece s, ParseExprOpt opt = ParseExprOpt::NORMAL) { + return ::ParseExpr(loc_, s, opt); + } + + void ParseLine(StringPiece line) { + if (!define_name_.empty()) { + ParseInsideDefine(line); + return; + } + + if (line.empty() || (line.size() == 1 && line[0] == '\r')) + return; + + current_directive_ = AssignDirective::NONE; + + if (line[0] == '\t' && state_ != ParserState::NOT_AFTER_RULE) { + CommandStmt* stmt = new CommandStmt(); + stmt->set_loc(loc_); + stmt->expr = ParseExpr(line.substr(1), ParseExprOpt::COMMAND); + stmt->orig = line; + out_stmts_->push_back(stmt); + return; + } + + line = TrimLeftSpace(line); + + if (line[0] == '#') + return; + + if (HandleDirective(line, make_directives_)) { + return; + } + + ParseRuleOrAssign(line); + } + + void ParseRuleOrAssign(StringPiece line) { + size_t sep = FindThreeOutsideParen(line, ':', '=', ';'); + if (sep == string::npos || line[sep] == ';') { + ParseRule(line, string::npos); + } else if (line[sep] == '=') { + ParseAssign(line, sep); + } else if (line.get(sep + 1) == '=') { + ParseAssign(line, sep + 1); + } else if (line[sep] == ':') { + ParseRule(line, sep); + } else { + CHECK(false); + } + } + + void ParseRule(StringPiece line, size_t sep) { + if (current_directive_ != AssignDirective::NONE) { + if (IsInExport()) + return; + if (sep != string::npos) { + sep += orig_line_with_directives_.size() - line.size(); + } + line = orig_line_with_directives_; + } + + line = TrimLeftSpace(line); + if (line.empty()) + return; + + if (orig_line_with_directives_[0] == '\t') { + Error("*** commands commence before first target."); + return; + } + + const bool is_rule = sep != string::npos && line[sep] == ':'; + RuleStmt* rule_stmt = new RuleStmt(); + rule_stmt->set_loc(loc_); + + size_t found = FindTwoOutsideParen(line.substr(sep + 1), '=', ';'); + if (found != string::npos) { + found += sep + 1; + rule_stmt->lhs = ParseExpr(TrimSpace(line.substr(0, found))); + if (line[found] == ';') { + rule_stmt->sep = RuleStmt::SEP_SEMICOLON; + } else if (line[found] == '=') { + if (line.size() > (found + 2) && line[found + 1] == '$' && + line[found + 2] == '=') { + rule_stmt->sep = RuleStmt::SEP_FINALEQ; + found += 2; + } else { + rule_stmt->sep = RuleStmt::SEP_EQ; + } + } + ParseExprOpt opt = rule_stmt->sep == RuleStmt::SEP_SEMICOLON + ? ParseExprOpt::COMMAND + : ParseExprOpt::NORMAL; + rule_stmt->rhs = ParseExpr(TrimLeftSpace(line.substr(found + 1)), opt); + } else { + rule_stmt->lhs = ParseExpr(line); + rule_stmt->sep = RuleStmt::SEP_NULL; + rule_stmt->rhs = NULL; + } + out_stmts_->push_back(rule_stmt); + state_ = is_rule ? ParserState::AFTER_RULE : ParserState::MAYBE_AFTER_RULE; + } + + void ParseAssign(StringPiece line, size_t separator_pos) { + if (separator_pos == 0) { + Error("*** empty variable name ***"); + return; + } + StringPiece lhs; + StringPiece rhs; + AssignOp op; + ParseAssignStatement(line, separator_pos, &lhs, &rhs, &op); + + // If rhs starts with '$=', this is 'final assignment', + // e.g., a combination of the assignment and + // .KATI_READONLY := <lhs> + // statement. Note that we assume that ParseAssignStatement + // trimmed the left + bool is_final = (rhs.size() >= 2 && rhs[0] == '$' && rhs[1] == '='); + if (is_final) { + rhs = TrimLeftSpace(rhs.substr(2)); + } + + AssignStmt* stmt = new AssignStmt(); + stmt->set_loc(loc_); + stmt->lhs = ParseExpr(lhs); + stmt->rhs = ParseExpr(rhs); + stmt->orig_rhs = rhs; + stmt->op = op; + stmt->directive = current_directive_; + stmt->is_final = is_final; + out_stmts_->push_back(stmt); + state_ = ParserState::NOT_AFTER_RULE; + } + + void ParseInclude(StringPiece line, StringPiece directive) { + IncludeStmt* stmt = new IncludeStmt(); + stmt->set_loc(loc_); + stmt->expr = ParseExpr(line); + stmt->should_exist = directive[0] == 'i'; + out_stmts_->push_back(stmt); + state_ = ParserState::NOT_AFTER_RULE; + } + + void ParseDefine(StringPiece line, StringPiece) { + if (line.empty()) { + Error("*** empty variable name."); + return; + } + define_name_ = line; + num_define_nest_ = 1; + define_start_ = 0; + define_start_line_ = loc_.lineno; + state_ = ParserState::NOT_AFTER_RULE; + } + + void ParseInsideDefine(StringPiece line) { + line = TrimLeftSpace(line); + StringPiece directive = GetDirective(line); + if (directive == "define") + num_define_nest_++; + else if (directive == "endef") + num_define_nest_--; + if (num_define_nest_ > 0) { + if (define_start_ == 0) + define_start_ = l_; + return; + } + + StringPiece rest = TrimRightSpace( + RemoveComment(TrimLeftSpace(line.substr(sizeof("endef"))))); + if (!rest.empty()) { + WARN_LOC(loc_, "extraneous text after `endef' directive"); + } + + AssignStmt* stmt = new AssignStmt(); + stmt->set_loc(Loc(loc_.filename, define_start_line_)); + stmt->lhs = ParseExpr(define_name_); + StringPiece rhs; + if (define_start_) + rhs = buf_.substr(define_start_, l_ - define_start_ - 1); + stmt->rhs = ParseExpr(rhs, ParseExprOpt::DEFINE); + stmt->orig_rhs = rhs; + stmt->op = AssignOp::EQ; + stmt->directive = current_directive_; + out_stmts_->push_back(stmt); + define_name_.clear(); + } + + void EnterIf(IfStmt* stmt) { + IfState* st = new IfState(); + st->stmt = stmt; + st->is_in_else = false; + st->num_nest = num_if_nest_; + if_stack_.push(st); + out_stmts_ = &stmt->true_stmts; + } + + void ParseIfdef(StringPiece line, StringPiece directive) { + IfStmt* stmt = new IfStmt(); + stmt->set_loc(loc_); + stmt->op = directive[2] == 'n' ? CondOp::IFNDEF : CondOp::IFDEF; + stmt->lhs = ParseExpr(line); + stmt->rhs = NULL; + out_stmts_->push_back(stmt); + EnterIf(stmt); + } + + bool ParseIfEqCond(StringPiece s, IfStmt* stmt) { + if (s.empty()) { + return false; + } + + if (s[0] == '(' && s[s.size() - 1] == ')') { + s = s.substr(1, s.size() - 2); + char terms[] = {',', '\0'}; + size_t n; + stmt->lhs = ParseExprImpl(loc_, s, terms, ParseExprOpt::NORMAL, &n, true); + if (s[n] != ',') + return false; + s = TrimLeftSpace(s.substr(n + 1)); + stmt->rhs = ParseExprImpl(loc_, s, NULL, ParseExprOpt::NORMAL, &n); + s = TrimLeftSpace(s.substr(n)); + } else { + for (int i = 0; i < 2; i++) { + if (s.empty()) + return false; + char quote = s[0]; + if (quote != '\'' && quote != '"') + return false; + size_t end = s.find(quote, 1); + if (end == string::npos) + return false; + Value* v = ParseExpr(s.substr(1, end - 1), ParseExprOpt::NORMAL); + if (i == 0) + stmt->lhs = v; + else + stmt->rhs = v; + s = TrimLeftSpace(s.substr(end + 1)); + } + } + if (!s.empty()) { + WARN_LOC(loc_, "extraneous text after `ifeq' directive"); + return true; + } + return true; + } + + void ParseIfeq(StringPiece line, StringPiece directive) { + IfStmt* stmt = new IfStmt(); + stmt->set_loc(loc_); + stmt->op = directive[2] == 'n' ? CondOp::IFNEQ : CondOp::IFEQ; + + if (!ParseIfEqCond(line, stmt)) { + Error("*** invalid syntax in conditional."); + return; + } + + out_stmts_->push_back(stmt); + EnterIf(stmt); + } + + void ParseElse(StringPiece line, StringPiece) { + if (!CheckIfStack("else")) + return; + IfState* st = if_stack_.top(); + if (st->is_in_else) { + Error("*** only one `else' per conditional."); + return; + } + st->is_in_else = true; + out_stmts_ = &st->stmt->false_stmts; + + StringPiece next_if = TrimLeftSpace(line); + if (next_if.empty()) + return; + + num_if_nest_ = st->num_nest + 1; + if (!HandleDirective(next_if, else_if_directives_)) { + WARN_LOC(loc_, "extraneous text after `else' directive"); + } + num_if_nest_ = 0; + } + + void ParseEndif(StringPiece line, StringPiece) { + if (!CheckIfStack("endif")) + return; + if (!line.empty()) { + Error("extraneous text after `endif` directive"); + return; + } + IfState st = *if_stack_.top(); + for (int t = 0; t <= st.num_nest; t++) { + delete if_stack_.top(); + if_stack_.pop(); + if (if_stack_.empty()) { + out_stmts_ = stmts_; + } else { + IfState* st = if_stack_.top(); + if (st->is_in_else) + out_stmts_ = &st->stmt->false_stmts; + else + out_stmts_ = &st->stmt->true_stmts; + } + } + } + + bool IsInExport() const { + return (static_cast<int>(current_directive_) & + static_cast<int>(AssignDirective::EXPORT)); + } + + void CreateExport(StringPiece line, bool is_export) { + ExportStmt* stmt = new ExportStmt; + stmt->set_loc(loc_); + stmt->expr = ParseExpr(line); + stmt->is_export = is_export; + out_stmts_->push_back(stmt); + } + + void ParseOverride(StringPiece line, StringPiece) { + current_directive_ = static_cast<AssignDirective>( + (static_cast<int>(current_directive_) | + static_cast<int>(AssignDirective::OVERRIDE))); + if (HandleDirective(line, assign_directives_)) + return; + if (IsInExport()) { + CreateExport(line, true); + } + ParseRuleOrAssign(line); + } + + void ParseExport(StringPiece line, StringPiece) { + current_directive_ = static_cast<AssignDirective>( + (static_cast<int>(current_directive_) | + static_cast<int>(AssignDirective::EXPORT))); + if (HandleDirective(line, assign_directives_)) + return; + CreateExport(line, true); + ParseRuleOrAssign(line); + } + + void ParseUnexport(StringPiece line, StringPiece) { + CreateExport(line, false); + } + + bool CheckIfStack(const char* keyword) { + if (if_stack_.empty()) { + Error(StringPrintf("*** extraneous `%s'.", keyword)); + return false; + } + return true; + } + + StringPiece RemoveComment(StringPiece line) { + size_t i = FindOutsideParen(line, '#'); + if (i == string::npos) + return line; + return line.substr(0, i); + } + + StringPiece GetDirective(StringPiece line) { + if (line.size() < shortest_directive_len_) + return StringPiece(); + StringPiece prefix = line.substr(0, longest_directive_len_ + 1); + size_t space_index = prefix.find_first_of(" \t#"); + return prefix.substr(0, space_index); + } + + bool HandleDirective(StringPiece line, const DirectiveMap* directive_map) { + StringPiece directive = GetDirective(line); + auto found = directive_map->find(directive); + if (found == directive_map->end()) + return false; + + StringPiece rest = TrimRightSpace( + RemoveComment(TrimLeftSpace(line.substr(directive.size())))); + (this->*found->second)(rest, directive); + return true; + } + + StringPiece buf_; + size_t l_; + ParserState state_; + + vector<Stmt*>* stmts_; + vector<Stmt*>* out_stmts_; + + StringPiece define_name_; + int num_define_nest_; + size_t define_start_; + int define_start_line_; + + StringPiece orig_line_with_directives_; + AssignDirective current_directive_; + + int num_if_nest_; + stack<IfState*> if_stack_; + + Loc loc_; + bool fixed_lineno_; + + static DirectiveMap* make_directives_; + static DirectiveMap* else_if_directives_; + static DirectiveMap* assign_directives_; + static size_t shortest_directive_len_; + static size_t longest_directive_len_; +}; + +void Parse(Makefile* mk) { + COLLECT_STATS("parse file time"); + Parser parser(StringPiece(mk->buf()), mk->filename().c_str(), + mk->mutable_stmts()); + parser.Parse(); +} + +void Parse(StringPiece buf, const Loc& loc, vector<Stmt*>* out_stmts) { + COLLECT_STATS("parse eval time"); + Parser parser(buf, loc, out_stmts); + parser.Parse(); +} + +void ParseNotAfterRule(StringPiece buf, + const Loc& loc, + vector<Stmt*>* out_stmts) { + Parser parser(buf, loc, out_stmts); + parser.set_state(ParserState::NOT_AFTER_RULE); + parser.Parse(); +} + +void InitParser() { + Parser::Init(); +} + +void QuitParser() { + Parser::Quit(); +} + +Parser::DirectiveMap* Parser::make_directives_; +Parser::DirectiveMap* Parser::else_if_directives_; +Parser::DirectiveMap* Parser::assign_directives_; +size_t Parser::shortest_directive_len_; +size_t Parser::longest_directive_len_; +vector<ParseErrorStmt*> Parser::parse_errors; + +void ParseAssignStatement(StringPiece line, + size_t sep, + StringPiece* lhs, + StringPiece* rhs, + AssignOp* op) { + CHECK(sep != 0); + *op = AssignOp::EQ; + size_t lhs_end = sep; + switch (line[sep - 1]) { + case ':': + lhs_end--; + *op = AssignOp::COLON_EQ; + break; + case '+': + lhs_end--; + *op = AssignOp::PLUS_EQ; + break; + case '?': + lhs_end--; + *op = AssignOp::QUESTION_EQ; + break; + } + *lhs = TrimSpace(line.substr(0, lhs_end)); + *rhs = TrimLeftSpace(line.substr(sep + 1)); +} + +const vector<ParseErrorStmt*>& GetParseErrors() { + return Parser::parse_errors; +} |
