#pragma once
#include <map>
#include <optional>
#include <variant>
#include <absl/container/flat_hash_map.h>
#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<Symbol> file;
unsigned int line, column;
Pos(const std::optional<Symbol>& 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<Symbol, Expr*>;
typedef std::vector<AttrName> 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<Symbol, AttrDef> 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<DynamicAttrDef> DynamicAttrDefs;
DynamicAttrDefs dynamicAttrs;
ExprAttrs() : recursive(false){};
COMMON_METHODS
};
struct ExprList : Expr {
std::vector<Expr*> 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<Formal> Formals_;
Formals_ formals;
std::set<Symbol> argNames; // used during parsing
bool ellipsis;
};
struct ExprLambda : Expr {
public:
Pos pos;
std::optional<Symbol> 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<Expr*>* es;
ExprConcatStrings(const Pos& pos, bool forceString, std::vector<Expr*>* 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<Symbol, unsigned int> Vars;
Vars vars;
StaticEnv(bool isWith, const StaticEnv* up) : isWith(isWith), up(up){};
};
} // namespace nix