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