// Copyright 2015 Google Inc. All rights reserved // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // +build ignore #include "expr.h" #include <vector> #include "eval.h" #include "func.h" #include "log.h" #include "stringprintf.h" #include "strutil.h" #include "var.h" Evaluable::Evaluable() { } Evaluable::~Evaluable() { } string Evaluable::Eval(Evaluator* ev) const { string s; Eval(ev, &s); return s; } Value::Value() { } Value::~Value() { } string Value::DebugString() const { if (static_cast<const Value*>(this)) { return NoLineBreak(DebugString_()); } return "(null)"; } class Literal : public Value { public: explicit Literal(StringPiece s) : s_(s) { } StringPiece val() const { return s_; } virtual void Eval(Evaluator*, string* s) const override { s->append(s_.begin(), s_.end()); } virtual bool IsLiteral() const override { return true; } virtual StringPiece GetLiteralValueUnsafe() const override { return s_; } virtual string DebugString_() const override { return s_.as_string(); } private: StringPiece s_; }; class Expr : public Value { public: Expr() { } virtual ~Expr() { for (Value* v : vals_) { delete v; } } // Takes the ownership of |v|. void AddValue(Value* v) { vals_.push_back(v); } virtual void Eval(Evaluator* ev, string* s) const override { for (Value* v : vals_) { v->Eval(ev, s); } } virtual string DebugString_() const override { string r; for (Value* v : vals_) { if (r.empty()) { r += "Expr("; } else { r += ", "; } r += v->DebugString(); } if (!r.empty()) r += ")"; return r; } virtual Value* Compact() override { if (vals_.size() != 1) { return this; } Value* r = vals_[0]; vals_.clear(); delete this; return r; } private: vector<Value*> vals_; }; class SymRef : public Value { public: explicit SymRef(Symbol n) : name_(n) { } virtual ~SymRef() { } virtual void Eval(Evaluator* ev, string* s) const override { Var* v = ev->LookupVar(name_); v->Eval(ev, s); } virtual string DebugString_() const override { return StringPrintf("SymRef(%s)", name_.c_str()); } private: Symbol name_; }; class VarRef : public Value { public: explicit VarRef(Value* n) : name_(n) { } virtual ~VarRef() { delete name_; } virtual void Eval(Evaluator* ev, string* s) const override { ev->IncrementEvalDepth(); const string&& name = name_->Eval(ev); ev->DecrementEvalDepth(); Var* v = ev->LookupVar(Intern(name)); v->Eval(ev, s); } virtual string DebugString_() const override { return StringPrintf("VarRef(%s)", name_->DebugString().c_str()); } private: Value* name_; }; class VarSubst : public Value { public: explicit VarSubst(Value* n, Value* p, Value* s) : name_(n), pat_(p), subst_(s) { } virtual ~VarSubst() { delete name_; delete pat_; delete subst_; } virtual void Eval(Evaluator* ev, string* s) const override { ev->IncrementEvalDepth(); const string&& name = name_->Eval(ev); Var* v = ev->LookupVar(Intern(name)); const string&& pat_str = pat_->Eval(ev); const string&& subst = subst_->Eval(ev); ev->DecrementEvalDepth(); const string&& value = v->Eval(ev); WordWriter ww(s); Pattern pat(pat_str); for (StringPiece tok : WordScanner(value)) { ww.MaybeAddWhitespace(); pat.AppendSubstRef(tok, subst, s); } } virtual string DebugString_() const override { return StringPrintf("VarSubst(%s:%s=%s)", name_->DebugString().c_str(), pat_->DebugString().c_str(), subst_->DebugString().c_str()); } private: Value* name_; Value* pat_; Value* subst_; }; class Func : public Value { public: explicit Func(FuncInfo* fi) : fi_(fi) { } ~Func() { for (Value* a : args_) delete a; } virtual void Eval(Evaluator* ev, string* s) const override { LOG("Invoke func %s(%s)", name(), JoinValues(args_, ",").c_str()); ev->IncrementEvalDepth(); fi_->func(args_, ev, s); ev->DecrementEvalDepth(); } virtual string DebugString_() const override { return StringPrintf("Func(%s %s)", fi_->name, JoinValues(args_, ",").c_str()); } void AddArg(Value* v) { args_.push_back(v); } const char* name() const { return fi_->name; } int arity() const { return fi_->arity; } int min_arity() const { return fi_->min_arity; } bool trim_space() const { return fi_->trim_space; } bool trim_right_space_1st() const { return fi_->trim_right_space_1st; } private: FuncInfo* fi_; vector<Value*> args_; }; static char CloseParen(char c) { switch (c) { case '(': return ')'; case '{': return '}'; } return 0; } static size_t SkipSpaces(StringPiece s, const char* terms) { for (size_t i = 0; i < s.size(); i++) { char c = s[i]; if (strchr(terms, c)) return i; if (!isspace(c)) { if (c != '\\') return i; char n = s.get(i + 1); if (n != '\r' && n != '\n') return i; } } return s.size(); } bool ShouldHandleComments(ParseExprOpt opt) { return opt != ParseExprOpt::DEFINE && opt != ParseExprOpt::COMMAND; } void ParseFunc(const Loc& loc, Func* f, StringPiece s, size_t i, char* terms, size_t* index_out) { terms[1] = ','; terms[2] = '\0'; i += SkipSpaces(s.substr(i), terms); if (i == s.size()) { *index_out = i; return; } int nargs = 1; while (true) { if (f->arity() && nargs >= f->arity()) { terms[1] = '\0'; // Drop ','. } if (f->trim_space()) { for (; i < s.size(); i++) { if (isspace(s[i])) continue; if (s[i] == '\\') { char c = s.get(i+1); if (c == '\r' || c == '\n') continue; } break; } } const bool trim_right_space = (f->trim_space() || (nargs == 1 && f->trim_right_space_1st())); size_t n; Value* v = ParseExprImpl(loc, s.substr(i), terms, ParseExprOpt::FUNC, &n, trim_right_space); // TODO: concatLine??? f->AddArg(v); i += n; if (i == s.size()) { ERROR("%s:%d: *** unterminated call to function '%s': " "missing '%c'.", LOCF(loc), f->name(), terms[0]); } nargs++; if (s[i] == terms[0]) { i++; break; } i++; // Should be ','. if (i == s.size()) break; } if (nargs <= f->min_arity()) { ERROR("%s:%d: *** insufficient number of arguments (%d) to function `%s'.", LOCF(loc), nargs - 1, f->name()); } *index_out = i; return; } Value* ParseDollar(const Loc& loc, StringPiece s, size_t* index_out) { CHECK(s.size() >= 2); CHECK(s[0] == '$'); CHECK(s[1] != '$'); char cp = CloseParen(s[1]); if (cp == 0) { *index_out = 2; return new SymRef(Intern(s.substr(1, 1))); } char terms[] = {cp, ':', ' ', 0}; for (size_t i = 2;;) { size_t n; Value* vname = ParseExprImpl(loc, s.substr(i), terms, ParseExprOpt::NORMAL, &n); i += n; if (s[i] == cp) { *index_out = i + 1; if (vname->IsLiteral()) { Literal* lit = static_cast<Literal*>(vname); Symbol sym = Intern(lit->val()); if (g_flags.enable_kati_warnings) { size_t found = sym.str().find_first_of(" ({"); if (found != string::npos) { KATI_WARN("%s:%d: *warning*: variable lookup with '%c': %.*s", loc, sym.str()[found], SPF(s)); } } Value* r = new SymRef(sym); delete lit; return r; } return new VarRef(vname); } if (s[i] == ' ' || s[i] == '\\') { // ${func ...} if (vname->IsLiteral()) { Literal* lit = static_cast<Literal*>(vname); if (FuncInfo* fi = GetFuncInfo(lit->val())) { delete lit; Func* func = new Func(fi); ParseFunc(loc, func, s, i+1, terms, index_out); return func; } else { KATI_WARN("%s:%d: *warning*: unknown make function '%.*s': %.*s", loc, SPF(lit->val()), SPF(s)); } } // Not a function. Drop ' ' from |terms| and parse it // again. This is inefficient, but this code path should be // rarely used. delete vname; terms[2] = 0; i = 2; continue; } if (s[i] == ':') { terms[2] = '\0'; terms[1] = '='; size_t n; Value* pat = ParseExprImpl(loc, s.substr(i+1), terms, ParseExprOpt::NORMAL, &n); i += 1 + n; if (s[i] == cp) { Expr* v = new Expr; v->AddValue(vname); v->AddValue(new Literal(":")); v->AddValue(pat); *index_out = i + 1; return new VarRef(v); } terms[1] = '\0'; Value* subst = ParseExprImpl(loc, s.substr(i+1), terms, ParseExprOpt::NORMAL, &n); i += 1 + n; *index_out = i + 1; return new VarSubst(vname->Compact(), pat, subst); } // GNU make accepts expressions like $((). See unmatched_paren*.mk // for detail. size_t found = s.find(cp); if (found != string::npos) { KATI_WARN("%s:%d: *warning*: unmatched parentheses: %.*s", loc, SPF(s)); *index_out = s.size(); return new SymRef(Intern(s.substr(2, found-2))); } ERROR("%s:%d: *** unterminated variable reference.", LOCF(loc)); } } Value* ParseExprImpl(const Loc& loc, StringPiece s, const char* terms, ParseExprOpt opt, size_t* index_out, bool trim_right_space) { if (s.get(s.size() - 1) == '\r') s.remove_suffix(1); Expr* r = new Expr; size_t b = 0; char save_paren = 0; int paren_depth = 0; size_t i; for (i = 0; i < s.size(); i++) { char c = s[i]; if (terms && strchr(terms, c) && !save_paren) { break; } // Handle a comment. if (!terms && c == '#' && ShouldHandleComments(opt)) { if (i > b) r->AddValue(new Literal(s.substr(b, i-b))); bool was_backslash = false; for (; i < s.size() && !(s[i] == '\n' && !was_backslash); i++) { was_backslash = !was_backslash && s[i] == '\\'; } *index_out = i; return r->Compact(); } if (c == '$') { if (i + 1 >= s.size()) { break; } if (i > b) r->AddValue(new Literal(s.substr(b, i-b))); if (s[i+1] == '$') { r->AddValue(new Literal(StringPiece("$"))); i += 1; b = i + 1; continue; } if (terms && strchr(terms, s[i+1])) { *index_out = i + 1; return r->Compact(); } size_t n; Value* v = ParseDollar(loc, s.substr(i), &n); i += n; b = i; i--; r->AddValue(v); continue; } if ((c == '(' || c == '{') && opt == ParseExprOpt::FUNC) { char cp = CloseParen(c); if (terms && terms[0] == cp) { paren_depth++; save_paren = cp; terms++; } else if (cp == save_paren) { paren_depth++; } continue; } if (c == save_paren) { paren_depth--; if (paren_depth == 0) { terms--; save_paren = 0; } } if (c == '\\' && i + 1 < s.size() && opt != ParseExprOpt::COMMAND) { char n = s[i+1]; if (n == '\\') { i++; continue; } if (n == '#' && ShouldHandleComments(opt)) { r->AddValue(new Literal(s.substr(b, i-b))); i++; b = i; continue; } if (n == '\r' || n == '\n') { if (terms && strchr(terms, ' ')) { break; } if (i > b) { r->AddValue(new Literal(TrimRightSpace(s.substr(b, i-b)))); } r->AddValue(new Literal(StringPiece(" "))); // Skip the current escaped newline i += 2; // Then continue skipping escaped newlines, spaces, and tabs for (; i < s.size(); i++) { if (s[i] == '\\' && (s.get(i+1) == '\r' || s.get(i+1) == '\n')) { i++; continue; } if (s[i] != ' ' && s[i] != '\t') { break; } } b = i; i--; } } } if (i > b) { StringPiece rest = s.substr(b, i-b); if (trim_right_space) rest = TrimRightSpace(rest); if (!rest.empty()) r->AddValue(new Literal(rest)); } *index_out = i; return r->Compact(); } Value* ParseExpr(const Loc& loc, StringPiece s, ParseExprOpt opt) { size_t n; return ParseExprImpl(loc, s, NULL, opt, &n); } string JoinValues(const vector<Value*>& vals, const char* sep) { vector<string> val_strs; for (Value* v : vals) { val_strs.push_back(v->DebugString()); } return JoinStrings(val_strs, sep); } Value* NewExpr2(Value* v1, Value* v2) { Expr* e = new Expr(); e->AddValue(v1); e->AddValue(v2); return e; } Value* NewExpr3(Value* v1, Value* v2, Value* v3) { Expr* e = new Expr(); e->AddValue(v1); e->AddValue(v2); e->AddValue(v3); return e; } Value* NewLiteral(StringPiece s) { return new Literal(s); }