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