about summary refs log tree commit diff
path: root/third_party/nix/src/libexpr/nixexpr.hh
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/nix/src/libexpr/nixexpr.hh')
-rw-r--r--third_party/nix/src/libexpr/nixexpr.hh361
1 files changed, 361 insertions, 0 deletions
diff --git a/third_party/nix/src/libexpr/nixexpr.hh b/third_party/nix/src/libexpr/nixexpr.hh
new file mode 100644
index 000000000000..16b58dec2e84
--- /dev/null
+++ b/third_party/nix/src/libexpr/nixexpr.hh
@@ -0,0 +1,361 @@
+#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*>;
+using AttrPath = std::vector<AttrName>;
+using AttrNameVector = std::vector<AttrName>;
+
+using VectorExprs = std::vector<nix::Expr*>;
+
+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 {
+  Pos pos;
+  Expr* e;
+  AttrPath attrPath;
+  ExprOpHasAttr(Expr* e, const AttrPath& attrPath) : e(e), attrPath(attrPath){};
+  ExprOpHasAttr(const Pos& pos, Expr* e, const AttrPath& attrPath)
+      : pos(pos), 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(){};
+  };
+
+  using AttrDefs = absl::flat_hash_map<Symbol, AttrDef>;
+  AttrDefs attrs;
+
+  struct DynamicAttrDef {
+    Expr *nameExpr, *valueExpr;
+    Pos pos;
+    DynamicAttrDef(Expr* nameExpr, Expr* valueExpr, const Pos& pos)
+        : nameExpr(nameExpr), valueExpr(valueExpr), pos(pos){};
+  };
+
+  using DynamicAttrDefs = std::vector<DynamicAttrDef>;
+  DynamicAttrDefs dynamicAttrs;
+
+  ExprAttrs() : recursive(false){};
+  COMMON_METHODS
+};
+
+struct ExprList : Expr {
+  VectorExprs 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 {
+  using Formals_ = std::list<Formal>;
+  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 {
+  Pos pos;
+  Expr *cond, *then, *else_;
+  ExprIf(Expr* cond, Expr* then, Expr* else_)
+      : cond(cond), then(then), else_(else_){};
+  ExprIf(const Pos& pos, Expr* cond, Expr* then, Expr* else_)
+      : pos(pos), 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 {
+  Pos pos;
+  Expr* e;
+  explicit ExprOpNot(Expr* e) : e(e){};
+  ExprOpNot(const Pos& pos, Expr* e) : pos(pos), 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;
+  nix::VectorExprs* es;
+  ExprConcatStrings(const Pos& pos, bool forceString, nix::VectorExprs* 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