parser.cc revision 6aeab2afbbb7ec5c75f3f8ed054e601cd1c281b4
1// Copyright 2015 Google Inc. All rights reserved
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// +build ignore
16
17#include "parser.h"
18
19#include <stack>
20#include <unordered_map>
21
22#include "ast.h"
23#include "file.h"
24#include "loc.h"
25#include "log.h"
26#include "string_piece.h"
27#include "strutil.h"
28#include "value.h"
29
30enum struct ParserState {
31  NOT_AFTER_RULE = 0,
32  AFTER_RULE,
33  MAYBE_AFTER_RULE,
34};
35
36class Parser {
37  struct IfState {
38    IfAST* ast;
39    bool is_in_else;
40    int num_nest;
41  };
42
43  typedef void (Parser::*DirectiveHandler)(
44      StringPiece line, StringPiece directive);
45  typedef unordered_map<StringPiece, DirectiveHandler> DirectiveMap;
46
47 public:
48  Parser(StringPiece buf, const char* filename, vector<AST*>* asts)
49      : buf_(buf),
50        state_(ParserState::NOT_AFTER_RULE),
51        asts_(asts),
52        out_asts_(asts),
53        num_if_nest_(0),
54        loc_(filename, 0),
55        fixed_lineno_(false) {
56  }
57
58  Parser(StringPiece buf, const Loc& loc, vector<AST*>* asts)
59      : buf_(buf),
60        state_(ParserState::NOT_AFTER_RULE),
61        asts_(asts),
62        out_asts_(asts),
63        num_if_nest_(0),
64        loc_(loc),
65        fixed_lineno_(true) {
66  }
67
68  ~Parser() {
69  }
70
71  void Parse() {
72    l_ = 0;
73
74    for (l_ = 0; l_ < buf_.size();) {
75      size_t lf_cnt = 0;
76      size_t e = FindEndOfLine(&lf_cnt);
77      if (!fixed_lineno_)
78        loc_.lineno++;
79      StringPiece line(buf_.data() + l_, e - l_);
80      orig_line_with_directives_ = line;
81      ParseLine(line);
82      if (!fixed_lineno_)
83        loc_.lineno += lf_cnt - 1;
84      if (e == buf_.size())
85        break;
86
87      l_ = e + 1;
88    }
89  }
90
91  static void Init() {
92    make_directives_ = new DirectiveMap;
93    (*make_directives_)["include"] = &Parser::ParseInclude;
94    (*make_directives_)["-include"] = &Parser::ParseInclude;
95    (*make_directives_)["sinclude"] = &Parser::ParseInclude;
96    (*make_directives_)["define"] = &Parser::ParseDefine;
97    (*make_directives_)["ifdef"] = &Parser::ParseIfdef;
98    (*make_directives_)["ifndef"] = &Parser::ParseIfdef;
99    (*make_directives_)["ifeq"] = &Parser::ParseIfeq;
100    (*make_directives_)["ifneq"] = &Parser::ParseIfeq;
101    (*make_directives_)["else"] = &Parser::ParseElse;
102    (*make_directives_)["endif"] = &Parser::ParseEndif;
103    (*make_directives_)["override"] = &Parser::ParseOverride;
104    (*make_directives_)["export"] = &Parser::ParseExport;
105    (*make_directives_)["unexport"] = &Parser::ParseUnexport;
106
107    else_if_directives_ = new DirectiveMap;
108    (*else_if_directives_)["ifdef"] = &Parser::ParseIfdef;
109    (*else_if_directives_)["ifndef"] = &Parser::ParseIfdef;
110    (*else_if_directives_)["ifeq"] = &Parser::ParseIfeq;
111    (*else_if_directives_)["ifneq"] = &Parser::ParseIfeq;
112
113    assign_directives_ = new DirectiveMap;
114    (*assign_directives_)["define"] = &Parser::ParseDefine;
115    (*assign_directives_)["export"] = &Parser::ParseExport;
116    (*assign_directives_)["override"] = &Parser::ParseOverride;
117
118    shortest_directive_len_ = 9999;
119    longest_directive_len_ = 0;
120    for (auto p : *make_directives_) {
121      size_t len = p.first.size();
122      shortest_directive_len_ = min(len, shortest_directive_len_);
123      longest_directive_len_ = max(len, longest_directive_len_);
124    }
125  }
126
127  static void Quit() {
128    delete make_directives_;
129  }
130
131 private:
132  void Error(const string& msg) {
133    ERROR("%s:%d: %s", LOCF(loc_), msg.c_str());
134  }
135
136  size_t FindEndOfLine(size_t* lf_cnt) {
137    return ::FindEndOfLine(buf_, l_, lf_cnt);
138  }
139
140  Value* ParseExpr(StringPiece s, ParseExprOpt opt = ParseExprOpt::NORMAL) {
141    return ::ParseExpr(loc_, s, opt);
142  }
143
144  void ParseLine(StringPiece line) {
145    if (line.empty() || (line.size() == 1 && line[0] == '\r'))
146      return;
147
148    if (!define_name_.empty()) {
149      ParseInsideDefine(line);
150      return;
151    }
152
153    current_directive_ = AssignDirective::NONE;
154
155    if (line[0] == '\t' && state_ != ParserState::NOT_AFTER_RULE) {
156      CommandAST* ast = new CommandAST();
157      ast->set_loc(loc_);
158      ast->expr = ParseExpr(line.substr(1), ParseExprOpt::COMMAND);
159      out_asts_->push_back(ast);
160      return;
161    }
162
163    line = TrimLeftSpace(line);
164
165    if (line[0] == '#')
166      return;
167
168    if (HandleDirective(line, make_directives_)) {
169      return;
170    }
171
172    ParseRuleOrAssign(line);
173  }
174
175  void ParseRuleOrAssign(StringPiece line) {
176    size_t sep = FindTwoOutsideParen(line, ':', '=');
177    if (sep == string::npos) {
178      ParseRule(line, sep);
179    } else if (line[sep] == '=') {
180      ParseAssign(line, sep);
181    } else if (line.get(sep+1) == '=') {
182      ParseAssign(line, sep+1);
183    } else if (line[sep] == ':') {
184      ParseRule(line, sep);
185    } else {
186      CHECK(false);
187    }
188  }
189
190  void ParseRule(StringPiece line, size_t sep) {
191    if (current_directive_ != AssignDirective::NONE) {
192      if (IsInExport())
193        return;
194      if (sep != string::npos) {
195        sep += orig_line_with_directives_.size() - line.size();
196      }
197      line = orig_line_with_directives_;
198    }
199
200    line = TrimLeftSpace(line);
201    if (line.empty())
202      return;
203
204    if (orig_line_with_directives_[0] == '\t') {
205      Error("*** commands commence before first target.");
206    }
207
208    const bool is_rule = sep != string::npos && line[sep] == ':';
209    RuleAST* ast = new RuleAST();
210    ast->set_loc(loc_);
211
212    size_t found = FindTwoOutsideParen(line.substr(sep + 1), '=', ';');
213    if (found != string::npos) {
214      found += sep + 1;
215      ast->term = line[found];
216      ParseExprOpt opt =
217          ast->term == ';' ? ParseExprOpt::COMMAND : ParseExprOpt::NORMAL;
218      ast->after_term = ParseExpr(TrimLeftSpace(line.substr(found + 1)), opt);
219      ast->expr = ParseExpr(TrimSpace(line.substr(0, found)));
220    } else {
221      ast->term = 0;
222      ast->after_term = NULL;
223      ast->expr = ParseExpr(line);
224    }
225    out_asts_->push_back(ast);
226    state_ = is_rule ? ParserState::AFTER_RULE : ParserState::MAYBE_AFTER_RULE;
227  }
228
229  void ParseAssign(StringPiece line, size_t sep) {
230    if (sep == 0)
231      Error("*** empty variable name ***");
232    StringPiece lhs;
233    StringPiece rhs;
234    AssignOp op;
235    ParseAssignStatement(line, sep, &lhs, &rhs, &op);
236
237    AssignAST* ast = new AssignAST();
238    ast->set_loc(loc_);
239    ast->lhs = ParseExpr(lhs);
240    ast->rhs = ParseExpr(rhs);
241    ast->orig_rhs = rhs;
242    ast->op = op;
243    ast->directive = current_directive_;
244    out_asts_->push_back(ast);
245    state_ = ParserState::NOT_AFTER_RULE;
246  }
247
248  void ParseInclude(StringPiece line, StringPiece directive) {
249    IncludeAST* ast = new IncludeAST();
250    ast->set_loc(loc_);
251    ast->expr = ParseExpr(line);
252    ast->should_exist = directive[0] == 'i';
253    out_asts_->push_back(ast);
254    state_ = ParserState::NOT_AFTER_RULE;
255  }
256
257  void ParseDefine(StringPiece line, StringPiece) {
258    if (line.empty()) {
259      Error("*** empty variable name.");
260    }
261    define_name_ = line;
262    define_start_ = 0;
263    define_start_line_ = loc_.lineno;
264    state_ = ParserState::NOT_AFTER_RULE;
265  }
266
267  void ParseInsideDefine(StringPiece line) {
268    line = TrimLeftSpace(line);
269    if (GetDirective(line) != "endef") {
270      if (define_start_ == 0)
271        define_start_ = l_;
272      return;
273    }
274
275    StringPiece rest = TrimRightSpace(RemoveComment(TrimLeftSpace(
276        line.substr(sizeof("endef")))));
277    if (!rest.empty()) {
278      WARN("%s:%d: extraneous text after `endef' directive", LOCF(loc_));
279    }
280
281    AssignAST* ast = new AssignAST();
282    ast->set_loc(Loc(loc_.filename, define_start_line_));
283    ast->lhs = ParseExpr(define_name_);
284    StringPiece rhs;
285    if (define_start_)
286      rhs = buf_.substr(define_start_, l_ - define_start_ - 1);
287    ast->rhs = ParseExpr(rhs, ParseExprOpt::DEFINE);
288    ast->orig_rhs = rhs;
289    ast->op = AssignOp::EQ;
290    ast->directive = current_directive_;
291    out_asts_->push_back(ast);
292    define_name_.clear();
293  }
294
295  void EnterIf(IfAST* ast) {
296    IfState* st = new IfState();
297    st->ast = ast;
298    st->is_in_else = false;
299    st->num_nest = num_if_nest_;
300    if_stack_.push(st);
301    out_asts_ = &ast->true_asts;
302  }
303
304  void ParseIfdef(StringPiece line, StringPiece directive) {
305    IfAST* ast = new IfAST();
306    ast->set_loc(loc_);
307    ast->op = directive[2] == 'n' ? CondOp::IFNDEF : CondOp::IFDEF;
308    ast->lhs = ParseExpr(line);
309    ast->rhs = NULL;
310    out_asts_->push_back(ast);
311    EnterIf(ast);
312  }
313
314  bool ParseIfEqCond(StringPiece s, IfAST* ast) {
315    if (s.empty()) {
316      return false;
317    }
318
319    if (s[0] == '(' && s[s.size() - 1] == ')') {
320      s = s.substr(1, s.size() - 2);
321      char terms[] = {',', '\0'};
322      size_t n;
323      ast->lhs = ParseExprImpl(loc_, s, terms, ParseExprOpt::NORMAL, &n, true);
324      if (s[n] != ',')
325        return false;
326      s = TrimLeftSpace(s.substr(n+1));
327      ast->rhs = ParseExprImpl(loc_, s, NULL, ParseExprOpt::NORMAL, &n);
328      s = TrimLeftSpace(s.substr(n));
329    } else {
330      for (int i = 0; i < 2; i++) {
331        if (s.empty())
332          return false;
333        char quote = s[0];
334        if (quote != '\'' && quote != '"')
335          return false;
336        size_t end = s.find(quote, 1);
337        if (end == string::npos)
338          return false;
339        Value* v = ParseExpr(s.substr(1, end - 1), ParseExprOpt::NORMAL);
340        if (i == 0)
341          ast->lhs = v;
342        else
343          ast->rhs = v;
344        s = TrimLeftSpace(s.substr(end+1));
345      }
346    }
347    if (!s.empty()) {
348      Error("extraneous text after `ifeq' directive");
349    }
350    return true;
351  }
352
353  void ParseIfeq(StringPiece line, StringPiece directive) {
354    IfAST* ast = new IfAST();
355    ast->set_loc(loc_);
356    ast->op = directive[2] == 'n' ? CondOp::IFNEQ : CondOp::IFEQ;
357
358    if (!ParseIfEqCond(line, ast)) {
359      Error("*** invalid syntax in conditional.");
360    }
361
362    out_asts_->push_back(ast);
363    EnterIf(ast);
364  }
365
366  void ParseElse(StringPiece line, StringPiece) {
367    CheckIfStack("else");
368    IfState* st = if_stack_.top();
369    if (st->is_in_else)
370      Error("*** only one `else' per conditional.");
371    st->is_in_else = true;
372    out_asts_ = &st->ast->false_asts;
373
374    StringPiece next_if = TrimLeftSpace(line);
375    if (next_if.empty())
376      return;
377
378    num_if_nest_ = st->num_nest + 1;
379    if (!HandleDirective(next_if, else_if_directives_)) {
380      WARN("%s:%d: extraneous text after `else' directive", LOCF(loc_));
381    }
382    num_if_nest_ = 0;
383  }
384
385  void ParseEndif(StringPiece line, StringPiece) {
386    CheckIfStack("endif");
387    if (!line.empty())
388      Error("extraneous text after `endif` directive");
389    IfState st = *if_stack_.top();
390    for (int t = 0; t <= st.num_nest; t++) {
391      delete if_stack_.top();
392      if_stack_.pop();
393      if (if_stack_.empty()) {
394        out_asts_ = asts_;
395      } else {
396        IfState* st = if_stack_.top();
397        if (st->is_in_else)
398          out_asts_ = &st->ast->false_asts;
399        else
400          out_asts_ = &st->ast->true_asts;
401      }
402    }
403  }
404
405  bool IsInExport() const {
406    return (static_cast<int>(current_directive_) &
407            static_cast<int>(AssignDirective::EXPORT));
408  }
409
410  void CreateExport(StringPiece line, bool is_export) {
411    ExportAST* ast = new ExportAST;
412    ast->set_loc(loc_);
413    ast->expr = ParseExpr(line);
414    ast->is_export = is_export;
415    out_asts_->push_back(ast);
416  }
417
418  void ParseOverride(StringPiece line, StringPiece) {
419    current_directive_ =
420        static_cast<AssignDirective>(
421            (static_cast<int>(current_directive_) |
422             static_cast<int>(AssignDirective::OVERRIDE)));
423    if (HandleDirective(line, assign_directives_))
424      return;
425    if (IsInExport()) {
426      CreateExport(line, true);
427    }
428    ParseRuleOrAssign(line);
429  }
430
431  void ParseExport(StringPiece line, StringPiece) {
432    current_directive_ =
433        static_cast<AssignDirective>(
434            (static_cast<int>(current_directive_) |
435             static_cast<int>(AssignDirective::EXPORT)));
436    if (HandleDirective(line, assign_directives_))
437      return;
438    CreateExport(line, true);
439    ParseRuleOrAssign(line);
440  }
441
442  void ParseUnexport(StringPiece line, StringPiece) {
443    CreateExport(line, false);
444  }
445
446  void CheckIfStack(const char* keyword) {
447    if (if_stack_.empty()) {
448      Error(StringPrintf("*** extraneous `%s'.", keyword));
449    }
450  }
451
452  StringPiece RemoveComment(StringPiece line) {
453    size_t i = FindOutsideParen(line, '#');
454    if (i == string::npos)
455      return line;
456    return line.substr(0, i);
457  }
458
459  StringPiece GetDirective(StringPiece line) {
460    if (line.size() < shortest_directive_len_)
461      return StringPiece();
462    StringPiece prefix = line.substr(0, longest_directive_len_ + 1);
463    size_t space_index = prefix.find_first_of(" \t#");
464    return prefix.substr(0, space_index);
465  }
466
467  bool HandleDirective(StringPiece line, const DirectiveMap* directive_map) {
468    StringPiece directive = GetDirective(line);
469    auto found = directive_map->find(directive);
470    if (found == directive_map->end())
471      return false;
472
473    StringPiece rest = TrimRightSpace(RemoveComment(TrimLeftSpace(
474        line.substr(directive.size()))));
475    (this->*found->second)(rest, directive);
476    return true;
477  }
478
479  StringPiece buf_;
480  size_t l_;
481  ParserState state_;
482
483  vector<AST*>* asts_;
484  vector<AST*>* out_asts_;
485
486  StringPiece define_name_;
487  size_t define_start_;
488  int define_start_line_;
489
490  StringPiece orig_line_with_directives_;
491  AssignDirective current_directive_;
492
493  int num_if_nest_;
494  stack<IfState*> if_stack_;
495
496  Loc loc_;
497  bool fixed_lineno_;
498
499  static DirectiveMap* make_directives_;
500  static DirectiveMap* else_if_directives_;
501  static DirectiveMap* assign_directives_;
502  static size_t shortest_directive_len_;
503  static size_t longest_directive_len_;
504};
505
506void Parse(Makefile* mk) {
507  Parser parser(StringPiece(mk->buf(), mk->len()),
508                mk->filename().c_str(),
509                mk->mutable_asts());
510  parser.Parse();
511}
512
513void Parse(StringPiece buf, const Loc& loc, vector<AST*>* out_asts) {
514  Parser parser(buf, loc, out_asts);
515  parser.Parse();
516}
517
518void InitParser() {
519  Parser::Init();
520}
521
522void QuitParser() {
523  Parser::Quit();
524}
525
526Parser::DirectiveMap* Parser::make_directives_;
527Parser::DirectiveMap* Parser::else_if_directives_;
528Parser::DirectiveMap* Parser::assign_directives_;
529size_t Parser::shortest_directive_len_;
530size_t Parser::longest_directive_len_;
531
532void ParseAssignStatement(StringPiece line, size_t sep,
533                          StringPiece* lhs, StringPiece* rhs, AssignOp* op) {
534  CHECK(sep != 0);
535  *op = AssignOp::EQ;
536  size_t lhs_end = sep;
537  switch (line[sep-1]) {
538    case ':':
539      lhs_end--;
540      *op = AssignOp::COLON_EQ;
541      break;
542    case '+':
543      lhs_end--;
544      *op = AssignOp::PLUS_EQ;
545      break;
546    case '?':
547      lhs_end--;
548      *op = AssignOp::QUESTION_EQ;
549      break;
550  }
551  *lhs = TrimSpace(line.substr(0, lhs_end));
552  *rhs = TrimSpace(line.substr(sep + 1));
553}
554