* Sync with the trunk.
This commit is contained in:
commit
aa45027818
17
configure.ac
17
configure.ac
|
@ -195,23 +195,6 @@ AC_ARG_WITH(store-dir, AC_HELP_STRING([--with-store-dir=PATH],
|
||||||
storedir=$withval, storedir='/nix/store')
|
storedir=$withval, storedir='/nix/store')
|
||||||
AC_SUBST(storedir)
|
AC_SUBST(storedir)
|
||||||
|
|
||||||
AC_ARG_WITH(aterm, AC_HELP_STRING([--with-aterm=PATH],
|
|
||||||
[prefix of CWI ATerm library]),
|
|
||||||
aterm=$withval, aterm=)
|
|
||||||
AM_CONDITIONAL(HAVE_ATERM, test -n "$aterm")
|
|
||||||
if test -z "$aterm"; then
|
|
||||||
aterm_lib='-L${top_builddir}/externals/aterm-2.5/aterm -lATerm'
|
|
||||||
aterm_include='-I${top_builddir}/externals/aterm-2.5/aterm'
|
|
||||||
aterm_bin='${top_builddir}/externals/aterm-2.5/utils'
|
|
||||||
else
|
|
||||||
aterm_lib="-L$aterm/lib -lATerm"
|
|
||||||
aterm_include="-I$aterm/include"
|
|
||||||
aterm_bin="$aterm/bin"
|
|
||||||
fi
|
|
||||||
AC_SUBST(aterm_lib)
|
|
||||||
AC_SUBST(aterm_include)
|
|
||||||
AC_SUBST(aterm_bin)
|
|
||||||
|
|
||||||
AC_ARG_WITH(openssl, AC_HELP_STRING([--with-openssl=PATH],
|
AC_ARG_WITH(openssl, AC_HELP_STRING([--with-openssl=PATH],
|
||||||
[prefix of the OpenSSL library]),
|
[prefix of the OpenSSL library]),
|
||||||
openssl=$withval, openssl=)
|
openssl=$withval, openssl=)
|
||||||
|
|
|
@ -29,10 +29,18 @@ sub createLinks {
|
||||||
$baseName =~ s/^.*\///g; # strip directory
|
$baseName =~ s/^.*\///g; # strip directory
|
||||||
my $dstFile = "$dstDir/$baseName";
|
my $dstFile = "$dstDir/$baseName";
|
||||||
|
|
||||||
|
# The files below are special-cased so that they don't show up
|
||||||
|
# in user profiles, either because they are useless, or
|
||||||
|
# because they would cause pointless collisions (e.g., each
|
||||||
|
# Python package brings its own
|
||||||
|
# `$out/lib/pythonX.Y/site-packages/easy-install.pth'.)
|
||||||
# Urgh, hacky...
|
# Urgh, hacky...
|
||||||
if ($srcFile =~ /\/propagated-build-inputs$/ ||
|
if ($srcFile =~ /\/propagated-build-inputs$/ ||
|
||||||
$srcFile =~ /\/nix-support$/ ||
|
$srcFile =~ /\/nix-support$/ ||
|
||||||
$srcFile =~ /\/perllocal.pod$/ ||
|
$srcFile =~ /\/perllocal.pod$/ ||
|
||||||
|
$srcFile =~ /\/easy-install.pth$/ ||
|
||||||
|
$srcFile =~ /\/site.py$/ ||
|
||||||
|
$srcFile =~ /\/site.pyc$/ ||
|
||||||
$srcFile =~ /\/info\/dir$/ ||
|
$srcFile =~ /\/info\/dir$/ ||
|
||||||
$srcFile =~ /\/log$/)
|
$srcFile =~ /\/log$/)
|
||||||
{
|
{
|
||||||
|
@ -160,4 +168,4 @@ while (scalar(keys %postponed) > 0) {
|
||||||
print STDERR "created $symlinks symlinks in user environment\n";
|
print STDERR "created $symlinks symlinks in user environment\n";
|
||||||
|
|
||||||
|
|
||||||
symlink($ENV{"manifest"}, "$out/manifest") or die "cannot create manifest";
|
symlink($ENV{"manifest"}, "$out/manifest.nix") or die "cannot create manifest";
|
||||||
|
|
|
@ -96,15 +96,13 @@ ubiquitous 2.5.4a won't. Note that these are only required if you
|
||||||
modify the parser or when you are building from the Subversion
|
modify the parser or when you are building from the Subversion
|
||||||
repository.</para>
|
repository.</para>
|
||||||
|
|
||||||
<para>Nix uses CWI's ATerm library and the bzip2 compressor (including
|
<para>Nix uses the bzip2 compressor (including the bzip2 library). It
|
||||||
the bzip2 library). These are included in the Nix source
|
is included in the Nix source distribution. If you build from the
|
||||||
distribution. If you build from the Subversion repository, you must
|
Subversion repository, you must download it yourself and place it in
|
||||||
download them yourself and place them in the
|
the <filename>externals/</filename> directory. See
|
||||||
<filename>externals/</filename> directory. See
|
|
||||||
<filename>externals/Makefile.am</filename> for the precise URLs of
|
<filename>externals/Makefile.am</filename> for the precise URLs of
|
||||||
these packages. Alternatively, if you already have them installed,
|
this packages. Alternatively, if you already have it installed, you
|
||||||
you can use <command>configure</command>'s
|
can use <command>configure</command>'s <option>--with-bzip2</option>
|
||||||
<option>--with-aterm</option> and <option>--with-bzip2</option>
|
|
||||||
options to point to their respective locations.</para>
|
options to point to their respective locations.</para>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -342,7 +342,7 @@ $ nix-store --gc --max-freed $((100 * 1024 * 1024))</screen>
|
||||||
|
|
||||||
<cmdsynopsis>
|
<cmdsynopsis>
|
||||||
<command>nix-store</command>
|
<command>nix-store</command>
|
||||||
<arg choice='plain'><option>--gc</option></arg>
|
<arg choice='plain'><option>--delete</option></arg>
|
||||||
<arg><option>--ignore-liveness</option></arg>
|
<arg><option>--ignore-liveness</option></arg>
|
||||||
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||||
</cmdsynopsis>
|
</cmdsynopsis>
|
||||||
|
|
40
externals/Makefile.am
vendored
40
externals/Makefile.am
vendored
|
@ -1,35 +1,3 @@
|
||||||
# CWI ATerm
|
|
||||||
|
|
||||||
ATERM = aterm-$(ATERM_VERSION)
|
|
||||||
|
|
||||||
$(ATERM).tar.gz:
|
|
||||||
@echo "Nix requires the CWI ATerm library to build."
|
|
||||||
@echo "Please download version $(ATERM_VERSION) from"
|
|
||||||
@echo " http://nixos.org/tarballs/aterm-$(ATERM_VERSION).tar.gz"
|
|
||||||
@echo "and place it in the externals/ directory."
|
|
||||||
false
|
|
||||||
|
|
||||||
$(ATERM): $(ATERM).tar.gz
|
|
||||||
gzip -d < $(srcdir)/$(ATERM).tar.gz | tar xvf -
|
|
||||||
patch -d $(ATERM) -p1 < ./max-long.patch
|
|
||||||
patch -d $(ATERM) -p1 < ./sizeof.patch
|
|
||||||
|
|
||||||
if HAVE_ATERM
|
|
||||||
build-aterm:
|
|
||||||
else
|
|
||||||
build-aterm: $(ATERM)
|
|
||||||
(cd $(ATERM) && \
|
|
||||||
CC="$(CC)" ./configure --prefix=$(pkglibdir)/dummy --libdir=${pkglibdir} $(SUB_CONFIGURE_FLAGS) && \
|
|
||||||
$(MAKE) && \
|
|
||||||
$(MAKE) check)
|
|
||||||
touch build-aterm
|
|
||||||
|
|
||||||
install-exec-local:: build-aterm
|
|
||||||
cd $(ATERM) && make install
|
|
||||||
rm -rf "$(DESTDIR)/$(pkglibdir)/dummy"
|
|
||||||
endif
|
|
||||||
|
|
||||||
|
|
||||||
# bzip2
|
# bzip2
|
||||||
|
|
||||||
BZIP2 = bzip2-1.0.5
|
BZIP2 = bzip2-1.0.5
|
||||||
|
@ -89,11 +57,11 @@ install-exec-local:: build-sqlite
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
all: build-aterm build-bzip2 build-sqlite
|
all: build-bzip2 build-sqlite
|
||||||
|
|
||||||
EXTRA_DIST = $(ATERM).tar.gz $(BZIP2).tar.gz $(SQLITE_TAR) max-long.patch sizeof.patch
|
EXTRA_DIST = $(BZIP2).tar.gz $(SQLITE_TAR)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(RM) -f build-aterm build-bzip2 build-sqlite
|
$(RM) -f build-bzip2 build-sqlite
|
||||||
$(RM) -rf $(ATERM) $(BZIP2) $(SQLITE)
|
$(RM) -rf $(BZIP2) $(SQLITE)
|
||||||
$(RM) -rf inst-bzip2
|
$(RM) -rf inst-bzip2
|
||||||
|
|
77
externals/max-long.patch
vendored
77
externals/max-long.patch
vendored
|
@ -1,77 +0,0 @@
|
||||||
diff -rc aterm-2.8-orig/aterm/hash.c aterm-2.8/aterm/hash.c
|
|
||||||
*** aterm-2.8-orig/aterm/hash.c 2008-11-10 13:54:22.000000000 +0100
|
|
||||||
--- aterm-2.8/aterm/hash.c 2009-01-27 18:14:14.000000000 +0100
|
|
||||||
***************
|
|
||||||
*** 93,146 ****
|
|
||||||
}
|
|
||||||
|
|
||||||
/*}}} */
|
|
||||||
- /*{{{ static long calc_long_max() */
|
|
||||||
- static long calc_long_max()
|
|
||||||
- {
|
|
||||||
- long try_long_max;
|
|
||||||
- long long_max;
|
|
||||||
- long delta;
|
|
||||||
-
|
|
||||||
- try_long_max = 1;
|
|
||||||
- do {
|
|
||||||
- long_max = try_long_max;
|
|
||||||
- try_long_max = long_max * 2;
|
|
||||||
- } while (try_long_max > 0);
|
|
||||||
-
|
|
||||||
- delta = long_max;
|
|
||||||
- while (delta > 1) {
|
|
||||||
- while (long_max + delta < 0) {
|
|
||||||
- delta /= 2;
|
|
||||||
- }
|
|
||||||
- long_max += delta;
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- return long_max;
|
|
||||||
-
|
|
||||||
- }
|
|
||||||
- /*}}} */
|
|
||||||
/*{{{ static long calculateNewSize(sizeMinus1, nrdel, nrentries) */
|
|
||||||
|
|
||||||
static long calculateNewSize
|
|
||||||
(long sizeMinus1, long nr_deletions, long nr_entries)
|
|
||||||
{
|
|
||||||
-
|
|
||||||
- /* Hack: LONG_MAX (limits.h) is often unreliable, we need to find
|
|
||||||
- * out the maximum possible value of a signed long dynamically.
|
|
||||||
- */
|
|
||||||
- static long st_long_max = 0;
|
|
||||||
-
|
|
||||||
- /* the resulting length has the form 2^k-1 */
|
|
||||||
-
|
|
||||||
if (nr_deletions >= nr_entries/2) {
|
|
||||||
return sizeMinus1;
|
|
||||||
}
|
|
||||||
|
|
||||||
! if (st_long_max == 0) {
|
|
||||||
! st_long_max = calc_long_max();
|
|
||||||
! }
|
|
||||||
!
|
|
||||||
! if (sizeMinus1 > st_long_max / 2) {
|
|
||||||
! return st_long_max-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (2*sizeMinus1)+1;
|
|
||||||
--- 93,109 ----
|
|
||||||
}
|
|
||||||
|
|
||||||
/*}}} */
|
|
||||||
/*{{{ static long calculateNewSize(sizeMinus1, nrdel, nrentries) */
|
|
||||||
|
|
||||||
static long calculateNewSize
|
|
||||||
(long sizeMinus1, long nr_deletions, long nr_entries)
|
|
||||||
{
|
|
||||||
if (nr_deletions >= nr_entries/2) {
|
|
||||||
return sizeMinus1;
|
|
||||||
}
|
|
||||||
|
|
||||||
! if (sizeMinus1 > LONG_MAX / 2) {
|
|
||||||
! return LONG_MAX-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (2*sizeMinus1)+1;
|
|
56
externals/sizeof.patch
vendored
56
externals/sizeof.patch
vendored
|
@ -1,56 +0,0 @@
|
||||||
diff -rc -x '*~' aterm-2.5-orig/aterm/aterm.c aterm-2.5/aterm/aterm.c
|
|
||||||
*** aterm-2.5-orig/aterm/aterm.c 2007-02-27 23:41:31.000000000 +0100
|
|
||||||
--- aterm-2.5/aterm/aterm.c 2010-02-23 15:10:38.000000000 +0100
|
|
||||||
***************
|
|
||||||
*** 150,155 ****
|
|
||||||
--- 150,157 ----
|
|
||||||
if (initialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
+ assert(sizeof(long) == sizeof(void *));
|
|
||||||
+
|
|
||||||
/*{{{ Handle arguments */
|
|
||||||
|
|
||||||
for (lcv=1; lcv < argc; lcv++) {
|
|
||||||
diff -rc -x '*~' aterm-2.5-orig/aterm/encoding.h aterm-2.5/aterm/encoding.h
|
|
||||||
*** aterm-2.5-orig/aterm/encoding.h 2007-02-27 23:41:31.000000000 +0100
|
|
||||||
--- aterm-2.5/aterm/encoding.h 2010-02-23 15:36:05.000000000 +0100
|
|
||||||
***************
|
|
||||||
*** 10,24 ****
|
|
||||||
{
|
|
||||||
#endif/* __cplusplus */
|
|
||||||
|
|
||||||
! #if SIZEOF_LONG > 4
|
|
||||||
! #define AT_64BIT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
! #if SIZEOF_LONG != SIZEOF_VOID_P
|
|
||||||
! #error Size of long is not the same as the size of a pointer
|
|
||||||
#endif
|
|
||||||
|
|
||||||
! #if SIZEOF_INT > 4
|
|
||||||
#error Size of int is not 32 bits
|
|
||||||
#endif
|
|
||||||
|
|
||||||
--- 10,30 ----
|
|
||||||
{
|
|
||||||
#endif/* __cplusplus */
|
|
||||||
|
|
||||||
! #include <limits.h>
|
|
||||||
!
|
|
||||||
! #ifndef SIZEOF_LONG
|
|
||||||
! #if ULONG_MAX > 4294967295
|
|
||||||
! #define SIZEOF_LONG 8
|
|
||||||
! #else
|
|
||||||
! #define SIZEOF_LONG 4
|
|
||||||
! #endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
! #if SIZEOF_LONG > 4
|
|
||||||
! #define AT_64BIT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
! #if UINT_MAX > 4294967295
|
|
||||||
#error Size of int is not 32 bits
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -28,11 +28,8 @@ let
|
||||||
--with-xml-flags=--nonet
|
--with-xml-flags=--nonet
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# Include the ATerm and Bzip2 tarballs in the distribution.
|
# Include the Bzip2 tarball in the distribution.
|
||||||
preConfigure = ''
|
preConfigure = ''
|
||||||
stripHash ${aterm.src}
|
|
||||||
cp -pv ${aterm.src} externals/$strippedName
|
|
||||||
|
|
||||||
stripHash ${bzip2.src}
|
stripHash ${bzip2.src}
|
||||||
cp -pv ${bzip2.src} externals/$strippedName
|
cp -pv ${bzip2.src} externals/$strippedName
|
||||||
|
|
||||||
|
@ -77,7 +74,7 @@ let
|
||||||
|
|
||||||
configureFlags = ''
|
configureFlags = ''
|
||||||
--disable-init-state
|
--disable-init-state
|
||||||
--with-aterm=${aterm} --with-bzip2=${bzip2} --with-sqlite=${sqlite}
|
--with-bzip2=${bzip2} --with-sqlite=${sqlite}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -97,7 +94,7 @@ let
|
||||||
|
|
||||||
configureFlags = ''
|
configureFlags = ''
|
||||||
--disable-init-state --disable-shared
|
--disable-init-state --disable-shared
|
||||||
--with-aterm=${aterm} --with-bzip2=${bzip2} --with-sqlite=${sqlite}
|
--with-bzip2=${bzip2} --with-sqlite=${sqlite}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
lcovFilter = [ "*/boost/*" "*-tab.*" ];
|
lcovFilter = [ "*/boost/*" "*-tab.*" ];
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \
|
SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \
|
||||||
libexpr nix-instantiate nix-env nix-worker nix-setuid-helper \
|
libexpr nix-instantiate nix-env nix-worker nix-setuid-helper \
|
||||||
nix-log2xml bsdiff-4.3
|
nix-log2xml bsdiff-4.3
|
||||||
|
|
||||||
EXTRA_DIST = aterm-helper.pl
|
|
||||||
|
|
|
@ -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;
|
|
|
@ -2,27 +2,25 @@ pkglib_LTLIBRARIES = libexpr.la
|
||||||
|
|
||||||
libexpr_la_SOURCES = \
|
libexpr_la_SOURCES = \
|
||||||
nixexpr.cc eval.cc primops.cc lexer-tab.cc parser-tab.cc \
|
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
|
names.cc
|
||||||
|
|
||||||
pkginclude_HEADERS = \
|
pkginclude_HEADERS = \
|
||||||
nixexpr.hh eval.hh parser.hh lexer-tab.hh parser-tab.hh \
|
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 \
|
get-drvs.hh attr-path.hh value-to-xml.hh common-opts.hh \
|
||||||
names.hh nixexpr-ast.hh
|
names.hh symbol-table.hh
|
||||||
|
|
||||||
libexpr_la_LIBADD = ../libutil/libutil.la ../libstore/libstore.la \
|
libexpr_la_LIBADD = ../libutil/libutil.la ../libstore/libstore.la \
|
||||||
../boost/format/libformat.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
|
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 = \
|
AM_CXXFLAGS = \
|
||||||
-I$(srcdir)/.. ${aterm_include} \
|
-I$(srcdir)/.. \
|
||||||
-I$(srcdir)/../libutil -I$(srcdir)/../libstore
|
-I$(srcdir)/../libutil -I$(srcdir)/../libstore
|
||||||
AM_CFLAGS = \
|
|
||||||
${aterm_include}
|
|
||||||
|
|
||||||
|
|
||||||
# Parser generation.
|
# 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
|
$(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).
|
# SDF stuff (not built by default).
|
||||||
nix.tbl: nix.sdf
|
nix.tbl: nix.sdf
|
||||||
sdf2table -m Nix -s -i nix.sdf -o nix.tbl
|
sdf2table -m Nix -s -i nix.sdf -o nix.tbl
|
||||||
|
|
|
@ -1,24 +1,12 @@
|
||||||
#include "attr-path.hh"
|
#include "attr-path.hh"
|
||||||
#include "nixexpr-ast.hh"
|
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "aterm.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
bool isAttrs(EvalState & state, Expr e, ATermMap & attrs)
|
void findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||||
{
|
const Bindings & autoArgs, Expr * e, Value & v)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
Strings tokens = tokenizeString(attrPath, ".");
|
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);
|
Error(format("attribute selection path `%1%' does not match expression") % attrPath);
|
||||||
|
|
||||||
string curPath;
|
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 += ".";
|
if (!curPath.empty()) curPath += ".";
|
||||||
curPath += *i;
|
curPath += *i;
|
||||||
|
@ -39,7 +29,10 @@ Expr findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||||
if (string2Int(attr, attrIndex)) apType = apIndex;
|
if (string2Int(attr, attrIndex)) apType = apIndex;
|
||||||
|
|
||||||
/* Evaluate the expression. */
|
/* 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
|
/* It should evaluate to either an attribute set or an
|
||||||
expression, according to what is specified in the
|
expression, according to what is specified in the
|
||||||
|
@ -47,36 +40,31 @@ Expr findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||||
|
|
||||||
if (apType == apAttr) {
|
if (apType == apAttr) {
|
||||||
|
|
||||||
ATermMap attrs;
|
if (v.type != tAttrs)
|
||||||
|
|
||||||
if (!isAttrs(state, e, attrs))
|
|
||||||
throw TypeError(
|
throw TypeError(
|
||||||
format("the expression selected by the selection path `%1%' should be an attribute set but is %2%")
|
format("the expression selected by the selection path `%1%' should be an attribute set but is %2%")
|
||||||
% curPath % showType(e));
|
% curPath % showType(v));
|
||||||
|
|
||||||
e = attrs.get(toATerm(attr));
|
|
||||||
if (!e)
|
|
||||||
throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath);
|
|
||||||
|
|
||||||
|
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) {
|
else if (apType == apIndex) {
|
||||||
|
|
||||||
ATermList es;
|
if (v.type != tList)
|
||||||
if (!matchList(e, es))
|
|
||||||
throw TypeError(
|
throw TypeError(
|
||||||
format("the expression selected by the selection path `%1%' should be a list but is %2%")
|
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 (attrIndex >= v.list.length)
|
||||||
if (!e)
|
throw Error(format("list index %1% in selection path `%2%' is out of range") % attrIndex % curPath);
|
||||||
throw Error(format("list index %1% in selection path `%2%' not found") % attrIndex % curPath);
|
|
||||||
|
v = *v.list.elems[attrIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
Expr findAlongAttrPath(EvalState & state, const string & attrPath,
|
void findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||||
const ATermMap & autoArgs, Expr e);
|
const Bindings & autoArgs, Expr * e, Value & v);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#include "../libmain/shared.hh"
|
#include "../libmain/shared.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "parser.hh"
|
#include "parser.hh"
|
||||||
#include "aterm.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -10,7 +9,7 @@ namespace nix {
|
||||||
|
|
||||||
bool parseOptionArg(const string & arg, Strings::iterator & i,
|
bool parseOptionArg(const string & arg, Strings::iterator & i,
|
||||||
const Strings::iterator & argsEnd, EvalState & state,
|
const Strings::iterator & argsEnd, EvalState & state,
|
||||||
ATermMap & autoArgs)
|
Bindings & autoArgs)
|
||||||
{
|
{
|
||||||
if (arg != "--arg" && arg != "--argstr") return false;
|
if (arg != "--arg" && arg != "--argstr") return false;
|
||||||
|
|
||||||
|
@ -20,11 +19,13 @@ bool parseOptionArg(const string & arg, Strings::iterator & i,
|
||||||
string name = *i++;
|
string name = *i++;
|
||||||
if (i == argsEnd) throw error;
|
if (i == argsEnd) throw error;
|
||||||
string value = *i++;
|
string value = *i++;
|
||||||
|
|
||||||
Expr e = arg == "--arg"
|
Value & v(autoArgs[state.symbols.create(name)].value);
|
||||||
? parseExprFromString(state, value, absPath("."))
|
|
||||||
: makeStr(value);
|
if (arg == "--arg")
|
||||||
autoArgs.set(toATerm(name), e);
|
state.mkThunk_( v, parseExprFromString(state, value, absPath(".")));
|
||||||
|
else
|
||||||
|
mkString(v, value);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace nix {
|
||||||
/* Some common option parsing between nix-env and nix-instantiate. */
|
/* Some common option parsing between nix-env and nix-instantiate. */
|
||||||
bool parseOptionArg(const string & arg, Strings::iterator & i,
|
bool parseOptionArg(const string & arg, Strings::iterator & i,
|
||||||
const Strings::iterator & argsEnd, EvalState & state,
|
const Strings::iterator & argsEnd, EvalState & state,
|
||||||
ATermMap & autoArgs);
|
Bindings & autoArgs);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1628
src/libexpr/eval.cc
1628
src/libexpr/eval.cc
File diff suppressed because it is too large
Load diff
|
@ -4,6 +4,7 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "nixexpr.hh"
|
#include "nixexpr.hh"
|
||||||
|
#include "symbol-table.hh"
|
||||||
|
|
||||||
typedef union _ATermList * ATermList;
|
typedef union _ATermList * ATermList;
|
||||||
|
|
||||||
|
@ -12,9 +13,157 @@ namespace nix {
|
||||||
|
|
||||||
|
|
||||||
class Hash;
|
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;
|
typedef std::map<Path, Hash> DrvHashes;
|
||||||
|
|
||||||
/* Cache for calls to addToStore(); maps source paths to the store
|
/* Cache for calls to addToStore(); maps source paths to the store
|
||||||
|
@ -23,75 +172,153 @@ typedef std::map<Path, Path> SrcToStore;
|
||||||
|
|
||||||
struct EvalState;
|
struct EvalState;
|
||||||
|
|
||||||
/* Note: using a ATermVector is safe here, since when we call a primop
|
|
||||||
we also have an ATermList on the stack. */
|
std::ostream & operator << (std::ostream & str, Value & v);
|
||||||
typedef Expr (* PrimOp) (EvalState &, const ATermVector & args);
|
|
||||||
|
|
||||||
|
|
||||||
struct EvalState
|
class EvalState
|
||||||
{
|
{
|
||||||
ATermMap normalForms;
|
public:
|
||||||
ATermMap primOps;
|
|
||||||
DrvRoots drvRoots;
|
|
||||||
DrvHashes drvHashes; /* normalised derivation hashes */
|
DrvHashes drvHashes; /* normalised derivation hashes */
|
||||||
SrcToStore srcToStore;
|
|
||||||
|
|
||||||
unsigned int nrEvaluated;
|
SymbolTable symbols;
|
||||||
unsigned int nrCached;
|
|
||||||
|
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sSystem;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SrcToStore srcToStore;
|
||||||
|
|
||||||
bool allowUnsafeEquality;
|
bool allowUnsafeEquality;
|
||||||
|
|
||||||
EvalState();
|
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,
|
void addPrimOp(const string & name,
|
||||||
unsigned int arity, PrimOp primOp);
|
unsigned int arity, PrimOp primOp);
|
||||||
|
|
||||||
|
Value * lookupVar(Env * env, const VarRef & var);
|
||||||
|
|
||||||
|
friend class ExprVar;
|
||||||
|
friend class ExprAttrs;
|
||||||
|
friend class ExprLet;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/* Do a deep equality test between two values. That is, list
|
||||||
|
elements and attributes are compared recursively. */
|
||||||
|
bool eqValues(Value & v1, Value & v2);
|
||||||
|
|
||||||
|
void callFunction(Value & fun, Value & arg, Value & v);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
void mkList(Value & v, unsigned int length);
|
||||||
|
void mkAttrs(Value & v);
|
||||||
|
void mkThunk_(Value & v, Expr * expr);
|
||||||
|
|
||||||
|
void cloneAttrs(Value & src, Value & dst);
|
||||||
|
|
||||||
|
/* Print statistics. */
|
||||||
|
void printStats();
|
||||||
|
|
||||||
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Evaluate an expression to normal form. */
|
/* Return a string representing the type of the value `v'. */
|
||||||
Expr evalExpr(EvalState & state, Expr e);
|
string showType(const Value & v);
|
||||||
|
|
||||||
/* Evaluate an expression read from the given file to normal form. */
|
|
||||||
Expr evalFile(EvalState & state, const Path & path);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* Flatten nested lists into a single list (or expand a singleton into
|
|
||||||
a list). */
|
|
||||||
ATermList flattenList(EvalState & state, Expr e);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* Print statistics. */
|
|
||||||
void printEvalStats(EvalState & state);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -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 */
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include "get-drvs.hh"
|
#include "get-drvs.hh"
|
||||||
#include "nixexpr-ast.hh"
|
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "aterm.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -9,17 +7,10 @@ namespace nix {
|
||||||
|
|
||||||
string DrvInfo::queryDrvPath(EvalState & state) const
|
string DrvInfo::queryDrvPath(EvalState & state) const
|
||||||
{
|
{
|
||||||
if (drvPath == "") {
|
if (drvPath == "" && attrs) {
|
||||||
Expr a = attrs->get(toATerm("drvPath"));
|
Bindings::iterator i = attrs->find(state.sDrvPath);
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
PathSet context;
|
PathSet context;
|
||||||
(string &) drvPath = a ? coerceToPath(state, a, context) : "";
|
(string &) drvPath = i != attrs->end() ? state.coerceToPath(i->second.value, context) : "";
|
||||||
}
|
}
|
||||||
return drvPath;
|
return drvPath;
|
||||||
}
|
}
|
||||||
|
@ -27,11 +18,10 @@ string DrvInfo::queryDrvPath(EvalState & state) const
|
||||||
|
|
||||||
string DrvInfo::queryOutPath(EvalState & state) const
|
string DrvInfo::queryOutPath(EvalState & state) const
|
||||||
{
|
{
|
||||||
if (outPath == "") {
|
if (outPath == "" && attrs) {
|
||||||
Expr a = attrs->get(toATerm("outPath"));
|
Bindings::iterator i = attrs->find(state.sOutPath);
|
||||||
if (!a) throw TypeError("output path missing");
|
|
||||||
PathSet context;
|
PathSet context;
|
||||||
(string &) outPath = coerceToPath(state, a, context);
|
(string &) outPath = i != attrs->end() ? state.coerceToPath(i->second.value, context) : "";
|
||||||
}
|
}
|
||||||
return outPath;
|
return outPath;
|
||||||
}
|
}
|
||||||
|
@ -39,35 +29,30 @@ string DrvInfo::queryOutPath(EvalState & state) const
|
||||||
|
|
||||||
MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
|
MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
|
||||||
{
|
{
|
||||||
MetaInfo meta;
|
if (metaInfoRead) return meta;
|
||||||
|
|
||||||
Expr a = attrs->get(toATerm("meta"));
|
(bool &) metaInfoRead = true;
|
||||||
if (!a) return meta; /* fine, empty meta information */
|
|
||||||
|
Bindings::iterator a = attrs->find(state.sMeta);
|
||||||
|
if (a == attrs->end()) return meta; /* fine, empty meta information */
|
||||||
|
|
||||||
ATermMap attrs2;
|
state.forceAttrs(a->second.value);
|
||||||
queryAllAttrs(evalExpr(state, a), attrs2);
|
|
||||||
|
|
||||||
for (ATermMap::const_iterator i = attrs2.begin(); i != attrs2.end(); ++i) {
|
foreach (Bindings::iterator, i, *a->second.value.attrs) {
|
||||||
Expr e = evalExpr(state, i->value);
|
|
||||||
string s;
|
|
||||||
PathSet context;
|
|
||||||
MetaValue value;
|
MetaValue value;
|
||||||
int n;
|
state.forceValue(i->second.value);
|
||||||
ATermList es;
|
if (i->second.value.type == tString) {
|
||||||
if (matchStr(e, s, context)) {
|
|
||||||
value.type = MetaValue::tpString;
|
value.type = MetaValue::tpString;
|
||||||
value.stringValue = s;
|
value.stringValue = i->second.value.string.s;
|
||||||
meta[aterm2String(i->key)] = value;
|
} else if (i->second.value.type == tInt) {
|
||||||
} else if (matchInt(e, n)) {
|
|
||||||
value.type = MetaValue::tpInt;
|
value.type = MetaValue::tpInt;
|
||||||
value.intValue = n;
|
value.intValue = i->second.value.integer;
|
||||||
meta[aterm2String(i->key)] = value;
|
} else if (i->second.value.type == tList) {
|
||||||
} else if (matchList(e, es)) {
|
|
||||||
value.type = MetaValue::tpStrings;
|
value.type = MetaValue::tpStrings;
|
||||||
for (ATermIterator j(es); j; ++j)
|
for (unsigned int j = 0; j < i->second.value.list.length; ++j)
|
||||||
value.stringValues.push_back(evalStringNoCtx(state, *j));
|
value.stringValues.push_back(state.forceStringNoCtx(*i->second.value.list.elems[j]));
|
||||||
meta[aterm2String(i->key)] = value;
|
} else continue;
|
||||||
}
|
((MetaInfo &) meta)[i->first] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return meta;
|
return meta;
|
||||||
|
@ -83,73 +68,46 @@ MetaValue DrvInfo::queryMetaInfo(EvalState & state, const string & name) const
|
||||||
|
|
||||||
void DrvInfo::setMetaInfo(const MetaInfo & meta)
|
void DrvInfo::setMetaInfo(const MetaInfo & meta)
|
||||||
{
|
{
|
||||||
ATermMap metaAttrs;
|
metaInfoRead = true;
|
||||||
foreach (MetaInfo::const_iterator, i, meta) {
|
this->meta = 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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Cache for already evaluated derivations. Usually putting ATerms in
|
/* Cache for already considered attrsets. */
|
||||||
a STL container is unsafe (they're not scanning for GC roots), but
|
typedef set<Bindings *> Done;
|
||||||
here it doesn't matter; everything in this set is reachable from
|
|
||||||
the stack as well. */
|
|
||||||
typedef set<Expr> Exprs;
|
|
||||||
|
|
||||||
|
|
||||||
/* Evaluate expression `e'. If it evaluates to an attribute set of
|
/* Evaluate value `v'. If it evaluates to an attribute set of type
|
||||||
type `derivation', then put information about it in `drvs' (unless
|
`derivation', then put information about it in `drvs' (unless it's
|
||||||
it's already in `doneExprs'). The result boolean indicates whether
|
already in `doneExprs'). The result boolean indicates whether it
|
||||||
it makes sense for the caller to recursively search for derivations
|
makes sense for the caller to recursively search for derivations in
|
||||||
in `e'. */
|
`v'. */
|
||||||
static bool getDerivation(EvalState & state, Expr e,
|
static bool getDerivation(EvalState & state, Value & v,
|
||||||
const string & attrPath, DrvInfos & drvs, Exprs & doneExprs)
|
const string & attrPath, DrvInfos & drvs, Done & done)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
state.forceValue(v);
|
||||||
ATermList es;
|
if (!state.isDerivation(v)) return true;
|
||||||
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;
|
|
||||||
|
|
||||||
/* Remove spurious duplicates (e.g., an attribute set like
|
/* Remove spurious duplicates (e.g., an attribute set like
|
||||||
`rec { x = derivation {...}; y = x;}'. */
|
`rec { x = derivation {...}; y = x;}'. */
|
||||||
if (doneExprs.find(e) != doneExprs.end()) return false;
|
if (done.find(v.attrs) != done.end()) return false;
|
||||||
doneExprs.insert(e);
|
done.insert(v.attrs);
|
||||||
|
|
||||||
DrvInfo drv;
|
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. */
|
/* !!! We really would like to have a decent back trace here. */
|
||||||
if (!a) throw TypeError("derivation name missing");
|
if (i == v.attrs->end()) throw TypeError("derivation name missing");
|
||||||
drv.name = evalStringNoCtx(state, a);
|
drv.name = state.forceStringNoCtx(i->second.value);
|
||||||
|
|
||||||
a = attrs->get(toATerm("system"));
|
i = v.attrs->find(state.sSystem);
|
||||||
if (!a)
|
if (i == v.attrs->end())
|
||||||
drv.system = "unknown";
|
drv.system = "unknown";
|
||||||
else
|
else
|
||||||
drv.system = evalStringNoCtx(state, a);
|
drv.system = state.forceStringNoCtx(i->second.value);
|
||||||
|
|
||||||
drv.attrs = attrs;
|
drv.attrs = v.attrs;
|
||||||
|
|
||||||
drv.attrPath = attrPath;
|
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;
|
DrvInfos drvs;
|
||||||
getDerivation(state, e, "", drvs, doneExprs);
|
getDerivation(state, v, "", drvs, done);
|
||||||
if (drvs.size() != 1) return false;
|
if (drvs.size() != 1) return false;
|
||||||
drv = drvs.front();
|
drv = drvs.front();
|
||||||
return true;
|
return true;
|
||||||
|
@ -179,83 +137,73 @@ static string addToPath(const string & s1, const string & s2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void getDerivations(EvalState & state, Expr e,
|
static void getDerivations(EvalState & state, Value & vIn,
|
||||||
const string & pathPrefix, const ATermMap & autoArgs,
|
const string & pathPrefix, const Bindings & autoArgs,
|
||||||
DrvInfos & drvs, Exprs & doneExprs)
|
DrvInfos & drvs, Done & done)
|
||||||
{
|
{
|
||||||
e = evalExpr(state, autoCallFunction(evalExpr(state, e), autoArgs));
|
Value v;
|
||||||
|
state.autoCallFunction(autoArgs, vIn, v);
|
||||||
|
|
||||||
/* Process the expression. */
|
/* Process the expression. */
|
||||||
ATermList es;
|
|
||||||
DrvInfo drv;
|
DrvInfo drv;
|
||||||
|
|
||||||
if (!getDerivation(state, e, pathPrefix, drvs, doneExprs))
|
if (!getDerivation(state, v, pathPrefix, drvs, done)) ;
|
||||||
return;
|
|
||||||
|
|
||||||
if (matchAttrs(e, es)) {
|
else if (v.type == tAttrs) {
|
||||||
ATermMap drvMap(ATgetLength(es));
|
|
||||||
queryAllAttrs(e, drvMap);
|
|
||||||
|
|
||||||
/* !!! undocumented hackery to support combining channels in
|
/* !!! undocumented hackery to support combining channels in
|
||||||
nix-env.cc. */
|
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
|
/* Consider the attributes in sorted order to get more
|
||||||
deterministic behaviour in nix-env operations (e.g. when
|
deterministic behaviour in nix-env operations (e.g. when
|
||||||
there are names clashes between derivations, the derivation
|
there are names clashes between derivations, the derivation
|
||||||
bound to the attribute with the "lower" name should take
|
bound to the attribute with the "lower" name should take
|
||||||
precedence). */
|
precedence). */
|
||||||
typedef std::map<string, Expr> AttrsSorted;
|
typedef std::map<string, Symbol> SortedSymbols;
|
||||||
AttrsSorted attrsSorted;
|
SortedSymbols attrs;
|
||||||
foreach (ATermMap::const_iterator, i, drvMap)
|
foreach (Bindings::iterator, i, *v.attrs)
|
||||||
attrsSorted[aterm2String(i->key)] = i->value;
|
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);
|
startNest(nest, lvlDebug, format("evaluating attribute `%1%'") % i->first);
|
||||||
string pathPrefix2 = addToPath(pathPrefix, i->first);
|
string pathPrefix2 = addToPath(pathPrefix, i->first);
|
||||||
|
Value & v2((*v.attrs)[i->second].value);
|
||||||
if (combineChannels)
|
if (combineChannels)
|
||||||
getDerivations(state, i->second, pathPrefix2, autoArgs, drvs, doneExprs);
|
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
|
||||||
else if (getDerivation(state, i->second, pathPrefix2, drvs, doneExprs)) {
|
else if (getDerivation(state, v2, pathPrefix2, drvs, done)) {
|
||||||
/* If the value of this attribute is itself an
|
/* If the value of this attribute is itself an
|
||||||
attribute set, should we recurse into it? => Only
|
attribute set, should we recurse into it? => Only
|
||||||
if it has a `recurseForDerivations = true'
|
if it has a `recurseForDerivations = true'
|
||||||
attribute. */
|
attribute. */
|
||||||
ATermList es;
|
if (v2.type == tAttrs) {
|
||||||
Expr e = evalExpr(state, i->second), e2;
|
Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations"));
|
||||||
if (matchAttrs(e, es)) {
|
if (j != v2.attrs->end() && state.forceBool(j->second.value))
|
||||||
ATermMap attrs(ATgetLength(es));
|
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
|
||||||
queryAllAttrs(e, attrs, false);
|
|
||||||
if (((e2 = attrs.get(toATerm("recurseForDerivations")))
|
|
||||||
&& evalBool(state, e2)))
|
|
||||||
getDerivations(state, e, pathPrefix2, autoArgs, drvs, doneExprs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchList(e, es)) {
|
else if (v.type == tList) {
|
||||||
int n = 0;
|
for (unsigned int n = 0; n < v.list.length; ++n) {
|
||||||
for (ATermIterator i(es); i; ++i, ++n) {
|
|
||||||
startNest(nest, lvlDebug,
|
startNest(nest, lvlDebug,
|
||||||
format("evaluating list element"));
|
format("evaluating list element"));
|
||||||
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
|
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
|
||||||
if (getDerivation(state, *i, pathPrefix2, drvs, doneExprs))
|
if (getDerivation(state, *v.list.elems[n], pathPrefix2, drvs, done))
|
||||||
getDerivations(state, *i, pathPrefix2, autoArgs, drvs, doneExprs);
|
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,
|
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
|
||||||
const ATermMap & autoArgs, DrvInfos & drvs)
|
const Bindings & autoArgs, DrvInfos & drvs)
|
||||||
{
|
{
|
||||||
Exprs doneExprs;
|
Done done;
|
||||||
getDerivations(state, e, pathPrefix, autoArgs, drvs, doneExprs);
|
getDerivations(state, v, pathPrefix, autoArgs, drvs, done);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,16 +29,19 @@ struct DrvInfo
|
||||||
private:
|
private:
|
||||||
string drvPath;
|
string drvPath;
|
||||||
string outPath;
|
string outPath;
|
||||||
|
|
||||||
|
bool metaInfoRead;
|
||||||
|
MetaInfo meta;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
string name;
|
string name;
|
||||||
string attrPath; /* path towards the derivation */
|
string attrPath; /* path towards the derivation */
|
||||||
string system;
|
string system;
|
||||||
|
|
||||||
/* !!! these should really be hidden, and setMetaInfo() should
|
/* !!! make this private */
|
||||||
make a copy since the ATermMap can be shared between multiple
|
Bindings * attrs;
|
||||||
DrvInfos. */
|
|
||||||
boost::shared_ptr<ATermMap> attrs;
|
DrvInfo() : metaInfoRead(false), attrs(0) { };
|
||||||
|
|
||||||
string queryDrvPath(EvalState & state) const;
|
string queryDrvPath(EvalState & state) const;
|
||||||
string queryOutPath(EvalState & state) const;
|
string queryOutPath(EvalState & state) const;
|
||||||
|
@ -62,13 +65,12 @@ public:
|
||||||
typedef list<DrvInfo> DrvInfos;
|
typedef list<DrvInfo> DrvInfos;
|
||||||
|
|
||||||
|
|
||||||
/* Evaluate expression `e'. If it evaluates to a derivation, store
|
/* If value `v' denotes a derivation, store information about the
|
||||||
information about the derivation in `drv' and return true.
|
derivation in `drv' and return true. Otherwise, return false. */
|
||||||
Otherwise, return false. */
|
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv);
|
||||||
bool getDerivation(EvalState & state, Expr e, DrvInfo & drv);
|
|
||||||
|
|
||||||
void getDerivations(EvalState & state, Expr e, const string & pathPrefix,
|
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
|
||||||
const ATermMap & autoArgs, DrvInfos & drvs);
|
const Bindings & autoArgs, DrvInfos & drvs);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,7 @@
|
||||||
|
|
||||||
|
|
||||||
%{
|
%{
|
||||||
#include "aterm.hh"
|
|
||||||
#include "nixexpr.hh"
|
#include "nixexpr.hh"
|
||||||
#include "nixexpr-ast.hh"
|
|
||||||
#define BISON_HEADER_HACK
|
#define BISON_HEADER_HACK
|
||||||
#include "parser-tab.hh"
|
#include "parser-tab.hh"
|
||||||
|
|
||||||
|
@ -21,13 +19,16 @@ namespace nix {
|
||||||
|
|
||||||
static void initLoc(YYLTYPE * loc)
|
static void initLoc(YYLTYPE * loc)
|
||||||
{
|
{
|
||||||
loc->first_line = 1;
|
loc->first_line = loc->last_line = 1;
|
||||||
loc->first_column = 1;
|
loc->first_column = loc->last_column = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
|
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--) {
|
while (len--) {
|
||||||
switch (*s++) {
|
switch (*s++) {
|
||||||
case '\r':
|
case '\r':
|
||||||
|
@ -35,17 +36,17 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
|
||||||
s++;
|
s++;
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case '\n':
|
case '\n':
|
||||||
++loc->first_line;
|
++loc->last_line;
|
||||||
loc->first_column = 1;
|
loc->last_column = 1;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
++loc->first_column;
|
++loc->last_column;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Expr unescapeStr(const char * s)
|
static Expr * unescapeStr(const char * s)
|
||||||
{
|
{
|
||||||
string t;
|
string t;
|
||||||
char c;
|
char c;
|
||||||
|
@ -65,7 +66,7 @@ static Expr unescapeStr(const char * s)
|
||||||
}
|
}
|
||||||
else t += c;
|
else t += c;
|
||||||
}
|
}
|
||||||
return makeStr(toATerm(t), ATempty);
|
return new ExprString(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,19 +106,20 @@ inherit { return INHERIT; }
|
||||||
\/\/ { return UPDATE; }
|
\/\/ { return UPDATE; }
|
||||||
\+\+ { return CONCAT; }
|
\+\+ { return CONCAT; }
|
||||||
|
|
||||||
{ID} { yylval->t = toATerm(yytext); return ID; /* !!! alloc */ }
|
{ID} { yylval->id = strdup(yytext); return ID; }
|
||||||
{INT} { int n = atoi(yytext); /* !!! overflow */
|
{INT} { int n = atoi(yytext); /* !!! overflow */
|
||||||
yylval->t = ATmake("<int>", n);
|
yylval->n = n;
|
||||||
return INT;
|
return INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
\" { BEGIN(STRING); return '"'; }
|
\" { BEGIN(STRING); return '"'; }
|
||||||
<STRING>([^\$\"\\]|\$[^\{\"]|\\.)+ {
|
<STRING>([^\$\"\\]|\$[^\{\"]|\\.)+ {
|
||||||
/* !!! Not quite right: we want a follow restriction on "$", it
|
/* !!! Not quite right: we want a follow restriction on
|
||||||
shouldn't be followed by a "{". Right now "$\"" will be consumed
|
"$", it shouldn't be followed by a "{". Right now
|
||||||
as part of a string, rather than a "$" followed by the string
|
"$\"" will be consumed as part of a string, rather
|
||||||
terminator. Disallow "$\"" for now. */
|
than a "$" followed by the string terminator.
|
||||||
yylval->t = unescapeStr(yytext); /* !!! alloc */
|
Disallow "$\"" for now. */
|
||||||
|
yylval->e = unescapeStr(yytext);
|
||||||
return STR;
|
return STR;
|
||||||
}
|
}
|
||||||
<STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
|
<STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
|
||||||
|
@ -126,31 +128,31 @@ inherit { return INHERIT; }
|
||||||
|
|
||||||
\'\'(\ *\n)? { BEGIN(IND_STRING); return IND_STRING_OPEN; }
|
\'\'(\ *\n)? { BEGIN(IND_STRING); return IND_STRING_OPEN; }
|
||||||
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
|
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
|
||||||
yylval->t = makeIndStr(toATerm(yytext));
|
yylval->e = new ExprIndStr(yytext);
|
||||||
return IND_STR;
|
return IND_STR;
|
||||||
}
|
}
|
||||||
<IND_STRING>\'\'\$ {
|
<IND_STRING>\'\'\$ {
|
||||||
yylval->t = makeIndStr(toATerm("$"));
|
yylval->e = new ExprIndStr("$");
|
||||||
return IND_STR;
|
return IND_STR;
|
||||||
}
|
}
|
||||||
<IND_STRING>\'\'\' {
|
<IND_STRING>\'\'\' {
|
||||||
yylval->t = makeIndStr(toATerm("''"));
|
yylval->e = new ExprIndStr("''");
|
||||||
return IND_STR;
|
return IND_STR;
|
||||||
}
|
}
|
||||||
<IND_STRING>\'\'\\. {
|
<IND_STRING>\'\'\\. {
|
||||||
yylval->t = unescapeStr(yytext + 2);
|
yylval->e = unescapeStr(yytext + 2);
|
||||||
return IND_STR;
|
return IND_STR;
|
||||||
}
|
}
|
||||||
<IND_STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
|
<IND_STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
|
||||||
<IND_STRING>\'\' { BEGIN(INITIAL); return IND_STRING_CLOSE; }
|
<IND_STRING>\'\' { BEGIN(INITIAL); return IND_STRING_CLOSE; }
|
||||||
<IND_STRING>\' {
|
<IND_STRING>\' {
|
||||||
yylval->t = makeIndStr(toATerm("'"));
|
yylval->e = new ExprIndStr("'");
|
||||||
return IND_STR;
|
return IND_STR;
|
||||||
}
|
}
|
||||||
<IND_STRING>. return yytext[0]; /* just in case: shouldn't be reached */
|
<IND_STRING>. return yytext[0]; /* just in case: shouldn't be reached */
|
||||||
|
|
||||||
{PATH} { yylval->t = toATerm(yytext); return PATH; /* !!! alloc */ }
|
{PATH} { yylval->path = strdup(yytext); return PATH; }
|
||||||
{URI} { yylval->t = toATerm(yytext); return URI; /* !!! alloc */ }
|
{URI} { yylval->uri = strdup(yytext); return URI; }
|
||||||
|
|
||||||
[ \t\r\n]+ /* eat up whitespace */
|
[ \t\r\n]+ /* eat up whitespace */
|
||||||
\#[^\r\n]* /* single-line comments */
|
\#[^\r\n]* /* single-line comments */
|
||||||
|
|
|
@ -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")
|
|
|
@ -1,407 +1,325 @@
|
||||||
#include "nixexpr.hh"
|
#include "nixexpr.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "aterm.hh"
|
|
||||||
|
|
||||||
#include "nixexpr-ast.hh"
|
|
||||||
#include "nixexpr-ast.cc"
|
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
|
/* Displaying abstract syntax trees. */
|
||||||
|
|
||||||
|
std::ostream & operator << (std::ostream & str, Expr & e)
|
||||||
|
{
|
||||||
|
e.show(str);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Expr::show(std::ostream & str)
|
||||||
|
{
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprInt::show(std::ostream & str)
|
||||||
|
{
|
||||||
|
str << n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprString::show(std::ostream & str)
|
||||||
|
{
|
||||||
|
str << "\"" << s << "\""; // !!! escaping
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprPath::show(std::ostream & str)
|
||||||
|
{
|
||||||
|
str << s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprVar::show(std::ostream & str)
|
||||||
|
{
|
||||||
|
str << info.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprSelect::show(std::ostream & str)
|
||||||
|
{
|
||||||
|
str << "(" << *e << ")." << name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprOpHasAttr::show(std::ostream & str)
|
||||||
|
{
|
||||||
|
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 << "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprList::show(std::ostream & str)
|
||||||
|
{
|
||||||
|
str << "[ ";
|
||||||
|
foreach (vector<Expr *>::iterator, i, elems)
|
||||||
|
str << "(" << **i << ") ";
|
||||||
|
str << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
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 << " @ ";
|
||||||
|
}
|
||||||
|
if (!arg.empty()) str << arg;
|
||||||
|
str << ": " << *body << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprLet::show(std::ostream & str)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprWith::show(std::ostream & str)
|
||||||
|
{
|
||||||
|
str << "with " << *attrs << "; " << *body;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprIf::show(std::ostream & str)
|
||||||
|
{
|
||||||
|
str << "if " << *cond << " then " << *then << " else " << *else_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprAssert::show(std::ostream & str)
|
||||||
|
{
|
||||||
|
str << "assert " << *cond << "; " << *body;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprOpNot::show(std::ostream & str)
|
||||||
|
{
|
||||||
|
str << "! " << *e;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprConcatStrings::show(std::ostream & str)
|
||||||
|
{
|
||||||
|
bool first = true;
|
||||||
|
foreach (vector<Expr *>::iterator, i, *es) {
|
||||||
|
if (first) first = false; else str << " + ";
|
||||||
|
str << **i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Pos noPos;
|
||||||
|
|
||||||
|
|
||||||
|
/* Computing levels/displacements for variables. */
|
||||||
|
|
||||||
|
void Expr::bindVars(const StaticEnv & env)
|
||||||
|
{
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprInt::bindVars(const StaticEnv & env)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprString::bindVars(const StaticEnv & env)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprPath::bindVars(const StaticEnv & env)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
fromWith = true;
|
||||||
|
this->level = withLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprVar::bindVars(const StaticEnv & env)
|
||||||
|
{
|
||||||
|
info.bind(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprSelect::bindVars(const StaticEnv & env)
|
||||||
|
{
|
||||||
|
e->bindVars(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprOpHasAttr::bindVars(const StaticEnv & env)
|
||||||
|
{
|
||||||
|
e->bindVars(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprAttrs::bindVars(const StaticEnv & env)
|
||||||
|
{
|
||||||
|
if (recursive) {
|
||||||
|
StaticEnv newEnv(false, &env);
|
||||||
|
|
||||||
|
unsigned int displ = 0;
|
||||||
|
|
||||||
string showPos(ATerm pos)
|
foreach (ExprAttrs::Attrs::iterator, i, attrs)
|
||||||
{
|
newEnv.vars[i->first] = displ++;
|
||||||
ATerm path;
|
|
||||||
int line, column;
|
foreach (list<Inherited>::iterator, i, inherited) {
|
||||||
if (matchNoPos(pos)) return "undefined position";
|
newEnv.vars[i->first.name] = displ++;
|
||||||
if (!matchPos(pos, path, line, column))
|
i->first.bind(env);
|
||||||
throw badTerm("position expected", pos);
|
}
|
||||||
return (format("`%1%:%2%:%3%'") % aterm2String(path) % line % column).str();
|
|
||||||
|
foreach (ExprAttrs::Attrs::iterator, i, attrs)
|
||||||
|
i->second.first->bindVars(newEnv);
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
foreach (ExprAttrs::Attrs::iterator, i, attrs)
|
||||||
|
i->second.first->bindVars(env);
|
||||||
|
|
||||||
|
foreach (list<Inherited>::iterator, i, inherited)
|
||||||
|
i->first.bind(env);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExprList::bindVars(const StaticEnv & env)
|
||||||
|
{
|
||||||
|
foreach (vector<Expr *>::iterator, i, elems)
|
||||||
|
(*i)->bindVars(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprLambda::bindVars(const StaticEnv & env)
|
||||||
|
{
|
||||||
|
StaticEnv newEnv(false, &env);
|
||||||
|
|
||||||
|
unsigned int displ = 0;
|
||||||
ATerm bottomupRewrite(TermFun & f, ATerm e)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return f(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void queryAllAttrs(Expr e, ATermMap & attrs, bool withPos)
|
|
||||||
{
|
|
||||||
ATermList bnds;
|
|
||||||
if (!matchAttrs(e, bnds))
|
|
||||||
throw TypeError(format("value is %1% while an attribute set was expected") % showType(e));
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Expr queryAttr(Expr e, const string & name)
|
|
||||||
{
|
|
||||||
ATerm dummy;
|
|
||||||
return queryAttr(e, name, dummy);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Expr queryAttr(Expr e, const string & name, ATerm & pos)
|
|
||||||
{
|
|
||||||
ATermList bnds;
|
|
||||||
if (!matchAttrs(e, bnds))
|
|
||||||
throw TypeError(format("value is %1% while an attribute set was expected") % showType(e));
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Expr makeAttrs(const ATermMap & attrs)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void varsBoundByPattern(ATermMap & map, Pattern pat)
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Expr substitute(const Substitution & subs, Expr e)
|
|
||||||
{
|
|
||||||
checkInterrupt();
|
|
||||||
|
|
||||||
//if (subs.size() == 0) return e;
|
|
||||||
|
|
||||||
ATerm name, pos, e2;
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* 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)
|
|
||||||
{
|
|
||||||
if (done.find(e) != done.end()) return;
|
|
||||||
done.insert(e);
|
|
||||||
|
|
||||||
ATerm name, pos, value;
|
if (!arg.empty()) newEnv.vars[arg] = displ++;
|
||||||
ATerm with, body;
|
|
||||||
ATermList rbnds, nrbnds;
|
|
||||||
Pattern pat;
|
|
||||||
|
|
||||||
/* Closed terms don't have free variables, so we don't have to
|
if (matchAttrs) {
|
||||||
check by definition. */
|
foreach (Formals::Formals_::iterator, i, formals->formals)
|
||||||
if (matchClosed(e, value)) return;
|
newEnv.vars[i->name] = displ++;
|
||||||
|
|
||||||
|
foreach (Formals::Formals_::iterator, i, formals->formals)
|
||||||
|
if (i->def) i->def->bindVars(newEnv);
|
||||||
|
}
|
||||||
|
|
||||||
|
body->bindVars(newEnv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprLet::bindVars(const StaticEnv & env)
|
||||||
|
{
|
||||||
|
StaticEnv newEnv(false, &env);
|
||||||
|
|
||||||
else if (matchVar(e, name)) {
|
unsigned int displ = 0;
|
||||||
if (!defs.get(name))
|
|
||||||
throw EvalError(format("undefined variable `%1%'")
|
foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs)
|
||||||
% aterm2String(name));
|
newEnv.vars[i->first] = displ++;
|
||||||
|
|
||||||
|
foreach (list<ExprAttrs::Inherited>::iterator, i, attrs->inherited) {
|
||||||
|
newEnv.vars[i->first.name] = displ++;
|
||||||
|
i->first.bind(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (matchFunction(e, pat, body, pos)) {
|
foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs)
|
||||||
ATermMap defs2(defs);
|
i->second.first->bindVars(newEnv);
|
||||||
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);
|
|
||||||
}
|
|
||||||
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) {
|
body->bindVars(newEnv);
|
||||||
int arity = ATgetArity(ATgetAFun(e));
|
|
||||||
for (int i = 0; i < arity; ++i)
|
|
||||||
checkVarDefs2(done, defs, ATgetArgument(e, i));
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (ATgetType(e) == AT_LIST)
|
|
||||||
for (ATermIterator i((ATermList) e); i; ++i)
|
|
||||||
checkVarDefs2(done, defs, *i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExprWith::bindVars(const StaticEnv & env)
|
||||||
void checkVarDefs(const ATermMap & defs, Expr e)
|
|
||||||
{
|
{
|
||||||
set<Expr> done;
|
/* Does this `with' have an enclosing `with'? If so, record its
|
||||||
checkVarDefs2(done, defs, e);
|
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;
|
||||||
struct Canonicalise : TermFun
|
prevWith = 0;
|
||||||
{
|
for (curEnv = &env, level = 1; curEnv; curEnv = curEnv->up, level++)
|
||||||
ATerm operator () (ATerm e)
|
if (curEnv->isWith) {
|
||||||
{
|
prevWith = level;
|
||||||
/* Remove position info. */
|
break;
|
||||||
ATerm path;
|
|
||||||
int line, column;
|
|
||||||
if (matchPos(e, path, line, column))
|
|
||||||
return makeNoPos();
|
|
||||||
|
|
||||||
/* 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));
|
|
||||||
|
|
||||||
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()));
|
|
||||||
|
|
||||||
return makeAttrs(attrs2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return e;
|
attrs->bindVars(env);
|
||||||
}
|
StaticEnv newEnv(true, &env);
|
||||||
};
|
body->bindVars(newEnv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprIf::bindVars(const StaticEnv & env)
|
||||||
Expr canonicaliseExpr(Expr e)
|
|
||||||
{
|
{
|
||||||
Canonicalise canonicalise;
|
cond->bindVars(env);
|
||||||
return bottomupRewrite(canonicalise, e);
|
then->bindVars(env);
|
||||||
|
else_->bindVars(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExprAssert::bindVars(const StaticEnv & env)
|
||||||
Expr makeBool(bool b)
|
|
||||||
{
|
{
|
||||||
return b ? eTrue : eFalse;
|
cond->bindVars(env);
|
||||||
|
body->bindVars(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExprOpNot::bindVars(const StaticEnv & env)
|
||||||
bool matchStr(Expr e, string & s, PathSet & context)
|
|
||||||
{
|
{
|
||||||
ATermList l;
|
e->bindVars(env);
|
||||||
ATerm s_;
|
|
||||||
|
|
||||||
if (!matchStr(e, s_, l)) return false;
|
|
||||||
|
|
||||||
s = aterm2String(s_);
|
|
||||||
|
|
||||||
for (ATermIterator i(l); i; ++i)
|
|
||||||
context.insert(aterm2String(*i));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExprConcatStrings::bindVars(const StaticEnv & env)
|
||||||
Expr makeStr(const string & s, const PathSet & context)
|
|
||||||
{
|
{
|
||||||
return makeStr(toATerm(s), toATermList(context));
|
foreach (vector<Expr *>::iterator, i, *es)
|
||||||
|
(*i)->bindVars(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string showType(Expr e)
|
|
||||||
{
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
string showValue(Expr e)
|
|
||||||
{
|
|
||||||
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>";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "aterm-map.hh"
|
#include "symbol-table.hh"
|
||||||
#include "types.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -18,105 +17,254 @@ MakeError(Abort, EvalError)
|
||||||
MakeError(TypeError, EvalError)
|
MakeError(TypeError, EvalError)
|
||||||
|
|
||||||
|
|
||||||
/* Nix expressions are represented as ATerms. The maximal sharing
|
/* Position objects. */
|
||||||
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;
|
|
||||||
|
|
||||||
|
struct 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;
|
|
||||||
|
|
||||||
|
|
||||||
/* 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
|
|
||||||
{
|
{
|
||||||
ATermMap * map;
|
string file;
|
||||||
const Substitution * prev;
|
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) { };
|
||||||
|
};
|
||||||
|
|
||||||
Substitution(const Substitution * prev, ATermMap * map)
|
extern Pos noPos;
|
||||||
{
|
|
||||||
this->prev = prev;
|
std::ostream & operator << (std::ostream & str, const Pos & pos);
|
||||||
this->map = map;
|
|
||||||
}
|
|
||||||
|
struct Env;
|
||||||
|
struct Value;
|
||||||
|
struct EvalState;
|
||||||
|
struct StaticEnv;
|
||||||
|
|
||||||
|
|
||||||
|
/* Abstract syntax of Nix expressions. */
|
||||||
|
|
||||||
|
struct Expr
|
||||||
|
{
|
||||||
|
virtual void show(std::ostream & str);
|
||||||
|
virtual void bindVars(const StaticEnv & env);
|
||||||
|
virtual void eval(EvalState & state, Env & env, Value & v);
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Temporary class used during parsing of indented strings. */
|
||||||
|
struct ExprIndStr : Expr
|
||||||
|
{
|
||||||
|
string s;
|
||||||
|
ExprIndStr(const string & s) : s(s) { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ExprPath : Expr
|
||||||
|
{
|
||||||
|
string s;
|
||||||
|
ExprPath(const string & s) : s(s) { };
|
||||||
|
COMMON_METHODS
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VarRef
|
||||||
|
{
|
||||||
|
Symbol name;
|
||||||
|
|
||||||
|
/* Whether the variable comes from an environment (e.g. a rec, let
|
||||||
|
or function argument) or from a "with". */
|
||||||
|
bool fromWith;
|
||||||
|
|
||||||
Expr lookup(Expr name) const
|
/* In the former case, the value is obtained by going `level'
|
||||||
{
|
levels up from the current environment and getting the
|
||||||
Expr x;
|
`displ'th value in that environment. In the latter case, the
|
||||||
for (const Substitution * s(this); s; s = s->prev)
|
value is obtained by getting the attribute named `name' from
|
||||||
if ((x = s->map->get(name))) return x;
|
the attribute set stored in the environment that is `level'
|
||||||
return 0;
|
levels up from the current one.*/
|
||||||
}
|
unsigned int level;
|
||||||
|
unsigned int displ;
|
||||||
|
|
||||||
|
VarRef(const Symbol & name) : name(name) { };
|
||||||
|
void bind(const StaticEnv & env);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ExprVar : Expr
|
||||||
/* Show a position. */
|
|
||||||
string showPos(ATerm pos);
|
|
||||||
|
|
||||||
/* 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
|
|
||||||
{
|
{
|
||||||
virtual ~TermFun() { }
|
VarRef info;
|
||||||
virtual ATerm operator () (ATerm e) = 0;
|
ExprVar(const Symbol & name) : info(name) { };
|
||||||
|
COMMON_METHODS
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ExprAssert : Expr
|
||||||
|
{
|
||||||
|
Pos pos;
|
||||||
|
Expr * cond, * body;
|
||||||
|
ExprAssert(const Pos & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
|
||||||
|
COMMON_METHODS
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ExprOpNot : Expr
|
||||||
|
{
|
||||||
|
Expr * e;
|
||||||
|
ExprOpNot(Expr * e) : e(e) { };
|
||||||
|
COMMON_METHODS
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MakeBinOp(name, s) \
|
||||||
|
struct 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
|
||||||
};
|
};
|
||||||
ATerm bottomupRewrite(TermFun & f, ATerm e);
|
|
||||||
|
|
||||||
|
|
||||||
/* Query all attributes in an attribute set expression. The
|
/* Static environments are used to map variable names onto (level,
|
||||||
expression must be in normal form. */
|
displacement) pairs used to obtain the value of the variable at
|
||||||
void queryAllAttrs(Expr e, ATermMap & attrs, bool withPos = false);
|
runtime. */
|
||||||
|
struct StaticEnv
|
||||||
/* Query a specific attribute from an attribute set expression. The
|
{
|
||||||
expression must be in normal form. */
|
bool isWith;
|
||||||
Expr queryAttr(Expr e, const string & name);
|
const StaticEnv * up;
|
||||||
Expr queryAttr(Expr e, const string & name, ATerm & pos);
|
typedef std::map<Symbol, unsigned int> Vars;
|
||||||
|
Vars vars;
|
||||||
/* Create an attribute set expression from an Attrs value. */
|
StaticEnv(bool isWith, const StaticEnv * up) : isWith(isWith), up(up) { };
|
||||||
Expr makeAttrs(const ATermMap & attrs);
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Perform a set of substitutions on an expression. */
|
|
||||||
Expr substitute(const Substitution & subs, Expr e);
|
|
||||||
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
|
|
||||||
/* Canonicalise a Nix expression by sorting attributes and removing
|
|
||||||
location information. */
|
|
||||||
Expr canonicaliseExpr(Expr e);
|
|
||||||
|
|
||||||
|
|
||||||
/* Create an expression representing a boolean. */
|
|
||||||
Expr makeBool(bool b);
|
|
||||||
|
|
||||||
|
|
||||||
/* Manipulation of Str() nodes. Note: matchStr() does not clear
|
|
||||||
context! */
|
|
||||||
bool matchStr(Expr e, string & s, PathSet & context);
|
|
||||||
|
|
||||||
Expr makeStr(const string & s, const PathSet & context = PathSet());
|
|
||||||
|
|
||||||
|
|
||||||
/* Showing types, values. */
|
|
||||||
string showType(Expr e);
|
|
||||||
|
|
||||||
string showValue(Expr e);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,11 @@ namespace nix {
|
||||||
|
|
||||||
|
|
||||||
/* Parse a Nix expression from the specified file. If `path' refers
|
/* Parse a Nix expression from the specified file. If `path' refers
|
||||||
to a directory, the "/default.nix" is appended. */
|
to a directory, then "/default.nix" is appended. */
|
||||||
Expr parseExprFromFile(EvalState & state, Path path);
|
Expr * parseExprFromFile(EvalState & state, Path path);
|
||||||
|
|
||||||
/* Parse a Nix expression from the specified string. */
|
/* Parse a Nix expression from the specified string. */
|
||||||
Expr parseExprFromString(EvalState & state, const string & s,
|
Expr * parseExprFromString(EvalState & state, const string & s, const Path & basePath);
|
||||||
const Path & basePath);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,16 +20,14 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "aterm.hh"
|
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
|
||||||
|
#include "nixexpr.hh"
|
||||||
|
|
||||||
#include "parser-tab.hh"
|
#include "parser-tab.hh"
|
||||||
#include "lexer-tab.hh"
|
#include "lexer-tab.hh"
|
||||||
#define YYSTYPE YYSTYPE // workaround a bug in Bison 2.4
|
#define YYSTYPE YYSTYPE // workaround a bug in Bison 2.4
|
||||||
|
|
||||||
#include "nixexpr.hh"
|
|
||||||
#include "nixexpr-ast.hh"
|
|
||||||
|
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
|
||||||
|
@ -39,145 +37,85 @@ namespace nix {
|
||||||
|
|
||||||
struct ParseData
|
struct ParseData
|
||||||
{
|
{
|
||||||
Expr result;
|
SymbolTable & symbols;
|
||||||
|
Expr * result;
|
||||||
Path basePath;
|
Path basePath;
|
||||||
Path path;
|
Path path;
|
||||||
string error;
|
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;
|
string s;
|
||||||
for (ATermIterator i(attrPath); i; ++i) {
|
foreach (vector<Symbol>::const_iterator, i, attrPath) {
|
||||||
if (!s.empty()) s += '.';
|
if (!s.empty()) s += '.';
|
||||||
s += aterm2String(*i);
|
s += *i;
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct Tree
|
|
||||||
|
static void dupAttr(const vector<Symbol> & attrPath, const Pos & pos, const Pos & prevPos)
|
||||||
{
|
{
|
||||||
Expr leaf; ATerm pos; bool recursive;
|
throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
|
||||||
typedef std::map<ATerm, Tree> Children;
|
% showAttrPath(attrPath) % pos % prevPos);
|
||||||
Children children;
|
|
||||||
Tree() { leaf = 0; recursive = true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static ATermList buildAttrs(const Tree & t, ATermList & nonrec)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Expr fixAttrs(bool recursive, ATermList as)
|
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
|
||||||
{
|
{
|
||||||
Tree attrs;
|
vector<Symbol> attrPath; attrPath.push_back(attr);
|
||||||
|
throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
|
||||||
|
% showAttrPath(attrPath) % pos % prevPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* This ATermMap is needed to ensure that the `leaf' fields in the
|
static void addAttr(ExprAttrs * attrs, const vector<Symbol> & attrPath,
|
||||||
Tree nodes are not garbage collected. */
|
Expr * e, const Pos & pos)
|
||||||
ATermMap gcRoots;
|
{
|
||||||
|
unsigned int n = 0;
|
||||||
for (ATermIterator i(as); i; ++i) {
|
foreach (vector<Symbol>::const_iterator, i, attrPath) {
|
||||||
ATermList names, attrPath; Expr src, e; ATerm name, pos;
|
n++;
|
||||||
|
ExprAttrs::Attrs::iterator j = attrs->attrs.find(*i);
|
||||||
if (matchInherit(*i, src, names, pos)) {
|
if (j != attrs->attrs.end()) {
|
||||||
bool fromScope = matchScope(src);
|
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.first);
|
||||||
for (ATermIterator j(names); j; ++j) {
|
if (!attrs2 || n == attrPath.size()) dupAttr(attrPath, pos, j->second.second);
|
||||||
if (attrs.children.find(*j) != attrs.children.end())
|
attrs = attrs2;
|
||||||
throw ParseError(format("duplicate definition of attribute `%1%' at %2%")
|
} else {
|
||||||
% showAttrPath(ATmakeList1(*j)) % showPos(pos));
|
if (attrs->attrNames.find(*i) != attrs->attrNames.end())
|
||||||
Tree & t(attrs.children[*j]);
|
dupAttr(attrPath, pos, attrs->attrNames[*i]);
|
||||||
Expr leaf = fromScope ? makeVar(*j) : makeSelect(src, *j);
|
attrs->attrNames[*i] = pos;
|
||||||
gcRoots.set(leaf, leaf);
|
if (n == attrPath.size())
|
||||||
t.leaf = leaf;
|
attrs->attrs[*i] = ExprAttrs::Attr(e, pos);
|
||||||
t.pos = pos;
|
else {
|
||||||
if (recursive && fromScope) t.recursive = false;
|
ExprAttrs * nested = new ExprAttrs;
|
||||||
|
attrs->attrs[*i] = ExprAttrs::Attr(nested, pos);
|
||||||
|
attrs = nested;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat)
|
static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
|
||||||
{
|
{
|
||||||
ATerm name;
|
if (formals->argNames.find(formal.name) != formals->argNames.end())
|
||||||
ATermList formals;
|
throw ParseError(format("duplicate formal function argument `%1%' at %2%")
|
||||||
Pattern pat1, pat2;
|
% formal.name % pos);
|
||||||
ATermBool ellipsis;
|
formals->formals.push_front(formal);
|
||||||
if (matchVarPat(pat, name)) {
|
formals->argNames.insert(formal.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (matchAtPat(pat, pat1, pat2)) {
|
|
||||||
checkPatternVars(pos, map, pat1);
|
|
||||||
checkPatternVars(pos, map, pat2);
|
|
||||||
}
|
|
||||||
else abort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void checkPatternVars(ATerm pos, Pattern pat)
|
static Expr * stripIndentation(vector<Expr *> & es)
|
||||||
{
|
{
|
||||||
ATermMap map;
|
if (es.empty()) return new ExprString("");
|
||||||
checkPatternVars(pos, map, pat);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static Expr stripIndentation(ATermList es)
|
|
||||||
{
|
|
||||||
if (es == ATempty) return makeStr("");
|
|
||||||
|
|
||||||
/* Figure out the minimum indentation. Note that by design
|
/* Figure out the minimum indentation. Note that by design
|
||||||
whitespace-only final lines are not taken into account. (So
|
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 */
|
bool atStartOfLine = true; /* = seen only whitespace in the current line */
|
||||||
unsigned int minIndent = 1000000;
|
unsigned int minIndent = 1000000;
|
||||||
unsigned int curIndent = 0;
|
unsigned int curIndent = 0;
|
||||||
ATerm e;
|
foreach (vector<Expr *>::iterator, i, es) {
|
||||||
for (ATermIterator i(es); i; ++i) {
|
ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
|
||||||
if (!matchIndStr(*i, e)) {
|
if (!e) {
|
||||||
/* Anti-quotations end the current start-of-line whitespace. */
|
/* Anti-quotations end the current start-of-line whitespace. */
|
||||||
if (atStartOfLine) {
|
if (atStartOfLine) {
|
||||||
atStartOfLine = false;
|
atStartOfLine = false;
|
||||||
|
@ -195,12 +133,11 @@ static Expr stripIndentation(ATermList es)
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
string s = aterm2String(e);
|
for (unsigned int j = 0; j < e->s.size(); ++j) {
|
||||||
for (unsigned int j = 0; j < s.size(); ++j) {
|
|
||||||
if (atStartOfLine) {
|
if (atStartOfLine) {
|
||||||
if (s[j] == ' ')
|
if (e->s[j] == ' ')
|
||||||
curIndent++;
|
curIndent++;
|
||||||
else if (s[j] == '\n') {
|
else if (e->s[j] == '\n') {
|
||||||
/* Empty line, doesn't influence minimum
|
/* Empty line, doesn't influence minimum
|
||||||
indentation. */
|
indentation. */
|
||||||
curIndent = 0;
|
curIndent = 0;
|
||||||
|
@ -208,7 +145,7 @@ static Expr stripIndentation(ATermList es)
|
||||||
atStartOfLine = false;
|
atStartOfLine = false;
|
||||||
if (curIndent < minIndent) minIndent = curIndent;
|
if (curIndent < minIndent) minIndent = curIndent;
|
||||||
}
|
}
|
||||||
} else if (s[j] == '\n') {
|
} else if (e->s[j] == '\n') {
|
||||||
atStartOfLine = true;
|
atStartOfLine = true;
|
||||||
curIndent = 0;
|
curIndent = 0;
|
||||||
}
|
}
|
||||||
|
@ -216,37 +153,37 @@ static Expr stripIndentation(ATermList es)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Strip spaces from each line. */
|
/* Strip spaces from each line. */
|
||||||
ATermList es2 = ATempty;
|
vector<Expr *> * es2 = new vector<Expr *>;
|
||||||
atStartOfLine = true;
|
atStartOfLine = true;
|
||||||
unsigned int curDropped = 0;
|
unsigned int curDropped = 0;
|
||||||
unsigned int n = ATgetLength(es);
|
unsigned int n = es.size();
|
||||||
for (ATermIterator i(es); i; ++i, --n) {
|
for (vector<Expr *>::iterator i = es.begin(); i != es.end(); ++i, --n) {
|
||||||
if (!matchIndStr(*i, e)) {
|
ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
|
||||||
|
if (!e) {
|
||||||
atStartOfLine = false;
|
atStartOfLine = false;
|
||||||
curDropped = 0;
|
curDropped = 0;
|
||||||
es2 = ATinsert(es2, *i);
|
es2->push_back(*i);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
string s = aterm2String(e);
|
|
||||||
string s2;
|
string s2;
|
||||||
for (unsigned int j = 0; j < s.size(); ++j) {
|
for (unsigned int j = 0; j < e->s.size(); ++j) {
|
||||||
if (atStartOfLine) {
|
if (atStartOfLine) {
|
||||||
if (s[j] == ' ') {
|
if (e->s[j] == ' ') {
|
||||||
if (curDropped++ >= minIndent)
|
if (curDropped++ >= minIndent)
|
||||||
s2 += s[j];
|
s2 += e->s[j];
|
||||||
}
|
}
|
||||||
else if (s[j] == '\n') {
|
else if (e->s[j] == '\n') {
|
||||||
curDropped = 0;
|
curDropped = 0;
|
||||||
s2 += s[j];
|
s2 += e->s[j];
|
||||||
} else {
|
} else {
|
||||||
atStartOfLine = false;
|
atStartOfLine = false;
|
||||||
curDropped = 0;
|
curDropped = 0;
|
||||||
s2 += s[j];
|
s2 += e->s[j];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
s2 += s[j];
|
s2 += e->s[j];
|
||||||
if (s[j] == '\n') atStartOfLine = true;
|
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)
|
if (p != string::npos && s2.find_first_not_of(' ', p + 1) == string::npos)
|
||||||
s2 = string(s2, 0, p + 1);
|
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);
|
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),
|
return Pos(data->path, loc.first_line, loc.first_column);
|
||||||
loc->first_line, loc->first_column);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CUR_POS makeCurPos(yylocp, data)
|
#define CUR_POS makeCurPos(*yylocp, data)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -283,50 +219,43 @@ static Pos makeCurPos(YYLTYPE * loc, ParseData * data)
|
||||||
|
|
||||||
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error)
|
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error)
|
||||||
{
|
{
|
||||||
data->error = (format("%1%, at `%2%':%3%:%4%")
|
data->error = (format("%1%, at %2%")
|
||||||
% error % data->path % loc->first_line % loc->first_column).str();
|
% error % makeCurPos(*loc, data)).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);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define YYMALLOC mallocAndProtect
|
|
||||||
#define YYFREE freeAndUnprotect
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%union {
|
%union {
|
||||||
ATerm t;
|
nix::Expr * e;
|
||||||
ATermList ts;
|
nix::ExprList * list;
|
||||||
struct {
|
nix::ExprAttrs * attrs;
|
||||||
ATermList formals;
|
nix::Formals * formals;
|
||||||
bool ellipsis;
|
nix::Formal * formal;
|
||||||
} formals;
|
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 <e> start expr expr_function expr_if expr_op
|
||||||
%type <t> expr_app expr_select expr_simple bind inheritsrc formal
|
%type <e> expr_app expr_select expr_simple
|
||||||
%type <t> pattern pattern2
|
%type <list> expr_list
|
||||||
%type <ts> binds ids attrpath expr_list string_parts ind_string_parts
|
%type <attrs> binds
|
||||||
%type <formals> formals
|
%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 IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL
|
||||||
%token DOLLAR_CURLY /* == ${ */
|
%token DOLLAR_CURLY /* == ${ */
|
||||||
%token IND_STRING_OPEN IND_STRING_CLOSE
|
%token IND_STRING_OPEN IND_STRING_CLOSE
|
||||||
|
@ -350,163 +279,172 @@ start: expr { data->result = $1; };
|
||||||
expr: expr_function;
|
expr: expr_function;
|
||||||
|
|
||||||
expr_function
|
expr_function
|
||||||
: pattern ':' expr_function
|
: ID ':' expr_function
|
||||||
{ checkPatternVars(CUR_POS, $1); $$ = makeFunction($1, $3, CUR_POS); }
|
{ $$ = 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
|
| ASSERT expr ';' expr_function
|
||||||
{ $$ = makeAssert($2, $4, CUR_POS); }
|
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
|
||||||
| WITH expr ';' expr_function
|
| WITH expr ';' expr_function
|
||||||
{ $$ = makeWith($2, $4, CUR_POS); }
|
{ $$ = new ExprWith(CUR_POS, $2, $4); }
|
||||||
| LET binds IN expr_function
|
| 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
|
||||||
;
|
;
|
||||||
|
|
||||||
expr_if
|
expr_if
|
||||||
: IF expr THEN expr ELSE expr
|
: IF expr THEN expr ELSE expr { $$ = new ExprIf($2, $4, $6); }
|
||||||
{ $$ = makeIf($2, $4, $6); }
|
|
||||||
| expr_op
|
| expr_op
|
||||||
;
|
;
|
||||||
|
|
||||||
expr_op
|
expr_op
|
||||||
: '!' expr_op %prec NEG { $$ = makeOpNot($2); }
|
: '!' expr_op %prec NEG { $$ = new ExprOpNot($2); }
|
||||||
| expr_op EQ expr_op { $$ = makeOpEq($1, $3); }
|
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
|
||||||
| expr_op NEQ expr_op { $$ = makeOpNEq($1, $3); }
|
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
|
||||||
| expr_op AND expr_op { $$ = makeOpAnd($1, $3); }
|
| expr_op AND expr_op { $$ = new ExprOpAnd($1, $3); }
|
||||||
| expr_op OR expr_op { $$ = makeOpOr($1, $3); }
|
| expr_op OR expr_op { $$ = new ExprOpOr($1, $3); }
|
||||||
| expr_op IMPL expr_op { $$ = makeOpImpl($1, $3); }
|
| expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); }
|
||||||
| expr_op UPDATE expr_op { $$ = makeOpUpdate($1, $3); }
|
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate($1, $3); }
|
||||||
| expr_op '~' expr_op { $$ = makeSubPath($1, $3); }
|
| expr_op '?' ID { $$ = new ExprOpHasAttr($1, data->symbols.create($3)); }
|
||||||
| expr_op '?' ID { $$ = makeOpHasAttr($1, $3); }
|
| expr_op '+' expr_op
|
||||||
| expr_op '+' expr_op { $$ = makeOpPlus($1, $3); }
|
{ vector<Expr *> * l = new vector<Expr *>;
|
||||||
| expr_op CONCAT expr_op { $$ = makeOpConcat($1, $3); }
|
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_app
|
||||||
: expr_app expr_select
|
: expr_app expr_select
|
||||||
{ $$ = makeCall($1, $2); }
|
{ $$ = new ExprApp($1, $2); }
|
||||||
| expr_select { $$ = $1; }
|
| expr_select { $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
expr_select
|
expr_select
|
||||||
: expr_select '.' ID
|
: expr_select '.' ID
|
||||||
{ $$ = makeSelect($1, $3); }
|
{ $$ = new ExprSelect($1, data->symbols.create($3)); }
|
||||||
| expr_simple { $$ = $1; }
|
| expr_simple { $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
expr_simple
|
expr_simple
|
||||||
: ID { $$ = makeVar($1); }
|
: ID { $$ = new ExprVar(data->symbols.create($1)); }
|
||||||
| INT { $$ = makeInt(ATgetInt((ATermInt) $1)); }
|
| INT { $$ = new ExprInt($1); }
|
||||||
| '"' string_parts '"' {
|
| '"' string_parts '"' {
|
||||||
/* For efficiency, and to simplify parse trees a bit. */
|
/* For efficiency, and to simplify parse trees a bit. */
|
||||||
if ($2 == ATempty) $$ = makeStr(toATerm(""), ATempty);
|
if ($2->empty()) $$ = new ExprString("");
|
||||||
else if (ATgetNext($2) == ATempty) $$ = ATgetFirst($2);
|
else if ($2->size() == 1) $$ = $2->front();
|
||||||
else $$ = makeConcatStrings(ATreverse($2));
|
else $$ = new ExprConcatStrings($2);
|
||||||
}
|
}
|
||||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||||
$$ = stripIndentation(ATreverse($2));
|
$$ = stripIndentation(*$2);
|
||||||
}
|
}
|
||||||
| PATH { $$ = makePath(toATerm(absPath(aterm2String($1), data->basePath))); }
|
| PATH { $$ = new ExprPath(absPath($1, data->basePath)); }
|
||||||
| URI { $$ = makeStr($1, ATempty); }
|
| URI { $$ = new ExprString($1); }
|
||||||
| '(' expr ')' { $$ = $2; }
|
| '(' expr ')' { $$ = $2; }
|
||||||
/* Let expressions `let {..., body = ...}' are just desugared
|
/* Let expressions `let {..., body = ...}' are just desugared
|
||||||
into `(rec {..., body = ...}).body'. */
|
into `(rec {..., body = ...}).body'. */
|
||||||
| LET '{' binds '}'
|
| LET '{' binds '}'
|
||||||
{ $$ = makeSelect(fixAttrs(true, $3), toATerm("body")); }
|
{ $3->recursive = true; $$ = new ExprSelect($3, data->symbols.create("body")); }
|
||||||
| REC '{' binds '}'
|
| REC '{' binds '}'
|
||||||
{ $$ = fixAttrs(true, $3); }
|
{ $3->recursive = true; $$ = $3; }
|
||||||
| '{' binds '}'
|
| '{' binds '}'
|
||||||
{ $$ = fixAttrs(false, $2); }
|
{ $$ = $2; }
|
||||||
| '[' expr_list ']' { $$ = makeList(ATreverse($2)); }
|
| '[' expr_list ']' { $$ = $2; }
|
||||||
;
|
;
|
||||||
|
|
||||||
string_parts
|
string_parts
|
||||||
: string_parts STR { $$ = ATinsert($1, $2); }
|
: string_parts STR { $$ = $1; $1->push_back($2); }
|
||||||
| string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = ATinsert($1, $3); }
|
| string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = $1; $1->push_back($3); }
|
||||||
| { $$ = ATempty; }
|
| { $$ = new vector<Expr *>; }
|
||||||
;
|
;
|
||||||
|
|
||||||
ind_string_parts
|
ind_string_parts
|
||||||
: ind_string_parts IND_STR { $$ = ATinsert($1, $2); }
|
: ind_string_parts IND_STR { $$ = $1; $1->push_back($2); }
|
||||||
| ind_string_parts DOLLAR_CURLY expr '}' { backToIndString(scanner); $$ = ATinsert($1, $3); }
|
| ind_string_parts DOLLAR_CURLY expr '}' { backToIndString(scanner); $$ = $1; $1->push_back($3); }
|
||||||
| { $$ = ATempty; }
|
| { $$ = new vector<Expr *>; }
|
||||||
;
|
|
||||||
|
|
||||||
pattern
|
|
||||||
: pattern2 '@' pattern { $$ = makeAtPat($1, $3); }
|
|
||||||
| pattern2
|
|
||||||
;
|
|
||||||
|
|
||||||
pattern2
|
|
||||||
: ID { $$ = makeVarPat($1); }
|
|
||||||
| '{' formals '}' { $$ = makeAttrsPat($2.formals, $2.ellipsis ? eTrue : eFalse); }
|
|
||||||
;
|
;
|
||||||
|
|
||||||
binds
|
binds
|
||||||
: binds bind { $$ = ATinsert($1, $2); }
|
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); }
|
||||||
| { $$ = ATempty; }
|
| 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; }
|
||||||
;
|
;
|
||||||
|
|
||||||
bind
|
ids
|
||||||
: attrpath '=' expr ';'
|
: ids ID { $$ = $1; $1->push_back(data->symbols.create($2)); /* !!! dangerous */ }
|
||||||
{ $$ = makeBindAttrPath(ATreverse($1), $3, CUR_POS); }
|
| { $$ = new vector<Symbol>; }
|
||||||
| INHERIT inheritsrc ids ';'
|
|
||||||
{ $$ = makeInherit($2, $3, CUR_POS); }
|
|
||||||
;
|
;
|
||||||
|
|
||||||
inheritsrc
|
|
||||||
: '(' expr ')' { $$ = $2; }
|
|
||||||
| { $$ = makeScope(); }
|
|
||||||
;
|
|
||||||
|
|
||||||
ids: ids ID { $$ = ATinsert($1, $2); } | { $$ = ATempty; };
|
|
||||||
|
|
||||||
attrpath
|
attrpath
|
||||||
: attrpath '.' ID { $$ = ATinsert($1, $3); }
|
: attrpath '.' ID { $$ = $1; $1->push_back(data->symbols.create($3)); }
|
||||||
| ID { $$ = ATmakeList1($1); }
|
| ID { $$ = new vector<Symbol>; $$->push_back(data->symbols.create($1)); }
|
||||||
;
|
;
|
||||||
|
|
||||||
expr_list
|
expr_list
|
||||||
: expr_list expr_select { $$ = ATinsert($1, $2); }
|
: expr_list expr_select { $$ = $1; $1->elems.push_back($2); /* !!! dangerous */ }
|
||||||
| { $$ = ATempty; }
|
| { $$ = new ExprList; }
|
||||||
;
|
;
|
||||||
|
|
||||||
formals
|
formals
|
||||||
: formal ',' formals /* !!! right recursive */
|
: formal ',' formals
|
||||||
{ $$.formals = ATinsert($3.formals, $1); $$.ellipsis = $3.ellipsis; }
|
{ $$ = $3; addFormal(CUR_POS, $$, *$1); }
|
||||||
| formal
|
| formal
|
||||||
{ $$.formals = ATinsert(ATempty, $1); $$.ellipsis = false; }
|
{ $$ = new Formals; addFormal(CUR_POS, $$, *$1); $$->ellipsis = false; }
|
||||||
|
|
|
|
||||||
{ $$.formals = ATempty; $$.ellipsis = false; }
|
{ $$ = new Formals; $$->ellipsis = false; }
|
||||||
| ELLIPSIS
|
| ELLIPSIS
|
||||||
{ $$.formals = ATempty; $$.ellipsis = true; }
|
{ $$ = new Formals; $$->ellipsis = true; }
|
||||||
;
|
;
|
||||||
|
|
||||||
formal
|
formal
|
||||||
: ID { $$ = makeFormal($1, makeNoDefaultValue()); }
|
: ID { $$ = new Formal(data->symbols.create($1), 0); }
|
||||||
| ID '?' expr { $$ = makeFormal($1, makeDefaultValue($3)); }
|
| ID '?' expr { $$ = new Formal(data->symbols.create($1), $3); }
|
||||||
;
|
;
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
|
||||||
#include "eval.hh"
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <eval.hh>
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
static Expr parse(EvalState & state,
|
static Expr * parse(EvalState & state, const char * text,
|
||||||
const char * text, const Path & path,
|
const Path & path, const Path & basePath)
|
||||||
const Path & basePath)
|
|
||||||
{
|
{
|
||||||
yyscan_t scanner;
|
yyscan_t scanner;
|
||||||
ParseData data;
|
ParseData data(state.symbols);
|
||||||
data.basePath = basePath;
|
data.basePath = basePath;
|
||||||
data.path = path;
|
data.path = path;
|
||||||
|
|
||||||
|
@ -518,7 +456,7 @@ static Expr parse(EvalState & state,
|
||||||
if (res) throw ParseError(data.error);
|
if (res) throw ParseError(data.error);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
checkVarDefs(state.primOps, data.result);
|
data.result->bindVars(state.staticBaseEnv);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
throw ParseError(format("%1%, in `%2%'") % e.msg() % path);
|
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] == '/');
|
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
|
/* If `path' is a symlink, follow it. This is so that relative
|
||||||
path references work. */
|
path references work. */
|
||||||
struct stat st;
|
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)
|
const string & s, const Path & basePath)
|
||||||
{
|
{
|
||||||
return parse(state, s.c_str(), "(string)", basePath);
|
return parse(state, s.c_str(), "(string)", basePath);
|
||||||
|
|
File diff suppressed because it is too large
Load diff
81
src/libexpr/symbol-table.hh
Normal file
81
src/libexpr/symbol-table.hh
Normal file
|
@ -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 */
|
161
src/libexpr/value-to-xml.cc
Normal file
161
src/libexpr/value-to-xml.cc
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
17
src/libexpr/value-to-xml.hh
Normal file
17
src/libexpr/value-to-xml.hh
Normal file
|
@ -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 */
|
|
@ -15,5 +15,5 @@ AM_CXXFLAGS = \
|
||||||
-DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \
|
-DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \
|
||||||
-DNIX_BIN_DIR=\"$(bindir)\" \
|
-DNIX_BIN_DIR=\"$(bindir)\" \
|
||||||
-DNIX_VERSION=\"$(VERSION)\" \
|
-DNIX_VERSION=\"$(VERSION)\" \
|
||||||
-I$(srcdir)/.. ${aterm_include} -I$(srcdir)/../libutil \
|
-I$(srcdir)/.. -I$(srcdir)/../libutil \
|
||||||
-I$(srcdir)/../libstore
|
-I$(srcdir)/../libstore
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <aterm2.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -87,9 +85,6 @@ static void setLogType(string lt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void initDerivationsHelpers();
|
|
||||||
|
|
||||||
|
|
||||||
static void closeStore()
|
static void closeStore()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
@ -176,9 +171,6 @@ static void initAndRun(int argc, char * * argv)
|
||||||
string lt = getEnv("NIX_LOG_TYPE");
|
string lt = getEnv("NIX_LOG_TYPE");
|
||||||
if (lt != "") setLogType(lt);
|
if (lt != "") setLogType(lt);
|
||||||
|
|
||||||
/* ATerm stuff. !!! find a better place to put this */
|
|
||||||
initDerivationsHelpers();
|
|
||||||
|
|
||||||
/* Put the arguments in a vector. */
|
/* Put the arguments in a vector. */
|
||||||
Strings args, remaining;
|
Strings args, remaining;
|
||||||
while (argc--) args.push_back(*argv++);
|
while (argc--) args.push_back(*argv++);
|
||||||
|
@ -333,10 +325,6 @@ int main(int argc, char * * argv)
|
||||||
if (argc == 0) abort();
|
if (argc == 0) abort();
|
||||||
setuidInit();
|
setuidInit();
|
||||||
|
|
||||||
/* ATerm setup. */
|
|
||||||
ATerm bottomOfStack;
|
|
||||||
ATinit(argc, argv, &bottomOfStack);
|
|
||||||
|
|
||||||
/* Turn on buffering for cerr. */
|
/* Turn on buffering for cerr. */
|
||||||
#if HAVE_PUBSETBUF
|
#if HAVE_PUBSETBUF
|
||||||
std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
|
std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
|
||||||
|
|
|
@ -12,17 +12,12 @@ pkginclude_HEADERS = \
|
||||||
|
|
||||||
libstore_la_LIBADD = ../libutil/libutil.la ../boost/format/libformat.la ${aterm_lib} ${sqlite_lib}
|
libstore_la_LIBADD = ../libutil/libutil.la ../boost/format/libformat.la ${aterm_lib} ${sqlite_lib}
|
||||||
|
|
||||||
BUILT_SOURCES = derivations-ast.cc derivations-ast.hh
|
EXTRA_DIST = schema.sql
|
||||||
|
|
||||||
EXTRA_DIST = derivations-ast.def derivations-ast.cc schema.sql
|
|
||||||
|
|
||||||
AM_CXXFLAGS = -Wall \
|
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
|
local-store.lo: schema.sql.hh
|
||||||
|
|
||||||
%.sql.hh: %.sql
|
%.sql.hh: %.sql
|
||||||
../bin2c/bin2c schema < $< > $@ || (rm $@ && exit 1)
|
../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
|
|
||||||
|
|
|
@ -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 |
|
|
|
@ -1,22 +1,12 @@
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "aterm.hh"
|
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
|
||||||
#include "derivations-ast.hh"
|
|
||||||
#include "derivations-ast.cc"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
Hash hashTerm(ATerm t)
|
|
||||||
{
|
|
||||||
return hashString(htSHA256, atPrint(t));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Path writeDerivation(const Derivation & drv, const string & name)
|
Path writeDerivation(const Derivation & drv, const string & name)
|
||||||
{
|
{
|
||||||
PathSet references;
|
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
|
(that can be missing (of course) and should not necessarily be
|
||||||
held during a garbage collection). */
|
held during a garbage collection). */
|
||||||
string suffix = name + drvExtension;
|
string suffix = name + drvExtension;
|
||||||
string contents = atPrint(unparseDerivation(drv));
|
string contents = unparseDerivation(drv);
|
||||||
return readOnlyMode
|
return readOnlyMode
|
||||||
? computeStorePathForText(suffix, contents, references)
|
? computeStorePathForText(suffix, contents, references)
|
||||||
: store->addTextToStore(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] != '/')
|
if (s.size() == 0 || s[0] != '/')
|
||||||
throw Error(format("bad path `%1%' in derivation") % s);
|
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) {
|
StringSet res;
|
||||||
if (ATgetType(*i) != AT_APPL)
|
while (!endOfList(str))
|
||||||
throw badTerm("not a path", *i);
|
res.insert(arePaths ? parsePath(str) : parseString(str));
|
||||||
string s = aterm2String(*i);
|
return res;
|
||||||
if (arePaths) checkPath(s);
|
|
||||||
out.insert(s);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Derivation parseDerivation(const string & s)
|
||||||
/* 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 drv;
|
Derivation drv;
|
||||||
ATermList outs, inDrvs, inSrcs, args, bnds;
|
std::istringstream str(s);
|
||||||
ATerm builder, platform;
|
expect(str, "Derive([");
|
||||||
|
|
||||||
if (!matchDerive(t, outs, inDrvs, inSrcs, platform, builder, args, bnds))
|
/* Parse the list of outputs. */
|
||||||
throwBadDrv(t);
|
while (!endOfList(str)) {
|
||||||
|
|
||||||
for (ATermIterator i(outs); i; ++i) {
|
|
||||||
ATerm id, path, hashAlgo, hash;
|
|
||||||
if (!matchDerivationOutput(*i, id, path, hashAlgo, hash))
|
|
||||||
throwBadDrv(t);
|
|
||||||
DerivationOutput out;
|
DerivationOutput out;
|
||||||
out.path = aterm2String(path);
|
expect(str, "("); string id = parseString(str);
|
||||||
checkPath(out.path);
|
expect(str, ","); out.path = parsePath(str);
|
||||||
out.hashAlgo = aterm2String(hashAlgo);
|
expect(str, ","); out.hashAlgo = parseString(str);
|
||||||
out.hash = aterm2String(hash);
|
expect(str, ","); out.hash = parseString(str);
|
||||||
drv.outputs[aterm2String(id)] = out;
|
expect(str, ")");
|
||||||
|
drv.outputs[id] = out;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ATermIterator i(inDrvs); i; ++i) {
|
/* Parse the list of input derivations. */
|
||||||
ATerm drvPath;
|
expect(str, ",[");
|
||||||
ATermList ids;
|
while (!endOfList(str)) {
|
||||||
if (!matchDerivationInput(*i, drvPath, ids))
|
expect(str, "(");
|
||||||
throwBadDrv(t);
|
Path drvPath = parsePath(str);
|
||||||
Path drvPath2 = aterm2String(drvPath);
|
expect(str, ",[");
|
||||||
checkPath(drvPath2);
|
drv.inputDrvs[drvPath] = parseStrings(str, false);
|
||||||
StringSet ids2;
|
expect(str, ")");
|
||||||
parseStrings(ids, ids2, false);
|
}
|
||||||
drv.inputDrvs[drvPath2] = ids2;
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
parseStrings(inSrcs, drv.inputSrcs, true);
|
expect(str, ")");
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
return drv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ATerm unparseDerivation(const Derivation & drv)
|
static void printString(string & res, const string & s)
|
||||||
{
|
{
|
||||||
ATermList outputs = ATempty;
|
res += '"';
|
||||||
for (DerivationOutputs::const_reverse_iterator i = drv.outputs.rbegin();
|
for (const char * i = s.c_str(); *i; i++)
|
||||||
i != drv.outputs.rend(); ++i)
|
if (*i == '\"' || *i == '\\') { res += "\\"; res += *i; }
|
||||||
outputs = ATinsert(outputs,
|
else if (*i == '\n') res += "\\n";
|
||||||
makeDerivationOutput(
|
else if (*i == '\r') res += "\\r";
|
||||||
toATerm(i->first),
|
else if (*i == '\t') res += "\\t";
|
||||||
toATerm(i->second.path),
|
else res += *i;
|
||||||
toATerm(i->second.hashAlgo),
|
res += '"';
|
||||||
toATerm(i->second.hash)));
|
}
|
||||||
|
|
||||||
ATermList inDrvs = ATempty;
|
|
||||||
for (DerivationInputs::const_reverse_iterator i = drv.inputDrvs.rbegin();
|
template<class ForwardIterator>
|
||||||
i != drv.inputDrvs.rend(); ++i)
|
static void printStrings(string & res, ForwardIterator i, ForwardIterator j)
|
||||||
inDrvs = ATinsert(inDrvs,
|
{
|
||||||
makeDerivationInput(
|
res += '[';
|
||||||
toATerm(i->first),
|
bool first = true;
|
||||||
toATermList(i->second)));
|
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());
|
||||||
|
|
||||||
ATermList args = ATempty;
|
s += ','; printString(s, drv.platform);
|
||||||
for (Strings::const_reverse_iterator i = drv.args.rbegin();
|
s += ','; printString(s, drv.builder);
|
||||||
i != drv.args.rend(); ++i)
|
s += ','; printStrings(s, drv.args.begin(), drv.args.end());
|
||||||
args = ATinsert(args, toATerm(*i));
|
|
||||||
|
|
||||||
ATermList env = ATempty;
|
s += ",[";
|
||||||
for (StringPairs::const_reverse_iterator i = drv.env.rbegin();
|
first = true;
|
||||||
i != drv.env.rend(); ++i)
|
foreach (StringPairs::const_iterator, i, drv.env) {
|
||||||
env = ATinsert(env,
|
if (first) first = false; else s += ',';
|
||||||
makeEnvBinding(
|
s += '('; printString(s, i->first);
|
||||||
toATerm(i->first),
|
s += ','; printString(s, i->second);
|
||||||
toATerm(i->second)));
|
s += ')';
|
||||||
|
}
|
||||||
return makeDerive(
|
|
||||||
outputs,
|
s += "])";
|
||||||
inDrvs,
|
|
||||||
toATermList(drv.inputSrcs),
|
return s;
|
||||||
toATerm(drv.platform),
|
|
||||||
toATerm(drv.builder),
|
|
||||||
args,
|
|
||||||
env);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
#ifndef __DERIVATIONS_H
|
#ifndef __DERIVATIONS_H
|
||||||
#define __DERIVATIONS_H
|
#define __DERIVATIONS_H
|
||||||
|
|
||||||
typedef union _ATerm * ATerm;
|
|
||||||
|
|
||||||
#include "hash.hh"
|
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
#include "types.hh"
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
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. */
|
/* Write a derivation to the Nix store, and return its path. */
|
||||||
Path writeDerivation(const Derivation & drv, const string & name);
|
Path writeDerivation(const Derivation & drv, const string & name);
|
||||||
|
|
||||||
/* Parse a derivation. */
|
/* Parse a derivation. */
|
||||||
Derivation parseDerivation(ATerm t);
|
Derivation parseDerivation(const string & s);
|
||||||
|
|
||||||
/* Parse a derivation. */
|
/* Print a derivation. */
|
||||||
ATerm unparseDerivation(const Derivation & drv);
|
string unparseDerivation(const Derivation & drv);
|
||||||
|
|
||||||
/* Check whether a file name ends with the extensions for
|
/* Check whether a file name ends with the extensions for
|
||||||
derivations. */
|
derivations. */
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "archive.hh"
|
#include "archive.hh"
|
||||||
#include "pathlocks.hh"
|
#include "pathlocks.hh"
|
||||||
#include "derivations-ast.hh"
|
|
||||||
#include "worker-protocol.hh"
|
#include "worker-protocol.hh"
|
||||||
#include "derivations.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
|
efficiently query whether a path is an output of some
|
||||||
derivation. */
|
derivation. */
|
||||||
if (isDerivation(info.path)) {
|
if (isDerivation(info.path)) {
|
||||||
ATerm t = ATreadFromNamedFile(info.path.c_str());
|
Derivation drv = parseDerivation(readFile(info.path));
|
||||||
if (!t) throw Error(format("cannot read derivation `%1%'") % info.path);
|
|
||||||
Derivation drv = parseDerivation(t);
|
|
||||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||||
SQLiteStmtUse use(stmtAddDerivationOutput);
|
SQLiteStmtUse use(stmtAddDerivationOutput);
|
||||||
stmtAddDerivationOutput.bind(id);
|
stmtAddDerivationOutput.bind(id);
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "local-store.hh"
|
#include "local-store.hh"
|
||||||
|
|
||||||
#include <aterm2.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -12,9 +10,7 @@ Derivation derivationFromPath(const Path & drvPath)
|
||||||
{
|
{
|
||||||
assertStorePath(drvPath);
|
assertStorePath(drvPath);
|
||||||
store->ensurePath(drvPath);
|
store->ensurePath(drvPath);
|
||||||
ATerm t = ATreadFromNamedFile(drvPath.c_str());
|
return parseDerivation(readFile(drvPath));
|
||||||
if (!t) throw Error(format("cannot read aterm from `%1%'") % drvPath);
|
|
||||||
return parseDerivation(t);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
pkglib_LTLIBRARIES = libutil.la
|
pkglib_LTLIBRARIES = libutil.la
|
||||||
|
|
||||||
libutil_la_SOURCES = util.cc hash.cc serialise.cc \
|
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}
|
libutil_la_LIBADD = ../boost/format/libformat.la ${aterm_lib} ${sqlite_lib}
|
||||||
|
|
||||||
pkginclude_HEADERS = util.hh hash.hh serialise.hh \
|
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
|
if !HAVE_OPENSSL
|
||||||
libutil_la_SOURCES += \
|
libutil_la_SOURCES += \
|
||||||
md5.c md5.h sha1.c sha1.h sha256.c sha256.h md32_common.h
|
md5.c md5.h sha1.c sha1.h sha256.c sha256.h md32_common.h
|
||||||
endif
|
endif
|
||||||
|
|
||||||
AM_CXXFLAGS = -Wall -I$(srcdir)/.. ${aterm_include}
|
AM_CXXFLAGS = -Wall -I$(srcdir)/..
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -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 */
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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 */
|
|
|
@ -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 tokenizeString(const string & s, const string & separators)
|
||||||
{
|
{
|
||||||
Strings result;
|
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()
|
void ignoreException()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace nix {
|
||||||
|
|
||||||
|
|
||||||
#define foreach(it_type, it, collection) \
|
#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. */
|
/* Return an environment variable. */
|
||||||
|
@ -276,11 +276,6 @@ void inline checkInterrupt()
|
||||||
MakeError(Interrupted, BaseError)
|
MakeError(Interrupted, BaseError)
|
||||||
|
|
||||||
|
|
||||||
/* String packing / unpacking. */
|
|
||||||
string packStrings(const Strings & strings);
|
|
||||||
Strings unpackStrings(const string & s);
|
|
||||||
|
|
||||||
|
|
||||||
/* String tokenizer. */
|
/* String tokenizer. */
|
||||||
Strings tokenizeString(const string & s, const string & separators = " \t\n\r");
|
Strings tokenizeString(const string & s, const string & separators = " \t\n\r");
|
||||||
|
|
||||||
|
@ -307,34 +302,23 @@ string int2String(int n);
|
||||||
bool hasSuffix(const string & s, const string & suffix);
|
bool hasSuffix(const string & s, const string & suffix);
|
||||||
|
|
||||||
|
|
||||||
|
/* Read string `s' from stream `str'. */
|
||||||
|
void expect(std::istream & str, const string & s);
|
||||||
|
|
||||||
|
|
||||||
|
/* Read a C-style string from stream `str'. */
|
||||||
|
string parseString(std::istream & str);
|
||||||
|
|
||||||
|
|
||||||
|
/* Utility function used to parse legacy ATerms. */
|
||||||
|
bool endOfList(std::istream & str);
|
||||||
|
|
||||||
|
|
||||||
/* Exception handling in destructors: print an error message, then
|
/* Exception handling in destructors: print an error message, then
|
||||||
ignore the exception. */
|
ignore the exception. */
|
||||||
void ignoreException();
|
void ignoreException();
|
||||||
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
bin_PROGRAMS = nix-env
|
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 \
|
nix_env_LDADD = ../libmain/libmain.la ../libexpr/libexpr.la \
|
||||||
../libstore/libstore.la ../libutil/libutil.la \
|
../libstore/libstore.la ../libutil/libutil.la \
|
||||||
../boost/format/libformat.la
|
../boost/format/libformat.la
|
||||||
|
@ -11,7 +12,6 @@ nix-env.o: help.txt.hh
|
||||||
../bin2c/bin2c helpText < $< > $@ || (rm $@ && exit 1)
|
../bin2c/bin2c helpText < $< > $@ || (rm $@ && exit 1)
|
||||||
|
|
||||||
AM_CXXFLAGS = \
|
AM_CXXFLAGS = \
|
||||||
${aterm_include} \
|
|
||||||
-I$(srcdir)/.. \
|
-I$(srcdir)/.. \
|
||||||
-I$(srcdir)/../libutil -I$(srcdir)/../libstore \
|
-I$(srcdir)/../libutil -I$(srcdir)/../libstore \
|
||||||
-I$(srcdir)/../libexpr -I$(srcdir)/../libmain -I../libexpr
|
-I$(srcdir)/../libexpr -I$(srcdir)/../libmain -I../libexpr
|
||||||
|
|
|
@ -6,15 +6,13 @@
|
||||||
#include "parser.hh"
|
#include "parser.hh"
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
#include "help.txt.hh"
|
#include "help.txt.hh"
|
||||||
#include "nixexpr-ast.hh"
|
|
||||||
#include "get-drvs.hh"
|
#include "get-drvs.hh"
|
||||||
#include "attr-path.hh"
|
#include "attr-path.hh"
|
||||||
#include "pathlocks.hh"
|
|
||||||
#include "common-opts.hh"
|
#include "common-opts.hh"
|
||||||
#include "xml-writer.hh"
|
#include "xml-writer.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "user-env.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "aterm.hh"
|
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
@ -48,7 +46,7 @@ struct InstallSourceInfo
|
||||||
Path profile; /* for srcProfile */
|
Path profile; /* for srcProfile */
|
||||||
string systemFilter; /* for srcNixExprDrvs */
|
string systemFilter; /* for srcNixExprDrvs */
|
||||||
bool prebuiltOnly;
|
bool prebuiltOnly;
|
||||||
ATermMap autoArgs;
|
Bindings autoArgs;
|
||||||
InstallSourceInfo() : prebuiltOnly(false) { };
|
InstallSourceInfo() : prebuiltOnly(false) { };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -113,7 +111,7 @@ static bool isNixExpr(const Path & path)
|
||||||
|
|
||||||
|
|
||||||
static void getAllExprs(EvalState & state,
|
static void getAllExprs(EvalState & state,
|
||||||
const Path & path, ATermMap & attrs)
|
const Path & path, ExprAttrs & attrs)
|
||||||
{
|
{
|
||||||
Strings names = readDirectory(path);
|
Strings names = readDirectory(path);
|
||||||
StringSet namesSorted(names.begin(), names.end());
|
StringSet namesSorted(names.begin(), names.end());
|
||||||
|
@ -133,8 +131,8 @@ static void getAllExprs(EvalState & state,
|
||||||
string attrName = *i;
|
string attrName = *i;
|
||||||
if (hasSuffix(attrName, ".nix"))
|
if (hasSuffix(attrName, ".nix"))
|
||||||
attrName = string(attrName, 0, attrName.size() - 4);
|
attrName = string(attrName, 0, attrName.size() - 4);
|
||||||
attrs.set(toATerm(attrName), makeAttrRHS(
|
attrs.attrs[state.symbols.create(attrName)] =
|
||||||
parseExprFromFile(state, absPath(path2)), makeNoPos()));
|
ExprAttrs::Attr(parseExprFromFile(state, absPath(path2)), noPos);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
/* `path2' is a directory (with no default.nix in it);
|
/* `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));
|
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
|
(but keep the attribute set flat, not nested, to make it easier
|
||||||
for a user to have a ~/.nix-defexpr directory that includes
|
for a user to have a ~/.nix-defexpr directory that includes
|
||||||
some system-wide directory). */
|
some system-wide directory). */
|
||||||
ATermMap attrs;
|
ExprAttrs * attrs = new ExprAttrs;
|
||||||
attrs.set(toATerm("_combineChannels"), makeAttrRHS(makeList(ATempty), makeNoPos()));
|
attrs->attrs[state.symbols.create("_combineChannels")] =
|
||||||
getAllExprs(state, path, attrs);
|
ExprAttrs::Attr(new ExprList(), noPos);
|
||||||
return makeAttrs(attrs);
|
getAllExprs(state, path, *attrs);
|
||||||
|
return attrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void loadDerivations(EvalState & state, Path nixExprPath,
|
static void loadDerivations(EvalState & state, Path nixExprPath,
|
||||||
string systemFilter, const ATermMap & autoArgs,
|
string systemFilter, const Bindings & autoArgs,
|
||||||
const string & pathPrefix, DrvInfos & elems)
|
const string & pathPrefix, DrvInfos & elems)
|
||||||
{
|
{
|
||||||
getDerivations(state,
|
Value v;
|
||||||
findAlongAttrPath(state, pathPrefix, autoArgs, loadSourceExpr(state, nixExprPath)),
|
findAlongAttrPath(state, pathPrefix, autoArgs, loadSourceExpr(state, nixExprPath), v);
|
||||||
pathPrefix, autoArgs, elems);
|
|
||||||
|
getDerivations(state, v, pathPrefix, autoArgs, elems);
|
||||||
|
|
||||||
/* Filter out all derivations not applicable to the current
|
/* Filter out all derivations not applicable to the current
|
||||||
system. */
|
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)
|
static int getPriority(EvalState & state, const DrvInfo & drv)
|
||||||
{
|
{
|
||||||
MetaValue value = drv.queryMetaInfo(state, "priority");
|
MetaValue value = drv.queryMetaInfo(state, "priority");
|
||||||
|
@ -517,14 +351,13 @@ static void queryInstSources(EvalState & state,
|
||||||
(import ./foo.nix)' = `(import ./foo.nix).bar'. */
|
(import ./foo.nix)' = `(import ./foo.nix).bar'. */
|
||||||
case srcNixExprs: {
|
case srcNixExprs: {
|
||||||
|
|
||||||
Expr e1 = loadSourceExpr(state, instSource.nixExprPath);
|
Expr * e1 = loadSourceExpr(state, instSource.nixExprPath);
|
||||||
|
|
||||||
for (Strings::const_iterator i = args.begin();
|
foreach (Strings::const_iterator, i, args) {
|
||||||
i != args.end(); ++i)
|
Expr * e2 = parseExprFromString(state, *i, absPath("."));
|
||||||
{
|
Expr * call = new ExprApp(e2, e1);
|
||||||
Expr e2 = parseExprFromString(state, *i, absPath("."));
|
Value v; state.eval(call, v);
|
||||||
Expr call = makeCall(e2, e1);
|
getDerivations(state, v, "", instSource.autoArgs, elems);
|
||||||
getDerivations(state, call, "", instSource.autoArgs, elems);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -541,7 +374,7 @@ static void queryInstSources(EvalState & state,
|
||||||
Path path = followLinksToStorePath(*i);
|
Path path = followLinksToStorePath(*i);
|
||||||
|
|
||||||
DrvInfo elem;
|
DrvInfo elem;
|
||||||
elem.attrs = boost::shared_ptr<ATermMap>(new ATermMap(0)); /* ugh... */
|
elem.attrs = new Bindings;
|
||||||
string name = baseNameOf(path);
|
string name = baseNameOf(path);
|
||||||
string::size_type dash = name.find('-');
|
string::size_type dash = name.find('-');
|
||||||
if (dash != string::npos)
|
if (dash != string::npos)
|
||||||
|
@ -575,12 +408,12 @@ static void queryInstSources(EvalState & state,
|
||||||
}
|
}
|
||||||
|
|
||||||
case srcAttrPath: {
|
case srcAttrPath: {
|
||||||
for (Strings::const_iterator i = args.begin();
|
foreach (Strings::const_iterator, i, args) {
|
||||||
i != args.end(); ++i)
|
Value v;
|
||||||
getDerivations(state,
|
findAlongAttrPath(state, *i, instSource.autoArgs,
|
||||||
findAlongAttrPath(state, *i, instSource.autoArgs,
|
loadSourceExpr(state, instSource.nixExprPath), v);
|
||||||
loadSourceExpr(state, instSource.nixExprPath)),
|
getDerivations(state, v, "", instSource.autoArgs, elems);
|
||||||
"", instSource.autoArgs, elems);
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1103,6 +936,7 @@ static void opQuery(Globals & globals,
|
||||||
|
|
||||||
foreach (vector<DrvInfo>::iterator, i, elems2) {
|
foreach (vector<DrvInfo>::iterator, i, elems2) {
|
||||||
try {
|
try {
|
||||||
|
startNest(nest, lvlDebug, format("outputting query result `%1%'") % i->attrPath);
|
||||||
|
|
||||||
/* For table output. */
|
/* For table output. */
|
||||||
Strings columns;
|
Strings columns;
|
||||||
|
@ -1473,7 +1307,7 @@ void run(Strings args)
|
||||||
|
|
||||||
op(globals, remaining, opFlags, opArgs);
|
op(globals, remaining, opFlags, opArgs);
|
||||||
|
|
||||||
printEvalStats(globals.state);
|
globals.state.printStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -130,6 +130,20 @@ void switchLink(Path link, Path target)
|
||||||
throw SysError(format("renaming `%1%' to `%2%'") % tmp % link);
|
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) : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define __PROFILES_H
|
#define __PROFILES_H
|
||||||
|
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
|
#include "pathlocks.hh"
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
@ -37,6 +38,20 @@ void deleteGeneration(const Path & profile, unsigned int gen);
|
||||||
|
|
||||||
void switchLink(Path link, Path target);
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
257
src/nix-env/user-env.cc
Normal file
257
src/nix-env/user-env.cc
Normal file
|
@ -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, ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
20
src/nix-env/user-env.hh
Normal file
20
src/nix-env/user-env.hh
Normal file
|
@ -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 */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,5 @@ nix-instantiate.o: help.txt.hh
|
||||||
../bin2c/bin2c helpText < $< > $@ || (rm $@ && exit 1)
|
../bin2c/bin2c helpText < $< > $@ || (rm $@ && exit 1)
|
||||||
|
|
||||||
AM_CXXFLAGS = \
|
AM_CXXFLAGS = \
|
||||||
${aterm_include} \
|
|
||||||
-I$(srcdir)/.. -I$(srcdir)/../libutil -I$(srcdir)/../libstore \
|
-I$(srcdir)/.. -I$(srcdir)/../libutil -I$(srcdir)/../libstore \
|
||||||
-I$(srcdir)/../libexpr -I$(srcdir)/../libmain -I../libexpr
|
-I$(srcdir)/../libexpr -I$(srcdir)/../libmain -I../libexpr
|
||||||
|
|
|
@ -7,11 +7,10 @@
|
||||||
#include "parser.hh"
|
#include "parser.hh"
|
||||||
#include "get-drvs.hh"
|
#include "get-drvs.hh"
|
||||||
#include "attr-path.hh"
|
#include "attr-path.hh"
|
||||||
#include "expr-to-xml.hh"
|
#include "value-to-xml.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "common-opts.hh"
|
#include "common-opts.hh"
|
||||||
#include "aterm.hh"
|
|
||||||
#include "help.txt.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"));
|
startNest(nest, lvlTalkative, format("parsing standard input"));
|
||||||
string s, s2;
|
string s, s2;
|
||||||
|
@ -38,47 +37,41 @@ static int rootNr = 0;
|
||||||
static bool indirectRoot = false;
|
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,
|
void processExpr(EvalState & state, const Strings & attrPaths,
|
||||||
bool parseOnly, bool strict, const ATermMap & autoArgs,
|
bool parseOnly, bool strict, const Bindings & autoArgs,
|
||||||
bool evalOnly, bool xmlOutput, bool location, Expr e)
|
bool evalOnly, bool xmlOutput, bool location, Expr * e)
|
||||||
{
|
{
|
||||||
for (Strings::const_iterator i = attrPaths.begin(); i != attrPaths.end(); ++i) {
|
if (parseOnly)
|
||||||
Expr e2 = findAlongAttrPath(state, *i, autoArgs, e);
|
std::cout << format("%1%\n") % *e;
|
||||||
if (!parseOnly)
|
else
|
||||||
if (strict)
|
foreach (Strings::const_iterator, i, attrPaths) {
|
||||||
e2 = strictEvalExpr(state, e2);
|
Value v;
|
||||||
else
|
findAlongAttrPath(state, *i, autoArgs, e, v);
|
||||||
e2 = evalExpr(state, e2);
|
state.forceValue(v);
|
||||||
printResult(state, e2, evalOnly, xmlOutput, location, autoArgs);
|
|
||||||
}
|
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 xmlOutputSourceLocation = true;
|
||||||
bool strict = false;
|
bool strict = false;
|
||||||
Strings attrPaths;
|
Strings attrPaths;
|
||||||
ATermMap autoArgs(128);
|
Bindings autoArgs;
|
||||||
|
|
||||||
for (Strings::iterator i = args.begin();
|
for (Strings::iterator i = args.begin(); i != args.end(); ) {
|
||||||
i != args.end(); )
|
|
||||||
{
|
|
||||||
string arg = *i++;
|
string arg = *i++;
|
||||||
|
|
||||||
if (arg == "-")
|
if (arg == "-")
|
||||||
|
@ -141,21 +132,19 @@ void run(Strings args)
|
||||||
store = openStore();
|
store = openStore();
|
||||||
|
|
||||||
if (readStdin) {
|
if (readStdin) {
|
||||||
Expr e = parseStdin(state);
|
Expr * e = parseStdin(state);
|
||||||
processExpr(state, attrPaths, parseOnly, strict, autoArgs,
|
processExpr(state, attrPaths, parseOnly, strict, autoArgs,
|
||||||
evalOnly, xmlOutput, xmlOutputSourceLocation, e);
|
evalOnly, xmlOutput, xmlOutputSourceLocation, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Strings::iterator i = files.begin();
|
foreach (Strings::iterator, i, files) {
|
||||||
i != files.end(); i++)
|
|
||||||
{
|
|
||||||
Path path = absPath(*i);
|
Path path = absPath(*i);
|
||||||
Expr e = parseExprFromFile(state, path);
|
Expr * e = parseExprFromFile(state, path);
|
||||||
processExpr(state, attrPaths, parseOnly, strict, autoArgs,
|
processExpr(state, attrPaths, parseOnly, strict, autoArgs,
|
||||||
evalOnly, xmlOutput, xmlOutputSourceLocation, e);
|
evalOnly, xmlOutput, xmlOutputSourceLocation, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
printEvalStats(state);
|
state.printStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
-e "s^@xmllint\@^$(xmllint)^g" \
|
-e "s^@xmllint\@^$(xmllint)^g" \
|
||||||
-e "s^@xmlflags\@^$(xmlflags)^g" \
|
-e "s^@xmlflags\@^$(xmlflags)^g" \
|
||||||
-e "s^@xsltproc\@^$(xsltproc)^g" \
|
-e "s^@xsltproc\@^$(xsltproc)^g" \
|
||||||
-e "s^@aterm_bin\@^$(aterm_bin)^g" \
|
|
||||||
-e "s^@sqlite_bin\@^$(sqlite_bin)^g" \
|
-e "s^@sqlite_bin\@^$(sqlite_bin)^g" \
|
||||||
-e "s^@version\@^$(VERSION)^g" \
|
-e "s^@version\@^$(VERSION)^g" \
|
||||||
-e "s^@testPath\@^$(coreutils):$$(dirname $$(type -P expr))^g" \
|
-e "s^@testPath\@^$(coreutils):$$(dirname $$(type -P expr))^g" \
|
||||||
|
|
|
@ -30,7 +30,6 @@ export REAL_STORE_DIR=@storedir@
|
||||||
export NIX_BUILD_HOOK=
|
export NIX_BUILD_HOOK=
|
||||||
export PERL=perl
|
export PERL=perl
|
||||||
export TOP=$(pwd)/..
|
export TOP=$(pwd)/..
|
||||||
export aterm_bin=@aterm_bin@
|
|
||||||
export bzip2_bin_test="@bzip2_bin_test@"
|
export bzip2_bin_test="@bzip2_bin_test@"
|
||||||
if test "${bzip2_bin_test:0:1}" != "/"; then
|
if test "${bzip2_bin_test:0:1}" != "/"; then
|
||||||
bzip2_bin_test=`pwd`/${bzip2_bin_test}
|
bzip2_bin_test=`pwd`/${bzip2_bin_test}
|
||||||
|
@ -42,10 +41,6 @@ export xsltproc="@xsltproc@"
|
||||||
export sqlite3="@sqlite_bin@/bin/sqlite3"
|
export sqlite3="@sqlite_bin@/bin/sqlite3"
|
||||||
export SHELL="@shell@"
|
export SHELL="@shell@"
|
||||||
|
|
||||||
# Hack to get "atdiff" to run on Cygwin (Windows looks for
|
|
||||||
# DLLs in $PATH).
|
|
||||||
export PATH=$aterm_bin/../lib:$PATH
|
|
||||||
|
|
||||||
export version=@version@
|
export version=@version@
|
||||||
export system=@system@
|
export system=@system@
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ let {
|
||||||
|
|
||||||
input2 = mkDerivation {
|
input2 = mkDerivation {
|
||||||
name = "dependencies-input-2";
|
name = "dependencies-input-2";
|
||||||
builder = ./. ~ "dependencies.builder2.sh";
|
builder = ./dependencies.builder2.sh;
|
||||||
};
|
};
|
||||||
|
|
||||||
body = mkDerivation {
|
body = mkDerivation {
|
||||||
|
|
|
@ -16,14 +16,10 @@ done
|
||||||
for i in lang/parse-okay-*.nix; do
|
for i in lang/parse-okay-*.nix; do
|
||||||
echo "parsing $i (should succeed)";
|
echo "parsing $i (should succeed)";
|
||||||
i=$(basename $i .nix)
|
i=$(basename $i .nix)
|
||||||
if ! $nixinstantiate --parse-only - < lang/$i.nix > lang/$i.ast; then
|
if ! $nixinstantiate --parse-only - < lang/$i.nix > lang/$i.out; then
|
||||||
echo "FAIL: $i should parse"
|
echo "FAIL: $i should parse"
|
||||||
fail=1
|
fail=1
|
||||||
fi
|
fi
|
||||||
if ! $aterm_bin/atdiff lang/$i.ast lang/$i.exp; then
|
|
||||||
echo "FAIL: parse tree of $i not as expected"
|
|
||||||
fail=1
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
|
|
||||||
for i in lang/eval-fail-*.nix; do
|
for i in lang/eval-fail-*.nix; do
|
||||||
|
@ -44,10 +40,10 @@ for i in lang/eval-okay-*.nix; do
|
||||||
if test -e lang/$i.flags; then
|
if test -e lang/$i.flags; then
|
||||||
flags=$(cat lang/$i.flags)
|
flags=$(cat lang/$i.flags)
|
||||||
fi
|
fi
|
||||||
if ! $nixinstantiate $flags --eval-only lang/$i.nix > lang/$i.out; then
|
if ! $nixinstantiate $flags --eval-only --strict lang/$i.nix > lang/$i.out; then
|
||||||
echo "FAIL: $i should evaluate"
|
echo "FAIL: $i should evaluate"
|
||||||
fail=1
|
fail=1
|
||||||
elif ! $aterm_bin/atdiff lang/$i.out lang/$i.exp; then
|
elif ! diff lang/$i.out lang/$i.exp; then
|
||||||
echo "FAIL: evaluation result of $i not as expected"
|
echo "FAIL: evaluation result of $i not as expected"
|
||||||
fail=1
|
fail=1
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Int(1275)
|
1275
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("newxfoonewxy",[])
|
"newxfoonewxy"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Int(987)
|
987
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Int(987)
|
987
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("foo 22 80 itchyxac",[])
|
"foo 22 80 itchyxac"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("xyzzy!xyzzy!foobar",[])
|
"xyzzy!xyzzy!foobar"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Path("/foo")
|
/foo
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
List([Int(1),Int(2),Int(3),Int(4),Int(5),Int(6),Int(7),Int(8),Int(9)])
|
[ 1 2 3 4 5 6 7 8 9 ]
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("foo eval-okay-context.nix bar",[])
|
"foo eval-okay-context.nix bar"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("ab",[])
|
"ab"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("1234567",[])
|
"1234567"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("foobar",[])
|
"foobar"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Int(3)
|
3
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("This is an indented multi-line string\nliteral. An amount of whitespace at\nthe start of each line matching the minimum\nindentation of all lines in the string\nliteral together will be removed. Thus,\nin this case four spaces will be\nstripped from each line, even though\n THIS LINE is indented six spaces.\n\nAlso, empty lines don't count in the\ndetermination of the indentation level (the\nprevious empty line has indentation 0, but\nit doesn't matter).\nIf the string starts with whitespace\n followed by a newline, it's stripped, but\n that's not the case here. Two spaces are\n stripped because of the \" \" at the start. \nThis line is indented\na bit further.\nAnti-quotations, like so, are\nalso allowed.\n The \\ is not special here.\n' can be followed by any character except another ', e.g. 'x'.\nLikewise for $, e.g. $$ or $varName.\nBut ' followed by ' is special, as is $ followed by {.\nIf you want them, use anti-quotations: '', ${.\n Tabs are not interpreted as whitespace (since we can't guess\n what tab settings are intended), so don't use them.\n\tThis line starts with a space and a tab, so only one\n space will be stripped from each line.\nAlso note that if the last line (just before the closing ' ')\nconsists only of whitespace, it's ignored. But here there is\nsome non-whitespace stuff, so the line isn't removed. \nThis shows a hacky way to preserve an empty line after the start.\nBut there's no reason to do so: you could just repeat the empty\nline.\n Similarly you can force an indentation level,\n in this case to 2 spaces. This works because the anti-quote\n is significant (not whitespace).\nstart on network-interfaces\n\nstart script\n\n rm -f /var/run/opengl-driver\n ln -sf 123 /var/run/opengl-driver\n\n rm -f /var/log/slim.log\n \nend script\n\nenv SLIM_CFGFILE=abc\nenv SLIM_THEMESDIR=def\nenv FONTCONFIG_FILE=/etc/fonts/fonts.conf \t\t\t\t# !!! cleanup\nenv XKB_BINDIR=foo/bin \t\t\t\t# Needed for the Xkb extension.\nenv LD_LIBRARY_PATH=libX11/lib:libXext/lib:/usr/lib/ # related to xorg-sys-opengl - needed to load libglx for (AI)GLX support (for compiz)\n\nenv XORG_DRI_DRIVER_PATH=nvidiaDrivers/X11R6/lib/modules/drivers/ \n\nexec slim/bin/slim\nEscaping of ' followed by ': ''\nEscaping of $ followed by {: ${\nAnd finally to interpret \\n etc. as in a string: \n, \r, \t.\nfoo\n'bla'\nbar\n",[])
|
"This is an indented multi-line string\nliteral. An amount of whitespace at\nthe start of each line matching the minimum\nindentation of all lines in the string\nliteral together will be removed. Thus,\nin this case four spaces will be\nstripped from each line, even though\n THIS LINE is indented six spaces.\n\nAlso, empty lines don't count in the\ndetermination of the indentation level (the\nprevious empty line has indentation 0, but\nit doesn't matter).\nIf the string starts with whitespace\n followed by a newline, it's stripped, but\n that's not the case here. Two spaces are\n stripped because of the \" \" at the start. \nThis line is indented\na bit further.\nAnti-quotations, like so, are\nalso allowed.\n The \\ is not special here.\n' can be followed by any character except another ', e.g. 'x'.\nLikewise for $, e.g. $$ or $varName.\nBut ' followed by ' is special, as is $ followed by {.\nIf you want them, use anti-quotations: '', ${.\n Tabs are not interpreted as whitespace (since we can't guess\n what tab settings are intended), so don't use them.\n\tThis line starts with a space and a tab, so only one\n space will be stripped from each line.\nAlso note that if the last line (just before the closing ' ')\nconsists only of whitespace, it's ignored. But here there is\nsome non-whitespace stuff, so the line isn't removed. \nThis shows a hacky way to preserve an empty line after the start.\nBut there's no reason to do so: you could just repeat the empty\nline.\n Similarly you can force an indentation level,\n in this case to 2 spaces. This works because the anti-quote\n is significant (not whitespace).\nstart on network-interfaces\n\nstart script\n\n rm -f /var/run/opengl-driver\n ln -sf 123 /var/run/opengl-driver\n\n rm -f /var/log/slim.log\n \nend script\n\nenv SLIM_CFGFILE=abc\nenv SLIM_THEMESDIR=def\nenv FONTCONFIG_FILE=/etc/fonts/fonts.conf \t\t\t\t# !!! cleanup\nenv XKB_BINDIR=foo/bin \t\t\t\t# Needed for the Xkb extension.\nenv LD_LIBRARY_PATH=libX11/lib:libXext/lib:/usr/lib/ # related to xorg-sys-opengl - needed to load libglx for (AI)GLX support (for compiz)\n\nenv XORG_DRI_DRIVER_PATH=nvidiaDrivers/X11R6/lib/modules/drivers/ \n\nexec slim/bin/slim\nEscaping of ' followed by ': ''\nEscaping of $ followed by {: ${\nAnd finally to interpret \\n etc. as in a string: \n, \r, \t.\nfoo\n'bla'\nbar\n"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("foobar",[])
|
"foobar"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("foobarblatest",[])
|
"foobarblatest"
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
List([Attrs([Bind("a",Str("A",[]),NoPos),Bind("b",Str("B",[]),NoPos)]),Attrs([Bind("a",Str("A",[]),NoPos),Bind("b",Str("B",[]),NoPos)])])
|
|
1
tests/lang/eval-okay-listtoattrs.exp
Normal file
1
tests/lang/eval-okay-listtoattrs.exp
Normal file
|
@ -0,0 +1 @@
|
||||||
|
"AA"
|
|
@ -1,8 +1,10 @@
|
||||||
# this test shows how to use listToAttrs and that evaluation is still lazy (throw isn't called)
|
# this test shows how to use listToAttrs and that evaluation is still lazy (throw isn't called)
|
||||||
|
with import ./lib.nix;
|
||||||
|
|
||||||
let
|
let
|
||||||
asi = attr: value : { inherit attr value; };
|
asi = name: value : { inherit name value; };
|
||||||
list = [ ( asi "a" "A" ) ( asi "b" "B" ) ];
|
list = [ ( asi "a" "A" ) ( asi "b" "B" ) ];
|
||||||
a = builtins.listToAttrs list;
|
a = builtins.listToAttrs list;
|
||||||
b = builtins.listToAttrs ( list ++ list );
|
b = builtins.listToAttrs ( list ++ list );
|
||||||
r = builtins.listToAttrs [ (asi "result" [ a b ]) ( asi "throw" (throw "this should not be thrown")) ];
|
r = builtins.listToAttrs [ (asi "result" [ a b ]) ( asi "throw" (throw "this should not be thrown")) ];
|
||||||
in r.result
|
in concat (map (x: x.a) r.result)
|
|
@ -1 +1 @@
|
||||||
Int(1)
|
1
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("foobarblabarxyzzybar",[])
|
"foobarblabarxyzzybar"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("xyzzyfoobar",[])
|
"xyzzyfoobar"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Bool(True)
|
true
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("abcxyzDDDDEFghijk",[])
|
"abcxyzDDDDEFijk"
|
||||||
|
|
|
@ -6,8 +6,6 @@ let
|
||||||
|
|
||||||
h = {x ? "d", y ? x, z ? args.x}@args: x + y + z;
|
h = {x ? "d", y ? x, z ? args.x}@args: x + y + z;
|
||||||
|
|
||||||
i = args@args2: args.x + args2.y;
|
|
||||||
|
|
||||||
j = {x, y, z, ...}: x + y + z;
|
j = {x, y, z, ...}: x + y + z;
|
||||||
|
|
||||||
in
|
in
|
||||||
|
@ -15,5 +13,4 @@ in
|
||||||
g {x = "x"; y = "y"; z = "z";} +
|
g {x = "x"; y = "y"; z = "z";} +
|
||||||
h {x = "D";} +
|
h {x = "D";} +
|
||||||
h {x = "D"; y = "E"; z = "F";} +
|
h {x = "D"; y = "E"; z = "F";} +
|
||||||
i {x = "g"; y = "h";} +
|
|
||||||
j {x = "i"; y = "j"; z = "k"; bla = "bla"; foo = "bar";}
|
j {x = "i"; y = "j"; z = "k"; bla = "bla"; foo = "bar";}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("builtins.readFile ./eval-okay-readfile.nix\n",[])
|
"builtins.readFile ./eval-okay-readfile.nix\n"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Int(456)
|
456
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Int(3)
|
3
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Int(1)
|
1
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Int(4)
|
4
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("ccdd",[])
|
"ccdd"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("ccdd",[])
|
"ccdd"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Int(1)
|
1
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("foobar/a/b/c/d/foo/xyzzy/foo.txt/../foo/x/yescape: \"quote\" \n \\end\nof\nlinefoobarblaatfoo$bar",[])
|
"foobar/a/b/c/d/foo/xyzzy/foo.txt/../foo/x/yescape: \"quote\" \n \\end\nof\nlinefoobarblaatfoo$bar"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Str("ooxfoobarybarzobaabb",[])
|
"ooxfoobarybarzobaabb"
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Str("<?xml version='1.0' encoding='utf-8'?>\n<expr>\n <list>\n <string value=\"ab\" />\n <int value=\"10\" />\n <attrs>\n <attr name=\"x\">\n <string value=\"x\" />\n </attr>\n <attr name=\"y\">\n <string value=\"x\" />\n </attr>\n </attrs>\n </list>\n</expr>\n",[])
|
|
|
@ -1 +1 @@
|
||||||
Str("<?xml version='1.0' encoding='utf-8'?>\n<expr>\n <attrs>\n <attr name=\"a\">\n <string value=\"s\" />\n </attr>\n </attrs>\n</expr>\n",[])
|
"<?xml version='1.0' encoding='utf-8'?>\n<expr>\n <attrs>\n <attr name=\"a\">\n <string value=\"s\" />\n </attr>\n </attrs>\n</expr>\n"
|
||||||
|
|
1
tests/lang/eval-okay-toxml2.exp
Normal file
1
tests/lang/eval-okay-toxml2.exp
Normal file
|
@ -0,0 +1 @@
|
||||||
|
"<?xml version='1.0' encoding='utf-8'?>\n<expr>\n <list>\n <string value=\"ab\" />\n <int value=\"10\" />\n <attrs>\n <attr name=\"x\">\n <string value=\"x\" />\n </attr>\n <attr name=\"y\">\n <string value=\"x\" />\n </attr>\n </attrs>\n </list>\n</expr>\n"
|
1
tests/lang/eval-okay-tryeval.exp
Normal file
1
tests/lang/eval-okay-tryeval.exp
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{ x = { success = true; value = "x"; }; y = { success = false; value = false; }; z = { success = false; value = false; }; }
|
5
tests/lang/eval-okay-tryeval.nix
Normal file
5
tests/lang/eval-okay-tryeval.nix
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
x = builtins.tryEval "x";
|
||||||
|
y = builtins.tryEval (assert false; "y");
|
||||||
|
z = builtins.tryEval (throw "bla");
|
||||||
|
}
|
|
@ -1 +1 @@
|
||||||
Bool(True)
|
true
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue