aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rwxr-xr-xsrc/aterm-helper.pl179
-rw-r--r--src/libexpr/Makefile.am23
-rw-r--r--src/libexpr/attr-path.cc54
-rw-r--r--src/libexpr/attr-path.hh4
-rw-r--r--src/libexpr/common-opts.cc15
-rw-r--r--src/libexpr/common-opts.hh2
-rw-r--r--src/libexpr/eval.cc1456
-rw-r--r--src/libexpr/eval.hh323
-rw-r--r--src/libexpr/expr-to-xml.cc186
-rw-r--r--src/libexpr/expr-to-xml.hh15
-rw-r--r--src/libexpr/get-drvs.cc208
-rw-r--r--src/libexpr/get-drvs.hh22
-rw-r--r--src/libexpr/lexer.l48
-rw-r--r--src/libexpr/nixexpr-ast.def97
-rw-r--r--src/libexpr/nixexpr.cc538
-rw-r--r--src/libexpr/nixexpr.hh288
-rw-r--r--src/libexpr/parser.hh7
-rw-r--r--src/libexpr/parser.y454
-rw-r--r--src/libexpr/primops.cc839
-rw-r--r--src/libexpr/symbol-table.hh81
-rw-r--r--src/libexpr/value-to-xml.cc161
-rw-r--r--src/libexpr/value-to-xml.hh17
-rw-r--r--src/libmain/Makefile.am2
-rw-r--r--src/libmain/shared.cc12
-rw-r--r--src/libstore/Makefile.am9
-rw-r--r--src/libstore/derivations-ast.def10
-rw-r--r--src/libstore/derivations.cc224
-rw-r--r--src/libstore/derivations.hh15
-rw-r--r--src/libstore/local-store.cc5
-rw-r--r--src/libstore/misc.cc6
-rw-r--r--src/libutil/Makefile.am6
-rw-r--r--src/libutil/aterm-map.cc332
-rw-r--r--src/libutil/aterm-map.hh130
-rw-r--r--src/libutil/aterm.cc55
-rw-r--r--src/libutil/aterm.hh55
-rw-r--r--src/libutil/util.cc88
-rw-r--r--src/libutil/util.hh42
-rw-r--r--src/nix-env/Makefile.am4
-rw-r--r--src/nix-env/nix-env.cc228
-rw-r--r--src/nix-env/profiles.cc16
-rw-r--r--src/nix-env/profiles.hh15
-rw-r--r--src/nix-env/user-env.cc257
-rw-r--r--src/nix-env/user-env.hh20
-rw-r--r--src/nix-instantiate/Makefile.am1
-rw-r--r--src/nix-instantiate/nix-instantiate.cc93
46 files changed, 3084 insertions, 3560 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 971f7d9d1..ec3632773 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,3 @@
SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \
libexpr nix-instantiate nix-env nix-worker nix-setuid-helper \
nix-log2xml bsdiff-4.3
-
-EXTRA_DIST = aterm-helper.pl
diff --git a/src/aterm-helper.pl b/src/aterm-helper.pl
deleted file mode 100755
index f1eb77ee8..000000000
--- a/src/aterm-helper.pl
+++ /dev/null
@@ -1,179 +0,0 @@
-#! /usr/bin/perl -w
-
-# This program generates C/C++ code for efficiently manipulating
-# ATerms. It generates functions to build and match ATerms according
-# to a set of constructor definitions defined in a file read from
-# standard input. A constructor is defined by a line with the
-# following format:
-#
-# SYM | ARGS | TYPE | FUN?
-#
-# where SYM is the name of the constructor, ARGS is a
-# whitespace-separated list of argument types, TYPE is the type of the
-# resulting ATerm (which should be `ATerm' or a type synonym for
-# `ATerm'), and the optional FUN is used to construct the names of the
-# build and match functions (it defaults to SYM; overriding it is
-# useful if there are overloaded constructors, e.g., with different
-# arities). Note that SYM may be empty.
-#
-# A line of the form
-#
-# VAR = EXPR
-#
-# causes a ATerm variable to be generated that is initialised to the
-# value EXPR.
-#
-# Finally, a line of the form
-#
-# init NAME
-#
-# causes the initialisation function to be called `NAME'. This
-# function must be called before any of the build/match functions or
-# the generated variables are used.
-
-die if scalar @ARGV != 2;
-
-my $syms = "";
-my $init = "";
-my $initFun = "init";
-
-open HEADER, ">$ARGV[0]";
-open IMPL, ">$ARGV[1]";
-
-print HEADER "#include <aterm2.h>\n";
-print HEADER "#ifdef __cplusplus\n";
-print HEADER "namespace nix {\n";
-print HEADER "#endif\n\n\n";
-print IMPL "namespace nix {\n";
-
-while (<STDIN>) {
- s/\#.*//;
- next if (/^\s*$/);
-
- if (/^\s*(\w*)\s*\|([^\|]*)\|\s*(\w+)\s*\|\s*(\w+)?/) {
- my $const = $1;
- my @types = split ' ', $2;
- my $result = $3;
- my $funname = $4;
- $funname = $const unless defined $funname;
-
- my $formals = "";
- my $formals2 = "";
- my $args = "";
- my $unpack = "";
- my $n = 1;
- foreach my $type (@types) {
- my $realType = $type;
- $args .= ", ";
- if ($type eq "string") {
-# $args .= "(ATerm) ATmakeAppl0(ATmakeAFun((char *) e$n, 0, ATtrue))";
-# $type = "const char *";
- $type = "ATerm";
- $args .= "e$n";
- # !!! in the matcher, we should check that the
- # argument is a string (i.e., a nullary application).
- } elsif ($type eq "int") {
- $args .= "(ATerm) ATmakeInt(e$n)";
- } elsif ($type eq "ATermList" || $type eq "ATermBlob") {
- $args .= "(ATerm) e$n";
- } else {
- $args .= "e$n";
- }
- $formals .= ", " if $formals ne "";
- $formals .= "$type e$n";
- $formals2 .= ", ";
- $formals2 .= "$type & e$n";
- my $m = $n - 1;
- # !!! more checks here
- if ($type eq "int") {
- $unpack .= " e$n = ATgetInt((ATermInt) ATgetArgument(e, $m));\n";
- } elsif ($type eq "ATermList") {
- $unpack .= " e$n = (ATermList) ATgetArgument(e, $m);\n";
- } elsif ($type eq "ATermBlob") {
- $unpack .= " e$n = (ATermBlob) ATgetArgument(e, $m);\n";
- } elsif ($realType eq "string") {
- $unpack .= " e$n = ATgetArgument(e, $m);\n";
- $unpack .= " if (ATgetType(e$n) != AT_APPL) return false;\n";
- } else {
- $unpack .= " e$n = ATgetArgument(e, $m);\n";
- }
- $n++;
- }
-
- my $arity = scalar @types;
-
- print HEADER "extern AFun sym$funname;\n\n";
-
- print IMPL "AFun sym$funname = 0;\n";
-
- if ($arity == 0) {
- print HEADER "extern ATerm const$funname;\n\n";
- print IMPL "ATerm const$funname = 0;\n";
- }
-
- print HEADER "static inline $result make$funname($formals) __attribute__ ((pure, nothrow));\n";
- print HEADER "static inline $result make$funname($formals) {\n";
- if ($arity == 0) {
- print HEADER " return const$funname;\n";
- }
- elsif ($arity <= 6) {
- print HEADER " return (ATerm) ATmakeAppl$arity(sym$funname$args);\n";
- } else {
- $args =~ s/^,//;
- print HEADER " ATerm array[$arity] = {$args};\n";
- print HEADER " return (ATerm) ATmakeApplArray(sym$funname, array);\n";
- }
- print HEADER "}\n\n";
-
- print HEADER "#ifdef __cplusplus\n";
- print HEADER "static inline bool match$funname(ATerm e$formals2) {\n";
- print HEADER " if (ATgetType(e) != AT_APPL || (AFun) ATgetAFun(e) != sym$funname) return false;\n";
- print HEADER "$unpack";
- print HEADER " return true;\n";
- print HEADER "}\n";
- print HEADER "#endif\n\n\n";
-
- $init .= " sym$funname = ATmakeAFun(\"$const\", $arity, ATfalse);\n";
- $init .= " ATprotectAFun(sym$funname);\n";
- if ($arity == 0) {
- $init .= " const$funname = (ATerm) ATmakeAppl0(sym$funname);\n";
- $init .= " ATprotect(&const$funname);\n";
- }
- }
-
- elsif (/^\s*(\w+)\s*=\s*(.*)$/) {
- my $name = $1;
- my $value = $2;
- print HEADER "extern ATerm $name;\n";
- print IMPL "ATerm $name = 0;\n";
- $init .= " $name = $value;\n";
- $init .= " ATprotect(&$name);\n";
- }
-
- elsif (/^\s*init\s+(\w+)\s*$/) {
- $initFun = $1;
- }
-
- else {
- die "bad line: `$_'";
- }
-}
-
-print HEADER "void $initFun();\n\n";
-
-print HEADER "static inline const char * aterm2String(ATerm t) {\n";
-print HEADER " return (const char *) ATgetName(ATgetAFun(t));\n";
-print HEADER "}\n\n";
-
-print IMPL "\n";
-print IMPL "void $initFun() {\n";
-print IMPL "$init";
-print IMPL "}\n";
-
-print HEADER "#ifdef __cplusplus\n";
-print HEADER "}\n";
-print HEADER "#endif\n\n\n";
-print IMPL "}\n";
-
-close HEADER;
-close IMPL;
diff --git a/src/libexpr/Makefile.am b/src/libexpr/Makefile.am
index e16600fbf..e7228e183 100644
--- a/src/libexpr/Makefile.am
+++ b/src/libexpr/Makefile.am
@@ -2,27 +2,25 @@ pkglib_LTLIBRARIES = libexpr.la
libexpr_la_SOURCES = \
nixexpr.cc eval.cc primops.cc lexer-tab.cc parser-tab.cc \
- get-drvs.cc attr-path.cc expr-to-xml.cc common-opts.cc \
+ get-drvs.cc attr-path.cc value-to-xml.cc common-opts.cc \
names.cc
pkginclude_HEADERS = \
nixexpr.hh eval.hh parser.hh lexer-tab.hh parser-tab.hh \
- get-drvs.hh attr-path.hh expr-to-xml.hh common-opts.hh \
- names.hh nixexpr-ast.hh
+ get-drvs.hh attr-path.hh value-to-xml.hh common-opts.hh \
+ names.hh symbol-table.hh
libexpr_la_LIBADD = ../libutil/libutil.la ../libstore/libstore.la \
../boost/format/libformat.la
-BUILT_SOURCES = nixexpr-ast.cc nixexpr-ast.hh \
+BUILT_SOURCES = \
parser-tab.hh lexer-tab.hh parser-tab.cc lexer-tab.cc
-EXTRA_DIST = lexer.l parser.y nixexpr-ast.def nixexpr-ast.cc
+EXTRA_DIST = lexer.l parser.y
AM_CXXFLAGS = \
- -I$(srcdir)/.. ${aterm_include} \
+ -I$(srcdir)/.. \
-I$(srcdir)/../libutil -I$(srcdir)/../libstore
-AM_CFLAGS = \
- ${aterm_include}
# Parser generation.
@@ -34,15 +32,6 @@ lexer-tab.cc lexer-tab.hh: lexer.l
$(flex) --outfile lexer-tab.cc --header-file=lexer-tab.hh $(srcdir)/lexer.l
-# ATerm helper function generation.
-
-nixexpr-ast.cc nixexpr-ast.hh: ../aterm-helper.pl nixexpr-ast.def
- $(perl) $(srcdir)/../aterm-helper.pl nixexpr-ast.hh nixexpr-ast.cc < $(srcdir)/nixexpr-ast.def
-
-
-CLEANFILES =
-
-
# SDF stuff (not built by default).
nix.tbl: nix.sdf
sdf2table -m Nix -s -i nix.sdf -o nix.tbl
diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc
index 092d9b1c2..0660ba1c1 100644
--- a/src/libexpr/attr-path.cc
+++ b/src/libexpr/attr-path.cc
@@ -1,24 +1,12 @@
#include "attr-path.hh"
-#include "nixexpr-ast.hh"
#include "util.hh"
-#include "aterm.hh"
namespace nix {
-bool isAttrs(EvalState & state, Expr e, ATermMap & attrs)
-{
- e = evalExpr(state, e);
- ATermList dummy;
- if (!matchAttrs(e, dummy)) return false;
- queryAllAttrs(e, attrs, false);
- return true;
-}
-
-
-Expr findAlongAttrPath(EvalState & state, const string & attrPath,
- const ATermMap & autoArgs, Expr e)
+void findAlongAttrPath(EvalState & state, const string & attrPath,
+ const Bindings & autoArgs, Expr * e, Value & v)
{
Strings tokens = tokenizeString(attrPath, ".");
@@ -26,8 +14,10 @@ Expr findAlongAttrPath(EvalState & state, const string & attrPath,
Error(format("attribute selection path `%1%' does not match expression") % attrPath);
string curPath;
+
+ state.mkThunk_(v, e);
- for (Strings::iterator i = tokens.begin(); i != tokens.end(); ++i) {
+ foreach (Strings::iterator, i, tokens) {
if (!curPath.empty()) curPath += ".";
curPath += *i;
@@ -39,7 +29,10 @@ Expr findAlongAttrPath(EvalState & state, const string & attrPath,
if (string2Int(attr, attrIndex)) apType = apIndex;
/* Evaluate the expression. */
- e = evalExpr(state, autoCallFunction(evalExpr(state, e), autoArgs));
+ Value vTmp;
+ state.autoCallFunction(autoArgs, v, vTmp);
+ v = vTmp;
+ state.forceValue(v);
/* It should evaluate to either an attribute set or an
expression, according to what is specified in the
@@ -47,36 +40,31 @@ Expr findAlongAttrPath(EvalState & state, const string & attrPath,
if (apType == apAttr) {
- ATermMap attrs;
-
- if (!isAttrs(state, e, attrs))
+ if (v.type != tAttrs)
throw TypeError(
format("the expression selected by the selection path `%1%' should be an attribute set but is %2%")
- % curPath % showType(e));
-
- e = attrs.get(toATerm(attr));
- if (!e)
- throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath);
+ % curPath % showType(v));
+ Bindings::iterator a = v.attrs->find(state.symbols.create(attr));
+ if (a == v.attrs->end())
+ throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath);
+ v = a->second.value;
}
else if (apType == apIndex) {
- ATermList es;
- if (!matchList(e, es))
+ if (v.type != tList)
throw TypeError(
format("the expression selected by the selection path `%1%' should be a list but is %2%")
- % curPath % showType(e));
+ % curPath % showType(v));
- e = ATelementAt(es, attrIndex);
- if (!e)
- throw Error(format("list index %1% in selection path `%2%' not found") % attrIndex % curPath);
-
+ if (attrIndex >= v.list.length)
+ throw Error(format("list index %1% in selection path `%2%' is out of range") % attrIndex % curPath);
+
+ v = *v.list.elems[attrIndex];
}
}
-
- return e;
}
diff --git a/src/libexpr/attr-path.hh b/src/libexpr/attr-path.hh
index 7abaa83a0..33587e5ed 100644
--- a/src/libexpr/attr-path.hh
+++ b/src/libexpr/attr-path.hh
@@ -10,8 +10,8 @@
namespace nix {
-Expr findAlongAttrPath(EvalState & state, const string & attrPath,
- const ATermMap & autoArgs, Expr e);
+void findAlongAttrPath(EvalState & state, const string & attrPath,
+ const Bindings & autoArgs, Expr * e, Value & v);
}
diff --git a/src/libexpr/common-opts.cc b/src/libexpr/common-opts.cc
index 0ef488373..5a4856568 100644
--- a/src/libexpr/common-opts.cc
+++ b/src/libexpr/common-opts.cc
@@ -2,7 +2,6 @@
#include "../libmain/shared.hh"
#include "util.hh"
#include "parser.hh"
-#include "aterm.hh"
namespace nix {
@@ -10,7 +9,7 @@ namespace nix {
bool parseOptionArg(const string & arg, Strings::iterator & i,
const Strings::iterator & argsEnd, EvalState & state,
- ATermMap & autoArgs)
+ Bindings & autoArgs)
{
if (arg != "--arg" && arg != "--argstr") return false;
@@ -20,11 +19,13 @@ bool parseOptionArg(const string & arg, Strings::iterator & i,
string name = *i++;
if (i == argsEnd) throw error;
string value = *i++;
-
- Expr e = arg == "--arg"
- ? parseExprFromString(state, value, absPath("."))
- : makeStr(value);
- autoArgs.set(toATerm(name), e);
+
+ Value & v(autoArgs[state.symbols.create(name)].value);
+
+ if (arg == "--arg")
+ state.mkThunk_( v, parseExprFromString(state, value, absPath(".")));
+ else
+ mkString(v, value);
return true;
}
diff --git a/src/libexpr/common-opts.hh b/src/libexpr/common-opts.hh
index fb9659cdc..80298ce55 100644
--- a/src/libexpr/common-opts.hh
+++ b/src/libexpr/common-opts.hh
@@ -9,7 +9,7 @@ namespace nix {
/* Some common option parsing between nix-env and nix-instantiate. */
bool parseOptionArg(const string & arg, Strings::iterator & i,
const Strings::iterator & argsEnd, EvalState & state,
- ATermMap & autoArgs);
+ Bindings & autoArgs);
}
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 5a0e8bc27..26739faf6 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -4,9 +4,9 @@
#include "util.hh"
#include "store-api.hh"
#include "derivations.hh"
-#include "nixexpr-ast.hh"
#include "globals.hh"
-#include "aterm.hh"
+
+#include <cstring>
#define LocalNoInline(f) static f __attribute__((noinline)); f
@@ -16,23 +16,139 @@
namespace nix {
-EvalState::EvalState()
- : normalForms(32768), primOps(128)
+std::ostream & operator << (std::ostream & str, Value & v)
{
- nrEvaluated = nrCached = 0;
+ switch (v.type) {
+ case tInt:
+ str << v.integer;
+ break;
+ case tBool:
+ str << (v.boolean ? "true" : "false");
+ break;
+ case tString:
+ str << "\"";
+ for (const char * i = v.string.s; *i; i++)
+ if (*i == '\"' || *i == '\\') str << "\\" << *i;
+ else if (*i == '\n') str << "\\n";
+ else if (*i == '\r') str << "\\r";
+ else if (*i == '\t') str << "\\t";
+ else str << *i;
+ str << "\"";
+ break;
+ case tPath:
+ str << v.path; // !!! escaping?
+ break;
+ case tNull:
+ str << "true";
+ break;
+ case tAttrs: {
+ str << "{ ";
+ typedef std::map<string, Value *> Sorted;
+ Sorted sorted;
+ foreach (Bindings::iterator, i, *v.attrs)
+ sorted[i->first] = &i->second.value;
+ foreach (Sorted::iterator, i, sorted)
+ str << i->first << " = " << *i->second << "; ";
+ str << "}";
+ break;
+ }
+ case tList:
+ str << "[ ";
+ for (unsigned int n = 0; n < v.list.length; ++n)
+ str << *v.list.elems[n] << " ";
+ str << "]";
+ break;
+ case tThunk:
+ case tCopy:
+ str << "<CODE>";
+ break;
+ case tLambda:
+ str << "<LAMBDA>";
+ break;
+ case tPrimOp:
+ str << "<PRIMOP>";
+ break;
+ case tPrimOpApp:
+ str << "<PRIMOP-APP>";
+ break;
+ default:
+ throw Error("invalid value");
+ }
+ return str;
+}
- initNixExprHelpers();
- addPrimOps();
+string showType(const Value & v)
+{
+ switch (v.type) {
+ case tInt: return "an integer";
+ case tBool: return "a boolean";
+ case tString: return "a string";
+ case tPath: return "a path";
+ case tNull: return "null";
+ case tAttrs: return "an attribute set";
+ case tList: return "a list";
+ case tThunk: return "a thunk";
+ case tApp: return "a function application";
+ case tLambda: return "a function";
+ case tCopy: return "a copy";
+ case tBlackhole: return "a black hole";
+ case tPrimOp: return "a built-in function";
+ case tPrimOpApp: return "a partially applied built-in function";
+ }
+ abort();
+}
+
+EvalState::EvalState()
+ : sWith(symbols.create("<with>"))
+ , sOutPath(symbols.create("outPath"))
+ , sDrvPath(symbols.create("drvPath"))
+ , sType(symbols.create("type"))
+ , sMeta(symbols.create("meta"))
+ , sName(symbols.create("name"))
+ , sSystem(symbols.create("system"))
+ , baseEnv(allocEnv(128))
+ , baseEnvDispl(0)
+ , staticBaseEnv(false, 0)
+{
+ nrEnvs = nrValuesInEnvs = nrValues = nrListElems = 0;
+ nrEvaluated = recursionDepth = maxRecursionDepth = 0;
+ deepestStack = (char *) -1;
+
+ createBaseEnv();
+
allowUnsafeEquality = getEnv("NIX_NO_UNSAFE_EQ", "") == "";
}
+EvalState::~EvalState()
+{
+ assert(recursionDepth == 0);
+}
+
+
+void EvalState::addConstant(const string & name, Value & v)
+{
+ staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
+ baseEnv.values[baseEnvDispl++] = v;
+ string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
+ (*baseEnv.values[0].attrs)[symbols.create(name2)].value = v;
+}
+
+
void EvalState::addPrimOp(const string & name,
unsigned int arity, PrimOp primOp)
{
- primOps.set(toATerm(name), makePrimOpDef(arity, ATmakeBlob(0, (void *) primOp)));
+ Value v;
+ string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
+ v.type = tPrimOp;
+ v.primOp.arity = arity;
+ v.primOp.fun = primOp;
+ v.primOp.name = strdup(name2.c_str());
+ staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
+ baseEnv.values[baseEnvDispl++] = v;
+ (*baseEnv.values[0].attrs)[symbols.create(name2)].value = v;
}
@@ -51,6 +167,11 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
throw EvalError(format(s) % s2);
}
+LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3))
+{
+ throw EvalError(format(s) % s2 % s3);
+}
+
LocalNoInlineNoReturn(void throwTypeError(const char * s))
{
throw TypeError(s);
@@ -61,9 +182,19 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2))
throw TypeError(format(s) % s2);
}
-LocalNoInline(void addErrorPrefix(Error & e, const char * s))
+LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos, const string & s2))
+{
+ throw TypeError(format(s) % pos % s2);
+}
+
+LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos))
+{
+ throw TypeError(format(s) % pos);
+}
+
+LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos))
{
- e.addPrefix(s);
+ throw AssertionError(format(s) % pos);
}
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2))
@@ -71,845 +202,884 @@ LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2))
e.addPrefix(format(s) % s2);
}
-LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, const string & s3))
+LocalNoInline(void addErrorPrefix(Error & e, const char * s, const Pos & pos))
{
- e.addPrefix(format(s) % s2 % s3);
+ e.addPrefix(format(s) % pos);
+}
+
+LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, const Pos & pos))
+{
+ e.addPrefix(format(s) % s2 % pos);
}
-/* Pattern-match `pat' against `arg'. The result is a set of
- substitutions (`subs') and a set of recursive substitutions
- (`subsRecursive'). The latter can refer to the variables bound by
- both `subs' and `subsRecursive'. */
-static void patternMatch(EvalState & state,
- Pattern pat, Expr arg, ATermMap & subs, ATermMap & subsRecursive)
+void mkString(Value & v, const char * s)
{
- ATerm name;
- ATermList formals;
- Pattern pat1, pat2;
- ATermBool ellipsis;
-
- if (matchVarPat(pat, name))
- subs.set(name, arg);
+ v.type = tString;
+ v.string.s = strdup(s);
+ v.string.context = 0;
+}
- else if (matchAttrsPat(pat, formals, ellipsis)) {
- arg = evalExpr(state, arg);
+void mkString(Value & v, const string & s, const PathSet & context)
+{
+ mkString(v, s.c_str());
+ if (!context.empty()) {
+ unsigned int n = 0;
+ v.string.context = new const char *[context.size() + 1];
+ foreach (PathSet::const_iterator, i, context)
+ v.string.context[n++] = strdup(i->c_str());
+ v.string.context[n] = 0;
+ }
+}
- /* Get the actual arguments. */
- ATermMap attrs;
- queryAllAttrs(arg, attrs);
- unsigned int nrAttrs = attrs.size();
- /* For each formal argument, get the actual argument. If
- there is no matching actual argument but the formal
- argument has a default, use the default. */
- unsigned int attrsUsed = 0;
- for (ATermIterator i(formals); i; ++i) {
- Expr name, def;
- DefaultValue def2;
- if (!matchFormal(*i, name, def2)) abort(); /* can't happen */
-
- Expr value = attrs[name];
-
- if (value == 0) {
- if (!matchDefaultValue(def2, def)) def = 0;
- if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing")
- % aterm2String(name));
- subsRecursive.set(name, def);
- } else {
- attrsUsed++;
- attrs.remove(name);
- subs.set(name, value);
- }
+void mkPath(Value & v, const char * s)
+{
+ v.type = tPath;
+ v.path = strdup(s);
+}
+
+Value * EvalState::lookupVar(Env * env, const VarRef & var)
+{
+ for (unsigned int l = var.level; l; --l, env = env->up) ;
+
+ if (var.fromWith) {
+ while (1) {
+ Bindings::iterator j = env->values[0].attrs->find(var.name);
+ if (j != env->values[0].attrs->end())
+ return &j->second.value;
+ if (env->prevWith == 0)
+ throwEvalError("undefined variable `%1%'", var.name);
+ for (unsigned int l = env->prevWith; l; --l, env = env->up) ;
}
+ } else
+ return &env->values[var.displ];
+}
- /* Check that each actual argument is listed as a formal
- argument (unless the attribute match specifies a `...'). */
- if (ellipsis == eFalse && attrsUsed != nrAttrs)
- throw TypeError(format("the function does not expect an argument named `%1%'")
- % aterm2String(attrs.begin()->key));
- }
- else if (matchAtPat(pat, pat1, pat2)) {
- patternMatch(state, pat1, arg, subs, subsRecursive);
- patternMatch(state, pat2, arg, subs, subsRecursive);
- }
+Value * EvalState::allocValues(unsigned int count)
+{
+ nrValues += count;
+ return new Value[count]; // !!! check destructor
+}
+
- else abort();
+Env & EvalState::allocEnv(unsigned int size)
+{
+ nrEnvs++;
+ nrValuesInEnvs += size;
+ Env * env = (Env *) malloc(sizeof(Env) + size * sizeof(Value));
+ return *env;
}
-/* Substitute an argument set into the body of a function. */
-static Expr substArgs(EvalState & state,
- Expr body, Pattern pat, Expr arg)
+void EvalState::mkList(Value & v, unsigned int length)
{
- ATermMap subs(16), subsRecursive(16);
-
- patternMatch(state, pat, arg, subs, subsRecursive);
-
- /* If we used any default values, make a recursive attribute set
- out of the (argument-name, value) tuples. This is so that we
- can support default values that refer to each other, e.g. ({x,
- y ? x + x}: y) {x = "foo";} evaluates to "foofoo". */
- if (subsRecursive.size() != 0) {
- ATermList recAttrs = ATempty;
- foreach (ATermMap::const_iterator, i, subs)
- recAttrs = ATinsert(recAttrs, makeBind(i->key, i->value, makeNoPos()));
- foreach (ATermMap::const_iterator, i, subsRecursive)
- recAttrs = ATinsert(recAttrs, makeBind(i->key, i->value, makeNoPos()));
- Expr rec = makeRec(recAttrs, ATempty);
- foreach (ATermMap::const_iterator, i, subsRecursive)
- subs.set(i->key, makeSelect(rec, i->key));
- }
+ v.type = tList;
+ v.list.length = length;
+ v.list.elems = new Value *[length];
+ nrListElems += length;
+}
+
- return substitute(Substitution(0, &subs), body);
+void EvalState::mkAttrs(Value & v)
+{
+ v.type = tAttrs;
+ v.attrs = new Bindings;
}
-/* Transform a mutually recursive set into a non-recursive set. Each
- attribute is transformed into an expression that has all references
- to attributes substituted with selection expressions on the
- original set. E.g., e = `rec {x = f x y; y = x;}' becomes `{x = f
- (e.x) (e.y); y = e.x;}'. */
-LocalNoInline(ATerm expandRec(EvalState & state, ATerm e, ATermList rbnds, ATermList nrbnds))
+void EvalState::mkThunk_(Value & v, Expr * expr)
{
- ATerm name;
- Expr e2;
- Pos pos;
- Expr eOverrides = 0;
+ mkThunk(v, baseEnv, expr);
+}
- /* Create the substitution list. */
- ATermMap subs(ATgetLength(rbnds) + ATgetLength(nrbnds));
- for (ATermIterator i(rbnds); i; ++i) {
- if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
- subs.set(name, makeSelect(e, name));
- }
- for (ATermIterator i(nrbnds); i; ++i) {
- if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
- if (name == sOverrides) eOverrides = e2;
- subs.set(name, e2);
- }
- /* If the rec contains an attribute called `__overrides', then
- evaluate it, and add the attributes in that set to the rec.
- This allows overriding of recursive attributes, which is
- otherwise not possible. (You can use the // operator to
- replace an attribute, but other attributes in the rec will
- still reference the original value, because that value has been
- substituted into the bodies of the other attributes. Hence we
- need __overrides.) */
- ATermMap overrides;
- if (eOverrides) {
- eOverrides = evalExpr(state, eOverrides);
- queryAllAttrs(eOverrides, overrides, false);
- foreach (ATermMap::const_iterator, i, overrides)
- subs.set(i->key, i->value);
+void EvalState::cloneAttrs(Value & src, Value & dst)
+{
+ mkAttrs(dst);
+ foreach (Bindings::iterator, i, *src.attrs) {
+ Attr & a = (*dst.attrs)[i->first];
+ mkCopy(a.value, i->second.value);
+ a.pos = i->second.pos;
}
+}
- Substitution subs_(0, &subs);
- /* Create the non-recursive set. */
- ATermMap as(ATgetLength(rbnds) + ATgetLength(nrbnds));
- for (ATermIterator i(rbnds); i; ++i) {
- if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
- as.set(name, makeAttrRHS(substitute(subs_, e2), pos));
- }
+void EvalState::evalFile(const Path & path, Value & v)
+{
+ startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path);
- if (eOverrides)
- foreach (ATermMap::const_iterator, i, overrides)
- as.set(i->key, makeAttrRHS(i->value, makeNoPos()));
+ Expr * e = parseTrees[path];
- /* Copy the non-recursive bindings. !!! inefficient */
- for (ATermIterator i(nrbnds); i; ++i) {
- if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
- as.set(name, makeAttrRHS(e2, pos));
+ if (!e) {
+ e = parseExprFromFile(*this, path);
+ parseTrees[path] = e;
+ }
+
+ try {
+ eval(e, v);
+ } catch (Error & e) {
+ addErrorPrefix(e, "while evaluating the file `%1%':\n", path);
+ throw;
}
-
- return makeAttrs(as);
}
-LocalNoInline(Expr updateAttrs(Expr e1, Expr e2))
+struct RecursionCounter
{
- /* Note: e1 and e2 should be in normal form. */
+ EvalState & state;
+ RecursionCounter(EvalState & state) : state(state)
+ {
+ state.recursionDepth++;
+ if (state.recursionDepth > state.maxRecursionDepth)
+ state.maxRecursionDepth = state.recursionDepth;
+ }
+ ~RecursionCounter()
+ {
+ state.recursionDepth--;
+ }
+};
- ATermMap attrs;
- queryAllAttrs(e1, attrs, true);
- queryAllAttrs(e2, attrs, true);
- return makeAttrs(attrs);
-}
+void EvalState::eval(Env & env, Expr * e, Value & v)
+{
+ /* When changing this function, make sure that you don't cause a
+ (large) increase in stack consumption! */
+ /* !!! Disable this eventually. */
+ RecursionCounter r(*this);
+ char x;
+ if (&x < deepestStack) deepestStack = &x;
+
+ //debug(format("eval: %1%") % *e);
-string evalString(EvalState & state, Expr e, PathSet & context)
-{
- e = evalExpr(state, e);
- string s;
- if (!matchStr(e, s, context))
- throwTypeError("value is %1% while a string was expected", showType(e));
- return s;
+ checkInterrupt();
+
+ nrEvaluated++;
+
+ e->eval(*this, env, v);
}
-string evalStringNoCtx(EvalState & state, Expr e)
+void EvalState::eval(Expr * e, Value & v)
{
- PathSet context;
- string s = evalString(state, e, context);
- if (!context.empty())
- throw EvalError(format("the string `%1%' is not allowed to refer to a store path (such as `%2%')")
- % s % *(context.begin()));
- return s;
+ eval(baseEnv, e, v);
}
-int evalInt(EvalState & state, Expr e)
+bool EvalState::evalBool(Env & env, Expr * e)
{
- e = evalExpr(state, e);
- int i;
- if (!matchInt(e, i))
- throwTypeError("value is %1% while an integer was expected", showType(e));
- return i;
+ Value v;
+ eval(env, e, v);
+ if (v.type != tBool)
+ throwTypeError("value is %1% while a Boolean was expected", showType(v));
+ return v.boolean;
}
-bool evalBool(EvalState & state, Expr e)
+void EvalState::evalAttrs(Env & env, Expr * e, Value & v)
{
- e = evalExpr(state, e);
- if (e == eTrue) return true;
- else if (e == eFalse) return false;
- else throwTypeError("value is %1% while a boolean was expected", showType(e));
+ eval(env, e, v);
+ if (v.type != tAttrs)
+ throwTypeError("value is %1% while an attribute set was expected", showType(v));
}
-ATermList evalList(EvalState & state, Expr e)
+void Expr::eval(EvalState & state, Env & env, Value & v)
{
- e = evalExpr(state, e);
- ATermList list;
- if (!matchList(e, list))
- throwTypeError("value is %1% while a list was expected", showType(e));
- return list;
+ abort();
}
-static void flattenList(EvalState & state, Expr e, ATermList & result)
+void ExprInt::eval(EvalState & state, Env & env, Value & v)
{
- ATermList es;
- e = evalExpr(state, e);
- if (matchList(e, es))
- for (ATermIterator i(es); i; ++i)
- flattenList(state, *i, result);
- else
- result = ATinsert(result, e);
+ mkInt(v, n);
}
-ATermList flattenList(EvalState & state, Expr e)
+void ExprString::eval(EvalState & state, Env & env, Value & v)
{
- ATermList result = ATempty;
- flattenList(state, e, result);
- return ATreverse(result);
+ mkString(v, s.c_str());
}
-string coerceToString(EvalState & state, Expr e, PathSet & context,
- bool coerceMore, bool copyToStore)
+void ExprPath::eval(EvalState & state, Env & env, Value & v)
{
- e = evalExpr(state, e);
+ mkPath(v, s.c_str());
+}
- string s;
- if (matchStr(e, s, context)) return s;
+void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
+{
+ state.mkAttrs(v);
- ATerm s2;
- if (matchPath(e, s2)) {
- Path path(canonPath(aterm2String(s2)));
+ if (recursive) {
+ /* Create a new environment that contains the attributes in
+ this `rec'. */
+ Env & env2(state.allocEnv(attrs.size() + inherited.size()));
+ env2.up = &env;
- if (!copyToStore) return path;
+ unsigned int displ = 0;
- if (isDerivation(path))
- throw EvalError(format("file names are not allowed to end in `%1%'")
- % drvExtension);
+ /* The recursive attributes are evaluated in the new
+ environment. */
+ foreach (Attrs::iterator, i, attrs) {
+ nix::Attr & a = (*v.attrs)[i->first];
+ mkCopy(a.value, env2.values[displ]);
+ mkThunk(env2.values[displ++], env2, i->second.first);
+ a.pos = &i->second.second;
+ }
- Path dstPath;
- if (state.srcToStore[path] != "")
- dstPath = state.srcToStore[path];
- else {
- dstPath = readOnlyMode
- ? computeStorePathForPath(path).first
- : store->addToStore(path);
- state.srcToStore[path] = dstPath;
- printMsg(lvlChatty, format("copied source `%1%' -> `%2%'")
- % path % dstPath);
+ /* The inherited attributes, on the other hand, are
+ evaluated in the original environment. */
+ foreach (list<Inherited>::iterator, i, inherited) {
+ nix::Attr & a = (*v.attrs)[i->first.name];
+ Value * v2 = state.lookupVar(&env, i->first);
+ mkCopy(a.value, *v2);
+ mkCopy(env2.values[displ++], *v2);
+ a.pos = &i->second;
}
- context.insert(dstPath);
- return dstPath;
- }
-
- ATermList es;
- if (matchAttrs(e, es)) {
- Expr e2 = queryAttr(e, "outPath");
- if (!e2) throwTypeError("cannot coerce an attribute set (except a derivation) to a string");
- return coerceToString(state, e2, context, coerceMore, copyToStore);
}
- if (coerceMore) {
-
- /* Note that `false' is represented as an empty string for
- shell scripting convenience, just like `null'. */
- if (e == eTrue) return "1";
- if (e == eFalse) return "";
- int n;
- if (matchInt(e, n)) return int2String(n);
- if (matchNull(e)) return "";
+ else {
+ foreach (Attrs::iterator, i, attrs) {
+ nix::Attr & a = (*v.attrs)[i->first];
+ mkThunk(a.value, env, i->second.first);
+ a.pos = &i->second.second;
+ }
- if (matchList(e, es)) {
- string result;
- es = flattenList(state, e);
- bool first = true;
- for (ATermIterator i(es); i; ++i) {
- if (!first) result += " "; else first = false;
- result += coerceToString(state, *i,
- context, coerceMore, copyToStore);
- }
- return result;
+ foreach (list<Inherited>::iterator, i, inherited) {
+ nix::Attr & a = (*v.attrs)[i->first.name];
+ mkCopy(a.value, *state.lookupVar(&env, i->first));
+ a.pos = &i->second;
}
}
-
- throwTypeError("cannot coerce %1% to a string", showType(e));
}
-/* Common implementation of `+', ConcatStrings and `~'. */
-static ATerm concatStrings(EvalState & state, ATermVector & args,
- string separator = "")
+void ExprLet::eval(EvalState & state, Env & env, Value & v)
{
- if (args.empty()) return makeStr("", PathSet());
-
- PathSet context;
- std::ostringstream s;
+ /* Create a new environment that contains the attributes in this
+ `let'. */
+ Env & env2(state.allocEnv(attrs->attrs.size() + attrs->inherited.size()));
+ env2.up = &env;
- /* If the first element is a path, then the result will also be a
- path, we don't copy anything (yet - that's done later, since
- paths are copied when they are used in a derivation), and none
- of the strings are allowed to have contexts. */
- ATerm dummy;
- args.front() = evalExpr(state, args.front());
- bool isPath = matchPath(args.front(), dummy);
-
- for (ATermVector::const_iterator i = args.begin(); i != args.end(); ++i) {
- if (i != args.begin()) s << separator;
- s << coerceToString(state, *i, context, false, !isPath);
- }
+ unsigned int displ = 0;
- if (isPath && !context.empty())
- throw EvalError(format("a string that refers to a store path cannot be appended to a path, in `%1%'")
- % s.str());
-
- return isPath
- ? makePath(toATerm(s.str()))
- : makeStr(s.str(), context);
+ /* The recursive attributes are evaluated in the new
+ environment. */
+ foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs)
+ mkThunk(env2.values[displ++], env2, i->second.first);
+
+ /* The inherited attributes, on the other hand, are evaluated in
+ the original environment. */
+ foreach (list<ExprAttrs::Inherited>::iterator, i, attrs->inherited)
+ mkCopy(env2.values[displ++], *state.lookupVar(&env, i->first));
+
+ state.eval(env2, body, v);
}
-Path coerceToPath(EvalState & state, Expr e, PathSet & context)
+void ExprList::eval(EvalState & state, Env & env, Value & v)
{
- string path = coerceToString(state, e, context, false, false);
- if (path == "" || path[0] != '/')
- throw EvalError(format("string `%1%' doesn't represent an absolute path") % path);
- return path;
+ state.mkList(v, elems.size());
+ Value * vs = state.allocValues(v.list.length);
+ for (unsigned int n = 0; n < v.list.length; ++n) {
+ v.list.elems[n] = &vs[n];
+ mkThunk(vs[n], env, elems[n]);
+ }
}
-Expr autoCallFunction(Expr e, const ATermMap & args)
+void ExprVar::eval(EvalState & state, Env & env, Value & v)
{
- Pattern pat;
- ATerm body, pos;
- ATermList formals;
- ATermBool ellipsis;
-
- /* !!! this should be more general */
- if (matchFunction(e, pat, body, pos) && matchAttrsPat(pat, formals, ellipsis)) {
- ATermMap actualArgs(ATgetLength(formals));
-
- for (ATermIterator i(formals); i; ++i) {
- Expr name, def, value; ATerm def2;
- if (!matchFormal(*i, name, def2)) abort();
- if ((value = args.get(name)))
- actualArgs.set(name, makeAttrRHS(value, makeNoPos()));
- else if (!matchDefaultValue(def2, def))
- throw TypeError(format("cannot auto-call a function that has an argument without a default value (`%1%')")
- % aterm2String(name));
- }
-
- e = makeCall(e, makeAttrs(actualArgs));
+ Value * v2 = state.lookupVar(&env, info);
+ state.forceValue(*v2);
+ v = *v2;
+}
+
+
+void ExprSelect::eval(EvalState & state, Env & env, Value & v)
+{
+ Value v2;
+ state.evalAttrs(env, e, v2);
+ Bindings::iterator i = v2.attrs->find(name);
+ if (i == v2.attrs->end())
+ throwEvalError("attribute `%1%' missing", name);
+ try {
+ state.forceValue(i->second.value);
+ } catch (Error & e) {
+ addErrorPrefix(e, "while evaluating the attribute `%1%' at %2%:\n",
+ name, *i->second.pos);
+ throw;
}
-
- return e;
+ v = i->second.value;
}
-/* Evaluation of various language constructs. These have been taken
- out of evalExpr2 to reduce stack space usage. (GCC is really dumb
- about stack space: it just adds up all the local variables and
- temporaries of every scope into one huge stack frame. This is
- really bad for deeply recursive functions.) */
+void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
+{
+ Value vAttrs;
+ state.evalAttrs(env, e, vAttrs);
+ mkBool(v, vAttrs.attrs->find(name) != vAttrs.attrs->end());
+}
-LocalNoInline(Expr evalVar(EvalState & state, ATerm name))
+void ExprLambda::eval(EvalState & state, Env & env, Value & v)
{
- ATerm primOp = state.primOps.get(name);
- if (!primOp)
- throw EvalError(format("impossible: undefined variable `%1%'") % aterm2String(name));
- int arity;
- ATermBlob fun;
- if (!matchPrimOpDef(primOp, arity, fun)) abort();
- if (arity == 0)
- /* !!! backtrace for primop call */
- return ((PrimOp) ATgetBlobData(fun)) (state, ATermVector());
- else
- return makePrimOp(arity, fun, ATempty);
+ v.type = tLambda;
+ v.lambda.env = &env;
+ v.lambda.fun = this;
}
-LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg))
+void ExprApp::eval(EvalState & state, Env & env, Value & v)
{
- Pattern pat;
- ATerm pos;
- Expr body;
-
- /* Evaluate the left-hand side. */
- fun = evalExpr(state, fun);
-
- /* Is it a primop or a function? */
- int arity;
- ATermBlob funBlob;
- ATermList args;
- if (matchPrimOp(fun, arity, funBlob, args)) {
- args = ATinsert(args, arg);
- if (ATgetLength(args) == arity) {
- /* Put the arguments in a vector in reverse (i.e.,
- actual) order. */
- ATermVector args2(arity);
- for (ATermIterator i(args); i; ++i)
- args2[--arity] = *i;
- /* !!! backtrace for primop call */
- return ((PrimOp) ATgetBlobData(funBlob))
- (state, args2);
- } else
- /* Need more arguments, so propagate the primop. */
- return makePrimOp(arity, funBlob, args);
- }
+ Value vFun;
+ state.eval(env, e1, vFun);
+ Value vArg;
+ mkThunk(vArg, env, e2); // !!! should this be on the heap?
+ state.callFunction(vFun, vArg, v);
+}
- else if (matchFunction(fun, pat, body, pos)) {
- try {
- return evalExpr(state, substArgs(state, body, pat, arg));
- } catch (Error & e) {
- addErrorPrefix(e, "while evaluating the function at %1%:\n",
- showPos(pos));
- throw;
+
+void EvalState::callFunction(Value & fun, Value & arg, Value & v)
+{
+ if (fun.type == tPrimOp || fun.type == tPrimOpApp) {
+ unsigned int argsLeft =
+ fun.type == tPrimOp ? fun.primOp.arity : fun.primOpApp.argsLeft;
+ if (argsLeft == 1) {
+ /* We have all the arguments, so call the primop. First
+ find the primop. */
+ Value * primOp = &fun;
+ while (primOp->type == tPrimOpApp) primOp = primOp->primOpApp.left;
+ assert(primOp->type == tPrimOp);
+ unsigned int arity = primOp->primOp.arity;
+
+ /* Put all the arguments in an array. */
+ Value * vArgs[arity];
+ unsigned int n = arity - 1;
+ vArgs[n--] = &arg;
+ for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->primOpApp.left)
+ vArgs[n--] = arg->primOpApp.right;
+
+ /* And call the primop. */
+ try {
+ primOp->primOp.fun(*this, vArgs, v);
+ } catch (Error & e) {
+ addErrorPrefix(e, "while evaluating the builtin function `%1%':\n", primOp->primOp.name);
+ throw;
+ }
+ } else {
+ Value * v2 = allocValues(2);
+ v2[0] = fun;
+ v2[1] = arg;
+ v.type = tPrimOpApp;
+ v.primOpApp.left = &v2[0];
+ v.primOpApp.right = &v2[1];
+ v.primOpApp.argsLeft = argsLeft - 1;
}
+ return;
}
+
+ if (fun.type != tLambda)
+ throwTypeError("attempt to call something which is neither a function nor a primop (built-in operation) but %1%",
+ showType(fun));
+
+ unsigned int size =
+ (fun.lambda.fun->arg.empty() ? 0 : 1) +
+ (fun.lambda.fun->matchAttrs ? fun.lambda.fun->formals->formals.size() : 0);
+ Env & env2(allocEnv(size));
+ env2.up = fun.lambda.env;
+
+ unsigned int displ = 0;
+
+ if (!fun.lambda.fun->matchAttrs)
+ env2.values[displ++] = arg;
+
+ else {
+ forceAttrs(arg);
- else throwTypeError(
- "attempt to call something which is neither a function nor a primop (built-in operation) but %1%",
- showType(fun));
-}
+ if (!fun.lambda.fun->arg.empty())
+ env2.values[displ++] = arg;
+ /* For each formal argument, get the actual argument. If
+ there is no matching actual argument but the formal
+ argument has a default, use the default. */
+ unsigned int attrsUsed = 0;
+ foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
+ Bindings::iterator j = arg.attrs->find(i->name);
+ if (j == arg.attrs->end()) {
+ if (!i->def) throwTypeError("function at %1% called without required argument `%2%'",
+ fun.lambda.fun->pos, i->name);
+ mkThunk(env2.values[displ++], env2, i->def);
+ } else {
+ attrsUsed++;
+ mkCopy(env2.values[displ++], j->second.value);
+ }
+ }
+
+ /* Check that each actual argument is listed as a formal
+ argument (unless the attribute match specifies a `...').
+ TODO: show the names of the expected/unexpected
+ arguments. */
+ if (!fun.lambda.fun->formals->ellipsis && attrsUsed != arg.attrs->size())
+ throwTypeError("function at %1% called with unexpected argument", fun.lambda.fun->pos);
+ }
-LocalNoInline(Expr evalSelect(EvalState & state, Expr e, ATerm name))
-{
- ATerm pos;
- string s = aterm2String(name);
- Expr a = queryAttr(evalExpr(state, e), s, pos);
- if (!a) throwEvalError("attribute `%1%' missing", s);
try {
- return evalExpr(state, a);
+ eval(env2, fun.lambda.fun->body, v);
} catch (Error & e) {
- addErrorPrefix(e, "while evaluating the attribute `%1%' at %2%:\n",
- s, showPos(pos));
+ addErrorPrefix(e, "while evaluating the function at %1%:\n", fun.lambda.fun->pos);
throw;
}
}
-LocalNoInline(Expr evalAssert(EvalState & state, Expr cond, Expr body, ATerm pos))
+void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res)
{
- if (!evalBool(state, cond))
- throw AssertionError(format("assertion failed at %1%") % showPos(pos));
- return evalExpr(state, body);
-}
+ forceValue(fun);
+ if (fun.type != tLambda || !fun.lambda.fun->matchAttrs) {
+ res = fun;
+ return;
+ }
-LocalNoInline(Expr evalWith(EvalState & state, Expr defs, Expr body, ATerm pos))
-{
- ATermMap attrs;
- try {
- defs = evalExpr(state, defs);
- queryAllAttrs(defs, attrs);
- } catch (Error & e) {
- addErrorPrefix(e, "while evaluating the `with' definitions at %1%:\n",
- showPos(pos));
- throw;
+ Value actualArgs;
+ mkAttrs(actualArgs);
+
+ foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
+ Bindings::const_iterator j = args.find(i->name);
+ if (j != args.end())
+ (*actualArgs.attrs)[i->name] = j->second;
+ else if (!i->def)
+ throwTypeError("cannot auto-call a function that has an argument without a default value (`%1%')", i->name);
}
- try {
- body = substitute(Substitution(0, &attrs), body);
- checkVarDefs(state.primOps, body);
- return evalExpr(state, body);
- } catch (Error & e) {
- addErrorPrefix(e, "while evaluating the `with' body at %1%:\n",
- showPos(pos));
- throw;
- }
+
+ callFunction(fun, actualArgs, res);
}
-LocalNoInline(Expr evalHasAttr(EvalState & state, Expr e, ATerm name))
+void ExprWith::eval(EvalState & state, Env & env, Value & v)
{
- ATermMap attrs;
- queryAllAttrs(evalExpr(state, e), attrs);
- return makeBool(attrs.get(name) != 0);
+ Env & env2(state.allocEnv(1));
+ env2.up = &env;
+ env2.prevWith = prevWith;
+
+ state.evalAttrs(env, attrs, env2.values[0]);
+
+ state.eval(env2, body, v);
}
-LocalNoInline(Expr evalPlusConcat(EvalState & state, Expr e))
+void ExprIf::eval(EvalState & state, Env & env, Value & v)
{
- Expr e1, e2;
- ATermList es;
+ state.eval(env, state.evalBool(env, cond) ? then : else_, v);
+}
+
- ATermVector args;
+void ExprAssert::eval(EvalState & state, Env & env, Value & v)
+{
+ if (!state.evalBool(env, cond))
+ throwAssertionError("assertion failed at %1%", pos);
+ state.eval(env, body, v);
+}
+
- if (matchOpPlus(e, e1, e2)) {
-
- /* !!! Awful compatibility hack for `drv + /path'.
- According to regular concatenation, /path should be
- copied to the store and its store path should be
- appended to the string. However, in Nix <= 0.10, /path
- was concatenated. So handle that case separately, but
- do print out a warning. This code can go in Nix 0.12,
- maybe. */
- e1 = evalExpr(state, e1);
- e2 = evalExpr(state, e2);
-
- ATermList as;
- ATerm p;
- if (matchAttrs(e1, as) && matchPath(e2, p)) {
- static bool haveWarned = false;
- warnOnce(haveWarned, format(
- "concatenation of a derivation and a path is deprecated; "
- "you should write `drv + \"%1%\"' instead of `drv + %1%'")
- % aterm2String(p));
- PathSet context;
- return makeStr(
- coerceToString(state, makeSelect(e1, toATerm("outPath")), context)
- + aterm2String(p), context);
- }
+void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
+{
+ mkBool(v, !state.evalBool(env, e));
+}
- args.push_back(e1);
- args.push_back(e2);
- }
- else if (matchConcatStrings(e, es))
- for (ATermIterator i(es); i; ++i) args.push_back(*i);
-
- try {
- return concatStrings(state, args);
- } catch (Error & e) {
- addErrorPrefix(e, "in a string concatenation:\n");
- throw;
- }
+void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
+{
+ Value v1; state.eval(env, e1, v1);
+ Value v2; state.eval(env, e2, v2);
+ mkBool(v, state.eqValues(v1, v2));
}
-LocalNoInline(Expr evalSubPath(EvalState & state, Expr e1, Expr e2))
+void ExprOpNEq::eval(EvalState & state, Env & env, Value & v)
{
- static bool haveWarned = false;
- warnOnce(haveWarned, "the subpath operator (~) is deprecated, use string concatenation (+) instead");
- ATermVector args;
- args.push_back(e1);
- args.push_back(e2);
- return concatStrings(state, args, "/");
+ Value v1; state.eval(env, e1, v1);
+ Value v2; state.eval(env, e2, v2);
+ mkBool(v, !state.eqValues(v1, v2));
}
-LocalNoInline(Expr evalOpConcat(EvalState & state, Expr e1, Expr e2))
+void ExprOpAnd::eval(EvalState & state, Env & env, Value & v)
{
- try {
- ATermList l1 = evalList(state, e1);
- ATermList l2 = evalList(state, e2);
- return makeList(ATconcat(l1, l2));
- } catch (Error & e) {
- addErrorPrefix(e, "in a list concatenation:\n");
- throw;
- }
+ mkBool(v, state.evalBool(env, e1) && state.evalBool(env, e2));
}
-/* Implementation of the `==' and `!=' operators. */
-LocalNoInline(bool areEqual(EvalState & state, Expr e1, Expr e2))
+void ExprOpOr::eval(EvalState & state, Env & env, Value & v)
{
- e1 = evalExpr(state, e1);
- e2 = evalExpr(state, e2);
-
- /* We cannot test functions/primops for equality, and we currently
- don't support testing equality between attribute sets or lists
- - that would have to be a deep equality test to be sound. */
- AFun sym1 = ATgetAFun(e1);
- AFun sym2 = ATgetAFun(e2);
+ mkBool(v, state.evalBool(env, e1) || state.evalBool(env, e2));
+}
- if (sym1 != sym2) return false;
- /* Functions are incomparable. */
- if (sym1 == symFunction || sym1 == symPrimOp) return false;
+void ExprOpImpl::eval(EvalState & state, Env & env, Value & v)
+{
+ mkBool(v, !state.evalBool(env, e1) || state.evalBool(env, e2));
+}
- if (!state.allowUnsafeEquality && sym1 == symAttrs)
- throw EvalError("comparison of attribute sets is not implemented");
- /* !!! This allows comparisons of infinite data structures to
- succeed, such as `let x = [x]; in x == x'. This is
- undesirable, since equivalent (?) terms such as `let x = [x]; y
- = [y]; in x == y' don't terminate. */
- if (e1 == e2) return true;
+void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
+{
+ Value v2;
+ state.evalAttrs(env, e1, v2);
+
+ state.cloneAttrs(v2, v);
+
+ state.evalAttrs(env, e2, v2);
- if (sym1 == symList) {
- ATermList es1; matchList(e1, es1);
- ATermList es2; matchList(e2, es2);
- if (ATgetLength(es1) != ATgetLength(es2)) return false;
- ATermIterator i(es1), j(es2);
- while (*i) {
- if (!areEqual(state, *i, *j)) return false;
- ++i; ++j;
+ foreach (Bindings::iterator, i, *v2.attrs) {
+ Attr & a = (*v.attrs)[i->first];
+ mkCopy(a.value, i->second.value);
+ a.pos = i->second.pos;
+ }
+}
+
+
+void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
+{
+ Value v1; state.eval(env, e1, v1);
+ state.forceList(v1);
+ Value v2; state.eval(env, e2, v2);
+ state.forceList(v2);
+ state.mkList(v, v1.list.length + v2.list.length);
+ for (unsigned int n = 0; n < v1.list.length; ++n)
+ v.list.elems[n] = v1.list.elems[n];
+ for (unsigned int n = 0; n < v2.list.length; ++n)
+ v.list.elems[n + v1.list.length] = v2.list.elems[n];
+}
+
+
+void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
+{
+ PathSet context;
+ std::ostringstream s;
+
+ bool first = true, isPath = false;
+ Value vStr;
+
+ foreach (vector<Expr *>::iterator, i, *es) {
+ state.eval(env, *i, vStr);
+
+ /* If the first element is a path, then the result will also
+ be a path, we don't copy anything (yet - that's done later,
+ since paths are copied when they are used in a derivation),
+ and none of the strings are allowed to have contexts. */
+ if (first) {
+ isPath = vStr.type == tPath;
+ first = false;
}
- return true;
+
+ s << state.coerceToString(vStr, context, false, !isPath);
}
-
- return false;
+
+ if (isPath && !context.empty())
+ throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str());
+
+ if (isPath)
+ mkPath(v, s.str().c_str());
+ else
+ mkString(v, s.str(), context);
}
-static char * deepestStack = (char *) -1; /* for measuring stack usage */
+void EvalState::forceValue(Value & v)
+{
+ if (v.type == tThunk) {
+ ValueType saved = v.type;
+ try {
+ v.type = tBlackhole;
+ eval(*v.thunk.env, v.thunk.expr, v);
+ } catch (Error & e) {
+ v.type = saved;
+ throw;
+ }
+ }
+ else if (v.type == tCopy) {
+ forceValue(*v.val);
+ v = *v.val;
+ }
+ else if (v.type == tApp)
+ callFunction(*v.app.left, *v.app.right, v);
+ else if (v.type == tBlackhole)
+ throwEvalError("infinite recursion encountered");
+}
-Expr evalExpr2(EvalState & state, Expr e)
+void EvalState::strictForceValue(Value & v)
{
- /* When changing this function, make sure that you don't cause a
- (large) increase in stack consumption! */
-
- char x;
- if (&x < deepestStack) deepestStack = &x;
+ forceValue(v);
- Expr e1, e2, e3;
- ATerm name, pos;
- AFun sym = ATgetAFun(e);
-
- /* Normal forms. */
- if (sym == symStr ||
- sym == symPath ||
- sym == symNull ||
- sym == symInt ||
- sym == symBool ||
- sym == symFunction ||
- sym == symAttrs ||
- sym == symList ||
- sym == symPrimOp)
- return e;
+ if (v.type == tAttrs) {
+ foreach (Bindings::iterator, i, *v.attrs)
+ strictForceValue(i->second.value);
+ }
- /* The `Closed' constructor is just a way to prevent substitutions
- into expressions not containing free variables. */
- if (matchClosed(e, e1))
- return evalExpr(state, e1);
-
- /* Any encountered variables must be primops (since undefined
- variables are detected after parsing). */
- if (matchVar(e, name)) return evalVar(state, name);
-
- /* Function application. */
- if (matchCall(e, e1, e2)) return evalCall(state, e1, e2);
-
- /* Attribute selection. */
- if (matchSelect(e, e1, name)) return evalSelect(state, e1, name);
-
- /* Mutually recursive sets. */
- ATermList rbnds, nrbnds;
- if (matchRec(e, rbnds, nrbnds))
- return expandRec(state, e, rbnds, nrbnds);
-
- /* Conditionals. */
- if (matchIf(e, e1, e2, e3))
- return evalExpr(state, evalBool(state, e1) ? e2 : e3);
-
- /* Assertions. */
- if (matchAssert(e, e1, e2, pos)) return evalAssert(state, e1, e2, pos);
-
- /* Withs. */
- if (matchWith(e, e1, e2, pos)) return evalWith(state, e1, e2, pos);
-
- /* Generic equality/inequality. Note that the behaviour on
- composite data (lists, attribute sets) and functions is
- undefined, since the subterms of those terms are not evaluated.
- However, we don't want to make (==) strict, because that would
- make operations like `big_derivation == null' very slow (unless
- we were to evaluate them side-by-side). */
- if (matchOpEq(e, e1, e2)) return makeBool(areEqual(state, e1, e2));
-
- if (matchOpNEq(e, e1, e2)) return makeBool(!areEqual(state, e1, e2));
-
- /* Negation. */
- if (matchOpNot(e, e1))
- return makeBool(!evalBool(state, e1));
+ else if (v.type == tList) {
+ for (unsigned int n = 0; n < v.list.length; ++n)
+ strictForceValue(*v.list.elems[n]);
+ }
+}
- /* Implication. */
- if (matchOpImpl(e, e1, e2))
- return makeBool(!evalBool(state, e1) || evalBool(state, e2));
- /* Conjunction (logical AND). */
- if (matchOpAnd(e, e1, e2))
- return makeBool(evalBool(state, e1) && evalBool(state, e2));
+int EvalState::forceInt(Value & v)
+{
+ forceValue(v);
+ if (v.type != tInt)
+ throwTypeError("value is %1% while an integer was expected", showType(v));
+ return v.integer;
+}
- /* Disjunction (logical OR). */
- if (matchOpOr(e, e1, e2))
- return makeBool(evalBool(state, e1) || evalBool(state, e2));
- /* Attribute set update (//). */
- if (matchOpUpdate(e, e1, e2))
- return updateAttrs(evalExpr(state, e1), evalExpr(state, e2));
+bool EvalState::forceBool(Value & v)
+{
+ forceValue(v);
+ if (v.type != tBool)
+ throwTypeError("value is %1% while a Boolean was expected", showType(v));
+ return v.boolean;
+}
+
- /* Attribute existence test (?). */
- if (matchOpHasAttr(e, e1, name)) return evalHasAttr(state, e1, name);
+void EvalState::forceAttrs(Value & v)
+{
+ forceValue(v);
+ if (v.type != tAttrs)
+ throwTypeError("value is %1% while an attribute set was expected", showType(v));
+}
- /* String or path concatenation. */
- if (sym == symOpPlus || sym == symConcatStrings)
- return evalPlusConcat(state, e);
- /* Backwards compatability: subpath operator (~). */
- if (matchSubPath(e, e1, e2)) return evalSubPath(state, e1, e2);
+void EvalState::forceList(Value & v)
+{
+ forceValue(v);
+ if (v.type != tList)
+ throwTypeError("value is %1% while a list was expected", showType(v));
+}
- /* List concatenation. */
- if (matchOpConcat(e, e1, e2)) return evalOpConcat(state, e1, e2);
- /* Barf. */
- abort();
+void EvalState::forceFunction(Value & v)
+{
+ forceValue(v);
+ if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp)
+ throwTypeError("value is %1% while a function was expected", showType(v));
}
-Expr evalExpr(EvalState & state, Expr e)
+string EvalState::forceString(Value & v)
{
- checkInterrupt();
+ forceValue(v);
+ if (v.type != tString)
+ throwTypeError("value is %1% while a string was expected", showType(v));
+ return string(v.string.s);
+}
-#if 0
- startNest(nest, lvlVomit,
- format("evaluating expression: %1%") % e);
-#endif
-
- state.nrEvaluated++;
-
- /* Consult the memo table to quickly get the normal form of
- previously evaluated expressions. */
- Expr nf = state.normalForms.get(e);
- if (nf) {
- if (nf == makeBlackHole())
- throwEvalError("infinite recursion encountered");
- state.nrCached++;
- return nf;
- }
- /* Otherwise, evaluate and memoize. */
- state.normalForms.set(e, makeBlackHole());
- try {
- nf = evalExpr2(state, e);
- } catch (Error & err) {
- state.normalForms.remove(e);
- throw;
- }
- state.normalForms.set(e, nf);
- return nf;
+string EvalState::forceString(Value & v, PathSet & context)
+{
+ string s = forceString(v);
+ if (v.string.context)
+ for (const char * * p = v.string.context; *p; ++p)
+ context.insert(*p);
+ return s;
}
-Expr evalFile(EvalState & state, const Path & path)
+string EvalState::forceStringNoCtx(Value & v)
{
- startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path);
- Expr e = parseExprFromFile(state, path);
- try {
- return evalExpr(state, e);
- } catch (Error & e) {
- e.addPrefix(format("while evaluating the file `%1%':\n")
- % path);
- throw;
- }
+ string s = forceString(v);
+ if (v.string.context)
+ throwEvalError("the string `%1%' is not allowed to refer to a store path (such as `%2%')",
+ v.string.s, v.string.context[0]);
+ return s;
}
-static Expr strictEvalExpr(EvalState & state, Expr e, ATermMap & nfs);
+bool EvalState::isDerivation(Value & v)
+{
+ if (v.type != tAttrs) return false;
+ Bindings::iterator i = v.attrs->find(sType);
+ return i != v.attrs->end() && forceStringNoCtx(i->second.value) == "derivation";
+}
-static Expr strictEvalExpr_(EvalState & state, Expr e, ATermMap & nfs)
+string EvalState::coerceToString(Value & v, PathSet & context,
+ bool coerceMore, bool copyToStore)
{
- e = evalExpr(state, e);
+ forceValue(v);
- ATermList as;
- if (matchAttrs(e, as)) {
- ATermList as2 = ATempty;
- for (ATermIterator i(as); i; ++i) {
- ATerm name; Expr e; ATerm pos;
- if (!matchBind(*i, name, e, pos)) abort(); /* can't happen */
- as2 = ATinsert(as2, makeBind(name, strictEvalExpr(state, e, nfs), pos));
- }
- return makeAttrs(ATreverse(as2));
+ string s;
+
+ if (v.type == tString) {
+ if (v.string.context)
+ for (const char * * p = v.string.context; *p; ++p)
+ context.insert(*p);
+ return v.string.s;
}
-
- ATermList es;
- if (matchList(e, es)) {
- ATermList es2 = ATempty;
- for (ATermIterator i(es); i; ++i)
- es2 = ATinsert(es2, strictEvalExpr(state, *i, nfs));
- return makeList(ATreverse(es2));
+
+ if (v.type == tPath) {
+ Path path(canonPath(v.path));
+
+ if (!copyToStore) return path;
+
+ if (nix::isDerivation(path))
+ throwEvalError("file names are not allowed to end in `%1%'", drvExtension);
+
+ Path dstPath;
+ if (srcToStore[path] != "")
+ dstPath = srcToStore[path];
+ else {
+ dstPath = readOnlyMode
+ ? computeStorePathForPath(path).first
+ : store->addToStore(path);
+ srcToStore[path] = dstPath;
+ printMsg(lvlChatty, format("copied source `%1%' -> `%2%'")
+ % path % dstPath);
+ }
+
+ context.insert(dstPath);
+ return dstPath;
}
-
- return e;
-}
+ if (v.type == tAttrs) {
+ Bindings::iterator i = v.attrs->find(sOutPath);
+ if (i == v.attrs->end())
+ throwTypeError("cannot coerce an attribute set (except a derivation) to a string");
+ return coerceToString(i->second.value, context, coerceMore, copyToStore);
+ }
-static Expr strictEvalExpr(EvalState & state, Expr e, ATermMap & nfs)
-{
- Expr nf = nfs.get(e);
- if (nf) return nf;
+ if (coerceMore) {
- nf = strictEvalExpr_(state, e, nfs);
+ /* Note that `false' is represented as an empty string for
+ shell scripting convenience, just like `null'. */
+ if (v.type == tBool && v.boolean) return "1";
+ if (v.type == tBool && !v.boolean) return "";
+ if (v.type == tInt) return int2String(v.integer);
+ if (v.type == tNull) return "";
- nfs.set(e, nf);
+ if (v.type == tList) {
+ string result;
+ for (unsigned int n = 0; n < v.list.length; ++n) {
+ result += coerceToString(*v.list.elems[n],
+ context, coerceMore, copyToStore);
+ if (n < v.list.length - 1
+ /* !!! not quite correct */
+ && (v.list.elems[n]->type != tList || v.list.elems[n]->list.length != 0))
+ result += " ";
+ }
+ return result;
+ }
+ }
- return nf;
+ throwTypeError("cannot coerce %1% to a string", showType(v));
}
-Expr strictEvalExpr(EvalState & state, Expr e)
+Path EvalState::coerceToPath(Value & v, PathSet & context)
{
- ATermMap strictNormalForms;
- return strictEvalExpr(state, e, strictNormalForms);
+ string path = coerceToString(v, context, false, false);
+ if (path == "" || path[0] != '/')
+ throwEvalError("string `%1%' doesn't represent an absolute path", path);
+ return path;
}
-/* Yes, this is a really bad idea... */
-extern "C" {
- unsigned long AT_calcAllocatedSize();
+bool EvalState::eqValues(Value & v1, Value & v2)
+{
+ forceValue(v1);
+ forceValue(v2);
+
+ /* !!! Hack to support some old broken code that relies on pointer
+ equality tests between attribute sets. (Specifically,
+ builderDefs calls uniqList on a list of attribute sets.) Will
+ remove this eventually. */
+ if (&v1 == &v2) return true;
+
+ if (v1.type != v2.type) return false;
+
+ switch (v1.type) {
+
+ case tInt:
+ return v1.integer == v2.integer;
+
+ case tBool:
+ return v1.boolean == v2.boolean;
+
+ case tString: {
+ /* Compare both the string and its context. */
+ if (strcmp(v1.string.s, v2.string.s) != 0) return false;
+ const char * * p = v1.string.context, * * q = v2.string.context;
+ if (!p && !q) return true;
+ if (!p || !q) return false;
+ for ( ; *p && *q; ++p, ++q)
+ if (strcmp(*p, *q) != 0) return false;
+ if (*p || *q) return false;
+ return true;
+ }
+
+ case tPath:
+ return strcmp(v1.path, v2.path) == 0;
+
+ case tNull:
+ return true;
+
+ case tList:
+ if (v1.list.length != v2.list.length) return false;
+ for (unsigned int n = 0; n < v1.list.length; ++n)
+ if (!eqValues(*v1.list.elems[n], *v2.list.elems[n])) return false;
+ return true;
+
+ case tAttrs: {
+ if (v1.attrs->size() != v2.attrs->size()) return false;
+ Bindings::iterator i, j;
+ for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j)
+ if (i->first != j->first || !eqValues(i->second.value, j->second.value))
+ return false;
+ return true;
+ }
+
+ /* Functions are incomparable. */
+ case tLambda:
+ case tPrimOp:
+ case tPrimOpApp:
+ return false;
+
+ default:
+ throwEvalError("cannot compare %1% with %2%", showType(v1), showType(v2));
+ }
}
-void printEvalStats(EvalState & state)
+
+void EvalState::printStats()
{
char x;
bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
- printMsg(showStats ? lvlInfo : lvlDebug,
- format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency, used %4% ATerm bytes, used %5% bytes of stack space")
- % state.nrEvaluated % state.nrCached
- % ((float) state.nrCached / (float) state.nrEvaluated * 100)
- % AT_calcAllocatedSize()
- % (&x - deepestStack));
- if (showStats)
- printATermMapStats();
+ Verbosity v = showStats ? lvlInfo : lvlDebug;
+ printMsg(v, "evaluation statistics:");
+ printMsg(v, format(" expressions evaluated: %1%") % nrEvaluated);
+ printMsg(v, format(" stack space used: %1% bytes") % (&x - deepestStack));
+ printMsg(v, format(" max eval() nesting depth: %1%") % maxRecursionDepth);
+ printMsg(v, format(" stack space per eval() level: %1% bytes")
+ % ((&x - deepestStack) / (float) maxRecursionDepth));
+ printMsg(v, format(" environments allocated: %1% (%2% bytes)")
+ % nrEnvs % (nrEnvs * sizeof(Env)));
+ printMsg(v, format(" values allocated in environments: %1% (%2% bytes)")
+ % nrValuesInEnvs % (nrValuesInEnvs * sizeof(Value)));
+ printMsg(v, format(" list elements: %1% (%2% bytes)")
+ % nrListElems % (nrListElems * sizeof(Value *)));
+ printMsg(v, format(" misc. values allocated: %1% (%2% bytes)")
+ % nrValues % (nrValues * sizeof(Value)));
+ printMsg(v, format(" symbols in symbol table: %1%") % symbols.size());
}
-
+
}
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 00d0e3564..d1b4fdbea 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -4,6 +4,7 @@
#include <map>
#include "nixexpr.hh"
+#include "symbol-table.hh"
typedef union _ATermList * ATermList;
@@ -12,9 +13,157 @@ namespace nix {
class Hash;
-
+class EvalState;
+struct Env;
+struct Value;
+struct Attr;
+
+typedef std::map<Symbol, Attr> Bindings;
+
+
+typedef enum {
+ tInt = 1,
+ tBool,
+ tString,
+ tPath,
+ tNull,
+ tAttrs,
+ tList,
+ tThunk,
+ tApp,
+ tLambda,
+ tCopy,
+ tBlackhole,
+ tPrimOp,
+ tPrimOpApp,
+} ValueType;
+
+
+typedef void (* PrimOp) (EvalState & state, Value * * args, Value & v);
+
+
+struct Value
+{
+ ValueType type;
+ union
+ {
+ int integer;
+ bool boolean;
+
+ /* Strings in the evaluator carry a so-called `context' (the
+ ATermList) which is a list of strings representing store
+ paths. This is to allow users to write things like
+
+ "--with-freetype2-library=" + freetype + "/lib"
+
+ where `freetype' is a derivation (or a source to be copied
+ to the store). If we just concatenated the strings without
+ keeping track of the referenced store paths, then if the
+ string is used as a derivation attribute, the derivation
+ will not have the correct dependencies in its inputDrvs and
+ inputSrcs.
+
+ The semantics of the context is as follows: when a string
+ with context C is used as a derivation attribute, then the
+ derivations in C will be added to the inputDrvs of the
+ derivation, and the other store paths in C will be added to
+ the inputSrcs of the derivations.
+
+ For canonicity, the store paths should be in sorted order. */
+ struct {
+ const char * s;
+ const char * * context; // must be in sorted order
+ } string;
+
+ const char * path;
+ Bindings * attrs;
+ struct {
+ unsigned int length;
+ Value * * elems;
+ } list;
+ struct {
+ Env * env;
+ Expr * expr;
+ } thunk;
+ struct {
+ Value * left, * right;
+ } app;
+ struct {
+ Env * env;
+ ExprLambda * fun;
+ } lambda;
+ Value * val;
+ struct {
+ PrimOp fun;
+ char * name;
+ unsigned int arity;
+ } primOp;
+ struct {
+ Value * left, * right;
+ unsigned int argsLeft;
+ } primOpApp;
+ };
+};
+
+
+struct Env
+{
+ Env * up;
+ unsigned int prevWith; // nr of levels up to next `with' environment
+ Value values[0];
+};
+
+
+struct Attr
+{
+ Value value;
+ Pos * pos;
+ Attr() : pos(&noPos) { };
+};
+
+
+static inline void mkInt(Value & v, int n)
+{
+ v.type = tInt;
+ v.integer = n;
+}
+
+
+static inline void mkBool(Value & v, bool b)
+{
+ v.type = tBool;
+ v.boolean = b;
+}
+
+
+static inline void mkThunk(Value & v, Env & env, Expr * expr)
+{
+ v.type = tThunk;
+ v.thunk.env = &env;
+ v.thunk.expr = expr;
+}
+
+
+static inline void mkCopy(Value & v, Value & src)
+{
+ v.type = tCopy;
+ v.val = &src;
+}
+
+
+static inline void mkApp(Value & v, Value & left, Value & right)
+{
+ v.type = tApp;
+ v.app.left = &left;
+ v.app.right = &right;
+}
+
+
+void mkString(Value & v, const char * s);
+void mkString(Value & v, const string & s, const PathSet & context = PathSet());
+void mkPath(Value & v, const char * s);
+
-typedef std::map<Path, PathSet> DrvRoots;
typedef std::map<Path, Hash> DrvHashes;
/* Cache for calls to addToStore(); maps source paths to the store
@@ -23,75 +172,153 @@ typedef std::map<Path, Path> SrcToStore;
struct EvalState;
-/* Note: using a ATermVector is safe here, since when we call a primop
- we also have an ATermList on the stack. */
-typedef Expr (* PrimOp) (EvalState &, const ATermVector & args);
+
+std::ostream & operator << (std::ostream & str, Value & v);
-struct EvalState
+class EvalState
{
- ATermMap normalForms;
- ATermMap primOps;
- DrvRoots drvRoots;
+public:
DrvHashes drvHashes; /* normalised derivation hashes */
- SrcToStore srcToStore;
- unsigned int nrEvaluated;
- unsigned int nrCached;
+ SymbolTable symbols;
+
+ const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sSystem;
+
+private:
+ SrcToStore srcToStore;
bool allowUnsafeEquality;
+ std::map<Path, Expr *> parseTrees;
+
+public:
+
EvalState();
+ ~EvalState();
+
+ /* Evaluate an expression read from the given file to normal
+ form. */
+ void evalFile(const Path & path, Value & v);
+
+ /* Evaluate an expression to normal form, storing the result in
+ value `v'. */
+ void eval(Expr * e, Value & v);
+ void eval(Env & env, Expr * e, Value & v);
+
+ /* Evaluation the expression, then verify that it has the expected
+ type. */
+ bool evalBool(Env & env, Expr * e);
+ void evalAttrs(Env & env, Expr * e, Value & v);
+
+ /* If `v' is a thunk, enter it and overwrite `v' with the result
+ of the evaluation of the thunk. If `v' is a delayed function
+ application, call the function and overwrite `v' with the
+ result. Otherwise, this is a no-op. */
+ void forceValue(Value & v);
+
+ /* Force a value, then recursively force list elements and
+ attributes. */
+ void strictForceValue(Value & v);
+
+ /* Force `v', and then verify that it has the expected type. */
+ int forceInt(Value & v);
+ bool forceBool(Value & v);
+ void forceAttrs(Value & v);
+ void forceList(Value & v);
+ void forceFunction(Value & v); // either lambda or primop
+ string forceString(Value & v);
+ string forceString(Value & v, PathSet & context);
+ string forceStringNoCtx(Value & v);
+
+ /* Return true iff the value `v' denotes a derivation (i.e. a
+ set with attribute `type = "derivation"'). */
+ bool isDerivation(Value & v);
+
+ /* String coercion. Converts strings, paths and derivations to a
+ string. If `coerceMore' is set, also converts nulls, integers,
+ booleans and lists to a string. If `copyToStore' is set,
+ referenced paths are copied to the Nix store as a side effect.q */
+ string coerceToString(Value & v, PathSet & context,
+ bool coerceMore = false, bool copyToStore = true);
+
+ /* Path coercion. Converts strings, paths and derivations to a
+ path. The result is guaranteed to be a canonicalised, absolute
+ path. Nothing is copied to the store. */
+ Path coerceToPath(Value & v, PathSet & context);
+
+private:
+
+ /* The base environment, containing the builtin functions and
+ values. */
+ Env & baseEnv;
+
+ unsigned int baseEnvDispl;
+
+public:
+
+ /* The same, but used during parsing to resolve variables. */
+ StaticEnv staticBaseEnv; // !!! should be private
+
+private:
+
+ void createBaseEnv();
+
+ void addConstant(const string & name, Value & v);
- void addPrimOps();
void addPrimOp(const string & name,
unsigned int arity, PrimOp primOp);
-};
+ Value * lookupVar(Env * env, const VarRef & var);
+
+ friend class ExprVar;
+ friend class ExprAttrs;
+ friend class ExprLet;
-/* Evaluate an expression to normal form. */
-Expr evalExpr(EvalState & state, Expr e);
+public:
+
+ /* Do a deep equality test between two values. That is, list
+ elements and attributes are compared recursively. */
+ bool eqValues(Value & v1, Value & v2);
-/* Evaluate an expression read from the given file to normal form. */
-Expr evalFile(EvalState & state, const Path & path);
+ void callFunction(Value & fun, Value & arg, Value & v);
-/* Evaluate an expression, and recursively evaluate list elements and
- attributes. If `canonicalise' is true, we remove things like
- position information and make sure that attribute sets are in
- sorded order. */
-Expr strictEvalExpr(EvalState & state, Expr e);
+ /* Automatically call a function for which each argument has a
+ default value or has a binding in the `args' map. */
+ void autoCallFunction(const Bindings & args, Value & fun, Value & res);
+
+ /* Allocation primitives. */
+ Value * allocValues(unsigned int count);
+ Env & allocEnv(unsigned int size);
-/* Specific results. */
-string evalString(EvalState & state, Expr e, PathSet & context);
-string evalStringNoCtx(EvalState & state, Expr e);
-int evalInt(EvalState & state, Expr e);
-bool evalBool(EvalState & state, Expr e);
-ATermList evalList(EvalState & state, Expr e);
+ void mkList(Value & v, unsigned int length);
+ void mkAttrs(Value & v);
+ void mkThunk_(Value & v, Expr * expr);
+
+ void cloneAttrs(Value & src, Value & dst);
-/* Flatten nested lists into a single list (or expand a singleton into
- a list). */
-ATermList flattenList(EvalState & state, Expr e);
+ /* Print statistics. */
+ void printStats();
-/* String coercion. Converts strings, paths and derivations to a
- string. If `coerceMore' is set, also converts nulls, integers,
- booleans and lists to a string. */
-string coerceToString(EvalState & state, Expr e, PathSet & context,
- bool coerceMore = false, bool copyToStore = true);
+private:
+
+ unsigned long nrEnvs;
+ unsigned long nrValuesInEnvs;
+ unsigned long nrValues;
+ unsigned long nrListElems;
+ unsigned long nrEvaluated;
+ unsigned int recursionDepth;
+ unsigned int maxRecursionDepth;
+ char * deepestStack; /* for measuring stack usage */
+
+ friend class RecursionCounter;
+};
-/* Path coercion. Converts strings, paths and derivations to a path.
- The result is guaranteed to be an canonicalised, absolute path.
- Nothing is copied to the store. */
-Path coerceToPath(EvalState & state, Expr e, PathSet & context);
-/* Automatically call a function for which each argument has a default
- value or has a binding in the `args' map. Note: result is a call,
- not a normal form; it should be evaluated by calling evalExpr(). */
-Expr autoCallFunction(Expr e, const ATermMap & args);
+/* Return a string representing the type of the value `v'. */
+string showType(const Value & v);
-/* Print statistics. */
-void printEvalStats(EvalState & state);
-
}
diff --git a/src/libexpr/expr-to-xml.cc b/src/libexpr/expr-to-xml.cc
deleted file mode 100644
index 1e59eebfc..000000000
--- a/src/libexpr/expr-to-xml.cc
+++ /dev/null
@@ -1,186 +0,0 @@
-#include "expr-to-xml.hh"
-#include "xml-writer.hh"
-#include "nixexpr-ast.hh"
-#include "aterm.hh"
-#include "util.hh"
-
-#include <cstdlib>
-
-
-namespace nix {
-
-
-static XMLAttrs singletonAttrs(const string & name, const string & value)
-{
- XMLAttrs attrs;
- attrs[name] = value;
- return attrs;
-}
-
-
-/* set<Expr> is safe because all the expressions are also reachable
- from the stack, therefore can't be garbage-collected. */
-typedef set<Expr> ExprSet;
-
-
-static void printTermAsXML(Expr e, XMLWriter & doc, PathSet & context,
- ExprSet & drvsSeen, bool location);
-
-
-static void showAttrs(const ATermMap & attrs, XMLWriter & doc,
- PathSet & context, ExprSet & drvsSeen, bool location)
-{
- StringSet names;
- for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i)
- names.insert(aterm2String(i->key));
- for (StringSet::iterator i = names.begin(); i != names.end(); ++i) {
- ATerm attrRHS = attrs.get(toATerm(*i));
- ATerm attr;
- Pos pos;
- XMLAttrs xmlAttrs;
-
- xmlAttrs["name"] = *i;
- if(matchAttrRHS(attrRHS, attr, pos)) {
- ATerm path;
- int line, column;
- if (location && matchPos(pos, path, line, column)) {
- xmlAttrs["path"] = aterm2String(path);
- xmlAttrs["line"] = (format("%1%") % line).str();
- xmlAttrs["column"] = (format("%1%") % column).str();
- }
- } else
- abort(); // Should not happen.
-
- XMLOpenElement _(doc, "attr", xmlAttrs);
- printTermAsXML(attr, doc, context, drvsSeen, location);
- }
-}
-
-
-static void printPatternAsXML(Pattern pat, XMLWriter & doc)
-{
- ATerm name;
- ATermList formals;
- Pattern pat1, pat2;
- ATermBool ellipsis;
- if (matchVarPat(pat, name))
- doc.writeEmptyElement("varpat", singletonAttrs("name", aterm2String(name)));
- else if (matchAttrsPat(pat, formals, ellipsis)) {
- XMLOpenElement _(doc, "attrspat");
- for (ATermIterator i(formals); i; ++i) {
- Expr name; ATerm dummy;
- if (!matchFormal(*i, name, dummy)) abort();
- doc.writeEmptyElement("attr", singletonAttrs("name", aterm2String(name)));
- }
- if (ellipsis == eTrue) doc.writeEmptyElement("ellipsis");
- }
- else if (matchAtPat(pat, pat1, pat2)) {
- XMLOpenElement _(doc, "at");
- printPatternAsXML(pat1, doc);
- printPatternAsXML(pat2, doc);
- }
-}
-
-
-static void printTermAsXML(Expr e, XMLWriter & doc, PathSet & context,
- ExprSet & drvsSeen, bool location)
-{
- XMLAttrs attrs;
- string s;
- ATerm s2;
- int i;
- ATermList as, es;
- ATerm pat, body, pos;
-
- checkInterrupt();
-
- if (matchStr(e, s, context)) /* !!! show the context? */
- doc.writeEmptyElement("string", singletonAttrs("value", s));
-
- else if (matchPath(e, s2))
- doc.writeEmptyElement("path", singletonAttrs("value", aterm2String(s2)));
-
- else if (matchNull(e))
- doc.writeEmptyElement("null");
-
- else if (matchInt(e, i))
- doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % i).str()));
-
- else if (e == eTrue)
- doc.writeEmptyElement("bool", singletonAttrs("value", "true"));
-
- else if (e == eFalse)
- doc.writeEmptyElement("bool", singletonAttrs("value", "false"));
-
- else if (matchAttrs(e, as)) {
- ATermMap attrs;
- queryAllAttrs(e, attrs, true);
-
- Expr aRHS = attrs.get(toATerm("type"));
- Expr a = NULL;
- if (aRHS)
- matchAttrRHS(aRHS, a, pos);
- if (a && matchStr(a, s, context) && s == "derivation") {
-
- XMLAttrs xmlAttrs;
- Path outPath, drvPath;
-
- aRHS = attrs.get(toATerm("drvPath"));
- matchAttrRHS(aRHS, a, pos);
- if (matchStr(a, drvPath, context))
- xmlAttrs["drvPath"] = drvPath;
-
- aRHS = attrs.get(toATerm("outPath"));
- matchAttrRHS(aRHS, a, pos);
- if (matchStr(a, outPath, context))
- xmlAttrs["outPath"] = outPath;
-
- XMLOpenElement _(doc, "derivation", xmlAttrs);
-
- if (drvsSeen.find(e) == drvsSeen.end()) {
- drvsSeen.insert(e);
- showAttrs(attrs, doc, context, drvsSeen, location);
- } else
- doc.writeEmptyElement("repeated");
- }
-
- else {
- XMLOpenElement _(doc, "attrs");
- showAttrs(attrs, doc, context, drvsSeen, location);
- }
- }
-
- else if (matchList(e, es)) {
- XMLOpenElement _(doc, "list");
- for (ATermIterator i(es); i; ++i)
- printTermAsXML(*i, doc, context, drvsSeen, location);
- }
-
- else if (matchFunction(e, pat, body, pos)) {
- ATerm path;
- int line, column;
- XMLAttrs xmlAttrs;
- if (location && matchPos(pos, path, line, column)) {
- xmlAttrs["path"] = aterm2String(path);
- xmlAttrs["line"] = (format("%1%") % line).str();
- xmlAttrs["column"] = (format("%1%") % column).str();
- }
- XMLOpenElement _(doc, "function", xmlAttrs);
- printPatternAsXML(pat, doc);
- }
-
- else
- doc.writeEmptyElement("unevaluated");
-}
-
-
-void printTermAsXML(Expr e, std::ostream & out, PathSet & context, bool location)
-{
- XMLWriter doc(true, out);
- XMLOpenElement root(doc, "expr");
- ExprSet drvsSeen;
- printTermAsXML(e, doc, context, drvsSeen, location);
-}
-
-
-}
diff --git a/src/libexpr/expr-to-xml.hh b/src/libexpr/expr-to-xml.hh
deleted file mode 100644
index cf64f26e3..000000000
--- a/src/libexpr/expr-to-xml.hh
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef __EXPR_TO_XML_H
-#define __EXPR_TO_XML_H
-
-#include <string>
-#include <map>
-
-#include "nixexpr.hh"
-
-namespace nix {
-
-void printTermAsXML(Expr e, std::ostream & out, PathSet & context, bool location = false);
-
-}
-
-#endif /* !__EXPR_TO_XML_H */
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index cd5a85e5b..82a92416b 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -1,7 +1,5 @@
#include "get-drvs.hh"
-#include "nixexpr-ast.hh"
#include "util.hh"
-#include "aterm.hh"
namespace nix {
@@ -9,17 +7,10 @@ namespace nix {
string DrvInfo::queryDrvPath(EvalState & state) const
{
- if (drvPath == "") {
- Expr a = attrs->get(toATerm("drvPath"));
-
- /* Backwards compatibility hack with user environments made by
- Nix <= 0.10: these contain illegal Path("") expressions. */
- ATerm t;
- if (a && matchPath(evalExpr(state, a), t))
- return aterm2String(t);
-
+ if (drvPath == "" && attrs) {
+ Bindings::iterator i = attrs->find(state.sDrvPath);
PathSet context;
- (string &) drvPath = a ? coerceToPath(state, a, context) : "";
+ (string &) drvPath = i != attrs->end() ? state.coerceToPath(i->second.value, context) : "";
}
return drvPath;
}
@@ -27,11 +18,10 @@ string DrvInfo::queryDrvPath(EvalState & state) const
string DrvInfo::queryOutPath(EvalState & state) const
{
- if (outPath == "") {
- Expr a = attrs->get(toATerm("outPath"));
- if (!a) throw TypeError("output path missing");
+ if (outPath == "" && attrs) {
+ Bindings::iterator i = attrs->find(state.sOutPath);
PathSet context;
- (string &) outPath = coerceToPath(state, a, context);
+ (string &) outPath = i != attrs->end() ? state.coerceToPath(i->second.value, context) : "";
}
return outPath;
}
@@ -39,35 +29,30 @@ string DrvInfo::queryOutPath(EvalState & state) const
MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
{
- MetaInfo meta;
+ if (metaInfoRead) return meta;
- Expr a = attrs->get(toATerm("meta"));
- if (!a) return meta; /* fine, empty meta information */
+ (bool &) metaInfoRead = true;
+
+ Bindings::iterator a = attrs->find(state.sMeta);
+ if (a == attrs->end()) return meta; /* fine, empty meta information */
- ATermMap attrs2;
- queryAllAttrs(evalExpr(state, a), attrs2);
+ state.forceAttrs(a->second.value);
- for (ATermMap::const_iterator i = attrs2.begin(); i != attrs2.end(); ++i) {
- Expr e = evalExpr(state, i->value);
- string s;
- PathSet context;
+ foreach (Bindings::iterator, i, *a->second.value.attrs) {
MetaValue value;
- int n;
- ATermList es;
- if (matchStr(e, s, context)) {
+ state.forceValue(i->second.value);
+ if (i->second.value.type == tString) {
value.type = MetaValue::tpString;
- value.stringValue = s;
- meta[aterm2String(i->key)] = value;
- } else if (matchInt(e, n)) {
+ value.stringValue = i->second.value.string.s;
+ } else if (i->second.value.type == tInt) {
value.type = MetaValue::tpInt;
- value.intValue = n;
- meta[aterm2String(i->key)] = value;
- } else if (matchList(e, es)) {
+ value.intValue = i->second.value.integer;
+ } else if (i->second.value.type == tList) {
value.type = MetaValue::tpStrings;
- for (ATermIterator j(es); j; ++j)
- value.stringValues.push_back(evalStringNoCtx(state, *j));
- meta[aterm2String(i->key)] = value;
- }
+ for (unsigned int j = 0; j < i->second.value.list.length; ++j)
+ value.stringValues.push_back(state.forceStringNoCtx(*i->second.value.list.elems[j]));
+ } else continue;
+ ((MetaInfo &) meta)[i->first] = value;
}
return meta;
@@ -83,73 +68,46 @@ MetaValue DrvInfo::queryMetaInfo(EvalState & state, const string & name) const
void DrvInfo::setMetaInfo(const MetaInfo & meta)
{
- ATermMap metaAttrs;
- foreach (MetaInfo::const_iterator, i, meta) {
- Expr e;
- switch (i->second.type) {
- case MetaValue::tpInt: e = makeInt(i->second.intValue); break;
- case MetaValue::tpString: e = makeStr(i->second.stringValue); break;
- case MetaValue::tpStrings: {
- ATermList es = ATempty;
- foreach (Strings::const_iterator, j, i->second.stringValues)
- es = ATinsert(es, makeStr(*j));
- e = makeList(ATreverse(es));
- break;
- }
- default: abort();
- }
- metaAttrs.set(toATerm(i->first), makeAttrRHS(e, makeNoPos()));
- }
- attrs->set(toATerm("meta"), makeAttrs(metaAttrs));
+ metaInfoRead = true;
+ this->meta = meta;
}
-/* Cache for already evaluated derivations. Usually putting ATerms in
- a STL container is unsafe (they're not scanning for GC roots), but
- here it doesn't matter; everything in this set is reachable from
- the stack as well. */
-typedef set<Expr> Exprs;
+/* Cache for already considered attrsets. */
+typedef set<Bindings *> Done;
-/* Evaluate expression `e'. If it evaluates to an attribute set of
- type `derivation', then put information about it in `drvs' (unless
- it's already in `doneExprs'). The result boolean indicates whether
- it makes sense for the caller to recursively search for derivations
- in `e'. */
-static bool getDerivation(EvalState & state, Expr e,
- const string & attrPath, DrvInfos & drvs, Exprs & doneExprs)
+/* Evaluate value `v'. If it evaluates to an attribute set of type
+ `derivation', then put information about it in `drvs' (unless it's
+ already in `doneExprs'). The result boolean indicates whether it
+ makes sense for the caller to recursively search for derivations in
+ `v'. */
+static bool getDerivation(EvalState & state, Value & v,
+ const string & attrPath, DrvInfos & drvs, Done & done)
{
try {
-
- ATermList es;
- e = evalExpr(state, e);
- if (!matchAttrs(e, es)) return true;
-
- boost::shared_ptr<ATermMap> attrs(new ATermMap());
- queryAllAttrs(e, *attrs, false);
-
- Expr a = attrs->get(toATerm("type"));
- if (!a || evalStringNoCtx(state, a) != "derivation") return true;
+ state.forceValue(v);
+ if (!state.isDerivation(v)) return true;
/* Remove spurious duplicates (e.g., an attribute set like
`rec { x = derivation {...}; y = x;}'. */
- if (doneExprs.find(e) != doneExprs.end()) return false;
- doneExprs.insert(e);
+ if (done.find(v.attrs) != done.end()) return false;
+ done.insert(v.attrs);
DrvInfo drv;
- a = attrs->get(toATerm("name"));
+ Bindings::iterator i = v.attrs->find(state.sName);
/* !!! We really would like to have a decent back trace here. */
- if (!a) throw TypeError("derivation name missing");
- drv.name = evalStringNoCtx(state, a);
+ if (i == v.attrs->end()) throw TypeError("derivation name missing");
+ drv.name = state.forceStringNoCtx(i->second.value);
- a = attrs->get(toATerm("system"));
- if (!a)
+ i = v.attrs->find(state.sSystem);
+ if (i == v.attrs->end())
drv.system = "unknown";
else
- drv.system = evalStringNoCtx(state, a);
+ drv.system = state.forceStringNoCtx(i->second.value);
- drv.attrs = attrs;
+ drv.attrs = v.attrs;
drv.attrPath = attrPath;
@@ -162,11 +120,11 @@ static bool getDerivation(EvalState & state, Expr e,
}
-bool getDerivation(EvalState & state, Expr e, DrvInfo & drv)
+bool getDerivation(EvalState & state, Value & v, DrvInfo & drv)
{
- Exprs doneExprs;
+ Done done;
DrvInfos drvs;
- getDerivation(state, e, "", drvs, doneExprs);
+ getDerivation(state, v, "", drvs, done);
if (drvs.size() != 1) return false;
drv = drvs.front();
return true;
@@ -179,83 +137,73 @@ static string addToPath(const string & s1, const string & s2)
}
-static void getDerivations(EvalState & state, Expr e,
- const string & pathPrefix, const ATermMap & autoArgs,
- DrvInfos & drvs, Exprs & doneExprs)
+static void getDerivations(EvalState & state, Value & vIn,
+ const string & pathPrefix, const Bindings & autoArgs,
+ DrvInfos & drvs, Done & done)
{
- e = evalExpr(state, autoCallFunction(evalExpr(state, e), autoArgs));
-
+ Value v;
+ state.autoCallFunction(autoArgs, vIn, v);
+
/* Process the expression. */
- ATermList es;
DrvInfo drv;
- if (!getDerivation(state, e, pathPrefix, drvs, doneExprs))
- return;
+ if (!getDerivation(state, v, pathPrefix, drvs, done)) ;
- if (matchAttrs(e, es)) {
- ATermMap drvMap(ATgetLength(es));
- queryAllAttrs(e, drvMap);
+ else if (v.type == tAttrs) {
/* !!! undocumented hackery to support combining channels in
nix-env.cc. */
- bool combineChannels = drvMap.get(toATerm("_combineChannels"));
+ bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end();
/* Consider the attributes in sorted order to get more
deterministic behaviour in nix-env operations (e.g. when
there are names clashes between derivations, the derivation
bound to the attribute with the "lower" name should take
precedence). */
- typedef std::map<string, Expr> AttrsSorted;
- AttrsSorted attrsSorted;
- foreach (ATermMap::const_iterator, i, drvMap)
- attrsSorted[aterm2String(i->key)] = i->value;
+ typedef std::map<string, Symbol> SortedSymbols;
+ SortedSymbols attrs;
+ foreach (Bindings::iterator, i, *v.attrs)
+ attrs.insert(std::pair<string, Symbol>(i->first, i->first));
- foreach (AttrsSorted::iterator, i, attrsSorted) {
+ foreach (SortedSymbols::iterator, i, attrs) {
startNest(nest, lvlDebug, format("evaluating attribute `%1%'") % i->first);
string pathPrefix2 = addToPath(pathPrefix, i->first);
+ Value & v2((*v.attrs)[i->second].value);
if (combineChannels)
- getDerivations(state, i->second, pathPrefix2, autoArgs, drvs, doneExprs);
- else if (getDerivation(state, i->second, pathPrefix2, drvs, doneExprs)) {
+ getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
+ else if (getDerivation(state, v2, pathPrefix2, drvs, done)) {
/* If the value of this attribute is itself an
attribute set, should we recurse into it? => Only
if it has a `recurseForDerivations = true'
attribute. */
- ATermList es;
- Expr e = evalExpr(state, i->second), e2;
- if (matchAttrs(e, es)) {
- ATermMap attrs(ATgetLength(es));
- queryAllAttrs(e, attrs, false);
- if (((e2 = attrs.get(toATerm("recurseForDerivations")))
- && evalBool(state, e2)))
- getDerivations(state, e, pathPrefix2, autoArgs, drvs, doneExprs);
+ if (v2.type == tAttrs) {
+ Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations"));
+ if (j != v2.attrs->end() && state.forceBool(j->second.value))
+ getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
}
}
}
-
- return;
}
- if (matchList(e, es)) {
- int n = 0;
- for (ATermIterator i(es); i; ++i, ++n) {
+ else if (v.type == tList) {
+ for (unsigned int n = 0; n < v.list.length; ++n) {
startNest(nest, lvlDebug,
format("evaluating list element"));
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
- if (getDerivation(state, *i, pathPrefix2, drvs, doneExprs))
- getDerivations(state, *i, pathPrefix2, autoArgs, drvs, doneExprs);
+ if (getDerivation(state, *v.list.elems[n], pathPrefix2, drvs, done))
+ getDerivations(state, *v.list.elems[n], pathPrefix2, autoArgs, drvs, done);
}
- return;
}
- throw TypeError("expression does not evaluate to a derivation (or a set or list of those)");
+ else throw TypeError("expression does not evaluate to a derivation (or a set or list of those)");
}
-void getDerivations(EvalState & state, Expr e, const string & pathPrefix,
- const ATermMap & autoArgs, DrvInfos & drvs)
+void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
+ const Bindings & autoArgs, DrvInfos & drvs)
{
- Exprs doneExprs;
- getDerivations(state, e, pathPrefix, autoArgs, drvs, doneExprs);
+ Done done;
+ getDerivations(state, v, pathPrefix, autoArgs, drvs, done);
}
diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh
index b56f54711..ca7d98002 100644
--- a/src/libexpr/get-drvs.hh
+++ b/src/libexpr/get-drvs.hh
@@ -29,16 +29,19 @@ struct DrvInfo
private:
string drvPath;
string outPath;
+
+ bool metaInfoRead;
+ MetaInfo meta;
public:
string name;
string attrPath; /* path towards the derivation */
string system;
- /* !!! these should really be hidden, and setMetaInfo() should
- make a copy since the ATermMap can be shared between multiple
- DrvInfos. */
- boost::shared_ptr<ATermMap> attrs;
+ /* !!! make this private */
+ Bindings * attrs;
+
+ DrvInfo() : metaInfoRead(false), attrs(0) { };
string queryDrvPath(EvalState & state) const;
string queryOutPath(EvalState & state) const;
@@ -62,13 +65,12 @@ public:
typedef list<DrvInfo> DrvInfos;
-/* Evaluate expression `e'. If it evaluates to a derivation, store
- information about the derivation in `drv' and return true.
- Otherwise, return false. */
-bool getDerivation(EvalState & state, Expr e, DrvInfo & drv);
+/* If value `v' denotes a derivation, store information about the
+ derivation in `drv' and return true. Otherwise, return false. */
+bool getDerivation(EvalState & state, Value & v, DrvInfo & drv);
-void getDerivations(EvalState & state, Expr e, const string & pathPrefix,
- const ATermMap & autoArgs, DrvInfos & drvs);
+void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
+ const Bindings & autoArgs, DrvInfos & drvs);
}
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index 81aec99e1..f29f9b684 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -8,9 +8,7 @@
%{
-#include "aterm.hh"
#include "nixexpr.hh"
-#include "nixexpr-ast.hh"
#define BISON_HEADER_HACK
#include "parser-tab.hh"
@@ -21,13 +19,16 @@ namespace nix {
static void initLoc(YYLTYPE * loc)
{
- loc->first_line = 1;
- loc->first_column = 1;
+ loc->first_line = loc->last_line = 1;
+ loc->first_column = loc->last_column = 1;
}
static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
{
+ loc->first_line = loc->last_line;
+ loc->first_column = loc->last_column;
+
while (len--) {
switch (*s++) {
case '\r':
@@ -35,17 +36,17 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
s++;
/* fall through */
case '\n':
- ++loc->first_line;
- loc->first_column = 1;
+ ++loc->last_line;
+ loc->last_column = 1;
break;
default:
- ++loc->first_column;
+ ++loc->last_column;
}
}
}
-static Expr unescapeStr(const char * s)
+static Expr * unescapeStr(const char * s)
{
string t;
char c;
@@ -65,7 +66,7 @@ static Expr unescapeStr(const char * s)
}
else t += c;
}
- return makeStr(toATerm(t), ATempty);
+ return new ExprString(t);
}
@@ -105,19 +106,20 @@ inherit { return INHERIT; }
\/\/ { return UPDATE; }
\+\+ { return CONCAT; }
-{ID} { yylval->t = toATerm(yytext); return ID; /* !!! alloc */ }
+{ID} { yylval->id = strdup(yytext); return ID; }
{INT} { int n = atoi(yytext); /* !!! overflow */
- yylval->t = ATmake("<int>", n);
+ yylval->n = n;
return INT;
}
\" { BEGIN(STRING); return '"'; }
<STRING>([^\$\"\\]|\$[^\{\"]|\\.)+ {
-/* !!! Not quite right: we want a follow restriction on "$", it
- shouldn't be followed by a "{". Right now "$\"" will be consumed
- as part of a string, rather than a "$" followed by the string
- terminator. Disallow "$\"" for now. */
- yylval->t = unescapeStr(yytext); /* !!! alloc */
+ /* !!! Not quite right: we want a follow restriction on
+ "$", it shouldn't be followed by a "{". Right now
+ "$\"" will be consumed as part of a string, rather
+ than a "$" followed by the string terminator.
+ Disallow "$\"" for now. */
+ yylval->e = unescapeStr(yytext);
return STR;
}
<STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
@@ -126,31 +128,31 @@ inherit { return INHERIT; }
\'\'(\ *\n)? { BEGIN(IND_STRING); return IND_STRING_OPEN; }
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
- yylval->t = makeIndStr(toATerm(yytext));
+ yylval->e = new ExprIndStr(yytext);
return IND_STR;
}
<IND_STRING>\'\'\$ {
- yylval->t = makeIndStr(toATerm("$"));
+ yylval->e = new ExprIndStr("$");
return IND_STR;
}
<IND_STRING>\'\'\' {
- yylval->t = makeIndStr(toATerm("''"));
+ yylval->e = new ExprIndStr("''");
return IND_STR;
}
<IND_STRING>\'\'\\. {
- yylval->t = unescapeStr(yytext + 2);
+ yylval->e = unescapeStr(yytext + 2);
return IND_STR;
}
<IND_STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
<IND_STRING>\'\' { BEGIN(INITIAL); return IND_STRING_CLOSE; }
<IND_STRING>\' {
- yylval->t = makeIndStr(toATerm("'"));
+ yylval->e = new ExprIndStr("'");
return IND_STR;
}
<IND_STRING>. return yytext[0]; /* just in case: shouldn't be reached */
-{PATH} { yylval->t = toATerm(yytext); return PATH; /* !!! alloc */ }
-{URI} { yylval->t = toATerm(yytext); return URI; /* !!! alloc */ }
+{PATH} { yylval->path = strdup(yytext); return PATH; }
+{URI} { yylval->uri = strdup(yytext); return URI; }
[ \t\r\n]+ /* eat up whitespace */
\#[^\r\n]* /* single-line comments */
diff --git a/src/libexpr/nixexpr-ast.def b/src/libexpr/nixexpr-ast.def
deleted file mode 100644
index 00b5f5a13..000000000
--- a/src/libexpr/nixexpr-ast.def
+++ /dev/null
@@ -1,97 +0,0 @@
-init initNixExprHelpers
-
-Pos | string int int | Pos |
-NoPos | | Pos |
-
-Function | Pattern Expr Pos | Expr |
-Assert | Expr Expr Pos | Expr |
-With | Expr Expr Pos | Expr |
-If | Expr Expr Expr | Expr |
-OpNot | Expr | Expr |
-OpEq | Expr Expr | Expr |
-OpNEq | Expr Expr | Expr |
-OpAnd | Expr Expr | Expr |
-OpOr | Expr Expr | Expr |
-OpImpl | Expr Expr | Expr |
-OpUpdate | Expr Expr | Expr |
-SubPath | Expr Expr | Expr |
-OpHasAttr | Expr string | Expr |
-OpPlus | Expr Expr | Expr |
-OpConcat | Expr Expr | Expr |
-ConcatStrings | ATermList | Expr |
-Call | Expr Expr | Expr |
-Select | Expr string | Expr |
-Var | string | Expr |
-Int | int | Expr |
-
-# Strings in the evaluator carry a so-called `context' (the ATermList)
-# which is a list of strings representing store paths. This is to
-# allow users to write things like
-#
-# "--with-freetype2-library=" + freetype + "/lib"
-#
-# where `freetype' is a derivation (or a source to be copied to the
-# store). If we just concatenated the strings without keeping track
-# of the referenced store paths, then if the string is used as a
-# derivation attribute, the derivation will not have the correct
-# dependencies in its inputDrvs and inputSrcs.
-#
-# The semantics of the context is as follows: when a string with
-# context C is used as a derivation attribute, then the derivations in
-# C will be added to the inputDrvs of the derivation, and the other
-# store paths in C will be added to the inputSrcs of the derivations.
-#
-# For canonicity, the store paths should be in sorted order.
-Str | string ATermList | Expr |
-Str | string | Expr | ObsoleteStr
-
-# Internal to the parser, doesn't occur in ASTs.
-IndStr | string | Expr |
-
-# A path is a reference to a file system object that is to be copied
-# to the Nix store when used as a derivation attribute. When it is
-# concatenated to a string (i.e., `str + path'), it is also copied and
-# the resulting store path is concatenated to the string (with the
-# store path in the context). If a string or path is concatenated to
-# a path (i.e., `path + str' or `path + path'), the result is a new
-# path (if the right-hand side is a string, the context must be
-# empty).
-Path | string | Expr |
-
-List | ATermList | Expr |
-BlackHole | | Expr |
-Undefined | | Expr |
-Removed | | Expr |
-PrimOp | int ATermBlob ATermList | Expr |
-Attrs | ATermList | Expr |
-Closed | Expr | Expr |
-Rec | ATermList ATermList | Expr |
-Bool | ATermBool | Expr |
-Null | | Expr |
-
-Bind | string Expr Pos | ATerm |
-BindAttrPath | ATermList Expr Pos | ATerm | # desugared during parsing
-Bind | string Expr | ATerm | ObsoleteBind
-Inherit | Expr ATermList Pos | ATerm |
-
-Scope | | Expr |
-
-VarPat | string | Pattern |
-AttrsPat | ATermList ATermBool | Pattern | # bool = `...'
-AtPat | Pattern Pattern | Pattern |
-
-Formal | string DefaultValue | ATerm |
-
-DefaultValue | Expr | DefaultValue |
-NoDefaultValue | | DefaultValue |
-
-True | | ATermBool |
-False | | ATermBool |
-
-PrimOpDef | int ATermBlob | ATerm |
-
-AttrRHS | Expr Pos | ATerm |
-
-eTrue = makeBool(makeTrue())
-eFalse = makeBool(makeFalse())
-sOverrides = toATerm("__overrides")
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index 3675dccea..898fdb609 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -1,407 +1,325 @@
#include "nixexpr.hh"
#include "derivations.hh"
#include "util.hh"
-#include "aterm.hh"
-
-#include "nixexpr-ast.hh"
-#include "nixexpr-ast.cc"
#include <cstdlib>
namespace nix {
-
-string showPos(ATerm pos)
+
+/* Displaying abstract syntax trees. */
+
+std::ostream & operator << (std::ostream & str, Expr & e)
{
- ATerm path;
- int line, column;
- if (matchNoPos(pos)) return "undefined position";
- if (!matchPos(pos, path, line, column))
- throw badTerm("position expected", pos);
- return (format("`%1%:%2%:%3%'") % aterm2String(path) % line % column).str();
+ e.show(str);
+ return str;
}
-
-ATerm bottomupRewrite(TermFun & f, ATerm e)
+void Expr::show(std::ostream & str)
{
- checkInterrupt();
-
- if (ATgetType(e) == AT_APPL) {
- AFun fun = ATgetAFun(e);
- int arity = ATgetArity(fun);
- ATerm args[arity];
-
- for (int i = 0; i < arity; ++i)
- args[i] = bottomupRewrite(f, ATgetArgument(e, i));
-
- e = (ATerm) ATmakeApplArray(fun, args);
- }
-
- else if (ATgetType(e) == AT_LIST) {
- ATermList in = (ATermList) e;
- ATermList out = ATempty;
-
- for (ATermIterator i(in); i; ++i)
- out = ATinsert(out, bottomupRewrite(f, *i));
-
- e = (ATerm) ATreverse(out);
- }
+ abort();
+}
- return f(e);
+void ExprInt::show(std::ostream & str)
+{
+ str << n;
}
+void ExprString::show(std::ostream & str)
+{
+ str << "\"" << s << "\""; // !!! escaping
+}
-void queryAllAttrs(Expr e, ATermMap & attrs, bool withPos)
+void ExprPath::show(std::ostream & str)
{
- ATermList bnds;
- if (!matchAttrs(e, bnds))
- throw TypeError(format("value is %1% while an attribute set was expected") % showType(e));
+ str << s;
+}
- for (ATermIterator i(bnds); i; ++i) {
- ATerm name;
- Expr e;
- ATerm pos;
- if (!matchBind(*i, name, e, pos)) abort(); /* can't happen */
- attrs.set(name, withPos ? makeAttrRHS(e, pos) : e);
- }
+void ExprVar::show(std::ostream & str)
+{
+ str << info.name;
}
+void ExprSelect::show(std::ostream & str)
+{
+ str << "(" << *e << ")." << name;
+}
-Expr queryAttr(Expr e, const string & name)
+void ExprOpHasAttr::show(std::ostream & str)
{
- ATerm dummy;
- return queryAttr(e, name, dummy);
+ str << "(" << *e << ") ? " << name;
}
+void ExprAttrs::show(std::ostream & str)
+{
+ if (recursive) str << "rec ";
+ str << "{ ";
+ foreach (list<Inherited>::iterator, i, inherited)
+ str << "inherit " << i->first.name << "; ";
+ foreach (Attrs::iterator, i, attrs)
+ str << i->first << " = " << *i->second.first << "; ";
+ str << "}";
+}
-Expr queryAttr(Expr e, const string & name, ATerm & pos)
+void ExprList::show(std::ostream & str)
{
- ATermList bnds;
- if (!matchAttrs(e, bnds))
- throw TypeError(format("value is %1% while an attribute set was expected") % showType(e));
+ str << "[ ";
+ foreach (vector<Expr *>::iterator, i, elems)
+ str << "(" << **i << ") ";
+ str << "]";
+}
- for (ATermIterator i(bnds); i; ++i) {
- ATerm name2, pos2;
- Expr e;
- if (!matchBind(*i, name2, e, pos2))
- abort(); /* can't happen */
- if (aterm2String(name2) == name) {
- pos = pos2;
- return e;
+void ExprLambda::show(std::ostream & str)
+{
+ str << "(";
+ if (matchAttrs) {
+ str << "{ ";
+ bool first = true;
+ foreach (Formals::Formals_::iterator, i, formals->formals) {
+ if (first) first = false; else str << ", ";
+ str << i->name;
+ if (i->def) str << " ? " << *i->def;
}
+ str << " }";
+ if (!arg.empty()) str << " @ ";
}
-
- return 0;
+ if (!arg.empty()) str << arg;
+ str << ": " << *body << ")";
}
-
-Expr makeAttrs(const ATermMap & attrs)
+void ExprLet::show(std::ostream & str)
{
- ATermList bnds = ATempty;
- for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) {
- Expr e;
- ATerm pos;
- if (!matchAttrRHS(i->value, e, pos))
- abort(); /* can't happen */
- bnds = ATinsert(bnds, makeBind(i->key, e, pos));
- }
- return makeAttrs(bnds);
+ str << "let ";
+ foreach (list<ExprAttrs::Inherited>::iterator, i, attrs->inherited)
+ str << "inherit " << i->first.name << "; ";
+ foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs)
+ str << i->first << " = " << *i->second.first << "; ";
+ str << "in " << *body;
}
-
-static void varsBoundByPattern(ATermMap & map, Pattern pat)
+void ExprWith::show(std::ostream & str)
{
- ATerm name;
- ATermList formals;
- Pattern pat1, pat2;
- ATermBool ellipsis;
- /* Use makeRemoved() so that it can be used directly in
- substitute(). */
- if (matchVarPat(pat, name))
- map.set(name, makeRemoved());
- else if (matchAttrsPat(pat, formals, ellipsis)) {
- for (ATermIterator i(formals); i; ++i) {
- ATerm d1;
- if (!matchFormal(*i, name, d1)) abort();
- map.set(name, makeRemoved());
- }
- }
- else if (matchAtPat(pat, pat1, pat2)) {
- varsBoundByPattern(map, pat1);
- varsBoundByPattern(map, pat2);
- }
- else abort();
+ str << "with " << *attrs << "; " << *body;
}
+void ExprIf::show(std::ostream & str)
+{
+ str << "if " << *cond << " then " << *then << " else " << *else_;
+}
-Expr substitute(const Substitution & subs, Expr e)
+void ExprAssert::show(std::ostream & str)
{
- checkInterrupt();
+ str << "assert " << *cond << "; " << *body;
+}
- //if (subs.size() == 0) return e;
+void ExprOpNot::show(std::ostream & str)
+{
+ str << "! " << *e;
+}
- ATerm name, pos, e2;
+void ExprConcatStrings::show(std::ostream & str)
+{
+ bool first = true;
+ foreach (vector<Expr *>::iterator, i, *es) {
+ if (first) first = false; else str << " + ";
+ str << **i;
+ }
+}
- /* As an optimisation, don't substitute in subterms known to be
- closed. */
- if (matchClosed(e, e2)) return e;
- if (matchVar(e, name)) {
- Expr sub = subs.lookup(name);
- if (sub == makeRemoved()) sub = 0;
- Expr wrapped;
- /* Add a "closed" wrapper around terms that aren't already
- closed. The check is necessary to prevent repeated
- wrapping, e.g., closed(closed(closed(...))), which kills
- caching. */
- return sub ? (matchClosed(sub, wrapped) ? sub : makeClosed(sub)) : e;
- }
+std::ostream & operator << (std::ostream & str, const Pos & pos)
+{
+ if (!pos.line)
+ str << "undefined position";
+ else
+ str << (format("`%1%:%2%:%3%'") % pos.file % pos.line % pos.column).str();
+ return str;
+}
- /* In case of a function, filter out all variables bound by this
- function. */
- Pattern pat;
- ATerm body;
- if (matchFunction(e, pat, body, pos)) {
- ATermMap map(16);
- varsBoundByPattern(map, pat);
- Substitution subs2(&subs, &map);
- return makeFunction(
- (Pattern) substitute(subs2, (Expr) pat),
- substitute(subs2, body), pos);
- }
- /* Idem for a mutually recursive attribute set. */
- ATermList rbnds, nrbnds;
- if (matchRec(e, rbnds, nrbnds)) {
- ATermMap map(ATgetLength(rbnds) + ATgetLength(nrbnds));
- for (ATermIterator i(rbnds); i; ++i)
- if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved());
- else abort(); /* can't happen */
- for (ATermIterator i(nrbnds); i; ++i)
- if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved());
- else abort(); /* can't happen */
- return makeRec(
- (ATermList) substitute(Substitution(&subs, &map), (ATerm) rbnds),
- (ATermList) substitute(subs, (ATerm) nrbnds));
- }
+Pos noPos;
- if (ATgetType(e) == AT_APPL) {
- AFun fun = ATgetAFun(e);
- int arity = ATgetArity(fun);
- ATerm args[arity];
- bool changed = false;
- for (int i = 0; i < arity; ++i) {
- ATerm arg = ATgetArgument(e, i);
- args[i] = substitute(subs, arg);
- if (args[i] != arg) changed = true;
- }
-
- return changed ? (ATerm) ATmakeApplArray(fun, args) : e;
- }
+/* Computing levels/displacements for variables. */
- if (ATgetType(e) == AT_LIST) {
- unsigned int len = ATgetLength((ATermList) e);
- ATerm es[len];
- ATermIterator i((ATermList) e);
- for (unsigned int j = 0; i; ++i, ++j)
- es[j] = substitute(subs, *i);
- ATermList out = ATempty;
- for (unsigned int j = len; j; --j)
- out = ATinsert(out, es[j - 1]);
- return (ATerm) out;
- }
+void Expr::bindVars(const StaticEnv & env)
+{
+ abort();
+}
- return e;
+void ExprInt::bindVars(const StaticEnv & env)
+{
}
+void ExprString::bindVars(const StaticEnv & env)
+{
+}
-/* We use memoisation to prevent exponential complexity on heavily
- shared ATerms (remember, an ATerm is a graph, not a tree!). Note
- that using an STL set is fine here wrt to ATerm garbage collection
- since all the ATerms in the set are already reachable from
- somewhere else. */
-static void checkVarDefs2(set<Expr> & done, const ATermMap & defs, Expr e)
+void ExprPath::bindVars(const StaticEnv & env)
{
- if (done.find(e) != done.end()) return;
- done.insert(e);
-
- ATerm name, pos, value;
- ATerm with, body;
- ATermList rbnds, nrbnds;
- Pattern pat;
-
- /* Closed terms don't have free variables, so we don't have to
- check by definition. */
- if (matchClosed(e, value)) return;
-
- else if (matchVar(e, name)) {
- if (!defs.get(name))
- throw EvalError(format("undefined variable `%1%'")
- % aterm2String(name));
- }
+}
- else if (matchFunction(e, pat, body, pos)) {
- ATermMap defs2(defs);
- varsBoundByPattern(defs2, pat);
- set<Expr> done2;
- checkVarDefs2(done2, defs2, pat);
- checkVarDefs2(done2, defs2, body);
- }
-
- else if (matchRec(e, rbnds, nrbnds)) {
- checkVarDefs2(done, defs, (ATerm) nrbnds);
- ATermMap defs2(defs);
- for (ATermIterator i(rbnds); i; ++i) {
- if (!matchBind(*i, name, value, pos)) abort(); /* can't happen */
- defs2.set(name, (ATerm) ATempty);
- }
- for (ATermIterator i(nrbnds); i; ++i) {
- if (!matchBind(*i, name, value, pos)) abort(); /* can't happen */
- defs2.set(name, (ATerm) ATempty);
+void VarRef::bind(const StaticEnv & env)
+{
+ /* Check whether the variable appears in the environment. If so,
+ set its level and displacement. */
+ const StaticEnv * curEnv;
+ unsigned int level;
+ int withLevel = -1;
+ for (curEnv = &env, level = 0; curEnv; curEnv = curEnv->up, level++) {
+ if (curEnv->isWith) {
+ if (withLevel == -1) withLevel = level;
+ } else {
+ StaticEnv::Vars::const_iterator i = curEnv->vars.find(name);
+ if (i != curEnv->vars.end()) {
+ fromWith = false;
+ this->level = level;
+ displ = i->second;
+ return;
+ }
}
- set<Expr> done2;
- checkVarDefs2(done2, defs2, (ATerm) rbnds);
}
- else if (matchWith(e, with, body, pos)) {
- /* We can't check the body without evaluating the definitions
- (which is an arbitrary expression), so we don't do that
- here but only when actually evaluating the `with'. */
- checkVarDefs2(done, defs, with);
- }
-
- else if (ATgetType(e) == AT_APPL) {
- int arity = ATgetArity(ATgetAFun(e));
- for (int i = 0; i < arity; ++i)
- checkVarDefs2(done, defs, ATgetArgument(e, i));
- }
+ /* 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 == -1) throw EvalError(format("undefined variable `%1%'") % name);
- else if (ATgetType(e) == AT_LIST)
- for (ATermIterator i((ATermList) e); i; ++i)
- checkVarDefs2(done, defs, *i);
+ fromWith = true;
+ this->level = withLevel;
}
-
-void checkVarDefs(const ATermMap & defs, Expr e)
+void ExprVar::bindVars(const StaticEnv & env)
{
- set<Expr> done;
- checkVarDefs2(done, defs, e);
+ info.bind(env);
}
+void ExprSelect::bindVars(const StaticEnv & env)
+{
+ e->bindVars(env);
+}
-struct Canonicalise : TermFun
+void ExprOpHasAttr::bindVars(const StaticEnv & env)
{
- ATerm operator () (ATerm e)
- {
- /* Remove position info. */
- ATerm path;
- int line, column;
- if (matchPos(e, path, line, column))
- return makeNoPos();
+ e->bindVars(env);
+}
- /* Sort attribute sets. */
- ATermList _;
- if (matchAttrs(e, _)) {
- ATermMap attrs;
- queryAllAttrs(e, attrs);
- StringSet names;
- for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i)
- names.insert(aterm2String(i->key));
+void ExprAttrs::bindVars(const StaticEnv & env)
+{
+ if (recursive) {
+ StaticEnv newEnv(false, &env);
+
+ unsigned int displ = 0;
- ATermList attrs2 = ATempty;
- for (StringSet::reverse_iterator i = names.rbegin(); i != names.rend(); ++i)
- attrs2 = ATinsert(attrs2,
- makeBind(toATerm(*i), attrs.get(toATerm(*i)), makeNoPos()));
+ foreach (ExprAttrs::Attrs::iterator, i, attrs)
+ newEnv.vars[i->first] = displ++;
- return makeAttrs(attrs2);
+ foreach (list<Inherited>::iterator, i, inherited) {
+ newEnv.vars[i->first.name] = displ++;
+ i->first.bind(env);
}
-
- return e;
+
+ foreach (ExprAttrs::Attrs::iterator, i, attrs)
+ i->second.first->bindVars(newEnv);
}
-};
+ else {
+ foreach (ExprAttrs::Attrs::iterator, i, attrs)
+ i->second.first->bindVars(env);
-Expr canonicaliseExpr(Expr e)
-{
- Canonicalise canonicalise;
- return bottomupRewrite(canonicalise, e);
+ foreach (list<Inherited>::iterator, i, inherited)
+ i->first.bind(env);
+ }
}
-
-Expr makeBool(bool b)
+void ExprList::bindVars(const StaticEnv & env)
{
- return b ? eTrue : eFalse;
+ foreach (vector<Expr *>::iterator, i, elems)
+ (*i)->bindVars(env);
}
-
-bool matchStr(Expr e, string & s, PathSet & context)
+void ExprLambda::bindVars(const StaticEnv & env)
{
- ATermList l;
- ATerm s_;
-
- if (!matchStr(e, s_, l)) return false;
+ StaticEnv newEnv(false, &env);
+
+ unsigned int displ = 0;
+
+ if (!arg.empty()) newEnv.vars[arg] = displ++;
- s = aterm2String(s_);
+ if (matchAttrs) {
+ foreach (Formals::Formals_::iterator, i, formals->formals)
+ newEnv.vars[i->name] = displ++;
- for (ATermIterator i(l); i; ++i)
- context.insert(aterm2String(*i));
+ foreach (Formals::Formals_::iterator, i, formals->formals)
+ if (i->def) i->def->bindVars(newEnv);
+ }
- return true;
+ body->bindVars(newEnv);
}
+void ExprLet::bindVars(const StaticEnv & env)
+{
+ StaticEnv newEnv(false, &env);
+
+ unsigned int displ = 0;
+
+ foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs)
+ newEnv.vars[i->first] = displ++;
+
+ foreach (list<ExprAttrs::Inherited>::iterator, i, attrs->inherited) {
+ newEnv.vars[i->first.name] = displ++;
+ i->first.bind(env);
+ }
-Expr makeStr(const string & s, const PathSet & context)
+ foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs)
+ i->second.first->bindVars(newEnv);
+
+ body->bindVars(newEnv);
+}
+
+void ExprWith::bindVars(const StaticEnv & env)
{
- return makeStr(toATerm(s), toATermList(context));
+ /* 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; 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);
+}
-string showType(Expr e)
+void ExprAssert::bindVars(const StaticEnv & env)
{
- ATerm t1, t2;
- ATermList l1;
- ATermBlob b1;
- int i1;
- Pattern p1;
- if (matchStr(e, t1, l1)) return "a string";
- if (matchPath(e, t1)) return "a path";
- if (matchNull(e)) return "null";
- if (matchInt(e, i1)) return "an integer";
- if (matchBool(e, t1)) return "a boolean";
- if (matchFunction(e, p1, t1, t2)) return "a function";
- if (matchAttrs(e, l1)) return "an attribute set";
- if (matchList(e, l1)) return "a list";
- if (matchPrimOp(e, i1, b1, l1)) return "a partially applied built-in function";
- return "an unknown type";
+ cond->bindVars(env);
+ body->bindVars(env);
}
+void ExprOpNot::bindVars(const StaticEnv & env)
+{
+ e->bindVars(env);
+}
-string showValue(Expr e)
+void ExprConcatStrings::bindVars(const StaticEnv & env)
{
- PathSet context;
- string s;
- ATerm s2;
- int i;
- if (matchStr(e, s, context)) {
- string u;
- for (string::iterator i = s.begin(); i != s.end(); ++i)
- if (*i == '\"' || *i == '\\') u += "\\" + *i;
- else if (*i == '\n') u += "\\n";
- else if (*i == '\r') u += "\\r";
- else if (*i == '\t') u += "\\t";
- else u += *i;
- return "\"" + u + "\"";
- }
- if (matchPath(e, s2)) return aterm2String(s2);
- if (matchNull(e)) return "null";
- if (matchInt(e, i)) return (format("%1%") % i).str();
- if (e == eTrue) return "true";
- if (e == eFalse) return "false";
- /* !!! incomplete */
- return "<unknown>";
+ foreach (vector<Expr *>::iterator, i, *es)
+ (*i)->bindVars(env);
}
-
+
}
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index c7cdf46df..1c72441b2 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -3,8 +3,7 @@
#include <map>
-#include "aterm-map.hh"
-#include "types.hh"
+#include "symbol-table.hh"
namespace nix {
@@ -18,105 +17,254 @@ MakeError(Abort, EvalError)
MakeError(TypeError, EvalError)
-/* Nix expressions are represented as ATerms. The maximal sharing
- property of the ATerm library allows us to implement caching of
- normals forms efficiently. */
-typedef ATerm Expr;
-typedef ATerm DefaultValue;
-typedef ATerm Pos;
-typedef ATerm Pattern;
-typedef ATerm ATermBool;
+/* Position objects. */
+struct Pos
+{
+ string file;
+ unsigned int line, column;
+ Pos() : line(0), column(0) { };
+ Pos(const string & file, unsigned int line, unsigned int column)
+ : file(file), line(line), column(column) { };
+};
+
+extern Pos noPos;
+
+std::ostream & operator << (std::ostream & str, const Pos & pos);
-/* A STL vector of ATerms. Should be used with great care since it's
- stored on the heap, and the elements are therefore not roots to the
- ATerm garbage collector. */
-typedef vector<ATerm> ATermVector;
+struct Env;
+struct Value;
+struct EvalState;
+struct StaticEnv;
-/* A substitution is a linked list of ATermMaps that map names to
- identifiers. We use a list of ATermMaps rather than a single to
- make it easy to grow or shrink a substitution when entering a
- scope. */
-struct Substitution
+
+/* Abstract syntax of Nix expressions. */
+
+struct Expr
{
- ATermMap * map;
- const Substitution * prev;
+ virtual void show(std::ostream & str);
+ virtual void bindVars(const StaticEnv & env);
+ virtual void eval(EvalState & state, Env & env, Value & v);
+};
- Substitution(const Substitution * prev, ATermMap * map)
- {
- this->prev = prev;
- this->map = map;
- }
-
- Expr lookup(Expr name) const
- {
- Expr x;
- for (const Substitution * s(this); s; s = s->prev)
- if ((x = s->map->get(name))) return x;
- return 0;
- }
+std::ostream & operator << (std::ostream & str, Expr & e);
+
+#define COMMON_METHODS \
+ void show(std::ostream & str); \
+ void eval(EvalState & state, Env & env, Value & v); \
+ void bindVars(const StaticEnv & env);
+
+struct ExprInt : Expr
+{
+ int n;
+ ExprInt(int n) : n(n) { };
+ COMMON_METHODS
};
+struct ExprString : Expr
+{
+ string s;
+ ExprString(const string & s) : s(s) { };
+ COMMON_METHODS
+};
-/* Show a position. */
-string showPos(ATerm pos);
+/* Temporary class used during parsing of indented strings. */
+struct ExprIndStr : Expr
+{
+ string s;
+ ExprIndStr(const string & s) : s(s) { };
+};
-/* Generic bottomup traversal over ATerms. The traversal first
- recursively descends into subterms, and then applies the given term
- function to the resulting term. */
-struct TermFun
+struct ExprPath : Expr
{
- virtual ~TermFun() { }
- virtual ATerm operator () (ATerm e) = 0;
+ string s;
+ ExprPath(const string & s) : s(s) { };
+ COMMON_METHODS
};
-ATerm bottomupRewrite(TermFun & f, ATerm e);
+struct VarRef
+{
+ Symbol name;
-/* Query all attributes in an attribute set expression. The
- expression must be in normal form. */
-void queryAllAttrs(Expr e, ATermMap & attrs, bool withPos = false);
+ /* 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 attribute set stored in the environment that is `level'
+ levels up from the current one.*/
+ unsigned int level;
+ unsigned int displ;
+
+ VarRef(const Symbol & name) : name(name) { };
+ void bind(const StaticEnv & env);
+};
-/* Query a specific attribute from an attribute set expression. The
- expression must be in normal form. */
-Expr queryAttr(Expr e, const string & name);
-Expr queryAttr(Expr e, const string & name, ATerm & pos);
+struct ExprVar : Expr
+{
+ VarRef info;
+ ExprVar(const Symbol & name) : info(name) { };
+ COMMON_METHODS
+};
-/* Create an attribute set expression from an Attrs value. */
-Expr makeAttrs(const ATermMap & attrs);
+struct ExprSelect : Expr
+{
+ Expr * e;
+ Symbol name;
+ ExprSelect(Expr * e, const Symbol & name) : e(e), name(name) { };
+ COMMON_METHODS
+};
+struct ExprOpHasAttr : Expr
+{
+ Expr * e;
+ Symbol name;
+ ExprOpHasAttr(Expr * e, const Symbol & name) : e(e), name(name) { };
+ COMMON_METHODS
+};
-/* Perform a set of substitutions on an expression. */
-Expr substitute(const Substitution & subs, Expr e);
+struct ExprAttrs : Expr
+{
+ bool recursive;
+ typedef std::pair<Expr *, Pos> Attr;
+ typedef std::pair<VarRef, Pos> Inherited;
+ typedef std::map<Symbol, Attr> Attrs;
+ Attrs attrs;
+ list<Inherited> inherited;
+ std::map<Symbol, Pos> attrNames; // used during parsing
+ ExprAttrs() : recursive(false) { };
+ COMMON_METHODS
+};
+struct ExprList : Expr
+{
+ std::vector<Expr *> elems;
+ ExprList() { };
+ COMMON_METHODS
+};
-/* Check whether all variables are defined in the given expression.
- Throw an exception if this isn't the case. */
-void checkVarDefs(const ATermMap & def, Expr e);
+struct Formal
+{
+ Symbol name;
+ Expr * def;
+ Formal(const Symbol & name, Expr * def) : name(name), def(def) { };
+};
+struct Formals
+{
+ typedef std::list<Formal> Formals_;
+ Formals_ formals;
+ std::set<Symbol> argNames; // used during parsing
+ bool ellipsis;
+};
+
+struct ExprLambda : Expr
+{
+ Pos pos;
+ 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);
+ };
+ COMMON_METHODS
+};
+
+struct ExprLet : Expr
+{
+ ExprAttrs * attrs;
+ Expr * body;
+ ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body) { };
+ COMMON_METHODS
+};
-/* Canonicalise a Nix expression by sorting attributes and removing
- location information. */
-Expr canonicaliseExpr(Expr e);
+struct ExprWith : Expr
+{
+ Pos pos;
+ Expr * attrs, * body;
+ unsigned int 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
+};
-/* Create an expression representing a boolean. */
-Expr makeBool(bool b);
+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
+};
-/* Manipulation of Str() nodes. Note: matchStr() does not clear
- context! */
-bool matchStr(Expr e, string & s, PathSet & context);
+#define MakeBinOp(name, s) \
+ struct Expr##name : Expr \
+ { \
+ Expr * e1, * e2; \
+ Expr##name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \
+ void show(std::ostream & str) \
+ { \
+ str << *e1 << " " s " " << *e2; \
+ } \
+ void bindVars(const StaticEnv & env) \
+ { \
+ e1->bindVars(env); e2->bindVars(env); \
+ } \
+ void eval(EvalState & state, Env & env, Value & v); \
+ };
+
+MakeBinOp(App, "")
+MakeBinOp(OpEq, "==")
+MakeBinOp(OpNEq, "!=")
+MakeBinOp(OpAnd, "&&")
+MakeBinOp(OpOr, "||")
+MakeBinOp(OpImpl, "->")
+MakeBinOp(OpUpdate, "//")
+MakeBinOp(OpConcatLists, "++")
+
+struct ExprConcatStrings : Expr
+{
+ vector<Expr *> * es;
+ ExprConcatStrings(vector<Expr *> * es) : es(es) { };
+ COMMON_METHODS
+};
-Expr makeStr(const string & s, const PathSet & context = PathSet());
+/* 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 std::map<Symbol, unsigned int> Vars;
+ Vars vars;
+ StaticEnv(bool isWith, const StaticEnv * up) : isWith(isWith), up(up) { };
+};
-/* Showing types, values. */
-string showType(Expr e);
-string showValue(Expr e);
-
}
diff --git a/src/libexpr/parser.hh b/src/libexpr/parser.hh
index 334822b5e..c8c8ad809 100644
--- a/src/libexpr/parser.hh
+++ b/src/libexpr/parser.hh
@@ -8,12 +8,11 @@ namespace nix {
/* Parse a Nix expression from the specified file. If `path' refers
- to a directory, the "/default.nix" is appended. */
-Expr parseExprFromFile(EvalState & state, Path path);
+ to a directory, then "/default.nix" is appended. */
+Expr * parseExprFromFile(EvalState & state, Path path);
/* Parse a Nix expression from the specified string. */
-Expr parseExprFromString(EvalState & state, const string & s,
- const Path & basePath);
+Expr * parseExprFromString(EvalState & state, const string & s, const Path & basePath);
}
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 8706ce025..7236bab19 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -20,16 +20,14 @@
#include <stdlib.h>
#include <string.h>
-#include "aterm.hh"
#include "util.hh"
+#include "nixexpr.hh"
+
#include "parser-tab.hh"
#include "lexer-tab.hh"
#define YYSTYPE YYSTYPE // workaround a bug in Bison 2.4
-#include "nixexpr.hh"
-#include "nixexpr-ast.hh"
-
using namespace nix;
@@ -39,145 +37,85 @@ namespace nix {
struct ParseData
{
- Expr result;
+ SymbolTable & symbols;
+ Expr * result;
Path basePath;
Path path;
string error;
+ Symbol sLetBody;
+ ParseData(SymbolTable & symbols)
+ : symbols(symbols)
+ , sLetBody(symbols.create("<let-body>"))
+ { };
};
-
-static string showAttrPath(ATermList attrPath)
+
+static string showAttrPath(const vector<Symbol> & attrPath)
{
string s;
- for (ATermIterator i(attrPath); i; ++i) {
+ foreach (vector<Symbol>::const_iterator, i, attrPath) {
if (!s.empty()) s += '.';
- s += aterm2String(*i);
+ s += *i;
}
return s;
}
-
-
-struct Tree
-{
- Expr leaf; ATerm pos; bool recursive;
- typedef std::map<ATerm, Tree> Children;
- Children children;
- Tree() { leaf = 0; recursive = true; }
-};
-static ATermList buildAttrs(const Tree & t, ATermList & nonrec)
+static void dupAttr(const vector<Symbol> & attrPath, const Pos & pos, const Pos & prevPos)
{
- ATermList res = ATempty;
- for (Tree::Children::const_reverse_iterator i = t.children.rbegin();
- i != t.children.rend(); ++i)
- if (!i->second.recursive)
- nonrec = ATinsert(nonrec, makeBind(i->first, i->second.leaf, i->second.pos));
- else
- res = ATinsert(res, i->second.leaf
- ? makeBind(i->first, i->second.leaf, i->second.pos)
- : makeBind(i->first, makeAttrs(buildAttrs(i->second, nonrec)), makeNoPos()));
- return res;
+ throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
+ % showAttrPath(attrPath) % pos % prevPos);
}
-static Expr fixAttrs(bool recursive, ATermList as)
+static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
{
- Tree attrs;
-
- /* This ATermMap is needed to ensure that the `leaf' fields in the
- Tree nodes are not garbage collected. */
- ATermMap gcRoots;
-
- for (ATermIterator i(as); i; ++i) {
- ATermList names, attrPath; Expr src, e; ATerm name, pos;
-
- if (matchInherit(*i, src, names, pos)) {
- bool fromScope = matchScope(src);
- for (ATermIterator j(names); j; ++j) {
- if (attrs.children.find(*j) != attrs.children.end())
- throw ParseError(format("duplicate definition of attribute `%1%' at %2%")
- % showAttrPath(ATmakeList1(*j)) % showPos(pos));
- Tree & t(attrs.children[*j]);
- Expr leaf = fromScope ? makeVar(*j) : makeSelect(src, *j);
- gcRoots.set(leaf, leaf);
- t.leaf = leaf;
- t.pos = pos;
- if (recursive && fromScope) t.recursive = false;
- }
- }
-
- else if (matchBindAttrPath(*i, attrPath, e, pos)) {
-
- Tree * t(&attrs);
-
- for (ATermIterator j(attrPath); j; ) {
- name = *j; ++j;
- if (t->leaf) throw ParseError(format("attribute set containing `%1%' at %2% already defined at %3%")
- % showAttrPath(attrPath) % showPos(pos) % showPos (t->pos));
- t = &(t->children[name]);
- }
-
- if (t->leaf)
- throw ParseError(format("duplicate definition of attribute `%1%' at %2% and %3%")
- % showAttrPath(attrPath) % showPos(pos) % showPos (t->pos));
- if (!t->children.empty())
- throw ParseError(format("duplicate definition of attribute `%1%' at %2%")
- % showAttrPath(attrPath) % showPos(pos));
-
- t->leaf = e; t->pos = pos;
- }
-
- else abort(); /* can't happen */
- }
-
- ATermList nonrec = ATempty;
- ATermList rec = buildAttrs(attrs, nonrec);
-
- return recursive ? makeRec(rec, nonrec) : makeAttrs(rec);
+ vector<Symbol> attrPath; attrPath.push_back(attr);
+ throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
+ % showAttrPath(attrPath) % pos % prevPos);
}
+
-
-static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat)
+static void addAttr(ExprAttrs * attrs, const vector<Symbol> & attrPath,
+ Expr * e, const Pos & pos)
{
- ATerm name;
- ATermList formals;
- Pattern pat1, pat2;
- ATermBool ellipsis;
- if (matchVarPat(pat, name)) {
- if (map.get(name))
- throw ParseError(format("duplicate formal function argument `%1%' at %2%")
- % aterm2String(name) % showPos(pos));
- map.set(name, name);
- }
- else if (matchAttrsPat(pat, formals, ellipsis)) {
- for (ATermIterator i(formals); i; ++i) {
- ATerm d1;
- if (!matchFormal(*i, name, d1)) abort();
- if (map.get(name))
- throw ParseError(format("duplicate formal function argument `%1%' at %2%")
- % aterm2String(name) % showPos(pos));
- map.set(name, name);
+ unsigned int n = 0;
+ foreach (vector<Symbol>::const_iterator, i, attrPath) {
+ n++;
+ ExprAttrs::Attrs::iterator j = attrs->attrs.find(*i);
+ if (j != attrs->attrs.end()) {
+ ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.first);
+ if (!attrs2 || n == attrPath.size()) dupAttr(attrPath, pos, j->second.second);
+ attrs = attrs2;
+ } else {
+ if (attrs->attrNames.find(*i) != attrs->attrNames.end())
+ dupAttr(attrPath, pos, attrs->attrNames[*i]);
+ attrs->attrNames[*i] = pos;
+ if (n == attrPath.size())
+ attrs->attrs[*i] = ExprAttrs::Attr(e, pos);
+ else {
+ ExprAttrs * nested = new ExprAttrs;
+ attrs->attrs[*i] = ExprAttrs::Attr(nested, pos);
+ attrs = nested;
+ }
}
}
- else if (matchAtPat(pat, pat1, pat2)) {
- checkPatternVars(pos, map, pat1);
- checkPatternVars(pos, map, pat2);
- }
- else abort();
}
-static void checkPatternVars(ATerm pos, Pattern pat)
+static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
{
- ATermMap map;
- checkPatternVars(pos, map, pat);
+ if (formals->argNames.find(formal.name) != formals->argNames.end())
+ throw ParseError(format("duplicate formal function argument `%1%' at %2%")
+ % formal.name % pos);
+ formals->formals.push_front(formal);
+ formals->argNames.insert(formal.name);
}
-static Expr stripIndentation(ATermList es)
+static Expr * stripIndentation(vector<Expr *> & es)
{
- if (es == ATempty) return makeStr("");
+ if (es.empty()) return new ExprString("");
/* Figure out the minimum indentation. Note that by design
whitespace-only final lines are not taken into account. (So
@@ -185,9 +123,9 @@ static Expr stripIndentation(ATermList es)
bool atStartOfLine = true; /* = seen only whitespace in the current line */
unsigned int minIndent = 1000000;
unsigned int curIndent = 0;
- ATerm e;
- for (ATermIterator i(es); i; ++i) {
- if (!matchIndStr(*i, e)) {
+ foreach (vector<Expr *>::iterator, i, es) {
+ ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
+ if (!e) {
/* Anti-quotations end the current start-of-line whitespace. */
if (atStartOfLine) {
atStartOfLine = false;
@@ -195,12 +133,11 @@ static Expr stripIndentation(ATermList es)
}
continue;
}
- string s = aterm2String(e);
- for (unsigned int j = 0; j < s.size(); ++j) {
+ for (unsigned int j = 0; j < e->s.size(); ++j) {
if (atStartOfLine) {
- if (s[j] == ' ')
+ if (e->s[j] == ' ')
curIndent++;
- else if (s[j] == '\n') {
+ else if (e->s[j] == '\n') {
/* Empty line, doesn't influence minimum
indentation. */
curIndent = 0;
@@ -208,7 +145,7 @@ static Expr stripIndentation(ATermList es)
atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent;
}
- } else if (s[j] == '\n') {
+ } else if (e->s[j] == '\n') {
atStartOfLine = true;
curIndent = 0;
}
@@ -216,37 +153,37 @@ static Expr stripIndentation(ATermList es)
}
/* Strip spaces from each line. */
- ATermList es2 = ATempty;
+ vector<Expr *> * es2 = new vector<Expr *>;
atStartOfLine = true;
unsigned int curDropped = 0;
- unsigned int n = ATgetLength(es);
- for (ATermIterator i(es); i; ++i, --n) {
- if (!matchIndStr(*i, e)) {
+ unsigned int n = es.size();
+ for (vector<Expr *>::iterator i = es.begin(); i != es.end(); ++i, --n) {
+ ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
+ if (!e) {
atStartOfLine = false;
curDropped = 0;
- es2 = ATinsert(es2, *i);
+ es2->push_back(*i);
continue;
}
- string s = aterm2String(e);
string s2;
- for (unsigned int j = 0; j < s.size(); ++j) {
+ for (unsigned int j = 0; j < e->s.size(); ++j) {
if (atStartOfLine) {
- if (s[j] == ' ') {
+ if (e->s[j] == ' ') {
if (curDropped++ >= minIndent)
- s2 += s[j];
+ s2 += e->s[j];
}
- else if (s[j] == '\n') {
+ else if (e->s[j] == '\n') {
curDropped = 0;
- s2 += s[j];
+ s2 += e->s[j];
} else {
atStartOfLine = false;
curDropped = 0;
- s2 += s[j];
+ s2 += e->s[j];
}
} else {
- s2 += s[j];
- if (s[j] == '\n') atStartOfLine = true;
+ s2 += e->s[j];
+ if (e->s[j] == '\n') atStartOfLine = true;
}
}
@@ -257,11 +194,11 @@ static Expr stripIndentation(ATermList es)
if (p != string::npos && s2.find_first_not_of(' ', p + 1) == string::npos)
s2 = string(s2, 0, p + 1);
}
-
- es2 = ATinsert(es2, makeStr(s2));
+
+ es2->push_back(new ExprString(s2));
}
- return makeConcatStrings(ATreverse(es2));
+ return new ExprConcatStrings(es2);
}
@@ -269,13 +206,12 @@ void backToString(yyscan_t scanner);
void backToIndString(yyscan_t scanner);
-static Pos makeCurPos(YYLTYPE * loc, ParseData * data)
+static Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
{
- return makePos(toATerm(data->path),
- loc->first_line, loc->first_column);
+ return Pos(data->path, loc.first_line, loc.first_column);
}
-#define CUR_POS makeCurPos(yylocp, data)
+#define CUR_POS makeCurPos(*yylocp, data)
}
@@ -283,29 +219,10 @@ static Pos makeCurPos(YYLTYPE * loc, ParseData * data)
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error)
{
- data->error = (format("%1%, at `%2%':%3%:%4%")
- % error % data->path % loc->first_line % loc->first_column).str();
-}
-
-
-/* Make sure that the parse stack is scanned by the ATerm garbage
- collector. */
-static void * mallocAndProtect(size_t size)
-{
- void * p = malloc(size);
- if (p) ATprotectMemory(p, size);
- return p;
-}
-
-static void freeAndUnprotect(void * p)
-{
- ATunprotectMemory(p);
- free(p);
+ data->error = (format("%1%, at %2%")
+ % error % makeCurPos(*loc, data)).str();
}
-#define YYMALLOC mallocAndProtect
-#define YYFREE freeAndUnprotect
-
#endif
@@ -313,20 +230,32 @@ static void freeAndUnprotect(void * p)
%}
%union {
- ATerm t;
- ATermList ts;
- struct {
- ATermList formals;
- bool ellipsis;
- } formals;
+ nix::Expr * e;
+ nix::ExprList * list;
+ nix::ExprAttrs * attrs;
+ nix::Formals * formals;
+ nix::Formal * formal;
+ int n;
+ char * id; // !!! -> Symbol
+ char * path;
+ char * uri;
+ std::vector<nix::Symbol> * ids;
+ std::vector<nix::Expr *> * string_parts;
}
-%type <t> start expr expr_function expr_if expr_op
-%type <t> expr_app expr_select expr_simple bind inheritsrc formal
-%type <t> pattern pattern2
-%type <ts> binds ids attrpath expr_list string_parts ind_string_parts
+%type <e> start expr expr_function expr_if expr_op
+%type <e> expr_app expr_select expr_simple
+%type <list> expr_list
+%type <attrs> binds
%type <formals> formals
-%token <t> ID INT STR IND_STR PATH URI
+%type <formal> formal
+%type <ids> ids attrpath
+%type <string_parts> string_parts ind_string_parts
+%token <id> ID ATTRPATH
+%token <e> STR IND_STR
+%token <n> INT
+%token <path> PATH
+%token <uri> URI
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL
%token DOLLAR_CURLY /* == ${ */
%token IND_STRING_OPEN IND_STRING_CLOSE
@@ -350,163 +279,172 @@ start: expr { data->result = $1; };
expr: expr_function;
expr_function
- : pattern ':' expr_function
- { checkPatternVars(CUR_POS, $1); $$ = makeFunction($1, $3, CUR_POS); }
+ : ID ':' expr_function
+ { $$ = new ExprLambda(CUR_POS, data->symbols.create($1), false, 0, $3); }
+ | '{' formals '}' ':' expr_function
+ { $$ = new ExprLambda(CUR_POS, data->symbols.create(""), true, $2, $5); }
+ | '{' formals '}' '@' ID ':' expr_function
+ { $$ = new ExprLambda(CUR_POS, data->symbols.create($5), true, $2, $7); }
+ | ID '@' '{' formals '}' ':' expr_function
+ { $$ = new ExprLambda(CUR_POS, data->symbols.create($1), true, $4, $7); }
| ASSERT expr ';' expr_function
- { $$ = makeAssert($2, $4, CUR_POS); }
+ { $$ = new ExprAssert(CUR_POS, $2, $4); }
| WITH expr ';' expr_function
- { $$ = makeWith($2, $4, CUR_POS); }
+ { $$ = new ExprWith(CUR_POS, $2, $4); }
| LET binds IN expr_function
- { $$ = makeSelect(fixAttrs(true, ATinsert($2, makeBindAttrPath(ATmakeList1(toATerm("<let-body>")), $4, CUR_POS))), toATerm("<let-body>")); }
+ { $$ = new ExprLet($2, $4); }
| expr_if
;
expr_if
- : IF expr THEN expr ELSE expr
- { $$ = makeIf($2, $4, $6); }
+ : IF expr THEN expr ELSE expr { $$ = new ExprIf($2, $4, $6); }
| expr_op
;
expr_op
- : '!' expr_op %prec NEG { $$ = makeOpNot($2); }
- | expr_op EQ expr_op { $$ = makeOpEq($1, $3); }
- | expr_op NEQ expr_op { $$ = makeOpNEq($1, $3); }
- | expr_op AND expr_op { $$ = makeOpAnd($1, $3); }
- | expr_op OR expr_op { $$ = makeOpOr($1, $3); }
- | expr_op IMPL expr_op { $$ = makeOpImpl($1, $3); }
- | expr_op UPDATE expr_op { $$ = makeOpUpdate($1, $3); }
- | expr_op '~' expr_op { $$ = makeSubPath($1, $3); }
- | expr_op '?' ID { $$ = makeOpHasAttr($1, $3); }
- | expr_op '+' expr_op { $$ = makeOpPlus($1, $3); }
- | expr_op CONCAT expr_op { $$ = makeOpConcat($1, $3); }
+ : '!' expr_op %prec NEG { $$ = new ExprOpNot($2); }
+ | expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
+ | expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
+ | expr_op AND expr_op { $$ = new ExprOpAnd($1, $3); }
+ | expr_op OR expr_op { $$ = new ExprOpOr($1, $3); }
+ | expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); }
+ | expr_op UPDATE expr_op { $$ = new ExprOpUpdate($1, $3); }
+ | expr_op '?' ID { $$ = new ExprOpHasAttr($1, data->symbols.create($3)); }
+ | expr_op '+' expr_op
+ { vector<Expr *> * l = new vector<Expr *>;
+ l->push_back($1);
+ l->push_back($3);
+ $$ = new ExprConcatStrings(l);
+ }
+ | expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); }
| expr_app
;
expr_app
: expr_app expr_select
- { $$ = makeCall($1, $2); }
+ { $$ = new ExprApp($1, $2); }
| expr_select { $$ = $1; }
;
expr_select
: expr_select '.' ID
- { $$ = makeSelect($1, $3); }
+ { $$ = new ExprSelect($1, data->symbols.create($3)); }
| expr_simple { $$ = $1; }
;
expr_simple
- : ID { $$ = makeVar($1); }
- | INT { $$ = makeInt(ATgetInt((ATermInt) $1)); }
+ : ID { $$ = new ExprVar(data->symbols.create($1)); }
+ | INT { $$ = new ExprInt($1); }
| '"' string_parts '"' {
/* For efficiency, and to simplify parse trees a bit. */
- if ($2 == ATempty) $$ = makeStr(toATerm(""), ATempty);
- else if (ATgetNext($2) == ATempty) $$ = ATgetFirst($2);
- else $$ = makeConcatStrings(ATreverse($2));
+ if ($2->empty()) $$ = new ExprString("");
+ else if ($2->size() == 1) $$ = $2->front();
+ else $$ = new ExprConcatStrings($2);
}
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
- $$ = stripIndentation(ATreverse($2));
+ $$ = stripIndentation(*$2);
}
- | PATH { $$ = makePath(toATerm(absPath(aterm2String($1), data->basePath))); }
- | URI { $$ = makeStr($1, ATempty); }
+ | PATH { $$ = new ExprPath(absPath($1, data->basePath)); }
+ | URI { $$ = new ExprString($1); }
| '(' expr ')' { $$ = $2; }
/* Let expressions `let {..., body = ...}' are just desugared
into `(rec {..., body = ...}).body'. */
| LET '{' binds '}'
- { $$ = makeSelect(fixAttrs(true, $3), toATerm("body")); }
+ { $3->recursive = true; $$ = new ExprSelect($3, data->symbols.create("body")); }
| REC '{' binds '}'
- { $$ = fixAttrs(true, $3); }
+ { $3->recursive = true; $$ = $3; }
| '{' binds '}'
- { $$ = fixAttrs(false, $2); }
- | '[' expr_list ']' { $$ = makeList(ATreverse($2)); }
+ { $$ = $2; }
+ | '[' expr_list ']' { $$ = $2; }
;
string_parts
- : string_parts STR { $$ = ATinsert($1, $2); }
- | string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = ATinsert($1, $3); }
- | { $$ = ATempty; }
+ : string_parts STR { $$ = $1; $1->push_back($2); }
+ | string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = $1; $1->push_back($3); }
+ | { $$ = new vector<Expr *>; }
;
ind_string_parts
- : ind_string_parts IND_STR { $$ = ATinsert($1, $2); }
- | ind_string_parts DOLLAR_CURLY expr '}' { backToIndString(scanner); $$ = ATinsert($1, $3); }
- | { $$ = ATempty; }
- ;
-
-pattern
- : pattern2 '@' pattern { $$ = makeAtPat($1, $3); }
- | pattern2
- ;
-
-pattern2
- : ID { $$ = makeVarPat($1); }
- | '{' formals '}' { $$ = makeAttrsPat($2.formals, $2.ellipsis ? eTrue : eFalse); }
+ : ind_string_parts IND_STR { $$ = $1; $1->push_back($2); }
+ | ind_string_parts DOLLAR_CURLY expr '}' { backToIndString(scanner); $$ = $1; $1->push_back($3); }
+ | { $$ = new vector<Expr *>; }
;
binds
- : binds bind { $$ = ATinsert($1, $2); }
- | { $$ = ATempty; }
- ;
-
-bind
- : attrpath '=' expr ';'
- { $$ = makeBindAttrPath(ATreverse($1), $3, CUR_POS); }
- | INHERIT inheritsrc ids ';'
- { $$ = makeInherit($2, $3, CUR_POS); }
+ : binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); }
+ | binds INHERIT ids ';'
+ { $$ = $1;
+ foreach (vector<Symbol>::iterator, i, *$3) {
+ if ($$->attrNames.find(*i) != $$->attrNames.end())
+ dupAttr(*i, makeCurPos(@3, data), $$->attrNames[*i]);
+ Pos pos = makeCurPos(@3, data);
+ $$->inherited.push_back(ExprAttrs::Inherited(*i, pos));
+ $$->attrNames[*i] = pos;
+ }
+ }
+ | binds INHERIT '(' expr ')' ids ';'
+ { $$ = $1;
+ /* !!! Should ensure sharing of the expression in $4. */
+ foreach (vector<Symbol>::iterator, i, *$6) {
+ if ($$->attrNames.find(*i) != $$->attrNames.end())
+ dupAttr(*i, makeCurPos(@6, data), $$->attrNames[*i]);
+ $$->attrs[*i] = ExprAttrs::Attr(new ExprSelect($4, *i), makeCurPos(@6, data));
+ $$->attrNames[*i] = makeCurPos(@6, data);
+ }}
+
+ | { $$ = new ExprAttrs; }
;
-inheritsrc
- : '(' expr ')' { $$ = $2; }
- | { $$ = makeScope(); }
+ids
+ : ids ID { $$ = $1; $1->push_back(data->symbols.create($2)); /* !!! dangerous */ }
+ | { $$ = new vector<Symbol>; }
;
-ids: ids ID { $$ = ATinsert($1, $2); } | { $$ = ATempty; };
-
attrpath
- : attrpath '.' ID { $$ = ATinsert($1, $3); }
- | ID { $$ = ATmakeList1($1); }
+ : attrpath '.' ID { $$ = $1; $1->push_back(data->symbols.create($3)); }
+ | ID { $$ = new vector<Symbol>; $$->push_back(data->symbols.create($1)); }
;
expr_list
- : expr_list expr_select { $$ = ATinsert($1, $2); }
- | { $$ = ATempty; }
+ : expr_list expr_select { $$ = $1; $1->elems.push_back($2); /* !!! dangerous */ }
+ | { $$ = new ExprList; }
;
formals
- : formal ',' formals /* !!! right recursive */
- { $$.formals = ATinsert($3.formals, $1); $$.ellipsis = $3.ellipsis; }
+ : formal ',' formals
+ { $$ = $3; addFormal(CUR_POS, $$, *$1); }
| formal
- { $$.formals = ATinsert(ATempty, $1); $$.ellipsis = false; }
+ { $$ = new Formals; addFormal(CUR_POS, $$, *$1); $$->ellipsis = false; }
|
- { $$.formals = ATempty; $$.ellipsis = false; }
+ { $$ = new Formals; $$->ellipsis = false; }
| ELLIPSIS
- { $$.formals = ATempty; $$.ellipsis = true; }
+ { $$ = new Formals; $$->ellipsis = true; }
;
formal
- : ID { $$ = makeFormal($1, makeNoDefaultValue()); }
- | ID '?' expr { $$ = makeFormal($1, makeDefaultValue($3)); }
+ : ID { $$ = new Formal(data->symbols.create($1), 0); }
+ | ID '?' expr { $$ = new Formal(data->symbols.create($1), $3); }
;
%%
-#include "eval.hh"
-
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
+#include <eval.hh>
+
namespace nix {
-static Expr parse(EvalState & state,
- const char * text, const Path & path,
- const Path & basePath)
+static Expr * parse(EvalState & state, const char * text,
+ const Path & path, const Path & basePath)
{
yyscan_t scanner;
- ParseData data;
+ ParseData data(state.symbols);
data.basePath = basePath;
data.path = path;
@@ -518,7 +456,7 @@ static Expr parse(EvalState & state,
if (res) throw ParseError(data.error);
try {
- checkVarDefs(state.primOps, data.result);
+ data.result->bindVars(state.staticBaseEnv);
} catch (Error & e) {
throw ParseError(format("%1%, in `%2%'") % e.msg() % path);
}
@@ -527,16 +465,10 @@ static Expr parse(EvalState & state,
}
-Expr parseExprFromFile(EvalState & state, Path path)
+Expr * parseExprFromFile(EvalState & state, Path path)
{
assert(path[0] == '/');
-#if 0
- /* Perhaps this is already an imploded parse tree? */
- Expr e = ATreadFromNamedFile(path.c_str());
- if (e) return e;
-#endif
-
/* If `path' is a symlink, follow it. This is so that relative
path references work. */
struct stat st;
@@ -558,7 +490,7 @@ Expr parseExprFromFile(EvalState & state, Path path)
}
-Expr parseExprFromString(EvalState & state,
+Expr * parseExprFromString(EvalState & state,
const string & s, const Path & basePath)
{
return parse(state, s.c_str(), "(string)", basePath);
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index bf2752d0d..9d36fb6a0 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -4,98 +4,32 @@
#include "store-api.hh"
#include "util.hh"
#include "archive.hh"
-#include "expr-to-xml.hh"
-#include "nixexpr-ast.hh"
+#include "value-to-xml.hh"
#include "parser.hh"
#include "names.hh"
-#include "aterm.hh"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <algorithm>
+#include <cstring>
namespace nix {
/*************************************************************
- * Constants
- *************************************************************/
-
-
-static Expr prim_builtins(EvalState & state, const ATermVector & args)
-{
- /* Return an attribute set containing all primops. This allows
- Nix expressions to test for new primops and take appropriate
- action if they're not available. For instance, rather than
- calling a primop `foo' directly, they could say `if builtins ?
- foo then builtins.foo ... else ...'. */
-
- ATermMap builtins(state.primOps.size());
-
- for (ATermMap::const_iterator i = state.primOps.begin();
- i != state.primOps.end(); ++i)
- {
- string name = aterm2String(i->key);
- if (string(name, 0, 2) == "__")
- name = string(name, 2);
- /* !!! should use makePrimOp here, I guess. */
- builtins.set(toATerm(name), makeAttrRHS(makeVar(i->key), makeNoPos()));
- }
-
- return makeAttrs(builtins);
-}
-
-
-/* Boolean constructors. */
-static Expr prim_true(EvalState & state, const ATermVector & args)
-{
- return eTrue;
-}
-
-
-static Expr prim_false(EvalState & state, const ATermVector & args)
-{
- return eFalse;
-}
-
-
-/* Return the null value. */
-static Expr prim_null(EvalState & state, const ATermVector & args)
-{
- return makeNull();
-}
-
-
-/* Return a string constant representing the current platform. Note!
- that differs between platforms, so Nix expressions using
- `__currentSystem' can evaluate to different values on different
- platforms. */
-static Expr prim_currentSystem(EvalState & state, const ATermVector & args)
-{
- return makeStr(thisSystem);
-}
-
-
-static Expr prim_currentTime(EvalState & state, const ATermVector & args)
-{
- return ATmake("Int(<int>)", time(0));
-}
-
-
-/*************************************************************
* Miscellaneous
*************************************************************/
/* Load and evaluate an expression from path specified by the
argument. */
-static Expr prim_import(EvalState & state, const ATermVector & args)
+static void prim_import(EvalState & state, Value * * args, Value & v)
{
PathSet context;
- Path path = coerceToPath(state, args[0], context);
+ Path path = state.coerceToPath(*args[0], context);
for (PathSet::iterator i = context.begin(); i != context.end(); ++i) {
assert(isStorePath(*i));
@@ -106,164 +40,203 @@ static Expr prim_import(EvalState & state, const ATermVector & args)
store->buildDerivations(singleton<PathSet>(*i));
}
- return evalFile(state, path);
+ state.evalFile(path, v);
}
/* Determine whether the argument is the null value. */
-static Expr prim_isNull(EvalState & state, const ATermVector & args)
+static void prim_isNull(EvalState & state, Value * * args, Value & v)
{
- return makeBool(matchNull(evalExpr(state, args[0])));
+ state.forceValue(*args[0]);
+ mkBool(v, args[0]->type == tNull);
}
/* Determine whether the argument is a function. */
-static Expr prim_isFunction(EvalState & state, const ATermVector & args)
+static void prim_isFunction(EvalState & state, Value * * args, Value & v)
{
- Expr e = evalExpr(state, args[0]);
- Pattern pat;
- ATerm body, pos;
- return makeBool(matchFunction(e, pat, body, pos));
+ state.forceValue(*args[0]);
+ mkBool(v, args[0]->type == tLambda);
}
+
/* Determine whether the argument is an Int. */
-static Expr prim_isInt(EvalState & state, const ATermVector & args)
+static void prim_isInt(EvalState & state, Value * * args, Value & v)
{
- int i;
- return makeBool(matchInt(evalExpr(state, args[0]), i));
+ state.forceValue(*args[0]);
+ mkBool(v, args[0]->type == tInt);
}
+
/* Determine whether the argument is an String. */
-static Expr prim_isString(EvalState & state, const ATermVector & args)
+static void prim_isString(EvalState & state, Value * * args, Value & v)
{
- string s;
- PathSet l;
- return makeBool(matchStr(evalExpr(state, args[0]), s, l));
+ state.forceValue(*args[0]);
+ mkBool(v, args[0]->type == tString);
}
+
/* Determine whether the argument is an Bool. */
-static Expr prim_isBool(EvalState & state, const ATermVector & args)
+static void prim_isBool(EvalState & state, Value * * args, Value & v)
{
- ATermBool b;
- return makeBool(matchBool(evalExpr(state, args[0]), b));
+ state.forceValue(*args[0]);
+ mkBool(v, args[0]->type == tBool);
}
-static Expr prim_genericClosure(EvalState & state, const ATermVector & args)
+
+struct CompareValues
+{
+ bool operator () (const Value & v1, const Value & v2) const
+ {
+ if (v1.type != v2.type)
+ throw EvalError("cannot compare values of different types");
+ switch (v1.type) {
+ case tInt:
+ return v1.integer < v2.integer;
+ case tString:
+ return strcmp(v1.string.s, v2.string.s) < 0;
+ case tPath:
+ return strcmp(v1.path, v2.path) < 0;
+ default:
+ throw EvalError(format("cannot compare %1% with %2%") % showType(v1) % showType(v2));
+ }
+ }
+};
+
+
+static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
{
startNest(nest, lvlDebug, "finding dependencies");
- Expr attrs = evalExpr(state, args[0]);
+ state.forceAttrs(*args[0]);
/* Get the start set. */
- Expr startSet = queryAttr(attrs, "startSet");
- if (!startSet) throw EvalError("attribute `startSet' required");
- ATermList startSet2 = evalList(state, startSet);
+ Bindings::iterator startSet =
+ args[0]->attrs->find(state.symbols.create("startSet"));
+ if (startSet == args[0]->attrs->end())
+ throw EvalError("attribute `startSet' required");
+ state.forceList(startSet->second.value);
- set<Expr> workSet; // !!! gc roots
- for (ATermIterator i(startSet2); i; ++i) workSet.insert(*i);
+ list<Value *> workSet;
+ for (unsigned int n = 0; n < startSet->second.value.list.length; ++n)
+ workSet.push_back(startSet->second.value.list.elems[n]);
/* Get the operator. */
- Expr op = queryAttr(attrs, "operator");
- if (!op) throw EvalError("attribute `operator' required");
-
+ Bindings::iterator op =
+ args[0]->attrs->find(state.symbols.create("operator"));
+ if (op == args[0]->attrs->end())
+ throw EvalError("attribute `operator' required");
+ state.forceValue(op->second.value);
+
/* Construct the closure by applying the operator to element of
`workSet', adding the result to `workSet', continuing until
no new elements are found. */
- ATermList res = ATempty;
- set<Expr> doneKeys; // !!! gc roots
+ list<Value> res;
+ set<Value, CompareValues> doneKeys;
while (!workSet.empty()) {
- Expr e = *(workSet.begin());
- workSet.erase(e);
+ Value * e = *(workSet.begin());
+ workSet.pop_front();
- e = strictEvalExpr(state, e);
+ state.forceAttrs(*e);
- Expr key = queryAttr(e, "key");
- if (!key) throw EvalError("attribute `key' required");
+ Bindings::iterator key =
+ e->attrs->find(state.symbols.create("key"));
+ if (key == e->attrs->end())
+ throw EvalError("attribute `key' required");
+ state.forceValue(key->second.value);
- if (doneKeys.find(key) != doneKeys.end()) continue;
- doneKeys.insert(key);
- res = ATinsert(res, e);
+ if (doneKeys.find(key->second.value) != doneKeys.end()) continue;
+ doneKeys.insert(key->second.value);
+ res.push_back(*e);
/* Call the `operator' function with `e' as argument. */
- ATermList res = evalList(state, makeCall(op, e));
-
- /* Try to find the dependencies relative to the `path'. */
- for (ATermIterator i(res); i; ++i)
- workSet.insert(evalExpr(state, *i));
+ Value call;
+ mkApp(call, op->second.value, *e);
+ state.forceList(call);
+
+ /* Add the values returned by the operator to the work set. */
+ for (unsigned int n = 0; n < call.list.length; ++n) {
+ state.forceValue(*call.list.elems[n]);
+ workSet.push_back(call.list.elems[n]);
+ }
}
- return makeList(res);
+ /* Create the result list. */
+ state.mkList(v, res.size());
+ Value * vs = state.allocValues(res.size());
+
+ unsigned int n = 0;
+ foreach (list<Value>::iterator, i, res) {
+ v.list.elems[n] = &vs[n];
+ vs[n++] = *i;
+ }
}
-static Expr prim_abort(EvalState & state, const ATermVector & args)
+static void prim_abort(EvalState & state, Value * * args, Value & v)
{
PathSet context;
throw Abort(format("evaluation aborted with the following error message: `%1%'") %
- evalString(state, args[0], context));
+ state.coerceToString(*args[0], context));
}
-static Expr prim_throw(EvalState & state, const ATermVector & args)
+static void prim_throw(EvalState & state, Value * * args, Value & v)
{
PathSet context;
throw ThrownError(format("user-thrown exception: %1%") %
- evalString(state, args[0], context));
+ state.coerceToString(*args[0], context));
}
-static Expr prim_addErrorContext(EvalState & state, const ATermVector & args)
+static void prim_addErrorContext(EvalState & state, Value * * args, Value & v)
{
- PathSet context;
try {
- return evalExpr(state, args[1]);
+ state.forceValue(*args[1]);
+ v = *args[1];
} catch (Error & e) {
- e.addPrefix(format("%1%\n") %
- evalString(state, args[0], context));
+ PathSet context;
+ e.addPrefix(format("%1%\n") % state.coerceToString(*args[0], context));
throw;
}
}
+
/* Try evaluating the argument. Success => {success=true; value=something;},
* else => {success=false; value=false;} */
-static Expr prim_tryEval(EvalState & state, const ATermVector & args)
+static void prim_tryEval(EvalState & state, Value * * args, Value & v)
{
- ATermMap res = ATermMap();
+ state.mkAttrs(v);
try {
- Expr val = evalExpr(state, args[0]);
- res.set(toATerm("value"), makeAttrRHS(val, makeNoPos()));
- res.set(toATerm("success"), makeAttrRHS(eTrue, makeNoPos()));
+ state.forceValue(*args[0]);
+ (*v.attrs)[state.symbols.create("value")].value = *args[0];
+ mkBool((*v.attrs)[state.symbols.create("success")].value, true);
} catch (AssertionError & e) {
- printMsg(lvlDebug, format("tryEval caught an error: %1%: %2%") % e.prefix() % e.msg());
- res.set(toATerm("value"), makeAttrRHS(eFalse, makeNoPos()));
- res.set(toATerm("success"), makeAttrRHS(eFalse, makeNoPos()));
+ mkBool((*v.attrs)[state.symbols.create("value")].value, false);
+ mkBool((*v.attrs)[state.symbols.create("success")].value, false);
}
- return makeAttrs(res);
}
/* Return an environment variable. Use with care. */
-static Expr prim_getEnv(EvalState & state, const ATermVector & args)
+static void prim_getEnv(EvalState & state, Value * * args, Value & v)
{
- string name = evalStringNoCtx(state, args[0]);
- return makeStr(getEnv(name));
+ string name = state.forceStringNoCtx(*args[0]);
+ mkString(v, getEnv(name));
}
-/* Evaluate the first expression, and print its abstract syntax tree
- on standard error. Then return the second expression. Useful for
- debugging.
- */
-static Expr prim_trace(EvalState & state, const ATermVector & args)
+/* Evaluate the first expression and print it on standard error. Then
+ return the second expression. Useful for debugging. */
+static void prim_trace(EvalState & state, Value * * args, Value & v)
{
- Expr e = evalExpr(state, args[0]);
- string s;
- PathSet context;
- if (matchStr(e, s, context))
- printMsg(lvlError, format("trace: %1%") % s);
+ state.forceValue(*args[0]);
+ if (args[0]->type == tString)
+ printMsg(lvlError, format("trace: %1%") % args[0]->string.s);
else
- printMsg(lvlError, format("trace: %1%") % e);
- return evalExpr(state, args[1]);
+ printMsg(lvlError, format("trace: %1%") % *args[0]);
+ state.forceValue(*args[1]);
+ v = *args[1];
}
@@ -325,7 +298,7 @@ static Hash hashDerivationModulo(EvalState & state, Derivation drv)
}
drv.inputDrvs = inputs2;
- return hashTerm(unparseDerivation(drv));
+ return hashString(htSHA256, unparseDerivation(drv));
}
@@ -336,28 +309,25 @@ static Hash hashDerivationModulo(EvalState & state, Derivation drv)
derivation; `drvPath' containing the path of the Nix expression;
and `type' set to `derivation' to indicate that this is a
derivation. */
-static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
+static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
{
startNest(nest, lvlVomit, "evaluating derivation");
- ATermMap attrs;
- queryAllAttrs(evalExpr(state, args[0]), attrs, true);
+ state.forceAttrs(*args[0]);
- /* Figure out the name already (for stack backtraces). */
- ATerm posDrvName;
- Expr eDrvName = attrs.get(toATerm("name"));
- if (!eDrvName)
+ /* Figure out the name first (for stack backtraces). */
+ Bindings::iterator attr = args[0]->attrs->find(state.sName);
+ if (attr == args[0]->attrs->end())
throw EvalError("required attribute `name' missing");
- if (!matchAttrRHS(eDrvName, eDrvName, posDrvName)) abort();
string drvName;
+ Pos & posDrvName(*attr->second.pos);
try {
- drvName = evalStringNoCtx(state, eDrvName);
+ drvName = state.forceStringNoCtx(attr->second.value);
} catch (Error & e) {
- e.addPrefix(format("while evaluating the derivation attribute `name' at %1%:\n")
- % showPos(posDrvName));
+ e.addPrefix(format("while evaluating the derivation attribute `name' at %1%:\n") % posDrvName);
throw;
}
-
+
/* Build the derivation expression by processing the attributes. */
Derivation drv;
@@ -366,12 +336,8 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
string outputHash, outputHashAlgo;
bool outputHashRecursive = false;
- for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) {
- string key = aterm2String(i->key);
- ATerm value;
- Expr pos;
- ATerm rhs = i->value;
- if (!matchAttrRHS(rhs, value, pos)) abort();
+ foreach (Bindings::iterator, i, *args[0]->attrs) {
+ string key = i->first;
startNest(nest, lvlVomit, format("processing attribute `%1%'") % key);
try {
@@ -379,15 +345,9 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
/* The `args' attribute is special: it supplies the
command-line arguments to the builder. */
if (key == "args") {
- ATermList es;
- value = evalExpr(state, value);
- if (!matchList(value, es)) {
- static bool haveWarned = false;
- warnOnce(haveWarned, "the `args' attribute should evaluate to a list");
- es = flattenList(state, value);
- }
- for (ATermIterator i(es); i; ++i) {
- string s = coerceToString(state, *i, context, true);
+ state.forceList(i->second.value);
+ for (unsigned int n = 0; n < i->second.value.list.length; ++n) {
+ string s = state.coerceToString(*i->second.value.list.elems[n], context, true);
drv.args.push_back(s);
}
}
@@ -395,11 +355,11 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
/* All other attributes are passed to the builder through
the environment. */
else {
- string s = coerceToString(state, value, context, true);
+ string s = state.coerceToString(i->second.value, context, true);
drv.env[key] = s;
if (key == "builder") drv.builder = s;
- else if (key == "system") drv.platform = s;
- else if (key == "name") drvName = s;
+ else if (i->first == state.sSystem) drv.platform = s;
+ else if (i->first == state.sName) drvName = s;
else if (key == "outputHash") outputHash = s;
else if (key == "outputHashAlgo") outputHashAlgo = s;
else if (key == "outputHashMode") {
@@ -411,12 +371,11 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
} catch (Error & e) {
e.addPrefix(format("while evaluating the derivation attribute `%1%' at %2%:\n")
- % key % showPos(pos));
+ % key % *i->second.pos);
e.addPrefix(format("while instantiating the derivation named `%1%' at %2%:\n")
- % drvName % showPos(posDrvName));
+ % drvName % posDrvName);
throw;
}
-
}
/* Everything in the context of the strings in the derivation
@@ -524,33 +483,9 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
state.drvHashes[drvPath] = hashDerivationModulo(state, drv);
/* !!! assumes a single output */
- ATermMap outAttrs(2);
- outAttrs.set(toATerm("outPath"),
- makeAttrRHS(makeStr(outPath, singleton<PathSet>(drvPath)), makeNoPos()));
- outAttrs.set(toATerm("drvPath"),
- makeAttrRHS(makeStr(drvPath, singleton<PathSet>("=" + drvPath)), makeNoPos()));
-
- return makeAttrs(outAttrs);
-}
-
-
-static Expr prim_derivationLazy(EvalState & state, const ATermVector & args)
-{
- Expr eAttrs = evalExpr(state, args[0]);
- ATermMap attrs;
- queryAllAttrs(eAttrs, attrs, true);
-
- attrs.set(toATerm("type"),
- makeAttrRHS(makeStr("derivation"), makeNoPos()));
-
- Expr drvStrict = makeCall(makeVar(toATerm("derivation!")), eAttrs);
-
- attrs.set(toATerm("outPath"),
- makeAttrRHS(makeSelect(drvStrict, toATerm("outPath")), makeNoPos()));
- attrs.set(toATerm("drvPath"),
- makeAttrRHS(makeSelect(drvStrict, toATerm("drvPath")), makeNoPos()));
-
- return makeAttrs(attrs);
+ state.mkAttrs(v);
+ mkString((*v.attrs)[state.sOutPath].value, outPath, singleton<PathSet>(drvPath));
+ mkString((*v.attrs)[state.sDrvPath].value, drvPath, singleton<PathSet>("=" + drvPath));
}
@@ -560,11 +495,11 @@ static Expr prim_derivationLazy(EvalState & state, const ATermVector & args)
/* Convert the argument to a path. !!! obsolete? */
-static Expr prim_toPath(EvalState & state, const ATermVector & args)
+static void prim_toPath(EvalState & state, Value * * args, Value & v)
{
PathSet context;
- string path = coerceToPath(state, args[0], context);
- return makeStr(canonPath(path), context);
+ Path path = state.coerceToPath(*args[0], context);
+ mkString(v, canonPath(path), context);
}
@@ -576,60 +511,58 @@ static Expr prim_toPath(EvalState & state, const ATermVector & args)
/nix/store/newhash-oldhash-oldname. In the past, `toPath' had
special case behaviour for store paths, but that created weird
corner cases. */
-static Expr prim_storePath(EvalState & state, const ATermVector & args)
+static void prim_storePath(EvalState & state, Value * * args, Value & v)
{
PathSet context;
- Path path = canonPath(coerceToPath(state, args[0], context));
+ Path path = canonPath(state.coerceToPath(*args[0], context));
if (!isInStore(path))
throw EvalError(format("path `%1%' is not in the Nix store") % path);
Path path2 = toStorePath(path);
if (!store->isValidPath(path2))
throw EvalError(format("store path `%1%' is not valid") % path2);
context.insert(path2);
- return makeStr(path, context);
+ mkString(v, path, context);
}
-static Expr prim_pathExists(EvalState & state, const ATermVector & args)
+static void prim_pathExists(EvalState & state, Value * * args, Value & v)
{
PathSet context;
- Path path = coerceToPath(state, args[0], context);
+ Path path = state.coerceToPath(*args[0], context);
if (!context.empty())
throw EvalError(format("string `%1%' cannot refer to other paths") % path);
- return makeBool(pathExists(path));
+ mkBool(v, pathExists(path));
}
/* Return the base name of the given string, i.e., everything
following the last slash. */
-static Expr prim_baseNameOf(EvalState & state, const ATermVector & args)
+static void prim_baseNameOf(EvalState & state, Value * * args, Value & v)
{
PathSet context;
- return makeStr(baseNameOf(coerceToString(state, args[0], context)), context);
+ mkString(v, baseNameOf(state.coerceToString(*args[0], context)), context);
}
/* Return the directory of the given path, i.e., everything before the
last slash. Return either a path or a string depending on the type
of the argument. */
-static Expr prim_dirOf(EvalState & state, const ATermVector & args)
+static void prim_dirOf(EvalState & state, Value * * args, Value & v)
{
PathSet context;
- Expr e = evalExpr(state, args[0]); ATerm dummy;
- bool isPath = matchPath(e, dummy);
- Path dir = dirOf(coerceToPath(state, e, context));
- return isPath ? makePath(toATerm(dir)) : makeStr(dir, context);
+ Path dir = dirOf(state.coerceToPath(*args[0], context));
+ if (args[0]->type == tPath) mkPath(v, dir.c_str()); else mkString(v, dir, context);
}
/* Return the contents of a file as a string. */
-static Expr prim_readFile(EvalState & state, const ATermVector & args)
+static void prim_readFile(EvalState & state, Value * * args, Value & v)
{
PathSet context;
- Path path = coerceToPath(state, args[0], context);
+ Path path = state.coerceToPath(*args[0], context);
if (!context.empty())
throw EvalError(format("string `%1%' cannot refer to other paths") % path);
- return makeStr(readFile(path));
+ mkString(v, readFile(path).c_str());
}
@@ -641,26 +574,26 @@ static Expr prim_readFile(EvalState & state, const ATermVector & args)
/* Convert the argument (which can be any Nix expression) to an XML
representation returned in a string. Not all Nix expressions can
be sensibly or completely represented (e.g., functions). */
-static Expr prim_toXML(EvalState & state, const ATermVector & args)
+static void prim_toXML(EvalState & state, Value * * args, Value & v)
{
std::ostringstream out;
PathSet context;
- printTermAsXML(strictEvalExpr(state, args[0]), out, context);
- return makeStr(out.str(), context);
+ printValueAsXML(state, true, false, *args[0], out, context);
+ mkString(v, out.str(), context);
}
/* Store a string in the Nix store as a source file that can be used
as an input by derivations. */
-static Expr prim_toFile(EvalState & state, const ATermVector & args)
+static void prim_toFile(EvalState & state, Value * * args, Value & v)
{
PathSet context;
- string name = evalStringNoCtx(state, args[0]);
- string contents = evalString(state, args[1], context);
+ string name = state.forceStringNoCtx(*args[0]);
+ string contents = state.forceString(*args[1], context);
PathSet refs;
- for (PathSet::iterator i = context.begin(); i != context.end(); ++i) {
+ foreach (PathSet::iterator, i, context) {
Path path = *i;
if (path.at(0) == '=') path = string(path, 1);
if (isDerivation(path))
@@ -675,17 +608,17 @@ static Expr prim_toFile(EvalState & state, const ATermVector & args)
/* Note: we don't need to add `context' to the context of the
result, since `storePath' itself has references to the paths
used in args[1]. */
-
- return makeStr(storePath, singleton<PathSet>(storePath));
+
+ mkString(v, storePath, singleton<PathSet>(storePath));
}
struct FilterFromExpr : PathFilter
{
EvalState & state;
- Expr filter;
+ Value & filter;
- FilterFromExpr(EvalState & state, Expr filter)
+ FilterFromExpr(EvalState & state, Value & filter)
: state(state), filter(filter)
{
}
@@ -696,35 +629,47 @@ struct FilterFromExpr : PathFilter
if (lstat(path.c_str(), &st))
throw SysError(format("getting attributes of path `%1%'") % path);
- Expr call =
- makeCall(
- makeCall(filter, makeStr(path)),
- makeStr(
- S_ISREG(st.st_mode) ? "regular" :
- S_ISDIR(st.st_mode) ? "directory" :
- S_ISLNK(st.st_mode) ? "symlink" :
- "unknown" /* not supported, will fail! */
- ));
-
- return evalBool(state, call);
+ /* Call the filter function. The first argument is the path,
+ the second is a string indicating the type of the file. */
+ Value arg1;
+ mkString(arg1, path);
+
+ Value fun2;
+ state.callFunction(filter, arg1, fun2);
+
+ Value arg2;
+ mkString(arg2,
+ S_ISREG(st.st_mode) ? "regular" :
+ S_ISDIR(st.st_mode) ? "directory" :
+ S_ISLNK(st.st_mode) ? "symlink" :
+ "unknown" /* not supported, will fail! */);
+
+ Value res;
+ state.callFunction(fun2, arg2, res);
+
+ return state.forceBool(res);
}
};
-static Expr prim_filterSource(EvalState & state, const ATermVector & args)
+static void prim_filterSource(EvalState & state, Value * * args, Value & v)
{
PathSet context;
- Path path = coerceToPath(state, args[1], context);
+ Path path = state.coerceToPath(*args[1], context);
if (!context.empty())
throw EvalError(format("string `%1%' cannot refer to other paths") % path);
- FilterFromExpr filter(state, args[0]);
+ state.forceValue(*args[0]);
+ if (args[0]->type != tLambda)
+ throw TypeError(format("first argument in call to `filterSource' is not a function but %1%") % showType(*args[0]));
+
+ FilterFromExpr filter(state, *args[0]);
Path dstPath = readOnlyMode
? computeStorePathForPath(path, true, htSHA256, filter).first
: store->addToStore(path, true, htSHA256, filter);
- return makeStr(dstPath, singleton<PathSet>(dstPath));
+ mkString(v, dstPath, singleton<PathSet>(dstPath));
}
@@ -735,131 +680,119 @@ static Expr prim_filterSource(EvalState & state, const ATermVector & args)
/* Return the names of the attributes in an attribute set as a sorted
list of strings. */
-static Expr prim_attrNames(EvalState & state, const ATermVector & args)
+static void prim_attrNames(EvalState & state, Value * * args, Value & v)
{
- ATermMap attrs;
- queryAllAttrs(evalExpr(state, args[0]), attrs);
+ state.forceAttrs(*args[0]);
- StringSet names;
- for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i)
- names.insert(aterm2String(i->key));
+ state.mkList(v, args[0]->attrs->size());
+ Value * vs = state.allocValues(v.list.length);
- ATermList list = ATempty;
- for (StringSet::const_reverse_iterator i = names.rbegin();
- i != names.rend(); ++i)
- list = ATinsert(list, makeStr(*i, PathSet()));
+ StringSet names;
+ foreach (Bindings::iterator, i, *args[0]->attrs)
+ names.insert(i->first);
- return makeList(list);
+ unsigned int n = 0;
+ foreach (StringSet::iterator, i, names) {
+ v.list.elems[n] = &vs[n];
+ mkString(vs[n++], *i);
+ }
}
/* Dynamic version of the `.' operator. */
-static Expr prim_getAttr(EvalState & state, const ATermVector & args)
+static void prim_getAttr(EvalState & state, Value * * args, Value & v)
{
- string attr = evalStringNoCtx(state, args[0]);
- return evalExpr(state, makeSelect(args[1], toATerm(attr)));
+ string attr = state.forceStringNoCtx(*args[0]);
+ state.forceAttrs(*args[1]);
+ // !!! Should we create a symbol here or just do a lookup?
+ Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
+ if (i == args[1]->attrs->end())
+ throw EvalError(format("attribute `%1%' missing") % attr);
+ // !!! add to stack trace?
+ state.forceValue(i->second.value);
+ v = i->second.value;
}
/* Dynamic version of the `?' operator. */
-static Expr prim_hasAttr(EvalState & state, const ATermVector & args)
+static void prim_hasAttr(EvalState & state, Value * * args, Value & v)
{
- string attr = evalStringNoCtx(state, args[0]);
- return evalExpr(state, makeOpHasAttr(args[1], toATerm(attr)));
+ string attr = state.forceStringNoCtx(*args[0]);
+ state.forceAttrs(*args[1]);
+ mkBool(v, args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end());
}
-/* Builds an attribute set from a list specifying (name, value)
- pairs. To be precise, a list [{name = "name1"; value = value1;}
- ... {name = "nameN"; value = valueN;}] is transformed to {name1 =
- value1; ... nameN = valueN;}. */
-static Expr prim_listToAttrs(EvalState & state, const ATermVector & args)
+/* Determine whether the argument is an attribute set. */
+static void prim_isAttrs(EvalState & state, Value * * args, Value & v)
{
- try {
- ATermMap res = ATermMap();
- ATermList list;
- list = evalList(state, args[0]);
- for (ATermIterator i(list); i; ++i){
- // *i should now contain a pointer to the list item expression
- ATermList attrs;
- Expr evaledExpr = evalExpr(state, *i);
- if (matchAttrs(evaledExpr, attrs)){
- Expr e = evalExpr(state, makeSelect(evaledExpr, toATerm("name")));
- string attr = evalStringNoCtx(state,e);
- Expr r = makeSelect(evaledExpr, toATerm("value"));
- res.set(toATerm(attr), makeAttrRHS(r, makeNoPos()));
- }
- else
- throw TypeError(format("list element in `listToAttrs' is %s, expected a set { name = \"<name>\"; value = <value>; }")
- % showType(evaledExpr));
- }
-
- return makeAttrs(res);
-
- } catch (Error & e) {
- e.addPrefix(format("in `listToAttrs':\n"));
- throw;
- }
+ state.forceValue(*args[0]);
+ mkBool(v, args[0]->type == tAttrs);
}
-static Expr prim_removeAttrs(EvalState & state, const ATermVector & args)
+static void prim_removeAttrs(EvalState & state, Value * * args, Value & v)
{
- ATermMap attrs;
- queryAllAttrs(evalExpr(state, args[0]), attrs, true);
-
- ATermList list = evalList(state, args[1]);
+ state.forceAttrs(*args[0]);
+ state.forceList(*args[1]);
- for (ATermIterator i(list); i; ++i)
- /* It's not an error for *i not to exist. */
- attrs.remove(toATerm(evalStringNoCtx(state, *i)));
+ state.cloneAttrs(*args[0], v);
- return makeAttrs(attrs);
+ for (unsigned int i = 0; i < args[1]->list.length; ++i) {
+ state.forceStringNoCtx(*args[1]->list.elems[i]);
+ v.attrs->erase(state.symbols.create(args[1]->list.elems[i]->string.s));
+ }
}
-/* Determine whether the argument is an attribute set. */
-static Expr prim_isAttrs(EvalState & state, const ATermVector & args)
+/* Builds an attribute set from a list specifying (name, value)
+ pairs. To be precise, a list [{name = "name1"; value = value1;}
+ ... {name = "nameN"; value = valueN;}] is transformed to {name1 =
+ value1; ... nameN = valueN;}. */
+static void prim_listToAttrs(EvalState & state, Value * * args, Value & v)
{
- ATermList list;
- return makeBool(matchAttrs(evalExpr(state, args[0]), list));
-}
+ state.forceList(*args[0]);
+ state.mkAttrs(v);
-/* Return the right-biased intersection of two attribute sets as1 and
- as2, i.e. a set that contains every attribute from as2 that is also
- a member of as1. */
-static Expr prim_intersectAttrs(EvalState & state, const ATermVector & args)
-{
- ATermMap as1, as2;
- queryAllAttrs(evalExpr(state, args[0]), as1, true);
- queryAllAttrs(evalExpr(state, args[1]), as2, true);
-
- ATermMap res;
- foreach (ATermMap::const_iterator, i, as2)
- if (as1[i->key]) res.set(i->key, i->value);
+ for (unsigned int i = 0; i < args[0]->list.length; ++i) {
+ Value & v2(*args[0]->list.elems[i]);
+ state.forceAttrs(v2);
+
+ Bindings::iterator j = v2.attrs->find(state.sName);
+ if (j == v2.attrs->end())
+ throw TypeError("`name' attribute missing in a call to `listToAttrs'");
+ string name = state.forceStringNoCtx(j->second.value);
+
+ j = v2.attrs->find(state.symbols.create("value"));
+ if (j == v2.attrs->end())
+ throw TypeError("`value' attribute missing in a call to `listToAttrs'");
- return makeAttrs(res);
+ Attr & a = (*v.attrs)[state.symbols.create(name)];
+ mkCopy(a.value, j->second.value);
+ a.pos = j->second.pos;
+ }
}
-static void attrsInPattern(ATermMap & map, Pattern pat)
+/* Return the right-biased intersection of two attribute sets as1 and
+ as2, i.e. a set that contains every attribute from as2 that is also
+ a member of as1. */
+static void prim_intersectAttrs(EvalState & state, Value * * args, Value & v)
{
- ATerm name;
- ATermList formals;
- Pattern pat1, pat2;
- ATermBool ellipsis;
- if (matchAttrsPat(pat, formals, ellipsis)) {
- for (ATermIterator i(formals); i; ++i) {
- ATerm def;
- if (!matchFormal(*i, name, def)) abort();
- map.set(name, makeAttrRHS(makeBool(def != constNoDefaultValue), makeNoPos()));
+ state.forceAttrs(*args[0]);
+ state.forceAttrs(*args[1]);
+
+ state.mkAttrs(v);
+
+ foreach (Bindings::iterator, i, *args[1]->attrs) {
+ Bindings::iterator j = args[0]->attrs->find(i->first);
+ if (j != args[0]->attrs->end()) {
+ Attr & a = (*v.attrs)[i->first];
+ mkCopy(a.value, i->second.value);
+ a.pos = i->second.pos;
}
}
- else if (matchAtPat(pat, pat1, pat2)) {
- attrsInPattern(map, pat1);
- attrsInPattern(map, pat2);
- }
}
@@ -876,17 +809,18 @@ static void attrsInPattern(ATermMap & map, Pattern pat)
functionArgs (x: ...)
=> { }
*/
-static Expr prim_functionArgs(EvalState & state, const ATermVector & args)
+static void prim_functionArgs(EvalState & state, Value * * args, Value & v)
{
- Expr f = evalExpr(state, args[0]);
- ATerm pat, body, pos;
- if (!matchFunction(f, pat, body, pos))
- throw TypeError("`functionArgs' required a function");
-
- ATermMap as;
- attrsInPattern(as, pat);
+ state.forceValue(*args[0]);
+ if (args[0]->type != tLambda)
+ throw TypeError("`functionArgs' requires a function");
- return makeAttrs(as);
+ state.mkAttrs(v);
+
+ if (!args[0]->lambda.fun->matchAttrs) return;
+
+ foreach (Formals::Formals_::iterator, i, args[0]->lambda.fun->formals->formals)
+ mkBool((*v.attrs)[i->name].value, i->def);
}
@@ -896,53 +830,58 @@ static Expr prim_functionArgs(EvalState & state, const ATermVector & args)
/* Determine whether the argument is a list. */
-static Expr prim_isList(EvalState & state, const ATermVector & args)
+static void prim_isList(EvalState & state, Value * * args, Value & v)
{
- ATermList list;
- return makeBool(matchList(evalExpr(state, args[0]), list));
+ state.forceValue(*args[0]);
+ mkBool(v, args[0]->type == tList);
}
/* Return the first element of a list. */
-static Expr prim_head(EvalState & state, const ATermVector & args)
+static void prim_head(EvalState & state, Value * * args, Value & v)
{
- ATermList list = evalList(state, args[0]);
- if (ATisEmpty(list))
+ state.forceList(*args[0]);
+ if (args[0]->list.length == 0)
throw Error("`head' called on an empty list");
- return evalExpr(state, ATgetFirst(list));
+ state.forceValue(*args[0]->list.elems[0]);
+ v = *args[0]->list.elems[0];
}
/* Return a list consisting of everything but the the first element of
a list. */
-static Expr prim_tail(EvalState & state, const ATermVector & args)
+static void prim_tail(EvalState & state, Value * * args, Value & v)
{
- ATermList list = evalList(state, args[0]);
- if (ATisEmpty(list))
+ state.forceList(*args[0]);
+ if (args[0]->list.length == 0)
throw Error("`tail' called on an empty list");
- return makeList(ATgetNext(list));
+ state.mkList(v, args[0]->list.length - 1);
+ for (unsigned int n = 0; n < v.list.length; ++n)
+ v.list.elems[n] = args[0]->list.elems[n + 1];
}
/* Apply a function to every element of a list. */
-static Expr prim_map(EvalState & state, const ATermVector & args)
+static void prim_map(EvalState & state, Value * * args, Value & v)
{
- Expr fun = evalExpr(state, args[0]);
- ATermList list = evalList(state, args[1]);
+ state.forceFunction(*args[0]);
+ state.forceList(*args[1]);
- ATermList res = ATempty;
- for (ATermIterator i(list); i; ++i)
- res = ATinsert(res, makeCall(fun, *i));
+ state.mkList(v, args[1]->list.length);
+ Value * vs = state.allocValues(v.list.length);
- return makeList(ATreverse(res));
+ for (unsigned int n = 0; n < v.list.length; ++n) {
+ v.list.elems[n] = &vs[n];
+ mkApp(vs[n], *args[0], *args[1]->list.elems[n]);
+ }
}
/* Return the length of a list. This is an O(1) time operation. */
-static Expr prim_length(EvalState & state, const ATermVector & args)
+static void prim_length(EvalState & state, Value * * args, Value & v)
{
- ATermList list = evalList(state, args[0]);
- return makeInt(ATgetLength(list));
+ state.forceList(*args[0]);
+ mkInt(v, args[0]->list.length);
}
@@ -951,44 +890,35 @@ static Expr prim_length(EvalState & state, const ATermVector & args)
*************************************************************/
-static Expr prim_add(EvalState & state, const ATermVector & args)
+static void prim_add(EvalState & state, Value * * args, Value & v)
{
- int i1 = evalInt(state, args[0]);
- int i2 = evalInt(state, args[1]);
- return makeInt(i1 + i2);
+ mkInt(v, state.forceInt(*args[0]) + state.forceInt(*args[1]));
}
-static Expr prim_sub(EvalState & state, const ATermVector & args)
+static void prim_sub(EvalState & state, Value * * args, Value & v)
{
- int i1 = evalInt(state, args[0]);
- int i2 = evalInt(state, args[1]);
- return makeInt(i1 - i2);
+ mkInt(v, state.forceInt(*args[0]) - state.forceInt(*args[1]));
}
-static Expr prim_mul(EvalState & state, const ATermVector & args)
+static void prim_mul(EvalState & state, Value * * args, Value & v)
{
- int i1 = evalInt(state, args[0]);
- int i2 = evalInt(state, args[1]);
- return makeInt(i1 * i2);
+ mkInt(v, state.forceInt(*args[0]) * state.forceInt(*args[1]));
}
-static Expr prim_div(EvalState & state, const ATermVector & args)
+static void prim_div(EvalState & state, Value * * args, Value & v)
{
- int i1 = evalInt(state, args[0]);
- int i2 = evalInt(state, args[1]);
+ int i2 = state.forceInt(*args[1]);
if (i2 == 0) throw EvalError("division by zero");
- return makeInt(i1 / i2);
+ mkInt(v, state.forceInt(*args[0]) / i2);
}
-static Expr prim_lessThan(EvalState & state, const ATermVector & args)
+static void prim_lessThan(EvalState & state, Value * * args, Value & v)
{
- int i1 = evalInt(state, args[0]);
- int i2 = evalInt(state, args[1]);
- return makeBool(i1 < i2);
+ mkBool(v, state.forceInt(*args[0]) < state.forceInt(*args[1]));
}
@@ -1000,11 +930,11 @@ static Expr prim_lessThan(EvalState & state, const ATermVector & args)
/* Convert the argument to a string. Paths are *not* copied to the
store, so `toString /foo/bar' yields `"/foo/bar"', not
`"/nix/store/whatever..."'. */
-static Expr prim_toString(EvalState & state, const ATermVector & args)
+static void prim_toString(EvalState & state, Value * * args, Value & v)
{
PathSet context;
- string s = coerceToString(state, args[0], context, true, false);
- return makeStr(s, context);
+ string s = state.coerceToString(*args[0], context, true, false);
+ mkString(v, s, context);
}
@@ -1012,32 +942,32 @@ static Expr prim_toString(EvalState & state, const ATermVector & args)
at character position `min(start, stringLength str)' inclusive and
ending at `min(start + len, stringLength str)'. `start' must be
non-negative. */
-static Expr prim_substring(EvalState & state, const ATermVector & args)
+static void prim_substring(EvalState & state, Value * * args, Value & v)
{
- int start = evalInt(state, args[0]);
- int len = evalInt(state, args[1]);
+ int start = state.forceInt(*args[0]);
+ int len = state.forceInt(*args[1]);
PathSet context;
- string s = coerceToString(state, args[2], context);
+ string s = state.coerceToString(*args[2], context);
if (start < 0) throw EvalError("negative start position in `substring'");
- return makeStr(string(s, start, len), context);
+ mkString(v, string(s, start, len), context);
}
-static Expr prim_stringLength(EvalState & state, const ATermVector & args)
+static void prim_stringLength(EvalState & state, Value * * args, Value & v)
{
PathSet context;
- string s = coerceToString(state, args[0], context);
- return makeInt(s.size());
+ string s = state.coerceToString(*args[0], context);
+ mkInt(v, s.size());
}
-static Expr prim_unsafeDiscardStringContext(EvalState & state, const ATermVector & args)
+static void prim_unsafeDiscardStringContext(EvalState & state, Value * * args, Value & v)
{
PathSet context;
- string s = coerceToString(state, args[0], context);
- return makeStr(s, PathSet());
+ string s = state.coerceToString(*args[0], context);
+ mkString(v, s, PathSet());
}
@@ -1047,10 +977,10 @@ static Expr prim_unsafeDiscardStringContext(EvalState & state, const ATermVector
source-only deployment). This primop marks the string context so
that builtins.derivation adds the path to drv.inputSrcs rather than
drv.inputDrvs. */
-static Expr prim_unsafeDiscardOutputDependency(EvalState & state, const ATermVector & args)
+static void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args, Value & v)
{
PathSet context;
- string s = coerceToString(state, args[0], context);
+ string s = state.coerceToString(*args[0], context);
PathSet context2;
foreach (PathSet::iterator, i, context) {
@@ -1059,29 +989,7 @@ static Expr prim_unsafeDiscardOutputDependency(EvalState & state, const ATermVec
context2.insert(p);
}
- return makeStr(s, context2);
-}
-
-
-/* Expression serialization/deserialization */
-
-
-static Expr prim_exprToString(EvalState & state, const ATermVector & args)
-{
- /* !!! this disregards context */
- return makeStr(atPrint(evalExpr(state, args[0])));
-}
-
-
-static Expr prim_stringToExpr(EvalState & state, const ATermVector & args)
-{
- /* !!! this can introduce arbitrary garbage terms in the
- evaluator! */;
- string s;
- PathSet l;
- if (!matchStr(evalExpr(state, args[0]), s, l))
- throw EvalError("stringToExpr needs string argument!");
- return ATreadFromString(s.c_str());
+ mkString(v, s, context2);
}
@@ -1090,23 +998,21 @@ static Expr prim_stringToExpr(EvalState & state, const ATermVector & args)
*************************************************************/
-static Expr prim_parseDrvName(EvalState & state, const ATermVector & args)
+static void prim_parseDrvName(EvalState & state, Value * * args, Value & v)
{
- string name = evalStringNoCtx(state, args[0]);
+ string name = state.forceStringNoCtx(*args[0]);
DrvName parsed(name);
- ATermMap attrs(2);
- attrs.set(toATerm("name"), makeAttrRHS(makeStr(parsed.name), makeNoPos()));
- attrs.set(toATerm("version"), makeAttrRHS(makeStr(parsed.version), makeNoPos()));
- return makeAttrs(attrs);
+ state.mkAttrs(v);
+ mkString((*v.attrs)[state.sName].value, parsed.name);
+ mkString((*v.attrs)[state.symbols.create("version")].value, parsed.version);
}
-static Expr prim_compareVersions(EvalState & state, const ATermVector & args)
+static void prim_compareVersions(EvalState & state, Value * * args, Value & v)
{
- string version1 = evalStringNoCtx(state, args[0]);
- string version2 = evalStringNoCtx(state, args[1]);
- int d = compareVersions(version1, version2);
- return makeInt(d);
+ string version1 = state.forceStringNoCtx(*args[0]);
+ string version2 = state.forceStringNoCtx(*args[1]);
+ mkInt(v, compareVersions(version1, version2));
}
@@ -1115,16 +1021,31 @@ static Expr prim_compareVersions(EvalState & state, const ATermVector & args)
*************************************************************/
-void EvalState::addPrimOps()
+void EvalState::createBaseEnv()
{
- addPrimOp("builtins", 0, prim_builtins);
-
- // Constants
- addPrimOp("true", 0, prim_true);
- addPrimOp("false", 0, prim_false);
- addPrimOp("null", 0, prim_null);
- addPrimOp("__currentSystem", 0, prim_currentSystem);
- addPrimOp("__currentTime", 0, prim_currentTime);
+ baseEnv.up = 0;
+
+ /* Add global constants such as `true' to the base environment. */
+ Value v;
+
+ /* `builtins' must be first! */
+ mkAttrs(v);
+ addConstant("builtins", v);
+
+ mkBool(v, true);
+ addConstant("true", v);
+
+ mkBool(v, false);
+ addConstant("false", v);
+
+ v.type = tNull;
+ addConstant("null", v);
+
+ mkInt(v, time(0));
+ addConstant("__currentTime", v);
+
+ mkString(v, thisSystem.c_str());
+ addConstant("__currentSystem", v);
// Miscellaneous
addPrimOp("import", 1, prim_import);
@@ -1141,14 +1062,14 @@ void EvalState::addPrimOps()
addPrimOp("__getEnv", 1, prim_getEnv);
addPrimOp("__trace", 2, prim_trace);
-
- // Expr <-> String
- addPrimOp("__exprToString", 1, prim_exprToString);
- addPrimOp("__stringToExpr", 1, prim_stringToExpr);
-
// Derivations
- addPrimOp("derivation!", 1, prim_derivationStrict);
- addPrimOp("derivation", 1, prim_derivationLazy);
+ addPrimOp("derivationStrict", 1, prim_derivationStrict);
+
+ /* Add a wrapper around the derivation primop that computes the
+ `drvPath' and `outPath' attributes lazily. */
+ string s = "attrs: let res = derivationStrict attrs; in attrs // { drvPath = res.drvPath; outPath = res.outPath; type = \"derivation\"; }";
+ mkThunk(v, baseEnv, parseExprFromString(*this, s, "/"));
+ addConstant("derivation", v);
// Paths
addPrimOp("__toPath", 1, prim_toPath);
@@ -1179,7 +1100,7 @@ void EvalState::addPrimOps()
addPrimOp("__tail", 1, prim_tail);
addPrimOp("map", 2, prim_map);
addPrimOp("__length", 1, prim_length);
-
+
// Integer arithmetic
addPrimOp("__add", 2, prim_add);
addPrimOp("__sub", 2, prim_sub);
@@ -1196,7 +1117,7 @@ void EvalState::addPrimOps()
// Versions
addPrimOp("__parseDrvName", 1, prim_parseDrvName);
- addPrimOp("__compareVersions", 2, prim_compareVersions);
+ addPrimOp("__compareVersions", 2, prim_compareVersions);
}
diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh
new file mode 100644
index 000000000..424c23538
--- /dev/null
+++ b/src/libexpr/symbol-table.hh
@@ -0,0 +1,81 @@
+#ifndef __SYMBOL_TABLE_H
+#define __SYMBOL_TABLE_H
+
+#include <map>
+#include <tr1/unordered_set>
+
+#include "types.hh"
+
+namespace nix {
+
+/* Symbol table used by the parser and evaluator to represent and look
+ up identifiers and attribute sets efficiently.
+ SymbolTable::create() converts a string into a symbol. Symbols
+ have the property that they can be compared efficiently (using a
+ pointer equality test), because the symbol table stores only one
+ copy of each string. */
+
+class Symbol
+{
+private:
+ const string * s; // pointer into SymbolTable
+ Symbol(const string * s) : s(s) { };
+ friend class SymbolTable;
+
+public:
+ bool operator == (const Symbol & s2) const
+ {
+ return s == s2.s;
+ }
+
+ bool operator != (const Symbol & s2) const
+ {
+ return s != s2.s;
+ }
+
+ bool operator < (const Symbol & s2) const
+ {
+ return s < s2.s;
+ }
+
+ operator const string & () const
+ {
+ return *s;
+ }
+
+ bool empty() const
+ {
+ return s->empty();
+ }
+
+ friend std::ostream & operator << (std::ostream & str, const Symbol & sym);
+};
+
+inline std::ostream & operator << (std::ostream & str, const Symbol & sym)
+{
+ str << *sym.s;
+ return str;
+}
+
+class SymbolTable
+{
+private:
+ typedef std::tr1::unordered_set<string> Symbols;
+ Symbols symbols;
+
+public:
+ Symbol create(const string & s)
+ {
+ std::pair<Symbols::iterator, bool> res = symbols.insert(s);
+ return Symbol(&*res.first);
+ }
+
+ unsigned int size() const
+ {
+ return symbols.size();
+ }
+};
+
+}
+
+#endif /* !__SYMBOL_TABLE_H */
diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc
new file mode 100644
index 000000000..e751fd300
--- /dev/null
+++ b/src/libexpr/value-to-xml.cc
@@ -0,0 +1,161 @@
+#include "value-to-xml.hh"
+#include "xml-writer.hh"
+#include "util.hh"
+
+#include <cstdlib>
+
+
+namespace nix {
+
+
+static XMLAttrs singletonAttrs(const string & name, const string & value)
+{
+ XMLAttrs attrs;
+ attrs[name] = value;
+ return attrs;
+}
+
+
+static void printValueAsXML(EvalState & state, bool strict, bool location,
+ Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen);
+
+
+static void posToXML(XMLAttrs & xmlAttrs, const Pos & pos)
+{
+ xmlAttrs["path"] = pos.file;
+ xmlAttrs["line"] = (format("%1%") % pos.line).str();
+ xmlAttrs["column"] = (format("%1%") % pos.column).str();
+}
+
+
+static void showAttrs(EvalState & state, bool strict, bool location,
+ Bindings & attrs, XMLWriter & doc, PathSet & context, PathSet & drvsSeen)
+{
+ StringSet names;
+
+ foreach (Bindings::iterator, i, attrs)
+ names.insert(i->first);
+
+ foreach (StringSet::iterator, i, names) {
+ Attr & a(attrs[state.symbols.create(*i)]);
+
+ XMLAttrs xmlAttrs;
+ xmlAttrs["name"] = *i;
+ if (location && a.pos != &noPos) posToXML(xmlAttrs, *a.pos);
+
+ XMLOpenElement _(doc, "attr", xmlAttrs);
+ printValueAsXML(state, strict, location,
+ a.value, doc, context, drvsSeen);
+ }
+}
+
+
+static void printValueAsXML(EvalState & state, bool strict, bool location,
+ Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen)
+{
+ checkInterrupt();
+
+ if (strict) state.forceValue(v);
+
+ switch (v.type) {
+
+ case tInt:
+ doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % v.integer).str()));
+ break;
+
+ case tBool:
+ doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean ? "true" : "false"));
+ break;
+
+ case tString:
+ /* !!! show the context? */
+ doc.writeEmptyElement("string", singletonAttrs("value", v.string.s));
+ break;
+
+ case tPath:
+ doc.writeEmptyElement("path", singletonAttrs("value", v.path));
+ break;
+
+ case tNull:
+ doc.writeEmptyElement("null");
+ break;
+
+ case tAttrs:
+ if (state.isDerivation(v)) {
+ XMLAttrs xmlAttrs;
+
+ Bindings::iterator a = v.attrs->find(state.symbols.create("derivation"));
+
+ Path drvPath;
+ a = v.attrs->find(state.sDrvPath);
+ if (a != v.attrs->end()) {
+ if (strict) state.forceValue(a->second.value);
+ if (a->second.value.type == tString)
+ xmlAttrs["drvPath"] = drvPath = a->second.value.string.s;
+ }
+
+ a = v.attrs->find(state.sOutPath);
+ if (a != v.attrs->end()) {
+ if (strict) state.forceValue(a->second.value);
+ if (a->second.value.type == tString)
+ xmlAttrs["outPath"] = a->second.value.string.s;
+ }
+
+ XMLOpenElement _(doc, "derivation", xmlAttrs);
+
+ if (drvPath != "" && drvsSeen.find(drvPath) == drvsSeen.end()) {
+ drvsSeen.insert(drvPath);
+ showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen);
+ } else
+ doc.writeEmptyElement("repeated");
+ }
+
+ else {
+ XMLOpenElement _(doc, "attrs");
+ showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen);
+ }
+
+ break;
+
+ case tList: {
+ XMLOpenElement _(doc, "list");
+ for (unsigned int n = 0; n < v.list.length; ++n)
+ printValueAsXML(state, strict, location, *v.list.elems[n], doc, context, drvsSeen);
+ break;
+ }
+
+ case tLambda: {
+ XMLAttrs xmlAttrs;
+ if (location) posToXML(xmlAttrs, v.lambda.fun->pos);
+ XMLOpenElement _(doc, "function", xmlAttrs);
+
+ if (v.lambda.fun->matchAttrs) {
+ XMLAttrs attrs;
+ if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
+ if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
+ XMLOpenElement _(doc, "attrspat", attrs);
+ foreach (Formals::Formals_::iterator, i, v.lambda.fun->formals->formals)
+ doc.writeEmptyElement("attr", singletonAttrs("name", i->name));
+ } else
+ doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));
+
+ break;
+ }
+
+ default:
+ doc.writeEmptyElement("unevaluated");
+ }
+}
+
+
+void printValueAsXML(EvalState & state, bool strict, bool location,
+ Value & v, std::ostream & out, PathSet & context)
+{
+ XMLWriter doc(true, out);
+ XMLOpenElement root(doc, "expr");
+ PathSet drvsSeen;
+ printValueAsXML(state, strict, location, v, doc, context, drvsSeen);
+}
+
+
+}
diff --git a/src/libexpr/value-to-xml.hh b/src/libexpr/value-to-xml.hh
new file mode 100644
index 000000000..0c6de3a8b
--- /dev/null
+++ b/src/libexpr/value-to-xml.hh
@@ -0,0 +1,17 @@
+#ifndef __VALUE_TO_XML_H
+#define __VALUE_TO_XML_H
+
+#include <string>
+#include <map>
+
+#include "nixexpr.hh"
+#include "eval.hh"
+
+namespace nix {
+
+void printValueAsXML(EvalState & state, bool strict, bool location,
+ Value & v, std::ostream & out, PathSet & context);
+
+}
+
+#endif /* !__VALUE_TO_XML_H */
diff --git a/src/libmain/Makefile.am b/src/libmain/Makefile.am
index c2946febc..a9ee66042 100644
--- a/src/libmain/Makefile.am
+++ b/src/libmain/Makefile.am
@@ -15,5 +15,5 @@ AM_CXXFLAGS = \
-DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \
-DNIX_BIN_DIR=\"$(bindir)\" \
-DNIX_VERSION=\"$(VERSION)\" \
- -I$(srcdir)/.. ${aterm_include} -I$(srcdir)/../libutil \
+ -I$(srcdir)/.. -I$(srcdir)/../libutil \
-I$(srcdir)/../libstore
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index d9cf9a862..3fbec4b52 100644
--- a/src/libmain/shared.cc
+++ b/src/libmain/shared.cc
@@ -13,8 +13,6 @@
#include <sys/stat.h>
#include <unistd.h>
-#include <aterm2.h>
-
namespace nix {
@@ -87,9 +85,6 @@ static void setLogType(string lt)
}
-void initDerivationsHelpers();
-
-
static void closeStore()
{
try {
@@ -176,9 +171,6 @@ static void initAndRun(int argc, char * * argv)
string lt = getEnv("NIX_LOG_TYPE");
if (lt != "") setLogType(lt);
- /* ATerm stuff. !!! find a better place to put this */
- initDerivationsHelpers();
-
/* Put the arguments in a vector. */
Strings args, remaining;
while (argc--) args.push_back(*argv++);
@@ -333,10 +325,6 @@ int main(int argc, char * * argv)
if (argc == 0) abort();
setuidInit();
- /* ATerm setup. */
- ATerm bottomOfStack;
- ATinit(argc, argv, &bottomOfStack);
-
/* Turn on buffering for cerr. */
#if HAVE_PUBSETBUF
std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
diff --git a/src/libstore/Makefile.am b/src/libstore/Makefile.am
index dd6760554..e19256b92 100644
--- a/src/libstore/Makefile.am
+++ b/src/libstore/Makefile.am
@@ -12,17 +12,12 @@ pkginclude_HEADERS = \
libstore_la_LIBADD = ../libutil/libutil.la ../boost/format/libformat.la ${aterm_lib} ${sqlite_lib}
-BUILT_SOURCES = derivations-ast.cc derivations-ast.hh
-
-EXTRA_DIST = derivations-ast.def derivations-ast.cc schema.sql
+EXTRA_DIST = schema.sql
AM_CXXFLAGS = -Wall \
- -I$(srcdir)/.. ${aterm_include} ${sqlite_include} -I$(srcdir)/../libutil -I${top_srcdir}/externals/sqlite-3.6.22/
+ ${sqlite_include} -I$(srcdir)/.. -I$(srcdir)/../libutil
local-store.lo: schema.sql.hh
%.sql.hh: %.sql
../bin2c/bin2c schema < $< > $@ || (rm $@ && exit 1)
-
-derivations-ast.cc derivations-ast.hh: ../aterm-helper.pl derivations-ast.def
- $(perl) $(srcdir)/../aterm-helper.pl derivations-ast.hh derivations-ast.cc < $(srcdir)/derivations-ast.def
diff --git a/src/libstore/derivations-ast.def b/src/libstore/derivations-ast.def
deleted file mode 100644
index 574529ae7..000000000
--- a/src/libstore/derivations-ast.def
+++ /dev/null
@@ -1,10 +0,0 @@
-init initDerivationsHelpers
-
-Derive | ATermList ATermList ATermList string string ATermList ATermList | ATerm |
-
-| string string | ATerm | EnvBinding |
-| string ATermList | ATerm | DerivationInput |
-| string string string string | ATerm | DerivationOutput |
-
-Closure | ATermList ATermList | ATerm | OldClosure |
-| string ATermList | ATerm | OldClosureElem |
diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc
index bc2ec1f90..e321ae8aa 100644
--- a/src/libstore/derivations.cc
+++ b/src/libstore/derivations.cc
@@ -1,22 +1,12 @@
#include "derivations.hh"
#include "store-api.hh"
-#include "aterm.hh"
#include "globals.hh"
#include "util.hh"
-#include "derivations-ast.hh"
-#include "derivations-ast.cc"
-
namespace nix {
-Hash hashTerm(ATerm t)
-{
- return hashString(htSHA256, atPrint(t));
-}
-
-
Path writeDerivation(const Derivation & drv, const string & name)
{
PathSet references;
@@ -27,137 +17,151 @@ Path writeDerivation(const Derivation & drv, const string & name)
(that can be missing (of course) and should not necessarily be
held during a garbage collection). */
string suffix = name + drvExtension;
- string contents = atPrint(unparseDerivation(drv));
+ string contents = unparseDerivation(drv);
return readOnlyMode
? computeStorePathForText(suffix, contents, references)
: store->addTextToStore(suffix, contents, references);
}
-static void checkPath(const string & s)
+static Path parsePath(std::istream & str)
{
+ string s = parseString(str);
if (s.size() == 0 || s[0] != '/')
throw Error(format("bad path `%1%' in derivation") % s);
+ return s;
}
-static void parseStrings(ATermList paths, StringSet & out, bool arePaths)
+static StringSet parseStrings(std::istream & str, bool arePaths)
{
- for (ATermIterator i(paths); i; ++i) {
- if (ATgetType(*i) != AT_APPL)
- throw badTerm("not a path", *i);
- string s = aterm2String(*i);
- if (arePaths) checkPath(s);
- out.insert(s);
- }
+ StringSet res;
+ while (!endOfList(str))
+ res.insert(arePaths ? parsePath(str) : parseString(str));
+ return res;
}
+
-
-/* Shut up warnings. */
-void throwBadDrv(ATerm t) __attribute__ ((noreturn));
-
-void throwBadDrv(ATerm t)
-{
- throw badTerm("not a valid derivation", t);
-}
-
-
-Derivation parseDerivation(ATerm t)
+Derivation parseDerivation(const string & s)
{
Derivation drv;
- ATermList outs, inDrvs, inSrcs, args, bnds;
- ATerm builder, platform;
+ std::istringstream str(s);
+ expect(str, "Derive([");
- if (!matchDerive(t, outs, inDrvs, inSrcs, platform, builder, args, bnds))
- throwBadDrv(t);
-
- for (ATermIterator i(outs); i; ++i) {
- ATerm id, path, hashAlgo, hash;
- if (!matchDerivationOutput(*i, id, path, hashAlgo, hash))
- throwBadDrv(t);
+ /* Parse the list of outputs. */
+ while (!endOfList(str)) {
DerivationOutput out;
- out.path = aterm2String(path);
- checkPath(out.path);
- out.hashAlgo = aterm2String(hashAlgo);
- out.hash = aterm2String(hash);
- drv.outputs[aterm2String(id)] = out;
+ expect(str, "("); string id = parseString(str);
+ expect(str, ","); out.path = parsePath(str);
+ expect(str, ","); out.hashAlgo = parseString(str);
+ expect(str, ","); out.hash = parseString(str);
+ expect(str, ")");
+ drv.outputs[id] = out;
}
- for (ATermIterator i(inDrvs); i; ++i) {
- ATerm drvPath;
- ATermList ids;
- if (!matchDerivationInput(*i, drvPath, ids))
- throwBadDrv(t);
- Path drvPath2 = aterm2String(drvPath);
- checkPath(drvPath2);
- StringSet ids2;
- parseStrings(ids, ids2, false);
- drv.inputDrvs[drvPath2] = ids2;
+ /* Parse the list of input derivations. */
+ expect(str, ",[");
+ while (!endOfList(str)) {
+ expect(str, "(");
+ Path drvPath = parsePath(str);
+ expect(str, ",[");
+ drv.inputDrvs[drvPath] = parseStrings(str, false);
+ expect(str, ")");
}
-
- parseStrings(inSrcs, drv.inputSrcs, true);
- drv.builder = aterm2String(builder);
- drv.platform = aterm2String(platform);
-
- for (ATermIterator i(args); i; ++i) {
- if (ATgetType(*i) != AT_APPL)
- throw badTerm("string expected", *i);
- drv.args.push_back(aterm2String(*i));
+ expect(str, ",["); drv.inputSrcs = parseStrings(str, true);
+ expect(str, ","); drv.platform = parseString(str);
+ expect(str, ","); drv.builder = parseString(str);
+
+ /* Parse the builder arguments. */
+ expect(str, ",[");
+ while (!endOfList(str))
+ drv.args.push_back(parseString(str));
+
+ /* Parse the environment variables. */
+ expect(str, ",[");
+ while (!endOfList(str)) {
+ expect(str, "("); string name = parseString(str);
+ expect(str, ","); string value = parseString(str);
+ expect(str, ")");
+ drv.env[name] = value;
}
+
+ expect(str, ")");
+ return drv;
+}
- for (ATermIterator i(bnds); i; ++i) {
- ATerm s1, s2;
- if (!matchEnvBinding(*i, s1, s2))
- throw badTerm("tuple of strings expected", *i);
- drv.env[aterm2String(s1)] = aterm2String(s2);
- }
- return drv;
+static void printString(string & res, const string & s)
+{
+ res += '"';
+ for (const char * i = s.c_str(); *i; i++)
+ if (*i == '\"' || *i == '\\') { res += "\\"; res += *i; }
+ else if (*i == '\n') res += "\\n";
+ else if (*i == '\r') res += "\\r";
+ else if (*i == '\t') res += "\\t";
+ else res += *i;
+ res += '"';
}
-ATerm unparseDerivation(const Derivation & drv)
+template<class ForwardIterator>
+static void printStrings(string & res, ForwardIterator i, ForwardIterator j)
{
- ATermList outputs = ATempty;
- for (DerivationOutputs::const_reverse_iterator i = drv.outputs.rbegin();
- i != drv.outputs.rend(); ++i)
- outputs = ATinsert(outputs,
- makeDerivationOutput(
- toATerm(i->first),
- toATerm(i->second.path),
- toATerm(i->second.hashAlgo),
- toATerm(i->second.hash)));
-
- ATermList inDrvs = ATempty;
- for (DerivationInputs::const_reverse_iterator i = drv.inputDrvs.rbegin();
- i != drv.inputDrvs.rend(); ++i)
- inDrvs = ATinsert(inDrvs,
- makeDerivationInput(
- toATerm(i->first),
- toATermList(i->second)));
+ res += '[';
+ bool first = true;
+ for ( ; i != j; ++i) {
+ if (first) first = false; else res += ',';
+ printString(res, *i);
+ }
+ res += ']';
+}
+
+
+string unparseDerivation(const Derivation & drv)
+{
+ string s;
+ s.reserve(65536);
+ s += "Derive([";
+
+ bool first = true;
+ foreach (DerivationOutputs::const_iterator, i, drv.outputs) {
+ if (first) first = false; else s += ',';
+ s += '('; printString(s, i->first);
+ s += ','; printString(s, i->second.path);
+ s += ','; printString(s, i->second.hashAlgo);
+ s += ','; printString(s, i->second.hash);
+ s += ')';
+ }
+
+ s += "],[";
+ first = true;
+ foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) {
+ if (first) first = false; else s += ',';
+ s += '('; printString(s, i->first);
+ s += ','; printStrings(s, i->second.begin(), i->second.end());
+ s += ')';
+ }
+
+ s += "],";
+ printStrings(s, drv.inputSrcs.begin(), drv.inputSrcs.end());
+
+ s += ','; printString(s, drv.platform);
+ s += ','; printString(s, drv.builder);
+ s += ','; printStrings(s, drv.args.begin(), drv.args.end());
+
+ s += ",[";
+ first = true;
+ foreach (StringPairs::const_iterator, i, drv.env) {
+ if (first) first = false; else s += ',';
+ s += '('; printString(s, i->first);
+ s += ','; printString(s, i->second);
+ s += ')';
+ }
+
+ s += "])";
- ATermList args = ATempty;
- for (Strings::const_reverse_iterator i = drv.args.rbegin();
- i != drv.args.rend(); ++i)
- args = ATinsert(args, toATerm(*i));
-
- ATermList env = ATempty;
- for (StringPairs::const_reverse_iterator i = drv.env.rbegin();
- i != drv.env.rend(); ++i)
- env = ATinsert(env,
- makeEnvBinding(
- toATerm(i->first),
- toATerm(i->second)));
-
- return makeDerive(
- outputs,
- inDrvs,
- toATermList(drv.inputSrcs),
- toATerm(drv.platform),
- toATerm(drv.builder),
- args,
- env);
+ return s;
}
diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh
index c3f579bee..c14be48af 100644
--- a/src/libstore/derivations.hh
+++ b/src/libstore/derivations.hh
@@ -1,12 +1,10 @@
#ifndef __DERIVATIONS_H
#define __DERIVATIONS_H
-typedef union _ATerm * ATerm;
-
-#include "hash.hh"
-
#include <map>
+#include "types.hh"
+
namespace nix {
@@ -53,17 +51,14 @@ struct Derivation
};
-/* Hash an aterm. */
-Hash hashTerm(ATerm t);
-
/* Write a derivation to the Nix store, and return its path. */
Path writeDerivation(const Derivation & drv, const string & name);
/* Parse a derivation. */
-Derivation parseDerivation(ATerm t);
+Derivation parseDerivation(const string & s);
-/* Parse a derivation. */
-ATerm unparseDerivation(const Derivation & drv);
+/* Print a derivation. */
+string unparseDerivation(const Derivation & drv);
/* Check whether a file name ends with the extensions for
derivations. */
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index 633b3599e..071f8a223 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -3,7 +3,6 @@
#include "globals.hh"
#include "archive.hh"
#include "pathlocks.hh"
-#include "derivations-ast.hh"
#include "worker-protocol.hh"
#include "derivations.hh"
@@ -446,9 +445,7 @@ unsigned long long LocalStore::addValidPath(const ValidPathInfo & info)
efficiently query whether a path is an output of some
derivation. */
if (isDerivation(info.path)) {
- ATerm t = ATreadFromNamedFile(info.path.c_str());
- if (!t) throw Error(format("cannot read derivation `%1%'") % info.path);
- Derivation drv = parseDerivation(t);
+ Derivation drv = parseDerivation(readFile(info.path));
foreach (DerivationOutputs::iterator, i, drv.outputs) {
SQLiteStmtUse use(stmtAddDerivationOutput);
stmtAddDerivationOutput.bind(id);
diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc
index f79cb11cc..d52dd6346 100644
--- a/src/libstore/misc.cc
+++ b/src/libstore/misc.cc
@@ -2,8 +2,6 @@
#include "store-api.hh"
#include "local-store.hh"
-#include <aterm2.h>
-
namespace nix {
@@ -12,9 +10,7 @@ Derivation derivationFromPath(const Path & drvPath)
{
assertStorePath(drvPath);
store->ensurePath(drvPath);
- ATerm t = ATreadFromNamedFile(drvPath.c_str());
- if (!t) throw Error(format("cannot read aterm from `%1%'") % drvPath);
- return parseDerivation(t);
+ return parseDerivation(readFile(drvPath));
}
diff --git a/src/libutil/Makefile.am b/src/libutil/Makefile.am
index f17236d29..98f32633b 100644
--- a/src/libutil/Makefile.am
+++ b/src/libutil/Makefile.am
@@ -1,16 +1,16 @@
pkglib_LTLIBRARIES = libutil.la
libutil_la_SOURCES = util.cc hash.cc serialise.cc \
- archive.cc aterm.cc aterm-map.cc xml-writer.cc
+ archive.cc xml-writer.cc
libutil_la_LIBADD = ../boost/format/libformat.la ${aterm_lib} ${sqlite_lib}
pkginclude_HEADERS = util.hh hash.hh serialise.hh \
- archive.hh aterm.hh aterm-map.hh xml-writer.hh types.hh
+ archive.hh xml-writer.hh types.hh
if !HAVE_OPENSSL
libutil_la_SOURCES += \
md5.c md5.h sha1.c sha1.h sha256.c sha256.h md32_common.h
endif
-AM_CXXFLAGS = -Wall -I$(srcdir)/.. ${aterm_include}
+AM_CXXFLAGS = -Wall -I$(srcdir)/..
diff --git a/src/libutil/aterm-map.cc b/src/libutil/aterm-map.cc
deleted file mode 100644
index c31fcdba3..000000000
--- a/src/libutil/aterm-map.cc
+++ /dev/null
@@ -1,332 +0,0 @@
-#include "aterm-map.hh"
-
-#include <iostream>
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include <aterm2.h>
-
-
-namespace nix {
-
-
-static const unsigned int maxLoadFactor = /* 1 / */ 3;
-static unsigned int nrResizes = 0;
-static unsigned int sizeTotalAlloc = 0;
-static unsigned int sizeCurAlloc = 0;
-static unsigned int sizeMaxAlloc = 0;
-
-
-ATermMap::ATermMap(unsigned int expectedCount)
-{
- init(expectedCount);
-}
-
-
-ATermMap::ATermMap(const ATermMap & map)
-{
- init(map.maxCount);
- copy(map.hashTable, map.capacity);
-}
-
-
-ATermMap & ATermMap::operator = (const ATermMap & map)
-{
- if (this == &map) return *this;
- free();
- init(map.maxCount);
- copy(map.hashTable, map.capacity);
- return *this;
-}
-
-
-ATermMap::~ATermMap()
-{
- free();
-}
-
-
-void ATermMap::init(unsigned int expectedCount)
-{
- assert(sizeof(ATerm) * 2 == sizeof(KeyValue));
- capacity = 0;
- count = 0;
- maxCount = 0;
- hashTable = 0;
- resizeTable(expectedCount);
-}
-
-
-void ATermMap::free()
-{
- if (hashTable) {
- ATunprotectArray((ATerm *) hashTable);
- ::free(hashTable);
- sizeCurAlloc -= sizeof(KeyValue) * capacity;
- hashTable = 0;
- }
-}
-
-
-static unsigned int roundToPowerOf2(unsigned int x)
-{
- x--;
- x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16;
- x++;
- return x;
-}
-
-
-void ATermMap::resizeTable(unsigned int expectedCount)
-{
- if (expectedCount == 0) expectedCount = 1;
-// cout << maxCount << " -> " << expectedCount << endl;
-// cout << maxCount << " " << size << endl;
-// cout << (double) size / maxCount << endl;
-
- unsigned int oldCapacity = capacity;
- KeyValue * oldHashTable = hashTable;
-
- maxCount = expectedCount;
- capacity = roundToPowerOf2(maxCount * maxLoadFactor);
- hashTable = (KeyValue *) calloc(sizeof(KeyValue), capacity);
- sizeTotalAlloc += sizeof(KeyValue) * capacity;
- sizeCurAlloc += sizeof(KeyValue) * capacity;
- if (sizeCurAlloc > sizeMaxAlloc) sizeMaxAlloc = sizeCurAlloc;
- ATprotectArray((ATerm *) hashTable, capacity * 2);
-
-// cout << capacity << endl;
-
- /* Re-hash the elements in the old table. */
- if (oldCapacity != 0) {
- count = 0;
- copy(oldHashTable, oldCapacity);
- ATunprotectArray((ATerm *) oldHashTable);
- ::free(oldHashTable);
- sizeCurAlloc -= sizeof(KeyValue) * oldCapacity;
- nrResizes++;
- }
-}
-
-
-void ATermMap::copy(KeyValue * elements, unsigned int capacity)
-{
- for (unsigned int i = 0; i < capacity; ++i)
- if (elements[i].value) /* i.e., non-empty, non-deleted element */
- set(elements[i].key, elements[i].value);
-}
-
-
-/* !!! use a bigger shift for 64-bit platforms? */
-static const unsigned int shift = 16;
-static const unsigned long knuth = (unsigned long) (0.6180339887 * (1 << shift));
-
-
-unsigned long ATermMap::hash1(ATerm key) const
-{
- /* Don't care about the least significant bits of the ATerm
- pointer since they're always 0. */
- unsigned long key2 = ((unsigned long) key) >> 2;
-
- /* Approximately equal to:
- double d = key2 * 0.6180339887;
- unsigned int h = (int) (capacity * (d - floor(d)));
- */
-
- unsigned long h = (capacity * ((key2 * knuth) & ((1 << shift) - 1))) >> shift;
-
- return h;
-}
-
-
-unsigned long ATermMap::hash2(ATerm key) const
-{
- unsigned long key2 = ((unsigned long) key) >> 2;
- /* Note: the result must be relatively prime to `capacity' (which
- is a power of 2), so we make sure that the result is always
- odd. */
- unsigned long h = ((key2 * 134217689) & (capacity - 1)) | 1;
- return h;
-}
-
-
-static unsigned int nrItemsSet = 0;
-static unsigned int nrSetProbes = 0;
-
-
-void ATermMap::set(ATerm key, ATerm value)
-{
- if (count == maxCount) resizeTable(capacity * 2 / maxLoadFactor);
-
- nrItemsSet++;
- for (unsigned int i = 0, h = hash1(key); i < capacity;
- ++i, h = (h + hash2(key)) & (capacity - 1))
- {
- // assert(h < capacity);
- nrSetProbes++;
- /* Note: to see whether a slot is free, we check
- hashTable[h].value, not hashTable[h].key, since we use
- value == 0 to mark deleted slots. */
- if (hashTable[h].value == 0 || hashTable[h].key == key) {
- if (hashTable[h].value == 0) count++;
- hashTable[h].key = key;
- hashTable[h].value = value;
- return;
- }
- }
-
- abort();
-}
-
-
-static unsigned int nrItemsGet = 0;
-static unsigned int nrGetProbes = 0;
-
-
-ATerm ATermMap::get(ATerm key) const
-{
- nrItemsGet++;
- for (unsigned int i = 0, h = hash1(key); i < capacity;
- ++i, h = (h + hash2(key)) & (capacity - 1))
- {
- nrGetProbes++;
- if (hashTable[h].key == 0) return 0;
- if (hashTable[h].key == key) return hashTable[h].value;
- }
- return 0;
-}
-
-
-void ATermMap::remove(ATerm key)
-{
- for (unsigned int i = 0, h = hash1(key); i < capacity;
- ++i, h = (h + hash2(key)) & (capacity - 1))
- {
- if (hashTable[h].key == 0) return;
- if (hashTable[h].key == key) {
- if (hashTable[h].value != 0) {
- hashTable[h].value = 0;
- count--;
- }
- return;
- }
- }
-}
-
-
-unsigned int ATermMap::size()
-{
- return count; /* STL nomenclature */
-}
-
-
-void printATermMapStats()
-{
- using std::cerr;
- using std::endl;
-
- cerr << "RESIZES: " << nrResizes << " "
- << sizeTotalAlloc << " "
- << sizeCurAlloc << " "
- << sizeMaxAlloc << endl;
-
- cerr << "SET: "
- << nrItemsSet << " "
- << nrSetProbes << " "
- << (double) nrSetProbes / nrItemsSet << endl;
-
- cerr << "GET: "
- << nrItemsGet << " "
- << nrGetProbes << " "
- << (double) nrGetProbes / nrItemsGet << endl;
-}
-
-
-#if 0
-int main(int argc, char * * argv)
-{
- ATerm bottomOfStack;
- ATinit(argc, argv, &bottomOfStack);
-
- /* Make test terms. */
- int nrTestTerms = 100000;
- ATerm testTerms[nrTestTerms];
-
- for (int i = 0; i < nrTestTerms; ++i) {
- char name[10];
- sprintf(name, "%d", (int) random() % 37);
-
- int arity = i == 0 ? 0 : (random() % 37);
- ATerm kids[arity];
- for (int j = 0; j < arity; ++j)
- kids[j] = testTerms[random() % i];
-
- testTerms[i] = (ATerm) ATmakeApplArray(ATmakeAFun(name, arity, ATfalse), kids);
-// ATwriteToSharedTextFile(testTerms[i], stdout);
-// printf("\n");
- }
-
-
- cout << "testing...\n";
-
-
- #define someTerm() (testTerms[(int) random() % nrTestTerms])
-
-
- for (int test = 0; test < 100000; ++test) {
- //cerr << test << endl;
- unsigned int n = 300;
- ATermMap map(300);
- ATerm keys[n], values[n];
- for (unsigned int i = 0; i < n; ++i) {
- keys[i] = someTerm();
- values[i] = someTerm();
- map.set(keys[i], values[i]);
- //cerr << "INSERT: " << keys[i] << " " << values[i] << endl;
- }
-
- unsigned int size = map.size();
- assert(size <= n);
- values[n - 1] = 0;
- map.remove(keys[n - 1]);
- assert(map.size() == size - 1);
-
- unsigned int checksum;
- unsigned int count = 0;
- for (ATermMap::const_iterator i = map.begin(); i != map.end(); ++i, ++count) {
- assert(i->key);
- assert(i->value);
- checksum += (unsigned int) (*i).key;
- checksum += (unsigned int) (*i).value;
- // cout << (*i).key << " " << (*i).value << endl;
- }
- assert(count == size - 1);
-
- for (unsigned int i = 0; i < n; ++i) {
- for (unsigned int j = i + 1; j < n; ++j)
- if (keys[i] == keys[j]) goto x;
- if (map.get(keys[i]) != values[i]) {
- cerr << "MISMATCH: " << keys[i] << " " << values[i] << " " << map.get(keys[i]) << " " << i << endl;
- abort();
- }
- if (values[i] != 0) {
- checksum -= (unsigned int) keys[i];
- checksum -= (unsigned int) values[i];
- }
- x: ;
- }
-
- assert(checksum == 0);
-
- for (unsigned int i = 0; i < 100; ++i)
- map.get(someTerm());
-
- }
-
- printATermMapStats();
-}
-#endif
-
-
-}
diff --git a/src/libutil/aterm-map.hh b/src/libutil/aterm-map.hh
deleted file mode 100644
index bcfefd9ee..000000000
--- a/src/libutil/aterm-map.hh
+++ /dev/null
@@ -1,130 +0,0 @@
-#ifndef __ATERM_MAP_H
-#define __ATERM_MAP_H
-
-typedef union _ATerm * ATerm;
-
-#include <assert.h>
-
-
-namespace nix {
-
-
-class ATermMap
-{
-public:
-
- struct KeyValue
- {
- ATerm key;
- ATerm value;
- };
-
-private:
-
- /* Hash table for the map. We use open addressing, i.e., all
- key/value pairs are stored directly in the table, and there are
- no pointers. Collisions are resolved through probing. */
- KeyValue * hashTable;
-
- /* Current size of the hash table. */
- unsigned int capacity;
-
- /* Number of elements in the hash table. */
- unsigned int count;
-
- /* Maximum number of elements in the hash table. If `count'
- exceeds this number, the hash table is expanded. */
- unsigned int maxCount;
-
-public:
-
- /* Create a map. `expectedCount' is the number of elements the
- map is expected to hold. */
- ATermMap(unsigned int expectedCount = 16);
-
- ATermMap(const ATermMap & map);
-
- ~ATermMap();
-
- ATermMap & operator = (const ATermMap & map);
-
- void set(ATerm key, ATerm value);
-
- ATerm get(ATerm key) const;
-
- ATerm operator [](ATerm key) const
- {
- return get(key);
- }
-
- void remove(ATerm key);
-
- unsigned int size();
-
- struct const_iterator
- {
- const ATermMap & map;
- unsigned int pos;
- const_iterator(const ATermMap & map, int pos) : map(map)
- {
- this->pos = pos;
- }
- bool operator !=(const const_iterator & i)
- {
- return pos != i.pos;
- }
- void operator ++()
- {
- if (pos == map.capacity) return;
- do { ++pos;
- } while (pos < map.capacity && map.hashTable[pos].value == 0);
- }
- const KeyValue & operator *()
- {
- assert(pos < map.capacity);
- return map.hashTable[pos];
- }
- const KeyValue * operator ->()
- {
- assert(pos < map.capacity);
- return &map.hashTable[pos];
- }
- };
-
- friend class ATermMap::const_iterator;
-
- const_iterator begin() const
- {
- unsigned int i = 0;
- while (i < capacity && hashTable[i].value == 0) ++i;
- return const_iterator(*this, i);
- }
-
- const_iterator end() const
- {
- return const_iterator(*this, capacity);
- }
-
-private:
-
- void init(unsigned int expectedCount);
-
- void free();
-
- void resizeTable(unsigned int expectedCount);
-
- void copy(KeyValue * elements, unsigned int capacity);
-
- inline unsigned long hash1(ATerm key) const;
- inline unsigned long hash2(ATerm key) const;
-};
-
-
-/* Hack. */
-void printATermMapStats();
-
-
-}
-
-
-#endif /* !__ATERM_MAP_H */
diff --git a/src/libutil/aterm.cc b/src/libutil/aterm.cc
deleted file mode 100644
index 25d704785..000000000
--- a/src/libutil/aterm.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-#include "aterm.hh"
-
-#include <cstring>
-
-using std::string;
-
-
-string nix::atPrint(ATerm t)
-{
- if (!t) throw Error("attempt to print null aterm");
- char * s = ATwriteToString(t);
- if (!s) throw Error("cannot print term");
- return s;
-}
-
-
-std::ostream & operator << (std::ostream & stream, ATerm e)
-{
- return stream << nix::atPrint(e);
-}
-
-
-nix::Error nix::badTerm(const format & f, ATerm t)
-{
- char * s = ATwriteToString(t);
- if (!s) throw Error("cannot print term");
- if (strlen(s) > 1000) {
- int len;
- s = ATwriteToSharedString(t, &len);
- if (!s) throw Error("cannot print term");
- }
- return Error(format("%1%, in `%2%'") % f.str() % (string) s);
-}
-
-
-ATerm nix::toATerm(const char * s)
-{
- return (ATerm) ATmakeAppl0(ATmakeAFun((char *) s, 0, ATtrue));
-}
-
-
-ATerm nix::toATerm(const string & s)
-{
- return toATerm(s.c_str());
-}
-
-
-ATermList nix::toATermList(const StringSet & ss)
-{
- ATermList l = ATempty;
- for (StringSet::const_reverse_iterator i = ss.rbegin();
- i != ss.rend(); ++i)
- l = ATinsert(l, toATerm(*i));
- return l;
-}
diff --git a/src/libutil/aterm.hh b/src/libutil/aterm.hh
deleted file mode 100644
index b1cbc3b6d..000000000
--- a/src/libutil/aterm.hh
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef __ATERM_H
-#define __ATERM_H
-
-#include <aterm2.h>
-
-#include "types.hh"
-
-
-namespace nix {
-
-
-/* Print an ATerm. */
-string atPrint(ATerm t);
-
-class ATermIterator
-{
- ATermList t;
-
-public:
- ATermIterator(ATermList _t) : t(_t) { }
- ATermIterator & operator ++ ()
- {
- t = ATgetNext(t);
- return *this;
- }
- ATerm operator * ()
- {
- return ATgetFirst(t);
- }
- operator bool ()
- {
- return t != ATempty;
- }
-};
-
-
-/* Throw an exception with an error message containing the given
- aterm. */
-Error badTerm(const format & f, ATerm t);
-
-
-/* Convert strings to ATerms. */
-ATerm toATerm(const char * s);
-ATerm toATerm(const string & s);
-
-ATermList toATermList(const StringSet & ss);
-
-}
-
-
-/* Write an ATerm to an output stream. */
-std::ostream & operator << (std::ostream & stream, ATerm e);
-
-
-#endif /* !__ATERM_H */
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index 1c1c9a9e5..e0c41a58d 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -950,53 +950,6 @@ void _interrupted()
//////////////////////////////////////////////////////////////////////
-string packStrings(const Strings & strings)
-{
- string d;
- for (Strings::const_iterator i = strings.begin();
- i != strings.end(); ++i)
- {
- unsigned int len = i->size();
- d += len & 0xff;
- d += (len >> 8) & 0xff;
- d += (len >> 16) & 0xff;
- d += (len >> 24) & 0xff;
- d += *i;
- }
- return d;
-}
-
-
-Strings unpackStrings(const string & s)
-{
- Strings strings;
-
- string::const_iterator i = s.begin();
-
- while (i != s.end()) {
-
- if (i + 4 > s.end())
- throw Error(format("short db entry: `%1%'") % s);
-
- unsigned int len;
- len = (unsigned char) *i++;
- len |= ((unsigned char) *i++) << 8;
- len |= ((unsigned char) *i++) << 16;
- len |= ((unsigned char) *i++) << 24;
-
- if (len == 0xffffffff) return strings; /* explicit end-of-list */
-
- if (i + len > s.end())
- throw Error(format("short db entry: `%1%'") % s);
-
- strings.push_back(string(i, i + len));
- i += len;
- }
-
- return strings;
-}
-
-
Strings tokenizeString(const string & s, const string & separators)
{
Strings result;
@@ -1052,6 +1005,47 @@ bool hasSuffix(const string & s, const string & suffix)
}
+void expect(std::istream & str, const string & s)
+{
+ char s2[s.size()];
+ str.read(s2, s.size());
+ if (string(s2, s.size()) != s)
+ throw Error(format("expected string `%1%'") % s);
+}
+
+
+string parseString(std::istream & str)
+{
+ string res;
+ expect(str, "\"");
+ int c;
+ while ((c = str.get()) != '"')
+ if (c == '\\') {
+ c = str.get();
+ if (c == 'n') res += '\n';
+ else if (c == 'r') res += '\r';
+ else if (c == 't') res += '\t';
+ else res += c;
+ }
+ else res += c;
+ return res;
+}
+
+
+bool endOfList(std::istream & str)
+{
+ if (str.peek() == ',') {
+ str.get();
+ return false;
+ }
+ if (str.peek() == ']') {
+ str.get();
+ return true;
+ }
+ return false;
+}
+
+
void ignoreException()
{
try {
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index aa40c6177..c45ebc062 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -15,7 +15,7 @@ namespace nix {
#define foreach(it_type, it, collection) \
- for (it_type it = collection.begin(); it != collection.end(); ++it)
+ for (it_type it = (collection).begin(); it != (collection).end(); ++it)
/* Return an environment variable. */
@@ -276,11 +276,6 @@ void inline checkInterrupt()
MakeError(Interrupted, BaseError)
-/* String packing / unpacking. */
-string packStrings(const Strings & strings);
-Strings unpackStrings(const string & s);
-
-
/* String tokenizer. */
Strings tokenizeString(const string & s, const string & separators = " \t\n\r");
@@ -307,32 +302,21 @@ string int2String(int n);
bool hasSuffix(const string & s, const string & suffix);
-/* Exception handling in destructors: print an error message, then
- ignore the exception. */
-void ignoreException();
+/* Read string `s' from stream `str'. */
+void expect(std::istream & str, const string & s);
-/* STL functions such as sort() pass a binary function object around
- by value, so it gets cloned a lot. This is bad if the function
- object has state or is simply large. This adapter wraps the
- function object to simulate passing by reference. */
-template<class F>
-struct binary_function_ref_adapter
-{
- F * p;
+/* Read a C-style string from stream `str'. */
+string parseString(std::istream & str);
- binary_function_ref_adapter(F * _p)
- {
- p = _p;
- }
-
- typename F::result_type operator () (
- const typename F::first_argument_type & x,
- const typename F::second_argument_type & y)
- {
- return (*p)(x, y);
- }
-};
+
+/* Utility function used to parse legacy ATerms. */
+bool endOfList(std::istream & str);
+
+
+/* Exception handling in destructors: print an error message, then
+ ignore the exception. */
+void ignoreException();
}
diff --git a/src/nix-env/Makefile.am b/src/nix-env/Makefile.am
index 53fea3d9d..113baabc4 100644
--- a/src/nix-env/Makefile.am
+++ b/src/nix-env/Makefile.am
@@ -1,6 +1,7 @@
bin_PROGRAMS = nix-env
-nix_env_SOURCES = nix-env.cc profiles.cc profiles.hh help.txt
+nix_env_SOURCES = nix-env.cc profiles.cc profiles.hh user-env.cc user-env.hh help.txt
+
nix_env_LDADD = ../libmain/libmain.la ../libexpr/libexpr.la \
../libstore/libstore.la ../libutil/libutil.la \
../boost/format/libformat.la
@@ -11,7 +12,6 @@ nix-env.o: help.txt.hh
../bin2c/bin2c helpText < $< > $@ || (rm $@ && exit 1)
AM_CXXFLAGS = \
- ${aterm_include} \
-I$(srcdir)/.. \
-I$(srcdir)/../libutil -I$(srcdir)/../libstore \
-I$(srcdir)/../libexpr -I$(srcdir)/../libmain -I../libexpr
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index d3f74c7ed..ca67119e9 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -6,15 +6,13 @@
#include "parser.hh"
#include "eval.hh"
#include "help.txt.hh"
-#include "nixexpr-ast.hh"
#include "get-drvs.hh"
#include "attr-path.hh"
-#include "pathlocks.hh"
#include "common-opts.hh"
#include "xml-writer.hh"
#include "store-api.hh"
+#include "user-env.hh"
#include "util.hh"
-#include "aterm.hh"
#include <cerrno>
#include <ctime>
@@ -48,7 +46,7 @@ struct InstallSourceInfo
Path profile; /* for srcProfile */
string systemFilter; /* for srcNixExprDrvs */
bool prebuiltOnly;
- ATermMap autoArgs;
+ Bindings autoArgs;
InstallSourceInfo() : prebuiltOnly(false) { };
};
@@ -113,7 +111,7 @@ static bool isNixExpr(const Path & path)
static void getAllExprs(EvalState & state,
- const Path & path, ATermMap & attrs)
+ const Path & path, ExprAttrs & attrs)
{
Strings names = readDirectory(path);
StringSet namesSorted(names.begin(), names.end());
@@ -133,8 +131,8 @@ static void getAllExprs(EvalState & state,
string attrName = *i;
if (hasSuffix(attrName, ".nix"))
attrName = string(attrName, 0, attrName.size() - 4);
- attrs.set(toATerm(attrName), makeAttrRHS(
- parseExprFromFile(state, absPath(path2)), makeNoPos()));
+ attrs.attrs[state.symbols.create(attrName)] =
+ ExprAttrs::Attr(parseExprFromFile(state, absPath(path2)), noPos);
}
else
/* `path2' is a directory (with no default.nix in it);
@@ -144,7 +142,7 @@ static void getAllExprs(EvalState & state,
}
-static Expr loadSourceExpr(EvalState & state, const Path & path)
+static Expr * loadSourceExpr(EvalState & state, const Path & path)
{
if (isNixExpr(path)) return parseExprFromFile(state, absPath(path));
@@ -154,20 +152,22 @@ static Expr loadSourceExpr(EvalState & state, const Path & path)
(but keep the attribute set flat, not nested, to make it easier
for a user to have a ~/.nix-defexpr directory that includes
some system-wide directory). */
- ATermMap attrs;
- attrs.set(toATerm("_combineChannels"), makeAttrRHS(makeList(ATempty), makeNoPos()));
- getAllExprs(state, path, attrs);
- return makeAttrs(attrs);
+ ExprAttrs * attrs = new ExprAttrs;
+ attrs->attrs[state.symbols.create("_combineChannels")] =
+ ExprAttrs::Attr(new ExprList(), noPos);
+ getAllExprs(state, path, *attrs);
+ return attrs;
}
static void loadDerivations(EvalState & state, Path nixExprPath,
- string systemFilter, const ATermMap & autoArgs,
+ string systemFilter, const Bindings & autoArgs,
const string & pathPrefix, DrvInfos & elems)
{
- getDerivations(state,
- findAlongAttrPath(state, pathPrefix, autoArgs, loadSourceExpr(state, nixExprPath)),
- pathPrefix, autoArgs, elems);
+ Value v;
+ findAlongAttrPath(state, pathPrefix, autoArgs, loadSourceExpr(state, nixExprPath), v);
+
+ getDerivations(state, v, pathPrefix, autoArgs, elems);
/* Filter out all derivations not applicable to the current
system. */
@@ -193,172 +193,6 @@ static Path getDefNixExprPath()
}
-struct AddPos : TermFun
-{
- ATerm operator () (ATerm e)
- {
- ATerm x, y;
- if (matchObsoleteBind(e, x, y))
- return makeBind(x, y, makeNoPos());
- if (matchObsoleteStr(e, x))
- return makeStr(x, ATempty);
- return e;
- }
-};
-
-
-static DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
-{
- Path path = userEnv + "/manifest";
-
- if (!pathExists(path))
- return DrvInfos(); /* not an error, assume nothing installed */
-
- Expr e = ATreadFromNamedFile(path.c_str());
- if (!e) throw Error(format("cannot read Nix expression from `%1%'") % path);
-
- /* Compatibility: Bind(x, y) -> Bind(x, y, NoPos). */
- AddPos addPos;
- e = bottomupRewrite(addPos, e);
-
- DrvInfos elems;
- getDerivations(state, e, "", ATermMap(1), elems);
- return elems;
-}
-
-
-/* Ensure exclusive access to a profile. Any command that modifies
- the profile first acquires this lock. */
-static void lockProfile(PathLocks & lock, const Path & profile)
-{
- lock.lockPaths(singleton<PathSet>(profile),
- (format("waiting for lock on profile `%1%'") % profile).str());
- lock.setDeletion(true);
-}
-
-
-/* Optimistic locking is used by long-running operations like `nix-env
- -i'. Instead of acquiring the exclusive lock for the entire
- duration of the operation, we just perform the operation
- optimistically (without an exclusive lock), and check at the end
- whether the profile changed while we were busy (i.e., the symlink
- target changed). If so, the operation is restarted. Restarting is
- generally cheap, since the build results are still in the Nix
- store. Most of the time, only the user environment has to be
- rebuilt. */
-static string optimisticLockProfile(const Path & profile)
-{
- return pathExists(profile) ? readLink(profile) : "";
-}
-
-
-static bool createUserEnv(EvalState & state, DrvInfos & elems,
- const Path & profile, bool keepDerivations,
- const string & lockToken)
-{
- /* Build the components in the user environment, if they don't
- exist already. */
- PathSet drvsToBuild;
- foreach (DrvInfos::const_iterator, i, elems)
- /* Call to `isDerivation' is for compatibility with Nix <= 0.7
- user environments. */
- if (i->queryDrvPath(state) != "" &&
- isDerivation(i->queryDrvPath(state)))
- drvsToBuild.insert(i->queryDrvPath(state));
-
- debug(format("building user environment dependencies"));
- store->buildDerivations(drvsToBuild);
-
- /* Get the environment builder expression. */
- Expr envBuilder = parseExprFromFile(state,
- nixDataDir + "/nix/corepkgs/buildenv"); /* !!! */
-
- /* Construct the whole top level derivation. */
- PathSet references;
- ATermList manifest = ATempty;
- ATermList inputs = ATempty;
- foreach (DrvInfos::iterator, i, elems) {
- /* Create a pseudo-derivation containing the name, system,
- output path, and optionally the derivation path, as well as
- the meta attributes. */
- Path drvPath = keepDerivations ? i->queryDrvPath(state) : "";
-
- /* Round trip to get rid of "bad" meta values (like
- functions). */
- MetaInfo meta = i->queryMetaInfo(state);
- i->setMetaInfo(meta);
-
- ATermList as = ATmakeList5(
- makeBind(toATerm("type"),
- makeStr("derivation"), makeNoPos()),
- makeBind(toATerm("name"),
- makeStr(i->name), makeNoPos()),
- makeBind(toATerm("system"),
- makeStr(i->system), makeNoPos()),
- makeBind(toATerm("outPath"),
- makeStr(i->queryOutPath(state)), makeNoPos()),
- makeBind(toATerm("meta"),
- i->attrs->get(toATerm("meta")), makeNoPos()));
-
- if (drvPath != "") as = ATinsert(as,
- makeBind(toATerm("drvPath"),
- makeStr(drvPath), makeNoPos()));
-
- manifest = ATinsert(manifest, makeAttrs(as));
-
- inputs = ATinsert(inputs, makeStr(i->queryOutPath(state)));
-
- /* This is only necessary when installing store paths, e.g.,
- `nix-env -i /nix/store/abcd...-foo'. */
- store->addTempRoot(i->queryOutPath(state));
- store->ensurePath(i->queryOutPath(state));
-
- references.insert(i->queryOutPath(state));
- if (drvPath != "") references.insert(drvPath);
- }
-
- /* Also write a copy of the list of inputs to the store; we need
- it for future modifications of the environment. */
- Path manifestFile = store->addTextToStore("env-manifest",
- atPrint(canonicaliseExpr(makeList(ATreverse(manifest)))), references);
-
- Expr topLevel = makeCall(envBuilder, makeAttrs(ATmakeList3(
- makeBind(toATerm("system"),
- makeStr(thisSystem), makeNoPos()),
- makeBind(toATerm("derivations"),
- makeList(ATreverse(manifest)), makeNoPos()),
- makeBind(toATerm("manifest"),
- makeStr(manifestFile, singleton<PathSet>(manifestFile)), makeNoPos())
- )));
-
- /* Instantiate it. */
- debug(format("evaluating builder expression `%1%'") % topLevel);
- DrvInfo topLevelDrv;
- if (!getDerivation(state, topLevel, topLevelDrv))
- abort();
-
- /* Realise the resulting store expression. */
- debug(format("building user environment"));
- store->buildDerivations(singleton<PathSet>(topLevelDrv.queryDrvPath(state)));
-
- /* Switch the current user environment to the output path. */
- PathLocks lock;
- lockProfile(lock, profile);
-
- Path lockTokenCur = optimisticLockProfile(profile);
- if (lockToken != lockTokenCur) {
- printMsg(lvlError, format("profile `%1%' changed while we were busy; restarting") % profile);
- return false;
- }
-
- debug(format("switching to new user environment"));
- Path generation = createGeneration(profile, topLevelDrv.queryOutPath(state));
- switchLink(profile, generation);
-
- return true;
-}
-
-
static int getPriority(EvalState & state, const DrvInfo & drv)
{
MetaValue value = drv.queryMetaInfo(state, "priority");
@@ -517,14 +351,13 @@ static void queryInstSources(EvalState & state,
(import ./foo.nix)' = `(import ./foo.nix).bar'. */
case srcNixExprs: {
- Expr e1 = loadSourceExpr(state, instSource.nixExprPath);
+ Expr * e1 = loadSourceExpr(state, instSource.nixExprPath);
- for (Strings::const_iterator i = args.begin();
- i != args.end(); ++i)
- {
- Expr e2 = parseExprFromString(state, *i, absPath("."));
- Expr call = makeCall(e2, e1);
- getDerivations(state, call, "", instSource.autoArgs, elems);
+ foreach (Strings::const_iterator, i, args) {
+ Expr * e2 = parseExprFromString(state, *i, absPath("."));
+ Expr * call = new ExprApp(e2, e1);
+ Value v; state.eval(call, v);
+ getDerivations(state, v, "", instSource.autoArgs, elems);
}
break;
@@ -541,7 +374,7 @@ static void queryInstSources(EvalState & state,
Path path = followLinksToStorePath(*i);
DrvInfo elem;
- elem.attrs = boost::shared_ptr<ATermMap>(new ATermMap(0)); /* ugh... */
+ elem.attrs = new Bindings;
string name = baseNameOf(path);
string::size_type dash = name.find('-');
if (dash != string::npos)
@@ -575,12 +408,12 @@ static void queryInstSources(EvalState & state,
}
case srcAttrPath: {
- for (Strings::const_iterator i = args.begin();
- i != args.end(); ++i)
- getDerivations(state,
- findAlongAttrPath(state, *i, instSource.autoArgs,
- loadSourceExpr(state, instSource.nixExprPath)),
- "", instSource.autoArgs, elems);
+ foreach (Strings::const_iterator, i, args) {
+ Value v;
+ findAlongAttrPath(state, *i, instSource.autoArgs,
+ loadSourceExpr(state, instSource.nixExprPath), v);
+ getDerivations(state, v, "", instSource.autoArgs, elems);
+ }
break;
}
}
@@ -1103,6 +936,7 @@ static void opQuery(Globals & globals,
foreach (vector<DrvInfo>::iterator, i, elems2) {
try {
+ startNest(nest, lvlDebug, format("outputting query result `%1%'") % i->attrPath);
/* For table output. */
Strings columns;
@@ -1473,7 +1307,7 @@ void run(Strings args)
op(globals, remaining, opFlags, opArgs);
- printEvalStats(globals.state);
+ globals.state.printStats();
}
diff --git a/src/nix-env/profiles.cc b/src/nix-env/profiles.cc
index 75585b1b2..60576f1ae 100644
--- a/src/nix-env/profiles.cc
+++ b/src/nix-env/profiles.cc
@@ -130,6 +130,20 @@ void switchLink(Path link, Path target)
throw SysError(format("renaming `%1%' to `%2%'") % tmp % link);
}
-
+
+void lockProfile(PathLocks & lock, const Path & profile)
+{
+ lock.lockPaths(singleton<PathSet>(profile),
+ (format("waiting for lock on profile `%1%'") % profile).str());
+ lock.setDeletion(true);
+}
+
+
+string optimisticLockProfile(const Path & profile)
+{
+ return pathExists(profile) ? readLink(profile) : "";
+}
+
+
}
diff --git a/src/nix-env/profiles.hh b/src/nix-env/profiles.hh
index 99c20f42d..a64258dae 100644
--- a/src/nix-env/profiles.hh
+++ b/src/nix-env/profiles.hh
@@ -2,6 +2,7 @@
#define __PROFILES_H
#include "types.hh"
+#include "pathlocks.hh"
#include <time.h>
@@ -37,6 +38,20 @@ void deleteGeneration(const Path & profile, unsigned int gen);
void switchLink(Path link, Path target);
+/* Ensure exclusive access to a profile. Any command that modifies
+ the profile first acquires this lock. */
+void lockProfile(PathLocks & lock, const Path & profile);
+
+/* Optimistic locking is used by long-running operations like `nix-env
+ -i'. Instead of acquiring the exclusive lock for the entire
+ duration of the operation, we just perform the operation
+ optimistically (without an exclusive lock), and check at the end
+ whether the profile changed while we were busy (i.e., the symlink
+ target changed). If so, the operation is restarted. Restarting is
+ generally cheap, since the build results are still in the Nix
+ store. Most of the time, only the user environment has to be
+ rebuilt. */
+string optimisticLockProfile(const Path & profile);
}
diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc
new file mode 100644
index 000000000..72e13fceb
--- /dev/null
+++ b/src/nix-env/user-env.cc
@@ -0,0 +1,257 @@
+#include "util.hh"
+#include "get-drvs.hh"
+#include "derivations.hh"
+#include "store-api.hh"
+#include "globals.hh"
+#include "shared.hh"
+#include "eval.hh"
+#include "parser.hh"
+#include "profiles.hh"
+
+
+namespace nix {
+
+
+static void readLegacyManifest(const Path & path, DrvInfos & elems);
+
+
+DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
+{
+ DrvInfos elems;
+
+ Path manifestFile = userEnv + "/manifest.nix";
+ Path oldManifestFile = userEnv + "/manifest";
+
+ if (pathExists(manifestFile)) {
+ Value v;
+ state.eval(parseExprFromFile(state, manifestFile), v);
+ getDerivations(state, v, "", Bindings(), elems);
+ } else if (pathExists(oldManifestFile))
+ readLegacyManifest(oldManifestFile, elems);
+
+ return elems;
+}
+
+
+bool createUserEnv(EvalState & state, DrvInfos & elems,
+ const Path & profile, bool keepDerivations,
+ const string & lockToken)
+{
+ /* Build the components in the user environment, if they don't
+ exist already. */
+ PathSet drvsToBuild;
+ foreach (DrvInfos::const_iterator, i, elems)
+ if (i->queryDrvPath(state) != "")
+ drvsToBuild.insert(i->queryDrvPath(state));
+
+ debug(format("building user environment dependencies"));
+ store->buildDerivations(drvsToBuild);
+
+ /* Construct the whole top level derivation. */
+ PathSet references;
+ Value manifest;
+ state.mkList(manifest, elems.size());
+ unsigned int n = 0;
+ foreach (DrvInfos::iterator, i, elems) {
+ /* Create a pseudo-derivation containing the name, system,
+ output path, and optionally the derivation path, as well as
+ the meta attributes. */
+ Path drvPath = keepDerivations ? i->queryDrvPath(state) : "";
+
+ Value & v(*state.allocValues(1));
+ manifest.list.elems[n++] = &v;
+ state.mkAttrs(v);
+
+ mkString((*v.attrs)[state.sType].value, "derivation");
+ mkString((*v.attrs)[state.sName].value, i->name);
+ mkString((*v.attrs)[state.sSystem].value, i->system);
+ mkString((*v.attrs)[state.sOutPath].value, i->queryOutPath(state));
+ if (drvPath != "")
+ mkString((*v.attrs)[state.sDrvPath].value, i->queryDrvPath(state));
+
+ state.mkAttrs((*v.attrs)[state.sMeta].value);
+
+ MetaInfo meta = i->queryMetaInfo(state);
+
+ foreach (MetaInfo::const_iterator, j, meta) {
+ Value & v2((*(*v.attrs)[state.sMeta].value.attrs)[state.symbols.create(j->first)].value);
+ switch (j->second.type) {
+ case MetaValue::tpInt: mkInt(v2, j->second.intValue); break;
+ case MetaValue::tpString: mkString(v2, j->second.stringValue); break;
+ case MetaValue::tpStrings: {
+ state.mkList(v2, j->second.stringValues.size());
+ unsigned int m = 0;
+ foreach (Strings::const_iterator, k, j->second.stringValues) {
+ v2.list.elems[m] = state.allocValues(1);
+ mkString(*v2.list.elems[m++], *k);
+ }
+ break;
+ }
+ default: abort();
+ }
+ }
+
+ /* This is only necessary when installing store paths, e.g.,
+ `nix-env -i /nix/store/abcd...-foo'. */
+ store->addTempRoot(i->queryOutPath(state));
+ store->ensurePath(i->queryOutPath(state));
+
+ references.insert(i->queryOutPath(state));
+ if (drvPath != "") references.insert(drvPath);
+ }
+
+ /* Also write a copy of the list of user environment elements to
+ the store; we need it for future modifications of the
+ environment. */
+ Path manifestFile = store->addTextToStore("env-manifest.nix",
+ (format("%1%") % manifest).str(), references);
+
+ printMsg(lvlError, manifestFile);
+
+ /* Get the environment builder expression. */
+ Value envBuilder;
+ state.eval(parseExprFromFile(state, nixDataDir + "/nix/corepkgs/buildenv"), envBuilder);
+
+ /* Construct a Nix expression that calls the user environment
+ builder with the manifest as argument. */
+ Value args, topLevel;
+ state.mkAttrs(args);
+ mkString((*args.attrs)[state.sSystem].value, thisSystem);
+ mkString((*args.attrs)[state.symbols.create("manifest")].value,
+ manifestFile, singleton<PathSet>(manifestFile));
+ (*args.attrs)[state.symbols.create("derivations")].value = manifest;
+ mkApp(topLevel, envBuilder, args);
+
+ /* Evaluate it. */
+ debug("evaluating user environment builder");
+ DrvInfo topLevelDrv;
+ if (!getDerivation(state, topLevel, topLevelDrv))
+ abort();
+
+ /* Realise the resulting store expression. */
+ debug("building user environment");
+ store->buildDerivations(singleton<PathSet>(topLevelDrv.queryDrvPath(state)));
+
+ /* Switch the current user environment to the output path. */
+ PathLocks lock;
+ lockProfile(lock, profile);
+
+ Path lockTokenCur = optimisticLockProfile(profile);
+ if (lockToken != lockTokenCur) {
+ printMsg(lvlError, format("profile `%1%' changed while we were busy; restarting") % profile);
+ return false;
+ }
+
+ debug(format("switching to new user environment"));
+ Path generation = createGeneration(profile, topLevelDrv.queryOutPath(state));
+ switchLink(profile, generation);
+
+ return true;
+}
+
+
+/* Code for parsing manifests in the old textual ATerm format. */
+
+static string parseStr(std::istream & str)
+{
+ expect(str, "Str(");
+ string s = parseString(str);
+ expect(str, ",[])");
+ return s;
+}
+
+
+static string parseWord(std::istream & str)
+{
+ string res;
+ while (isalpha(str.peek()))
+ res += str.get();
+ return res;
+}
+
+
+static MetaInfo parseMeta(std::istream & str)
+{
+ MetaInfo meta;
+
+ expect(str, "Attrs([");
+ while (!endOfList(str)) {
+ expect(str, "Bind(");
+
+ MetaValue value;
+
+ string name = parseString(str);
+ expect(str, ",");
+
+ string type = parseWord(str);
+
+ if (type == "Str") {
+ expect(str, "(");
+ value.type = MetaValue::tpString;
+ value.stringValue = parseString(str);
+ expect(str, ",[])");
+ }
+
+ else if (type == "List") {
+ expect(str, "([");
+ value.type = MetaValue::tpStrings;
+ while (!endOfList(str))
+ value.stringValues.push_back(parseStr(str));
+ expect(str, ")");
+ }
+
+ else throw Error(format("unexpected token `%1%'") % type);
+
+ expect(str, ",NoPos)");
+ meta[name] = value;
+ }
+
+ expect(str, ")");
+
+ return meta;
+}
+
+
+static void readLegacyManifest(const Path & path, DrvInfos & elems)
+{
+ string manifest = readFile(path);
+ std::istringstream str(manifest);
+ expect(str, "List([");
+
+ unsigned int n = 0;
+
+ while (!endOfList(str)) {
+ DrvInfo elem;
+ expect(str, "Attrs([");
+
+ while (!endOfList(str)) {
+ expect(str, "Bind(");
+ string name = parseString(str);
+ expect(str, ",");
+
+ if (name == "meta") elem.setMetaInfo(parseMeta(str));
+ else {
+ string value = parseStr(str);
+ if (name == "name") elem.name = value;
+ else if (name == "outPath") elem.setOutPath(value);
+ else if (name == "drvPath") elem.setDrvPath(value);
+ else if (name == "system") elem.system = value;
+ }
+
+ expect(str, ",NoPos)");
+ }
+
+ expect(str, ")");
+
+ if (elem.name != "") {
+ elem.attrPath = int2String(n++);
+ elems.push_back(elem);
+ }
+ }
+
+ expect(str, ")");
+}
+
+
+}
+
diff --git a/src/nix-env/user-env.hh b/src/nix-env/user-env.hh
new file mode 100644
index 000000000..4125d8217
--- /dev/null
+++ b/src/nix-env/user-env.hh
@@ -0,0 +1,20 @@
+#ifndef __USER_ENV_H
+#define __USER_ENV_H
+
+#include "get-drvs.hh"
+
+namespace nix {
+
+DrvInfos queryInstalled(EvalState & state, const Path & userEnv);
+
+bool createUserEnv(EvalState & state, DrvInfos & elems,
+ const Path & profile, bool keepDerivations,
+ const string & lockToken);
+
+}
+
+#endif /* !__USER_ENV_H */
+
+
+
+
diff --git a/src/nix-instantiate/Makefile.am b/src/nix-instantiate/Makefile.am
index 1c6d30873..b48dbd9d4 100644
--- a/src/nix-instantiate/Makefile.am
+++ b/src/nix-instantiate/Makefile.am
@@ -11,6 +11,5 @@ nix-instantiate.o: help.txt.hh
../bin2c/bin2c helpText < $< > $@ || (rm $@ && exit 1)
AM_CXXFLAGS = \
- ${aterm_include} \
-I$(srcdir)/.. -I$(srcdir)/../libutil -I$(srcdir)/../libstore \
-I$(srcdir)/../libexpr -I$(srcdir)/../libmain -I../libexpr
diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc
index 03fb35ada..38c62880b 100644
--- a/src/nix-instantiate/nix-instantiate.cc
+++ b/src/nix-instantiate/nix-instantiate.cc
@@ -7,11 +7,10 @@
#include "parser.hh"
#include "get-drvs.hh"
#include "attr-path.hh"
-#include "expr-to-xml.hh"
+#include "value-to-xml.hh"
#include "util.hh"
#include "store-api.hh"
#include "common-opts.hh"
-#include "aterm.hh"
#include "help.txt.hh"
@@ -24,7 +23,7 @@ void printHelp()
}
-static Expr parseStdin(EvalState & state)
+static Expr * parseStdin(EvalState & state)
{
startNest(nest, lvlTalkative, format("parsing standard input"));
string s, s2;
@@ -38,47 +37,41 @@ static int rootNr = 0;
static bool indirectRoot = false;
-static void printResult(EvalState & state, Expr e,
- bool evalOnly, bool xmlOutput, bool location, const ATermMap & autoArgs)
-{
- PathSet context;
-
- if (evalOnly)
- if (xmlOutput)
- printTermAsXML(e, std::cout, context, location);
- else
- std::cout << format("%1%\n") % canonicaliseExpr(e);
-
- else {
- DrvInfos drvs;
- getDerivations(state, e, "", autoArgs, drvs);
- for (DrvInfos::iterator i = drvs.begin(); i != drvs.end(); ++i) {
- Path drvPath = i->queryDrvPath(state);
- if (gcRoot == "")
- printGCWarning();
- else
- drvPath = addPermRoot(drvPath,
- makeRootName(gcRoot, rootNr),
- indirectRoot);
- std::cout << format("%1%\n") % drvPath;
- }
- }
-}
-
-
void processExpr(EvalState & state, const Strings & attrPaths,
- bool parseOnly, bool strict, const ATermMap & autoArgs,
- bool evalOnly, bool xmlOutput, bool location, Expr e)
+ bool parseOnly, bool strict, const Bindings & autoArgs,
+ bool evalOnly, bool xmlOutput, bool location, Expr * e)
{
- for (Strings::const_iterator i = attrPaths.begin(); i != attrPaths.end(); ++i) {
- Expr e2 = findAlongAttrPath(state, *i, autoArgs, e);
- if (!parseOnly)
- if (strict)
- e2 = strictEvalExpr(state, e2);
- else
- e2 = evalExpr(state, e2);
- printResult(state, e2, evalOnly, xmlOutput, location, autoArgs);
- }
+ if (parseOnly)
+ std::cout << format("%1%\n") % *e;
+ else
+ foreach (Strings::const_iterator, i, attrPaths) {
+ Value v;
+ findAlongAttrPath(state, *i, autoArgs, e, v);
+ state.forceValue(v);
+
+ PathSet context;
+ if (evalOnly)
+ if (xmlOutput)
+ printValueAsXML(state, strict, location, v, std::cout, context);
+ else {
+ if (strict) state.strictForceValue(v);
+ std::cout << v << std::endl;
+ }
+ else {
+ DrvInfos drvs;
+ getDerivations(state, v, "", autoArgs, drvs);
+ foreach (DrvInfos::iterator, i, drvs) {
+ Path drvPath = i->queryDrvPath(state);
+ if (gcRoot == "")
+ printGCWarning();
+ else
+ drvPath = addPermRoot(drvPath,
+ makeRootName(gcRoot, rootNr),
+ indirectRoot);
+ std::cout << format("%1%\n") % drvPath;
+ }
+ }
+ }
}
@@ -93,11 +86,9 @@ void run(Strings args)
bool xmlOutputSourceLocation = true;
bool strict = false;
Strings attrPaths;
- ATermMap autoArgs(128);
+ Bindings autoArgs;
- for (Strings::iterator i = args.begin();
- i != args.end(); )
- {
+ for (Strings::iterator i = args.begin(); i != args.end(); ) {
string arg = *i++;
if (arg == "-")
@@ -141,21 +132,19 @@ void run(Strings args)
store = openStore();
if (readStdin) {
- Expr e = parseStdin(state);
+ Expr * e = parseStdin(state);
processExpr(state, attrPaths, parseOnly, strict, autoArgs,
evalOnly, xmlOutput, xmlOutputSourceLocation, e);
}
- for (Strings::iterator i = files.begin();
- i != files.end(); i++)
- {
+ foreach (Strings::iterator, i, files) {
Path path = absPath(*i);
- Expr e = parseExprFromFile(state, path);
+ Expr * e = parseExprFromFile(state, path);
processExpr(state, attrPaths, parseOnly, strict, autoArgs,
evalOnly, xmlOutput, xmlOutputSourceLocation, e);
}
- printEvalStats(state);
+ state.printStats();
}