about summary refs log tree commit diff
path: root/third_party/nix/src/libexpr/nixexpr.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/nix/src/libexpr/nixexpr.cc')
-rw-r--r--third_party/nix/src/libexpr/nixexpr.cc414
1 files changed, 414 insertions, 0 deletions
diff --git a/third_party/nix/src/libexpr/nixexpr.cc b/third_party/nix/src/libexpr/nixexpr.cc
new file mode 100644
index 000000000000..391f0682059c
--- /dev/null
+++ b/third_party/nix/src/libexpr/nixexpr.cc
@@ -0,0 +1,414 @@
+#include "libexpr/nixexpr.hh"
+
+#include <cstdlib>
+#include <variant>
+
+#include "libstore/derivations.hh"
+#include "libutil/util.hh"
+#include "libutil/visitor.hh"
+
+namespace nix {
+
+/* Displaying abstract syntax trees. */
+
+std::ostream& operator<<(std::ostream& str, const Expr& e) {
+  e.show(str);
+  return str;
+}
+
+static void showString(std::ostream& str, const std::string& s) {
+  str << '"';
+  for (auto c : std::string(s)) {
+    if (c == '"' || c == '\\' || c == '$') {
+      str << "\\" << c;
+    } else if (c == '\n') {
+      str << "\\n";
+    } else if (c == '\r') {
+      str << "\\r";
+    } else if (c == '\t') {
+      str << "\\t";
+    } else {
+      str << c;
+    }
+  }
+  str << '"';
+}
+
+static void showId(std::ostream& str, const std::string& s) {
+  if (s.empty()) {
+    str << "\"\"";
+  } else if (s == "if") {  // FIXME: handle other keywords
+    str << '"' << s << '"';
+  } else {
+    char c = s[0];
+    if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) {
+      showString(str, s);
+      return;
+    }
+    for (auto c : s) {
+      if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+            (c >= '0' && c <= '9') || c == '_' || c == '\'' || c == '-')) {
+        showString(str, s);
+        return;
+      }
+    }
+    str << s;
+  }
+}
+
+std::ostream& operator<<(std::ostream& str, const Symbol& sym) {
+  showId(str, *sym.s);
+  return str;
+}
+
+void Expr::show(std::ostream& str) const { abort(); }
+
+void ExprInt::show(std::ostream& str) const { str << n; }
+
+void ExprFloat::show(std::ostream& str) const { str << nf; }
+
+void ExprString::show(std::ostream& str) const { showString(str, s); }
+
+void ExprPath::show(std::ostream& str) const { str << s; }
+
+void ExprVar::show(std::ostream& str) const { str << name; }
+
+void ExprSelect::show(std::ostream& str) const {
+  str << "(" << *e << ")." << showAttrPath(attrPath);
+  if (def != nullptr) {
+    str << " or (" << *def << ")";
+  }
+}
+
+void ExprOpHasAttr::show(std::ostream& str) const {
+  str << "((" << *e << ") ? " << showAttrPath(attrPath) << ")";
+}
+
+void ExprAttrs::show(std::ostream& str) const {
+  if (recursive) {
+    str << "rec ";
+  }
+  str << "{ ";
+  for (auto& i : attrs) {
+    if (i.second.inherited) {
+      str << "inherit " << i.first << " "
+          << "; ";
+    } else {
+      str << i.first << " = " << *i.second.e << "; ";
+    }
+  }
+  for (auto& i : dynamicAttrs) {
+    str << "\"${" << *i.nameExpr << "}\" = " << *i.valueExpr << "; ";
+  }
+  str << "}";
+}
+
+void ExprList::show(std::ostream& str) const {
+  str << "[ ";
+  for (auto& i : elems) {
+    str << "(" << *i << ") ";
+  }
+  str << "]";
+}
+
+void ExprLambda::show(std::ostream& str) const {
+  str << "(";
+  if (matchAttrs) {
+    str << "{ ";
+    bool first = true;
+    for (auto& i : formals->formals) {
+      if (first) {
+        first = false;
+      } else {
+        str << ", ";
+      }
+      str << i.name;
+      if (i.def != nullptr) {
+        str << " ? " << *i.def;
+      }
+    }
+    if (formals->ellipsis) {
+      if (!first) {
+        str << ", ";
+      }
+      str << "...";
+    }
+    str << " }";
+    if (!arg.empty()) {
+      str << " @ ";
+    }
+  }
+  if (!arg.empty()) {
+    str << arg;
+  }
+  str << ": " << *body << ")";
+}
+
+void ExprLet::show(std::ostream& str) const {
+  str << "(let ";
+  for (auto& i : attrs->attrs) {
+    if (i.second.inherited) {
+      str << "inherit " << i.first << "; ";
+    } else {
+      str << i.first << " = " << *i.second.e << "; ";
+    }
+  }
+  str << "in " << *body << ")";
+}
+
+void ExprWith::show(std::ostream& str) const {
+  str << "(with " << *attrs << "; " << *body << ")";
+}
+
+void ExprIf::show(std::ostream& str) const {
+  str << "(if " << *cond << " then " << *then << " else " << *else_ << ")";
+}
+
+void ExprAssert::show(std::ostream& str) const {
+  str << "assert " << *cond << "; " << *body;
+}
+
+void ExprOpNot::show(std::ostream& str) const { str << "(! " << *e << ")"; }
+
+void ExprConcatStrings::show(std::ostream& str) const {
+  bool first = true;
+  str << "(";
+  for (auto& i : *es) {
+    if (first) {
+      first = false;
+    } else {
+      str << " + ";
+    }
+    str << *i;
+  }
+  str << ")";
+}
+
+void ExprPos::show(std::ostream& str) const { str << "__curPos"; }
+
+std::ostream& operator<<(std::ostream& str, const Pos& pos) {
+  if (!pos || !pos.file.has_value()) {
+    str << "undefined position";
+  } else {
+    str << (format(ANSI_BOLD "%1%" ANSI_NORMAL ":%2%:%3%") %
+            std::string(pos.file.value()) % pos.line % pos.column)
+               .str();
+  }
+  return str;
+}
+
+std::string showAttrPath(const AttrPath& attrPath) {
+  std::ostringstream out;
+  bool first = true;
+  for (auto& attr : attrPath) {
+    if (!first) {
+      out << '.';
+    } else {
+      first = false;
+    }
+
+    std::visit(util::overloaded{
+                   [&](const Symbol& sym) { out << sym; },
+                   [&](const Expr* expr) { out << "\"${" << *expr << "}\""; }},
+               attr);
+  }
+  return out.str();
+}
+
+Pos noPos;
+
+/* Computing levels/displacements for variables. */
+
+void Expr::bindVars(const StaticEnv& env) { abort(); }
+
+void ExprInt::bindVars(const StaticEnv& env) {}
+
+void ExprFloat::bindVars(const StaticEnv& env) {}
+
+void ExprString::bindVars(const StaticEnv& env) {}
+
+void ExprPath::bindVars(const StaticEnv& env) {}
+
+void ExprVar::bindVars(const StaticEnv& env) {
+  /* Check whether the variable appears in the environment.  If so,
+     set its level and displacement. */
+  const StaticEnv* curEnv;
+  unsigned int level;
+  std::optional<unsigned int> withLevel = std::nullopt;
+  for (curEnv = &env, level = 0; curEnv != nullptr;
+       curEnv = curEnv->up, level++) {
+    if (curEnv->isWith) {
+      if (!withLevel.has_value()) {
+        withLevel = level;
+      }
+    } else {
+      auto i = curEnv->vars.find(name);
+      if (i != curEnv->vars.end()) {
+        fromWith = false;
+        this->level = level;
+        displ = i->second;
+        return;
+      }
+    }
+  }
+
+  /* Otherwise, the variable must be obtained from the nearest
+     enclosing `with'.  If there is no `with', then we can issue an
+     "undefined variable" error now. */
+  if (!withLevel.has_value()) {
+    throw UndefinedVarError(format("undefined variable '%1%' at %2%") % name %
+                            pos);
+  }
+
+  fromWith = true;
+  this->level = withLevel.value();
+}
+
+void ExprSelect::bindVars(const StaticEnv& env) {
+  e->bindVars(env);
+  if (def != nullptr) {
+    def->bindVars(env);
+  }
+  for (auto& i : attrPath) {
+    if (auto* expr = std::get_if<Expr*>(&i)) {
+      (*expr)->bindVars(env);
+    }
+  }
+}
+
+void ExprOpHasAttr::bindVars(const StaticEnv& env) {
+  e->bindVars(env);
+  for (auto& i : attrPath) {
+    if (auto* expr = std::get_if<Expr*>(&i)) {
+      (*expr)->bindVars(env);
+    }
+  }
+}
+
+void ExprAttrs::bindVars(const StaticEnv& env) {
+  const StaticEnv* dynamicEnv = &env;
+  StaticEnv newEnv(/* isWith = */ false, &env);
+
+  if (recursive) {
+    dynamicEnv = &newEnv;
+
+    unsigned int displ = 0;
+    for (auto& i : attrs) {
+      newEnv.vars[i.first] = i.second.displ = displ++;
+    }
+
+    for (auto& i : attrs) {
+      i.second.e->bindVars(i.second.inherited ? env : newEnv);
+    }
+  }
+
+  else {
+    for (auto& i : attrs) {
+      i.second.e->bindVars(env);
+    }
+  }
+
+  for (auto& i : dynamicAttrs) {
+    i.nameExpr->bindVars(*dynamicEnv);
+    i.valueExpr->bindVars(*dynamicEnv);
+  }
+}
+
+void ExprList::bindVars(const StaticEnv& env) {
+  for (auto& i : elems) {
+    i->bindVars(env);
+  }
+}
+
+void ExprLambda::bindVars(const StaticEnv& env) {
+  StaticEnv newEnv(false, &env);
+
+  unsigned int displ = 0;
+
+  if (!arg.empty()) {
+    newEnv.vars[arg] = displ++;
+  }
+
+  if (matchAttrs) {
+    for (auto& i : formals->formals) {
+      newEnv.vars[i.name] = displ++;
+    }
+
+    for (auto& i : formals->formals) {
+      if (i.def != nullptr) {
+        i.def->bindVars(newEnv);
+      }
+    }
+  }
+
+  body->bindVars(newEnv);
+}
+
+void ExprLet::bindVars(const StaticEnv& env) {
+  StaticEnv newEnv(false, &env);
+
+  unsigned int displ = 0;
+  for (auto& i : attrs->attrs) {
+    newEnv.vars[i.first] = i.second.displ = displ++;
+  }
+
+  for (auto& i : attrs->attrs) {
+    i.second.e->bindVars(i.second.inherited ? env : newEnv);
+  }
+
+  body->bindVars(newEnv);
+}
+
+void ExprWith::bindVars(const StaticEnv& env) {
+  /* Does this `with' have an enclosing `with'?  If so, record its
+     level so that `lookupVar' can look up variables in the previous
+     `with' if this one doesn't contain the desired attribute. */
+  const StaticEnv* curEnv;
+  unsigned int level;
+  prevWith = 0;
+  for (curEnv = &env, level = 1; curEnv != nullptr;
+       curEnv = curEnv->up, level++) {
+    if (curEnv->isWith) {
+      prevWith = level;
+      break;
+    }
+  }
+
+  attrs->bindVars(env);
+  StaticEnv newEnv(true, &env);
+  body->bindVars(newEnv);
+}
+
+void ExprIf::bindVars(const StaticEnv& env) {
+  cond->bindVars(env);
+  then->bindVars(env);
+  else_->bindVars(env);
+}
+
+void ExprAssert::bindVars(const StaticEnv& env) {
+  cond->bindVars(env);
+  body->bindVars(env);
+}
+
+void ExprOpNot::bindVars(const StaticEnv& env) { e->bindVars(env); }
+
+void ExprConcatStrings::bindVars(const StaticEnv& env) {
+  for (auto& i : *es) {
+    i->bindVars(env);
+  }
+}
+
+void ExprPos::bindVars(const StaticEnv& env) {}
+
+/* Storing function names. */
+void ExprLambda::setName(Symbol& name) { this->name = name; }
+
+std::string ExprLambda::showNamePos() const {
+  return (format("%1% at %2%") %
+          (name.has_value() ? "'" + std::string(name.value()) + "'"
+                            : "anonymous function") %
+          pos)
+      .str();
+}
+
+}  // namespace nix