#pragma once #include #include #include #include #include "libexpr/symbol-table.hh" #include "libexpr/value.hh" #include "libutil/types.hh" // TODO(tazjin): audit this include namespace nix { MakeError(EvalError, Error); MakeError(ParseError, Error); MakeError(AssertionError, EvalError); MakeError(ThrownError, AssertionError); MakeError(Abort, EvalError); MakeError(TypeError, EvalError); MakeError(UndefinedVarError, Error); MakeError(RestrictedPathError, Error); /* Position objects. */ struct Pos { std::optional file; unsigned int line, column; Pos(const std::optional& file, unsigned int line, unsigned int column) : file(file), line(line), column(column){}; // TODO(tazjin): remove this - empty pos is never useful Pos() : file(std::nullopt), line(0), column(0){}; operator bool() const { return line != 0; } bool operator<(const Pos& p2) const { if (!file.has_value()) { return true; } if (!line) { return p2.line; } if (!p2.line) { return false; } int d = ((std::string)file.value()).compare((std::string)p2.file.value()); if (d < 0) { return true; } if (d > 0) { return false; } if (line < p2.line) { return true; } if (line > p2.line) { return false; } return column < p2.column; } }; extern Pos noPos; std::ostream& operator<<(std::ostream& str, const Pos& pos); struct Env; struct Value; class EvalState; struct StaticEnv; /* An attribute path is a sequence of attribute names. */ using AttrName = std::variant; typedef std::vector AttrPath; std::string showAttrPath(const AttrPath& attrPath); /* Abstract syntax of Nix expressions. */ struct Expr { virtual ~Expr(){}; virtual void show(std::ostream& str) const; virtual void bindVars(const StaticEnv& env); virtual void eval(EvalState& state, Env& env, Value& v); virtual Value* maybeThunk(EvalState& state, Env& env); }; std::ostream& operator<<(std::ostream& str, const Expr& e); #define COMMON_METHODS \ void show(std::ostream& str) const; \ void eval(EvalState& state, Env& env, Value& v); \ void bindVars(const StaticEnv& env); struct ExprInt : Expr { NixInt n; Value v; ExprInt(NixInt n) : n(n) { mkInt(v, n); }; COMMON_METHODS Value* maybeThunk(EvalState& state, Env& env); }; struct ExprFloat : Expr { NixFloat nf; Value v; ExprFloat(NixFloat nf) : nf(nf) { mkFloat(v, nf); }; COMMON_METHODS Value* maybeThunk(EvalState& state, Env& env); }; struct ExprString : Expr { Symbol s; Value v; ExprString(const Symbol& s) : s(s) { mkString(v, s); }; COMMON_METHODS Value* maybeThunk(EvalState& state, Env& env); }; /* Temporary class used during parsing of indented strings. */ struct ExprIndStr : Expr { std::string s; ExprIndStr(const std::string& s) : s(s){}; }; struct ExprPath : Expr { std::string s; Value v; ExprPath(const std::string& s) : s(s) { mkPathNoCopy(v, this->s.c_str()); }; COMMON_METHODS Value* maybeThunk(EvalState& state, Env& env); }; struct ExprVar : Expr { Pos pos; Symbol name; /* Whether the variable comes from an environment (e.g. a rec, let or function argument) or from a "with". */ bool fromWith; /* In the former case, the value is obtained by going `level' levels up from the current environment and getting the `displ'th value in that environment. In the latter case, the value is obtained by getting the attribute named `name' from the set stored in the environment that is `level' levels up from the current one.*/ unsigned int level; unsigned int displ; ExprVar(const Symbol& name) : name(name){}; ExprVar(const Pos& pos, const Symbol& name) : pos(pos), name(name){}; COMMON_METHODS Value* maybeThunk(EvalState& state, Env& env); }; // [tazjin] I *think* that this struct describes the syntactic // construct for "selecting" something out of an attribute set, e.g. // `a.b.c` => ExprSelect{"b", "c"}. // // Each path element has got a pointer to an expression, which seems // to be the thing preceding its period, but afaict that is only set // for the first one in a path. struct ExprSelect : Expr { Pos pos; Expr *e, *def; AttrPath attrPath; ExprSelect(const Pos& pos, Expr* e, const AttrPath& attrPath, Expr* def) : pos(pos), e(e), def(def), attrPath(attrPath){}; ExprSelect(const Pos& pos, Expr* e, const Symbol& name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); }; COMMON_METHODS }; struct ExprOpHasAttr : Expr { Expr* e; AttrPath attrPath; ExprOpHasAttr(Expr* e, const AttrPath& attrPath) : e(e), attrPath(attrPath){}; COMMON_METHODS }; struct ExprAttrs : Expr { bool recursive; struct AttrDef { bool inherited; Expr* e; Pos pos; unsigned int displ; // displacement AttrDef(Expr* e, const Pos& pos, bool inherited = false) : inherited(inherited), e(e), pos(pos), displ(0){}; AttrDef(){}; }; typedef absl::flat_hash_map AttrDefs; AttrDefs attrs; struct DynamicAttrDef { Expr *nameExpr, *valueExpr; Pos pos; DynamicAttrDef(Expr* nameExpr, Expr* valueExpr, const Pos& pos) : nameExpr(nameExpr), valueExpr(valueExpr), pos(pos){}; }; typedef std::vector DynamicAttrDefs; DynamicAttrDefs dynamicAttrs; ExprAttrs() : recursive(false){}; COMMON_METHODS }; struct ExprList : Expr { std::vector elems; ExprList(){}; COMMON_METHODS }; struct Formal { Symbol name; Expr* def; // def = default, not definition Formal(const Symbol& name, Expr* def) : name(name), def(def){}; }; // Describes structured function arguments (e.g. `{ a }: ...`) struct Formals { typedef std::list Formals_; Formals_ formals; std::set argNames; // used during parsing bool ellipsis; }; struct ExprLambda : Expr { public: Pos pos; std::optional name; Symbol arg; bool matchAttrs; Formals* formals; Expr* body; ExprLambda(const Pos& pos, const Symbol& arg, bool matchAttrs, Formals* formals, Expr* body) : pos(pos), arg(arg), matchAttrs(matchAttrs), formals(formals), body(body) { if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end()) { throw ParseError( format("duplicate formal function argument '%1%' at %2%") % arg % pos); } }; void setName(Symbol& name); std::string showNamePos() const; COMMON_METHODS }; struct ExprLet : Expr { ExprAttrs* attrs; Expr* body; ExprLet(ExprAttrs* attrs, Expr* body) : attrs(attrs), body(body){}; COMMON_METHODS }; struct ExprWith : Expr { Pos pos; Expr *attrs, *body; size_t prevWith; ExprWith(const Pos& pos, Expr* attrs, Expr* body) : pos(pos), attrs(attrs), body(body){}; COMMON_METHODS }; struct ExprIf : Expr { Expr *cond, *then, *else_; ExprIf(Expr* cond, Expr* then, Expr* else_) : cond(cond), then(then), else_(else_){}; COMMON_METHODS }; struct ExprAssert : Expr { Pos pos; Expr *cond, *body; ExprAssert(const Pos& pos, Expr* cond, Expr* body) : pos(pos), cond(cond), body(body){}; COMMON_METHODS }; struct ExprOpNot : Expr { Expr* e; ExprOpNot(Expr* e) : e(e){}; COMMON_METHODS }; #define MakeBinOp(name, s) \ struct name : Expr { \ Pos pos; \ Expr *e1, *e2; \ name(Expr* e1, Expr* e2) : e1(e1), e2(e2){}; \ name(const Pos& pos, Expr* e1, Expr* e2) : pos(pos), e1(e1), e2(e2){}; \ void show(std::ostream& str) const { \ str << "(" << *e1 << " " s " " << *e2 << ")"; \ } \ void bindVars(const StaticEnv& env) { \ e1->bindVars(env); \ e2->bindVars(env); \ } \ void eval(EvalState& state, Env& env, Value& v); \ }; MakeBinOp(ExprApp, ""); MakeBinOp(ExprOpEq, "=="); MakeBinOp(ExprOpNEq, "!="); MakeBinOp(ExprOpAnd, "&&"); MakeBinOp(ExprOpOr, "||"); MakeBinOp(ExprOpImpl, "->"); MakeBinOp(ExprOpUpdate, "//"); MakeBinOp(ExprOpConcatLists, "++"); struct ExprConcatStrings : Expr { Pos pos; bool forceString; std::vector* es; ExprConcatStrings(const Pos& pos, bool forceString, std::vector* es) : pos(pos), forceString(forceString), es(es){}; COMMON_METHODS }; struct ExprPos : Expr { Pos pos; ExprPos(const Pos& pos) : pos(pos){}; COMMON_METHODS }; /* Static environments are used to map variable names onto (level, displacement) pairs used to obtain the value of the variable at runtime. */ struct StaticEnv { bool isWith; const StaticEnv* up; typedef absl::flat_hash_map Vars; Vars vars; StaticEnv(bool isWith, const StaticEnv* up) : isWith(isWith), up(up){}; }; } // namespace nix