blob: 7c606730630a8e33a7088253197d8c02007c89f3 [file] [log] [blame]
#include "parser.h"
#include <unordered_map>
#include "ast.h"
#include "file.h"
#include "loc.h"
#include "log.h"
#include "string_piece.h"
#include "value.h"
enum struct ParserState {
NOT_AFTER_RULE = 0,
AFTER_RULE,
MAYBE_AFTER_RULE,
};
class Parser {
public:
Parser(StringPiece buf, const char* filename, vector<AST*>* asts)
: buf_(buf),
state_(ParserState::NOT_AFTER_RULE),
out_asts_(asts),
loc_(filename, 0),
fixed_lineno_(false) {
}
~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 += lf_cnt;
StringPiece line(buf_.data() + l_, e - l_);
ParseLine(line);
if (e == buf_.size())
break;
l_ = e + 1;
}
}
static void Init() {
make_directives_ = new unordered_map<StringPiece, DirectiveHandler>;
(*make_directives_)["include"] = &Parser::ParseInclude;
(*make_directives_)["-include"] = &Parser::ParseInclude;
(*make_directives_)["sinclude"] = &Parser::ParseInclude;
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_;
}
private:
void Error(const string& msg) {
ERROR("%s:%d: %s", LOCF(loc_), msg.c_str());
}
size_t FindEndOfLine(size_t* lf_cnt) {
size_t e = l_;
bool prev_backslash = false;
for (; e < buf_.size(); e++) {
char c = buf_[e];
if (c == '\\') {
prev_backslash = !prev_backslash;
} else if (c == '\n') {
++*lf_cnt;
if (!prev_backslash) {
return e;
}
} else if (c != '\r') {
prev_backslash = false;
}
}
return e;
}
void ParseLine(StringPiece line) {
if (line.empty() || (line.size() == 1 && line[0] == '\r'))
return;
if (line[0] == '\t' && state_ != ParserState::NOT_AFTER_RULE) {
CommandAST* ast = new CommandAST();
ast->expr = ParseExpr(line.substr(1), true);
out_asts_->push_back(ast);
return;
}
line = line.StripLeftSpaces();
if (HandleDirective(line)) {
return;
}
size_t sep = line.find_first_of(STRING_PIECE("=:"));
if (sep == string::npos) {
ParseRule(line, sep);
} 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) {
const bool is_rule = line.find(':') != string::npos;
RuleAST* ast = new RuleAST;
ast->set_loc(loc_);
size_t found = line.substr(sep + 1).find_first_of("=;");
if (found != string::npos) {
found += sep + 1;
ast->term = line[found];
ast->after_term = ParseExpr(line.substr(found + 1).StripLeftSpaces(),
ast->term == ';');
ast->expr = ParseExpr(line.substr(0, found).StripSpaces(), false);
} else {
ast->term = 0;
ast->after_term = NULL;
ast->expr = ParseExpr(line.StripSpaces(), false);
}
out_asts_->push_back(ast);
state_ = is_rule ? ParserState::AFTER_RULE : ParserState::MAYBE_AFTER_RULE;
}
void ParseAssign(StringPiece line, size_t sep) {
if (sep == 0)
Error("*** empty variable name ***");
AssignOp 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;
}
AssignAST* ast = new AssignAST;
ast->set_loc(loc_);
ast->lhs = ParseExpr(line.substr(0, lhs_end).StripSpaces(), false);
ast->rhs = ParseExpr(line.substr(sep + 1).StripLeftSpaces(), false);
ast->op = op;
ast->directive = AssignDirective::NONE;
out_asts_->push_back(ast);
state_ = ParserState::NOT_AFTER_RULE;
}
void ParseInclude(StringPiece line, StringPiece directive) {
IncludeAST* ast = new IncludeAST();
ast->expr = ParseExpr(line, false);
ast->should_exist = directive[0] == 'i';
out_asts_->push_back(ast);
}
bool HandleDirective(StringPiece line) {
if (line.size() < shortest_directive_len_)
return false;
StringPiece prefix = line.substr(0, longest_directive_len_ + 1);
size_t space_index = prefix.find(' ');
if (space_index == string::npos)
return false;
StringPiece directive = prefix.substr(0, space_index);
auto found = make_directives_->find(directive);
if (found == make_directives_->end())
return false;
(this->*found->second)(line.substr(directive.size() + 1), directive);
return true;
}
StringPiece buf_;
size_t l_;
ParserState state_;
vector<AST*>* out_asts_;
Loc loc_;
bool fixed_lineno_;
typedef void (Parser::*DirectiveHandler)(
StringPiece line, StringPiece directive);
static unordered_map<StringPiece, DirectiveHandler>* make_directives_;
static size_t shortest_directive_len_;
static size_t longest_directive_len_;
};
void Parse(Makefile* mk) {
Parser parser(StringPiece(mk->buf(), mk->len()),
mk->filename().c_str(),
mk->mutable_asts());
parser.Parse();
}
void InitParser() {
Parser::Init();
}
void QuitParser() {
Parser::Quit();
}
unordered_map<StringPiece, Parser::DirectiveHandler>* Parser::make_directives_;
size_t Parser::shortest_directive_len_;
size_t Parser::longest_directive_len_;