parser.cc revision efad2dd288a5b5466c23b2dd7f275c25910e987a
1#include "parser.h"
2
3#include <unordered_map>
4
5#include "ast.h"
6#include "file.h"
7#include "loc.h"
8#include "log.h"
9#include "string_piece.h"
10#include "value.h"
11
12enum struct ParserState {
13  NOT_AFTER_RULE = 0,
14  AFTER_RULE,
15  MAYBE_AFTER_RULE,
16};
17
18class Parser {
19 public:
20  Parser(StringPiece buf, const char* filename, vector<AST*>* asts)
21      : buf_(buf),
22        state_(ParserState::NOT_AFTER_RULE),
23        out_asts_(asts),
24        loc_(filename, 0),
25        fixed_lineno_(false) {
26  }
27
28  ~Parser() {
29  }
30
31  void Parse() {
32    l_ = 0;
33
34    for (l_ = 0; l_ < buf_.size();) {
35      size_t lf_cnt = 0;
36      size_t e = FindEndOfLine(&lf_cnt);
37      if (!fixed_lineno_)
38        loc_.lineno += lf_cnt;
39      StringPiece line(buf_.data() + l_, e - l_);
40      ParseLine(line);
41      if (e == buf_.size())
42        break;
43
44      l_ = e + 1;
45    }
46  }
47
48  static void Init() {
49    make_directives_ = new unordered_map<StringPiece, DirectiveHandler>;
50    (*make_directives_)["include"] = &Parser::ParseIncludeAST;
51    (*make_directives_)["-include"] = &Parser::ParseIncludeAST;
52    (*make_directives_)["sinclude"] = &Parser::ParseIncludeAST;
53
54    shortest_directive_len_ = 9999;
55    longest_directive_len_ = 0;
56    for (auto p : *make_directives_) {
57      size_t len = p.first.size();
58      shortest_directive_len_ = min(len, shortest_directive_len_);
59      longest_directive_len_ = max(len, longest_directive_len_);
60    }
61  }
62
63  static void Quit() {
64    delete make_directives_;
65  }
66
67 private:
68  void Error(const string& msg) {
69    ERROR("%s:%d: %s", LOCF(loc_), msg.c_str());
70  }
71
72  size_t FindEndOfLine(size_t* lf_cnt) {
73    size_t e = l_;
74    bool prev_backslash = false;
75    for (; e < buf_.size(); e++) {
76      char c = buf_[e];
77      if (c == '\\') {
78        prev_backslash = !prev_backslash;
79      } else if (c == '\n') {
80        ++*lf_cnt;
81        if (!prev_backslash) {
82          return e;
83        }
84      } else if (c != '\r') {
85        prev_backslash = false;
86      }
87    }
88    return e;
89  }
90
91  void ParseLine(StringPiece line) {
92    if (line.empty() || (line.size() == 1 && line[0] == '\r'))
93      return;
94
95    if (line[0] == '\t' && state_ != ParserState::NOT_AFTER_RULE) {
96      CommandAST* ast = new CommandAST();
97      ast->expr = ParseExpr(line.substr(1), true);
98      out_asts_->push_back(ast);
99      return;
100    }
101
102    line = line.StripLeftSpaces();
103    if (HandleDirective(line)) {
104      return;
105    }
106
107    size_t sep = line.find_first_of(STRING_PIECE("=:"));
108    if (sep == string::npos) {
109      ParseRuleAST(line, sep);
110    } else if (line[sep] == '=') {
111      ParseAssignAST(line, sep);
112    } else if (line.get(sep+1) == '=') {
113      ParseAssignAST(line, sep+1);
114    } else if (line[sep] == ':') {
115      ParseRuleAST(line, sep);
116    } else {
117      CHECK(false);
118    }
119  }
120
121  void ParseRuleAST(StringPiece line, size_t sep) {
122    const bool is_rule = line.find(':') != string::npos;
123    RuleAST* ast = new RuleAST;
124    ast->set_loc(loc_);
125
126    size_t found = line.substr(sep + 1).find_first_of("=;");
127    if (found != string::npos) {
128      found += sep + 1;
129      ast->term = line[found];
130      ast->after_term = ParseExpr(line.substr(found + 1).StripLeftSpaces(),
131                                  ast->term == ';');
132      ast->expr = ParseExpr(line.substr(0, found).StripSpaces(), false);
133    } else {
134      ast->term = 0;
135      ast->after_term = NULL;
136      ast->expr = ParseExpr(line.StripSpaces(), false);
137    }
138    out_asts_->push_back(ast);
139    state_ = is_rule ? ParserState::AFTER_RULE : ParserState::MAYBE_AFTER_RULE;
140  }
141
142  void ParseAssignAST(StringPiece line, size_t sep) {
143    if (sep == 0)
144      Error("*** empty variable name ***");
145    AssignOp op = AssignOp::EQ;
146    size_t lhs_end = sep;
147    switch (line[sep-1]) {
148      case ':':
149        lhs_end--;
150        op = AssignOp::COLON_EQ;
151        break;
152      case '+':
153        lhs_end--;
154        op = AssignOp::PLUS_EQ;
155        break;
156      case '?':
157        lhs_end--;
158        op = AssignOp::QUESTION_EQ;
159        break;
160    }
161
162    AssignAST* ast = new AssignAST;
163    ast->set_loc(loc_);
164    ast->lhs = ParseExpr(line.substr(0, lhs_end).StripSpaces(), false);
165    ast->rhs = ParseExpr(line.substr(sep + 1).StripLeftSpaces(), false);
166    ast->op = op;
167    ast->directive = AssignDirective::NONE;
168    out_asts_->push_back(ast);
169    state_ = ParserState::NOT_AFTER_RULE;
170  }
171
172  void ParseIncludeAST(StringPiece line, StringPiece directive) {
173    IncludeAST* ast = new IncludeAST();
174    ast->expr = ParseExpr(line, false);
175    ast->should_exist = directive[0] == 'i';
176    out_asts_->push_back(ast);
177  }
178
179  bool HandleDirective(StringPiece line) {
180    if (line.size() < shortest_directive_len_)
181      return false;
182    StringPiece prefix = line.substr(0, longest_directive_len_ + 1);
183    size_t space_index = prefix.find(' ');
184    if (space_index == string::npos)
185      return false;
186    StringPiece directive = prefix.substr(0, space_index);
187    auto found = make_directives_->find(directive);
188    if (found == make_directives_->end())
189      return false;
190
191    (this->*found->second)(line.substr(directive.size() + 1), directive);
192    return true;
193  }
194
195  StringPiece buf_;
196  size_t l_;
197  ParserState state_;
198
199  vector<AST*>* out_asts_;
200
201  Loc loc_;
202  bool fixed_lineno_;
203
204  typedef void (Parser::*DirectiveHandler)(
205      StringPiece line, StringPiece directive);
206  static unordered_map<StringPiece, DirectiveHandler>* make_directives_;
207  static size_t shortest_directive_len_;
208  static size_t longest_directive_len_;
209};
210
211void Parse(Makefile* mk) {
212  Parser parser(StringPiece(mk->buf(), mk->len()),
213                mk->filename().c_str(),
214                mk->mutable_asts());
215  parser.Parse();
216}
217
218void InitParser() {
219  Parser::Init();
220}
221
222void QuitParser() {
223  Parser::Quit();
224}
225
226unordered_map<StringPiece, Parser::DirectiveHandler>* Parser::make_directives_;
227size_t Parser::shortest_directive_len_;
228size_t Parser::longest_directive_len_;
229