update fork from master

This commit is contained in:
gb 2018-07-28 13:23:23 -04:00
commit ea3b675f28
34 changed files with 1931 additions and 728 deletions

View file

@ -1,7 +1,17 @@
/.history
/Setup
/dist/
/nix-test-eval*
/nix/
TAGS
ctags
dist-newstyle
result
.cabal-sandbox/
.git
Dockerfile
Makefile
README.md
cabal.sandbox.config
.cabal-sandbox/
hnix.cabal

11
.gitignore vendored
View file

@ -1,9 +1,12 @@
/Setup
/dist/
**/*~
**/#*
**/.#*
result
/.history
/Setup
/dist/
/nix-test-eval*
/nix/
TAGS
ctags
dist-newstyle
/.history
result

View file

@ -10,30 +10,27 @@ language: nix
git:
depth: 1
install:
- nix-shell --argstr compiler ghc822 --run true
- nix-shell --argstr compiler ghc842 --run true
env:
global:
- NIXPKGS_TESTS=yes
- MATCHING_TESTS=yes
matrix:
- GHCVERSION=ghc822 STRICT=true TRACING=false PROFILING=false
- GHCVERSION=ghc822 STRICT=true TRACING=true PROFILING=false
- GHCVERSION=ghc822 STRICT=true TRACING=true PROFILING=true
- GHCVERSION=ghc842 STRICT=false TRACING=false PROFILING=false
- GHCVERSION=ghc842 STRICT=false TRACING=true PROFILING=false
- GHCVERSION=ghc842 STRICT=false TRACING=true PROFILING=true
- GHCVERSION=ghc802 STRICT=false TRACING=false
- GHCVERSION=ghc802 STRICT=false TRACING=true
- GHCVERSION=ghc822 STRICT=true TRACING=false
- GHCVERSION=ghc822 STRICT=true TRACING=true
- GHCVERSION=ghc843 STRICT=false TRACING=false
- GHCVERSION=ghc843 STRICT=false TRACING=true
- GHCVERSION=ghcjs
matrix:
allow_failures:
- env: GHCVERSION=ghc842 STRICT=true
- env: GHCVERSION=ghcjs
exclude:
- env: GHCVERSION=ghc822 STRICT=false
script:
- nix-build --argstr compiler $GHCVERSION --arg doProfiling $PROFILING --arg doTracing $TRACING --arg doStrict $STRICT
- bash -xe build.sh
branches:
only:

View file

@ -11,6 +11,4 @@ RUN nix-env -f '<nixpkgs>' -i gnutar gzip && \
nix-shell -Q -j2 --run true
COPY . /tmp/build
RUN nix-env -f . -i hnix
CMD ["/root/.nix-profile/bin/hnix"]
RUN bash -xe build.sh

View file

@ -2,6 +2,7 @@
[![Build Status](https://api.travis-ci.org/haskell-nix/hnix.svg)](https://travis-ci.org/haskell-nix/hnix)
[![Chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/haskell-nix/Lobby)
<sup>([Hackage Matrix Builder](https://matrix.hackage.haskell.org/package/hnix))</sup>
Haskell parser, evaluator and type checker for the Nix language.
@ -21,7 +22,9 @@ $ cabal configure --enable-tests
$ cabal build
$ cabal test
# To run all of the tests, which takes up to a minute:
$ LANGUAGE_TESTS=yes NIXPKGS_TESTS=yes cabal test
$ env ALL_TESTS=yes cabal test
# To run only specific tests (see `tests/Main.hs` for a list)
$ env NIXPKGS_TESTS=yes PRETTY_TESTS=yes cabal test
$ ./dist/build/hnix/hnix --help
```
@ -44,8 +47,8 @@ To build `hnix` for debugging, and with full tracing output and stack traces,
use:
```
$ nix-shell --arg doProfiling true
$ cabal configure --enable-tests --enable-profiling --flags=tracing
$ nix-shell
$ cabal configure --enable-tests --enable-profiling --flags=profiling --flags=tracing
$ cabal build
$ ./dist/build/hnix/hnix -v5 --trace <args> +RTS -xc
```
@ -69,12 +72,23 @@ $ cabal bench
To build `hnix` with profiling enabled:
```
$ nix-shell --arg doProfiling true
$ cabal configure --enable-tests --enable-profiling
$ nix-shell
$ cabal configure --enable-tests --enable-profiling --flags=profiling
$ cabal build
$ ./dist/build/hnix/hnix <args> +RTS -p
```
## Building with GHCJS
From the project root directory, run:
```
$ NIX_CONF_DIR=$PWD/ghcjs nix-build ghcjs
```
This will build an `hnix` library that can be linked to your GHCJS
application.
## How you can help
If you're looking for a way to help out, try taking a look

17
build.sh Executable file
View file

@ -0,0 +1,17 @@
#!/bin/bash -xe
set -euo pipefail
IFS=$'\n\t'
GHCVERSION=${GHCVERSION:-ghc822}
STRICT=${STRICT:-false}
TRACING=${TRACING:-false}
if [ "$GHCVERSION" = "ghcjs" ]; then
nix-build --substituters 'https://nixcache.reflex-frp.org?trusted=1' ghcjs
else
nix-build \
--argstr compiler $GHCVERSION \
--arg doTracing $TRACING \
--arg doStrict $STRICT
fi

View file

@ -1,71 +1,88 @@
{ compiler ? "ghc822" # "ghc842" also works
, doProfiling ? false
{ compiler ? "ghc822"
, doBenchmark ? false
, doTracing ? false
, doStrict ? false
, rev ? "255a833e841628c0b834575664eae373e28cdc27"
, sha256 ? "022xm1pf4fpjjy69g7qz6rpqnwpjcy1l0vj49m8xmgn553cs42ch"
# , nixpkgs ? import ((import <nixpkgs> {}).fetchFromGitHub {
# owner = "NixOS"; repo = "nixpkgs"; inherit rev sha256; }) {
, nixpkgs ? import (builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/${rev}.tar.gz";
inherit sha256; }) {
config.allowUnfree = true;
config.allowBroken = false;
}
, rev ? "49bdae006e66e70ad3245a463edc01b5749250d3"
, sha256 ? "1ijsifmap47nfzg0spny94lmj66y3x3x8i6vs471bnjamka3dx8p"
, pkgs ?
if builtins.compareVersions builtins.nixVersion "2.0" < 0
then abort "hnix requires at least nix 2.0"
else import (builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/${rev}.tar.gz";
inherit sha256; }) {
config.allowUnfree = true;
config.allowBroken = false;
}
, returnShellEnv ? pkgs.lib.inNixShell
, mkDerivation ? null
}:
let inherit (nixpkgs) pkgs;
haskellPackages = pkgs.haskell.packages.${compiler}.override {
overrides = with pkgs.haskell.lib; self: super: rec {
serialise = dontCheck super.serialise;
compact =
if compiler == "ghc842"
then doJailbreak super.compact
else super.compact;
ghc-datasize =
if doProfiling
then null
else pkgs.haskell.lib.overrideCabal super.ghc-datasize (attrs: {
enableLibraryProfiling = false;
enableExecutableProfiling = false;
});
ghc-heap-view =
if doProfiling
then null
else pkgs.haskell.lib.overrideCabal super.ghc-heap-view (attrs: {
enableLibraryProfiling = false;
enableExecutableProfiling = false;
});
};
};
let haskellPackages = pkgs.haskell.packages.${compiler};
in haskellPackages.developPackage {
root = ./.;
source-overrides = {
};
overrides = with pkgs.haskell.lib; self: super: {
megaparsec = super.megaparsec_6_5_0;
}
//
(if compiler == "ghc802"
then {
concurrent-output = doJailbreak super.concurrent-output;
}
else {})
//
(if compiler == "ghcjs" then {} else
{
cryptohash-md5 = doJailbreak super.cryptohash-md5;
cryptohash-sha1 = doJailbreak super.cryptohash-sha1;
cryptohash-sha256 = doJailbreak super.cryptohash-sha256;
cryptohash-sha512 = doJailbreak super.cryptohash-sha512;
serialise = dontCheck super.serialise;
ghc-datasize =
overrideCabal super.ghc-datasize (attrs: {
enableLibraryProfiling = false;
enableExecutableProfiling = false;
});
ghc-heap-view =
overrideCabal super.ghc-heap-view (attrs: {
enableLibraryProfiling = false;
enableExecutableProfiling = false;
});
});
source-overrides =
if compiler == "ghc802"
then {
lens-family-core = "1.2.1";
lens-family = "1.2.1";
hspec-discover = "2.5.5";
}
else {};
modifier = drv: pkgs.haskell.lib.overrideCabal drv (attrs: {
testHaskellDepends = attrs.testHaskellDepends ++
[
pkgs.nix
haskellPackages.hpack
# haskellPackages.cabal-install
];
[ pkgs.nix
enableLibraryProfiling = doProfiling;
enableExecutableProfiling = doProfiling;
# Use the same version of hpack no matter what the compiler version
# is, so that we know exactly what the contents of the generated
# .cabal file will be. Otherwise, Travis may error out claiming that
# the cabal file needs to be updated because the result is different
# that the version we committed to Git.
pkgs.haskell.packages.ghc822.hpack
pkgs.haskell.packages.ghc822.criterion
];
inherit doBenchmark;
configureFlags =
pkgs.stdenv.lib.optional doTracing "--flags=tracing"
++ pkgs.stdenv.lib.optional doProfiling "--flags=profiling"
++ pkgs.stdenv.lib.optional doStrict "--ghc-options=-Werror";
pkgs.stdenv.lib.optional doTracing "--flags=tracing"
++ pkgs.stdenv.lib.optional doStrict "--ghc-options=-Werror";
});
inherit returnShellEnv;
}

71
ghcjs/default.nix Normal file
View file

@ -0,0 +1,71 @@
{ rpRef ? "ea3c9a1536a987916502701fb6d319a880fdec96" }:
let rp = builtins.fetchTarball "https://github.com/reflex-frp/reflex-platform/archive/${rpRef}.tar.gz";
in
(import rp {}).project ({ pkgs, ... }:
with pkgs.haskell.lib; {
name = "hnix-ghcjs";
overrides = self: super:
let guardGhcjs = p: if self.ghc.isGhcjs or false then null else p;
in {
cryptohash-md5 = guardGhcjs super.cryptohash-md5;
cryptohash-sha1 = guardGhcjs super.cryptohash-sha1;
cryptohash-sha256 = guardGhcjs super.cryptohash-sha256;
cryptohash-sha512 = guardGhcjs super.cryptohash-sha512;
hashing = super.hashing;
haskeline = guardGhcjs super.haskeline;
serialise = doJailbreak super.serialise;
Glob = guardGhcjs super.Glob;
criterion = guardGhcjs super.criterion;
pretty-show = guardGhcjs super.pretty;
repline = guardGhcjs super.repline;
tasty = guardGhcjs super.tasty;
tasty-hunit = guardGhcjs super.tasty;
tasty-th = guardGhcjs super.tasty;
unix = guardGhcjs super.unix;
interpolate = self.callCabal2nix "interpolate" (pkgs.fetchFromGitHub {
owner = "sol";
repo = "interpolate";
rev = "2d654444365805458e0310d461b3ecd2826977ff";
sha256 = "01g88j6qv33r6j4yl6yisr9sk3kcvgp81z6qmhr94ka8z45raii9";
}) {};
lens-family-th = self.callCabal2nix "lens-family-th" (pkgs.fetchFromGitHub {
owner = "DanBurton";
repo = "lens-family-th";
rev = "483be4d5b53cc6253e3926623c3aa9334c53debc";
sha256 = "099dp4f1wwarvhwgnm4nhymnnwlqgrsgrfd8ch6dwmy6myzv2dij";
}) {};
megaparsec = dontCheck (self.callCabal2nix "megaparsec" (pkgs.fetchFromGitHub {
owner = "mrkkrp";
repo = "megaparsec";
rev = "7b271a5edc1af59fa435a705349310cfdeaaa7e9";
sha256 = "0415z18gl8dgms57rxzp870dpz7rcqvy008wrw5r22xw8qq0s13c";
}) {});
parser-combinators = self.callCabal2nix "parser-combinators" (pkgs.fetchFromGitHub {
owner = "mrkkrp";
repo = "parser-combinators";
rev = "dd6599224fe7eb224477ef8e9269602fb6b79fe0";
sha256 = "11cpfzlb6vl0r5i7vbhp147cfxds248fm5xq8pwxk92d1f5g9pxm";
}) {};
# Should be callHackage, but it gives an error "found zero or more than one cabal file"
# unordered-containers = pkgs.haskellPackages.callHackage "unordered-containers" "0.2.9.0" {};
unordered-containers = self.callCabal2nix "unordered-containers" (pkgs.fetchFromGitHub {
owner = "tibbe";
repo = "unordered-containers";
rev = "0a6b84ec103e28b73458f385ef846a7e2d3ea42f";
sha256 = "128q8k4py2wr1v0gmyvqvzikk6sksl9aqj0lxzf46763lis8x9my";
}) {};
};
packages = {
hnix = ../.;
};
shells = {
# ghc = [ "hnix" ];
ghcjs = [ "hnix" ];
};
})

2
ghcjs/nix.conf Normal file
View file

@ -0,0 +1,2 @@
binary-caches = https://nixcache.reflex-frp.org https://cache.nixos.org/
binary-cache-public-keys = ryantrinkle.com-1:JJiAKaRv9mWgpVAz8dwewnZe0AzzEAzPkagE9SP5NWI= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=

View file

@ -2,15 +2,15 @@
--
-- see: https://github.com/sol/hpack
--
-- hash: ee15abd2881ed4220e92355a685896317f421697cd97d7b5d384ec57662c8d44
-- hash: ba0f61f8a049f6970ff03fd924cfed6fc1251eceb90547c1be8051226c453632
name: hnix
version: 0.5.0
version: 0.5.2
synopsis: Haskell implementation of the Nix language
description: Haskell implementation of the Nix language.
category: System, Data, Nix
homepage: https://github.com/jwiegley/hnix#readme
bug-reports: https://github.com/jwiegley/hnix/issues
homepage: https://github.com/haskell-nix/hnix#readme
bug-reports: https://github.com/haskell-nix/hnix/issues
author: John Wiegley
maintainer: johnw@newartisans.com
license: BSD3
@ -19,11 +19,415 @@ build-type: Simple
cabal-version: >= 1.10
extra-source-files:
data/let-comments-multiline.nix
data/let-comments.nix
data/let.nix
data/nix/bootstrap.sh
data/nix/config/config.guess
data/nix/config/config.sub
data/nix/config/install-sh
data/nix/configure.ac
data/nix/COPYING
data/nix/corepkgs/buildenv.nix
data/nix/corepkgs/config.nix.in
data/nix/corepkgs/derivation.nix
data/nix/corepkgs/fetchurl.nix
data/nix/corepkgs/imported-drv-to-derivation.nix
data/nix/corepkgs/local.mk
data/nix/corepkgs/unpack-channel.nix
data/nix/local.mk
data/nix/maintainers/upload-release.pl
data/nix/Makefile
data/nix/Makefile.config.in
data/nix/mk/clean.mk
data/nix/mk/dist.mk
data/nix/mk/functions.mk
data/nix/mk/install.mk
data/nix/mk/jars.mk
data/nix/mk/lib.mk
data/nix/mk/libraries.mk
data/nix/mk/patterns.mk
data/nix/mk/programs.mk
data/nix/mk/README.md
data/nix/mk/templates.mk
data/nix/mk/tests.mk
data/nix/mk/tracing.mk
data/nix/nix.spec.in
data/nix/perl/configure.ac
data/nix/perl/lib/Nix/Config.pm.in
data/nix/perl/lib/Nix/CopyClosure.pm
data/nix/perl/lib/Nix/Manifest.pm
data/nix/perl/lib/Nix/SSH.pm
data/nix/perl/lib/Nix/Store.pm
data/nix/perl/lib/Nix/Store.xs
data/nix/perl/lib/Nix/Utils.pm
data/nix/perl/local.mk
data/nix/perl/Makefile
data/nix/perl/Makefile.config.in
data/nix/perl/MANIFEST
data/nix/README.md
data/nix/release-common.nix
data/nix/release.nix
data/nix/scripts/install-darwin-multi-user.sh
data/nix/scripts/install-multi-user.sh
data/nix/scripts/install-nix-from-closure.sh
data/nix/scripts/install-systemd-multi-user.sh
data/nix/scripts/local.mk
data/nix/scripts/nix-http-export.cgi.in
data/nix/scripts/nix-profile-daemon.sh.in
data/nix/scripts/nix-profile.sh.in
data/nix/scripts/nix-reduce-build.in
data/nix/shell.nix
data/nix/tests/add.sh
data/nix/tests/binary-cache.sh
data/nix/tests/brotli.sh
data/nix/tests/build-dry.sh
data/nix/tests/build-hook.nix
data/nix/tests/build-remote.sh
data/nix/tests/case-hack.sh
data/nix/tests/case.nar
data/nix/tests/check-refs.nix
data/nix/tests/check-refs.sh
data/nix/tests/check-reqs.nix
data/nix/tests/check-reqs.sh
data/nix/tests/check.nix
data/nix/tests/check.sh
data/nix/tests/common.sh.in
data/nix/tests/config.nix
data/nix/tests/dependencies.builder0.sh
data/nix/tests/dependencies.builder1.sh
data/nix/tests/dependencies.builder2.sh
data/nix/tests/dependencies.nix
data/nix/tests/dependencies.sh
data/nix/tests/dump-db.sh
data/nix/tests/export-graph.nix
data/nix/tests/export-graph.sh
data/nix/tests/export.sh
data/nix/tests/fetchGit.sh
data/nix/tests/fetchMercurial.sh
data/nix/tests/fetchurl.sh
data/nix/tests/filter-source.nix
data/nix/tests/filter-source.sh
data/nix/tests/fixed.builder1.sh
data/nix/tests/fixed.builder2.sh
data/nix/tests/fixed.nix
data/nix/tests/fixed.sh
data/nix/tests/gc-concurrent.builder.sh
data/nix/tests/gc-concurrent.nix
data/nix/tests/gc-concurrent.sh
data/nix/tests/gc-concurrent2.builder.sh
data/nix/tests/gc-runtime.nix
data/nix/tests/gc-runtime.sh
data/nix/tests/gc.sh
data/nix/tests/hash-check.nix
data/nix/tests/hash.sh
data/nix/tests/import-derivation.nix
data/nix/tests/import-derivation.sh
data/nix/tests/init.sh
data/nix/tests/install-darwin.sh
data/nix/tests/lang.sh
data/nix/tests/lang/data
data/nix/tests/lang/dir1/a.nix
data/nix/tests/lang/dir2/a.nix
data/nix/tests/lang/dir2/b.nix
data/nix/tests/lang/dir3/a.nix
data/nix/tests/lang/dir3/b.nix
data/nix/tests/lang/dir3/c.nix
data/nix/tests/lang/dir4/a.nix
data/nix/tests/lang/dir4/c.nix
data/nix/tests/lang/eval-fail-abort.nix
data/nix/tests/lang/eval-fail-antiquoted-path.nix
data/nix/tests/lang/eval-fail-assert.nix
data/nix/tests/lang/eval-fail-bad-antiquote-1.nix
data/nix/tests/lang/eval-fail-bad-antiquote-2.nix
data/nix/tests/lang/eval-fail-bad-antiquote-3.nix
data/nix/tests/lang/eval-fail-blackhole.nix
data/nix/tests/lang/eval-fail-deepseq.nix
data/nix/tests/lang/eval-fail-missing-arg.nix
data/nix/tests/lang/eval-fail-path-slash.nix
data/nix/tests/lang/eval-fail-remove.nix
data/nix/tests/lang/eval-fail-scope-5.nix
data/nix/tests/lang/eval-fail-seq.nix
data/nix/tests/lang/eval-fail-substring.nix
data/nix/tests/lang/eval-fail-to-path.nix
data/nix/tests/lang/eval-fail-undeclared-arg.nix
data/nix/tests/lang/eval-okay-any-all.exp
data/nix/tests/lang/eval-okay-any-all.nix
data/nix/tests/lang/eval-okay-arithmetic.exp
data/nix/tests/lang/eval-okay-arithmetic.nix
data/nix/tests/lang/eval-okay-attrnames.exp
data/nix/tests/lang/eval-okay-attrnames.nix
data/nix/tests/lang/eval-okay-attrs.exp
data/nix/tests/lang/eval-okay-attrs.nix
data/nix/tests/lang/eval-okay-attrs2.exp
data/nix/tests/lang/eval-okay-attrs2.nix
data/nix/tests/lang/eval-okay-attrs3.exp
data/nix/tests/lang/eval-okay-attrs3.nix
data/nix/tests/lang/eval-okay-attrs4.exp
data/nix/tests/lang/eval-okay-attrs4.nix
data/nix/tests/lang/eval-okay-attrs5.exp
data/nix/tests/lang/eval-okay-attrs5.nix
data/nix/tests/lang/eval-okay-autoargs.exp
data/nix/tests/lang/eval-okay-autoargs.flags
data/nix/tests/lang/eval-okay-autoargs.nix
data/nix/tests/lang/eval-okay-backslash-newline-1.exp
data/nix/tests/lang/eval-okay-backslash-newline-1.nix
data/nix/tests/lang/eval-okay-backslash-newline-2.exp
data/nix/tests/lang/eval-okay-backslash-newline-2.nix
data/nix/tests/lang/eval-okay-builtins-add.exp
data/nix/tests/lang/eval-okay-builtins-add.nix
data/nix/tests/lang/eval-okay-builtins.exp
data/nix/tests/lang/eval-okay-builtins.nix
data/nix/tests/lang/eval-okay-callable-attrs.exp
data/nix/tests/lang/eval-okay-callable-attrs.nix
data/nix/tests/lang/eval-okay-catattrs.exp
data/nix/tests/lang/eval-okay-catattrs.nix
data/nix/tests/lang/eval-okay-closure.exp.xml
data/nix/tests/lang/eval-okay-closure.nix
data/nix/tests/lang/eval-okay-comments.exp
data/nix/tests/lang/eval-okay-comments.nix
data/nix/tests/lang/eval-okay-concat.exp
data/nix/tests/lang/eval-okay-concat.nix
data/nix/tests/lang/eval-okay-concatstringssep.exp
data/nix/tests/lang/eval-okay-concatstringssep.nix
data/nix/tests/lang/eval-okay-context.exp
data/nix/tests/lang/eval-okay-context.nix
data/nix/tests/lang/eval-okay-curpos.exp
data/nix/tests/lang/eval-okay-curpos.nix
data/nix/tests/lang/eval-okay-deepseq.exp
data/nix/tests/lang/eval-okay-deepseq.nix
data/nix/tests/lang/eval-okay-delayed-with-inherit.exp
data/nix/tests/lang/eval-okay-delayed-with-inherit.nix
data/nix/tests/lang/eval-okay-delayed-with.exp
data/nix/tests/lang/eval-okay-delayed-with.nix
data/nix/tests/lang/eval-okay-dynamic-attrs-2.exp
data/nix/tests/lang/eval-okay-dynamic-attrs-2.nix
data/nix/tests/lang/eval-okay-dynamic-attrs-bare.exp
data/nix/tests/lang/eval-okay-dynamic-attrs-bare.nix
data/nix/tests/lang/eval-okay-dynamic-attrs.exp
data/nix/tests/lang/eval-okay-dynamic-attrs.nix
data/nix/tests/lang/eval-okay-elem.exp
data/nix/tests/lang/eval-okay-elem.nix
data/nix/tests/lang/eval-okay-empty-args.exp
data/nix/tests/lang/eval-okay-empty-args.nix
data/nix/tests/lang/eval-okay-eq-derivations.exp
data/nix/tests/lang/eval-okay-eq-derivations.nix
data/nix/tests/lang/eval-okay-eq.exp.disabled
data/nix/tests/lang/eval-okay-eq.nix
data/nix/tests/lang/eval-okay-filter.exp
data/nix/tests/lang/eval-okay-filter.nix
data/nix/tests/lang/eval-okay-flatten.exp
data/nix/tests/lang/eval-okay-flatten.nix
data/nix/tests/lang/eval-okay-fromjson.exp
data/nix/tests/lang/eval-okay-fromjson.nix
data/nix/tests/lang/eval-okay-functionargs.exp.xml
data/nix/tests/lang/eval-okay-functionargs.nix
data/nix/tests/lang/eval-okay-getattrpos-undefined.exp
data/nix/tests/lang/eval-okay-getattrpos-undefined.nix
data/nix/tests/lang/eval-okay-getattrpos.exp
data/nix/tests/lang/eval-okay-getattrpos.nix
data/nix/tests/lang/eval-okay-getenv.exp
data/nix/tests/lang/eval-okay-getenv.nix
data/nix/tests/lang/eval-okay-hash.exp
data/nix/tests/lang/eval-okay-hash.nix
data/nix/tests/lang/eval-okay-if.exp
data/nix/tests/lang/eval-okay-if.nix
data/nix/tests/lang/eval-okay-import.exp
data/nix/tests/lang/eval-okay-import.nix
data/nix/tests/lang/eval-okay-ind-string.exp
data/nix/tests/lang/eval-okay-ind-string.nix
data/nix/tests/lang/eval-okay-let.exp
data/nix/tests/lang/eval-okay-let.nix
data/nix/tests/lang/eval-okay-list.exp
data/nix/tests/lang/eval-okay-list.nix
data/nix/tests/lang/eval-okay-listtoattrs.exp
data/nix/tests/lang/eval-okay-listtoattrs.nix
data/nix/tests/lang/eval-okay-logic.exp
data/nix/tests/lang/eval-okay-logic.nix
data/nix/tests/lang/eval-okay-map.exp
data/nix/tests/lang/eval-okay-map.nix
data/nix/tests/lang/eval-okay-nested-with.exp
data/nix/tests/lang/eval-okay-nested-with.nix
data/nix/tests/lang/eval-okay-new-let.exp
data/nix/tests/lang/eval-okay-new-let.nix
data/nix/tests/lang/eval-okay-null-dynamic-attrs.exp
data/nix/tests/lang/eval-okay-null-dynamic-attrs.nix
data/nix/tests/lang/eval-okay-overrides.exp
data/nix/tests/lang/eval-okay-overrides.nix
data/nix/tests/lang/eval-okay-partition.exp
data/nix/tests/lang/eval-okay-partition.nix
data/nix/tests/lang/eval-okay-path.nix
data/nix/tests/lang/eval-okay-pathexists.exp
data/nix/tests/lang/eval-okay-pathexists.nix
data/nix/tests/lang/eval-okay-patterns.exp
data/nix/tests/lang/eval-okay-patterns.nix
data/nix/tests/lang/eval-okay-readDir.exp
data/nix/tests/lang/eval-okay-readDir.nix
data/nix/tests/lang/eval-okay-readfile.exp
data/nix/tests/lang/eval-okay-readfile.nix
data/nix/tests/lang/eval-okay-redefine-builtin.exp
data/nix/tests/lang/eval-okay-redefine-builtin.nix
data/nix/tests/lang/eval-okay-regex-match.exp
data/nix/tests/lang/eval-okay-regex-match.nix
data/nix/tests/lang/eval-okay-regex-split.exp
data/nix/tests/lang/eval-okay-regex-split.nix
data/nix/tests/lang/eval-okay-remove.exp
data/nix/tests/lang/eval-okay-remove.nix
data/nix/tests/lang/eval-okay-replacestrings.exp
data/nix/tests/lang/eval-okay-replacestrings.nix
data/nix/tests/lang/eval-okay-scope-1.exp
data/nix/tests/lang/eval-okay-scope-1.nix
data/nix/tests/lang/eval-okay-scope-2.exp
data/nix/tests/lang/eval-okay-scope-2.nix
data/nix/tests/lang/eval-okay-scope-3.exp
data/nix/tests/lang/eval-okay-scope-3.nix
data/nix/tests/lang/eval-okay-scope-4.exp
data/nix/tests/lang/eval-okay-scope-4.nix
data/nix/tests/lang/eval-okay-scope-6.exp
data/nix/tests/lang/eval-okay-scope-6.nix
data/nix/tests/lang/eval-okay-scope-7.exp
data/nix/tests/lang/eval-okay-scope-7.nix
data/nix/tests/lang/eval-okay-search-path.exp
data/nix/tests/lang/eval-okay-search-path.flags
data/nix/tests/lang/eval-okay-search-path.nix
data/nix/tests/lang/eval-okay-seq.exp
data/nix/tests/lang/eval-okay-seq.nix
data/nix/tests/lang/eval-okay-sort.exp
data/nix/tests/lang/eval-okay-sort.nix
data/nix/tests/lang/eval-okay-splitversion.exp
data/nix/tests/lang/eval-okay-splitversion.nix
data/nix/tests/lang/eval-okay-string.exp
data/nix/tests/lang/eval-okay-string.nix
data/nix/tests/lang/eval-okay-strings-as-attrs-names.exp
data/nix/tests/lang/eval-okay-strings-as-attrs-names.nix
data/nix/tests/lang/eval-okay-substring.exp
data/nix/tests/lang/eval-okay-substring.nix
data/nix/tests/lang/eval-okay-tail-call-1.exp-disabled
data/nix/tests/lang/eval-okay-tail-call-1.nix
data/nix/tests/lang/eval-okay-tojson.exp
data/nix/tests/lang/eval-okay-tojson.nix
data/nix/tests/lang/eval-okay-toxml.exp
data/nix/tests/lang/eval-okay-toxml.nix
data/nix/tests/lang/eval-okay-toxml2.exp
data/nix/tests/lang/eval-okay-toxml2.nix
data/nix/tests/lang/eval-okay-tryeval.exp
data/nix/tests/lang/eval-okay-tryeval.nix
data/nix/tests/lang/eval-okay-types.exp
data/nix/tests/lang/eval-okay-types.nix
data/nix/tests/lang/eval-okay-versions.exp
data/nix/tests/lang/eval-okay-versions.nix
data/nix/tests/lang/eval-okay-with.exp
data/nix/tests/lang/eval-okay-with.nix
data/nix/tests/lang/eval-okay-xml.exp.xml
data/nix/tests/lang/eval-okay-xml.nix
data/nix/tests/lang/imported.nix
data/nix/tests/lang/imported2.nix
data/nix/tests/lang/lib.nix
data/nix/tests/lang/parse-fail-dup-attrs-1.nix
data/nix/tests/lang/parse-fail-dup-attrs-2.nix
data/nix/tests/lang/parse-fail-dup-attrs-3.nix
data/nix/tests/lang/parse-fail-dup-attrs-4.nix
data/nix/tests/lang/parse-fail-dup-attrs-6.nix
data/nix/tests/lang/parse-fail-dup-attrs-7.nix
data/nix/tests/lang/parse-fail-dup-formals.nix
data/nix/tests/lang/parse-fail-patterns-1.nix
data/nix/tests/lang/parse-fail-regression-20060610.nix
data/nix/tests/lang/parse-fail-undef-var-2.nix
data/nix/tests/lang/parse-fail-undef-var.nix
data/nix/tests/lang/parse-okay-1.nix
data/nix/tests/lang/parse-okay-crlf.nix
data/nix/tests/lang/parse-okay-dup-attrs-5.nix
data/nix/tests/lang/parse-okay-regression-20041027.nix
data/nix/tests/lang/parse-okay-regression-751.nix
data/nix/tests/lang/parse-okay-subversion.nix
data/nix/tests/lang/parse-okay-url.nix
data/nix/tests/lang/readDir/bar
data/nix/tests/lang/readDir/foo/git-hates-directories
data/nix/tests/linux-sandbox.sh
data/nix/tests/local.mk
data/nix/tests/logging.sh
data/nix/tests/misc.sh
data/nix/tests/multiple-outputs.nix
data/nix/tests/multiple-outputs.sh
data/nix/tests/nar-access.nix
data/nix/tests/nar-access.sh
data/nix/tests/nix-build.sh
data/nix/tests/nix-channel.sh
data/nix/tests/nix-copy-closure.nix
data/nix/tests/nix-profile.sh
data/nix/tests/nix-shell.sh
data/nix/tests/optimise-store.sh
data/nix/tests/parallel.builder.sh
data/nix/tests/parallel.nix
data/nix/tests/parallel.sh
data/nix/tests/pass-as-file.sh
data/nix/tests/placeholders.sh
data/nix/tests/plugins.sh
data/nix/tests/plugins/local.mk
data/nix/tests/plugins/plugintest.cc
data/nix/tests/pure-eval.nix
data/nix/tests/pure-eval.sh
data/nix/tests/referrers.sh
data/nix/tests/remote-builds.nix
data/nix/tests/remote-store.sh
data/nix/tests/repair.sh
data/nix/tests/restricted.nix
data/nix/tests/restricted.sh
data/nix/tests/run.nix
data/nix/tests/run.sh
data/nix/tests/search.nix
data/nix/tests/search.sh
data/nix/tests/secure-drv-outputs.nix
data/nix/tests/secure-drv-outputs.sh
data/nix/tests/setuid.nix
data/nix/tests/shell.nix
data/nix/tests/shell.shebang.rb
data/nix/tests/shell.shebang.sh
data/nix/tests/signing.sh
data/nix/tests/simple.builder.sh
data/nix/tests/simple.nix
data/nix/tests/simple.sh
data/nix/tests/structured-attrs.nix
data/nix/tests/structured-attrs.sh
data/nix/tests/tarball.sh
data/nix/tests/timeout.nix
data/nix/tests/timeout.sh
data/nix/tests/user-envs.builder.sh
data/nix/tests/user-envs.nix
data/nix/tests/user-envs.sh
data/nix/version
data/nixpkgs-all-packages-pretty.nix
data/nixpkgs-all-packages.nix
data/simple-pretty.nix
data/simple.nix
LICENSE
package.yaml
README.md
tests/eval-compare/builtins.split-01.nix
tests/eval-compare/builtins.split-02.nix
tests/eval-compare/builtins.split-03.nix
tests/eval-compare/builtins.split-04.nix
tests/eval-compare/ind-string-01.nix
tests/eval-compare/ind-string-02.nix
tests/eval-compare/ind-string-03.nix
tests/eval-compare/ind-string-04.nix
tests/eval-compare/ind-string-05.nix
tests/eval-compare/ind-string-06.nix
tests/eval-compare/ind-string-07.nix
tests/eval-compare/ind-string-08.nix
tests/eval-compare/ind-string-09.nix
tests/eval-compare/ind-string-10.nix
tests/eval-compare/ind-string-11.nix
tests/eval-compare/ind-string-12.nix
tests/eval-compare/ind-string-13.nix
tests/eval-compare/ind-string-14.nix
tests/eval-compare/ind-string-15.nix
tests/eval-compare/ind-string-16.nix
tests/eval-compare/ind-string-17.nix
source-repository head
type: git
location: https://github.com/jwiegley/hnix
location: https://github.com/haskell-nix/hnix
flag optimize
description: Enable all optimization flags
@ -57,7 +461,6 @@ library
Nix.Expr.Types.Annotated
Nix.Frames
Nix.Lint
Nix.NixString
Nix.Normal
Nix.Options
Nix.Parser
@ -86,38 +489,31 @@ library
, ansi-wl-pprint
, array >=0.4 && <0.6
, base >=4.9 && <5
, base16-bytestring
, binary
, bytestring
, compact
, containers
, cryptohash
, data-fix
, deepseq
, deriving-compat >=0.3 && <0.5
, deepseq >=1.4.2 && <1.5
, deriving-compat >=0.3 && <0.6
, directory
, exceptions
, filepath
, hashable
, haskeline
, hashing
, http-client
, http-client-tls
, http-types
, lens-family
, lens-family-core
, interpolate
, lens-family-th
, logict
, megaparsec
, megaparsec >=6.5 && <7.0
, monadlist
, mtl
, optparse-applicative
, pretty-show
, process
, regex-tdfa
, regex-tdfa-text
, scientific
, semigroups >=0.18 && <0.19
, serialise
, split
, syb
, template-haskell
@ -129,11 +525,42 @@ library
, unordered-containers >=0.2.9 && <0.3
, vector
, xml
if flag(optimize)
ghc-options: -fexpose-all-unfoldings -fspecialise-aggressively -O2
if flag(tracing)
cpp-options: -DENABLE_TRACING=1
if os(linux) && impl(ghc >= 8.2) && impl(ghc < 8.3)
build-depends:
compact
if !impl(ghcjs)
build-depends:
base16-bytestring
, cryptohash-md5
, cryptohash-sha1
, cryptohash-sha256
, cryptohash-sha512
, serialise
if impl(ghc < 8.1)
build-depends:
lens-family ==1.2.1
, lens-family-core ==1.2.1
else
build-depends:
lens-family >=1.2.2
, lens-family-core >=1.2.2
if impl(ghc < 8.4.0) && !flag(profiling)
build-depends:
ghc-datasize
if impl(ghcjs)
build-depends:
hashable >=1.2.4 && <1.3
else
exposed-modules:
Nix.Options.Parser
build-depends:
hashable >=1.2.5 && <1.3
, haskeline
, pretty-show
default-language: Haskell2010
executable hnix
@ -149,26 +576,42 @@ executable hnix
, ansi-wl-pprint
, base >=4.9 && <5
, bytestring
, compact
, containers
, data-fix
, deepseq
, deepseq >=1.4.2 && <1.5
, exceptions
, filepath
, hashing
, haskeline
, hnix
, mtl
, optparse-applicative
, pretty-show
, repline
, serialise
, template-haskell
, text
, time
, transformers
, unordered-containers >=0.2.9 && <0.3
if flag(optimize)
ghc-options: -fexpose-all-unfoldings -fspecialise-aggressively -O2
if flag(tracing)
cpp-options: -DENABLE_TRACING=1
if os(linux) && impl(ghc >= 8.2) && impl(ghc < 8.3)
build-depends:
compact
if !impl(ghcjs)
build-depends:
base16-bytestring
, cryptohash-md5
, cryptohash-sha1
, cryptohash-sha256
, cryptohash-sha512
, serialise
if impl(ghcjs)
buildable: False
else
buildable: True
default-language: Haskell2010
test-suite hnix-tests
@ -180,6 +623,7 @@ test-suite hnix-tests
ParserTests
PrettyParseTests
PrettyTests
ReduceExprTests
TestCommon
Paths_hnix
hs-source-dirs:
@ -188,18 +632,18 @@ test-suite hnix-tests
build-depends:
Diff
, Glob
, QuickCheck
, ansi-wl-pprint
, base >=4.9 && <5
, bytestring
, compact
, containers
, data-fix
, deepseq
, deepseq >=1.4.2 && <1.5
, directory
, exceptions
, filepath
, generic-random
, hashing
, hedgehog
, hnix
, interpolate
, megaparsec
@ -207,10 +651,9 @@ test-suite hnix-tests
, optparse-applicative
, pretty-show
, process
, quickcheck-instances
, serialise
, split
, tasty
, tasty-hedgehog
, tasty-hunit
, tasty-quickcheck
, tasty-th
@ -220,9 +663,27 @@ test-suite hnix-tests
, transformers
, unix
, unordered-containers >=0.2.9 && <0.3
if flag(optimize)
ghc-options: -fexpose-all-unfoldings -fspecialise-aggressively -O2
if flag(tracing)
cpp-options: -DENABLE_TRACING=1
if os(linux) && impl(ghc >= 8.2) && impl(ghc < 8.3)
build-depends:
compact
if !impl(ghcjs)
build-depends:
base16-bytestring
, cryptohash-md5
, cryptohash-sha1
, cryptohash-sha256
, cryptohash-sha512
, serialise
if impl(ghcjs)
buildable: False
else
buildable: True
default-language: Haskell2010
build-tool-depends: hspec-discover:hspec-discover == 2.*
benchmark hnix-benchmarks
type: exitcode-stdio-1.0
@ -237,22 +698,38 @@ benchmark hnix-benchmarks
ansi-wl-pprint
, base >=4.9 && <5
, bytestring
, compact
, containers
, criterion
, data-fix
, deepseq
, deepseq >=1.4.2 && <1.5
, exceptions
, filepath
, hashing
, hnix
, mtl
, optparse-applicative
, serialise
, template-haskell
, text
, time
, transformers
, unordered-containers >=0.2.9 && <0.3
if flag(optimize)
ghc-options: -fexpose-all-unfoldings -fspecialise-aggressively -O2
if flag(tracing)
cpp-options: -DENABLE_TRACING=1
if os(linux) && impl(ghc >= 8.2) && impl(ghc < 8.3)
build-depends:
compact
if !impl(ghcjs)
build-depends:
base16-bytestring
, cryptohash-md5
, cryptohash-sha1
, cryptohash-sha256
, cryptohash-sha512
, serialise
if impl(ghcjs)
buildable: False
else
buildable: True
default-language: Haskell2010

View file

@ -28,6 +28,7 @@ import Nix
import Nix.Convert
import qualified Nix.Eval as Eval
-- import Nix.Lint
import Nix.Options.Parser
import qualified Nix.Type.Env as Env
import qualified Nix.Type.Infer as HM
import Nix.Utils
@ -143,7 +144,7 @@ main = do
. A.encodingToLazyByteString
. toEncodingSorted
<=< fromNix
| normalize opts =
| strict opts =
liftIO . print . prettyNValueNF <=< normalForm
| values opts =
liftIO . print <=< prettyNValueProv

View file

@ -103,7 +103,7 @@ exec update source = do
go expr = do
val <- evalExprLoc expr
opts :: Nix.Options <- asks (view hasLens)
if | normalize opts ->
if | strict opts ->
liftIO . print . prettyNValueNF =<< normalForm val
| values opts ->
liftIO . print =<< prettyNValueProv val

View file

@ -1,7 +1,7 @@
name: hnix
version: 0.5.0
version: 0.5.2
synopsis: Haskell implementation of the Nix language
github: jwiegley/hnix
github: haskell-nix/hnix
author: John Wiegley
maintainer: johnw@newartisans.com
category: System, Data, Nix
@ -11,29 +11,28 @@ description:
Haskell implementation of the Nix language.
extra-source-files:
- LICENSE
- README.md
dependencies:
- base >= 4.9 && < 5
- ansi-wl-pprint
- bytestring
- compact
- containers
- data-fix
- deepseq
- exceptions
- filepath
- mtl
- optparse-applicative
- serialise
- template-haskell
- text
- time
- transformers
- unordered-containers >= 0.2.9 && < 0.3
ghc-options:
- -Wall
- package.yaml
- data/*
- data/nix/*
- data/nix/corepkgs/*
- data/nix/config/*
- data/nix/perl/*
- data/nix/perl/lib/Nix/*
- data/nix/tests/*
- data/nix/tests/plugins/*
- data/nix/tests/lang/*
- data/nix/tests/lang/readDir/*
- data/nix/tests/lang/readDir/foo/*
- data/nix/tests/lang/dir2/*
- data/nix/tests/lang/dir4/*
- data/nix/tests/lang/dir3/*
- data/nix/tests/lang/dir1/*
- data/nix/maintainers/*
- data/nix/mk/*
- data/nix/scripts/*
- tests/eval-compare/*
flags:
tracing:
@ -51,6 +50,27 @@ flags:
manual: True
default: False
ghc-options:
- -Wall
dependencies:
- base >= 4.9 && < 5
- ansi-wl-pprint
- bytestring
- containers
- data-fix
- deepseq >= 1.4.2 && < 1.5
- exceptions
- filepath
- hashing
- mtl
- optparse-applicative
- template-haskell
- text
- time
- transformers
- unordered-containers >= 0.2.9 && < 0.3
when:
- condition: flag(optimize)
ghc-options:
@ -58,38 +78,44 @@ when:
- -fspecialise-aggressively
- -O2
when:
- condition: flag(tracing)
cpp-options: -DENABLE_TRACING=1
- condition: "os(linux) && impl(ghc >= 8.2) && impl(ghc < 8.3)"
dependencies:
- compact
- condition: "!impl(ghcjs)"
dependencies:
- base16-bytestring
- cryptohash-md5
- cryptohash-sha1
- cryptohash-sha256
- cryptohash-sha512
- serialise
library:
source-dirs: src
dependencies:
- aeson
- ansi-wl-pprint
- array >= 0.4 && < 0.6
- base16-bytestring
- array >= 0.4 && < 0.6
- binary
- cryptohash
- deriving-compat >= 0.3 && < 0.5
- deriving-compat >= 0.3 && < 0.6
- directory
- hashable
- http-types
- http-client
- http-client-tls
- haskeline
- lens-family
- lens-family-core
- interpolate
- lens-family-th
- logict
- megaparsec
- megaparsec >= 6.5 && < 7.0
- monadlist
- pretty-show
- process
- regex-tdfa
- regex-tdfa-text
- scientific
- semigroups >= 0.18 && < 0.19
- semigroups >= 0.18 && < 0.19
- split
- syb
- these
@ -97,10 +123,32 @@ library:
- vector
- xml
when:
- condition: impl(ghc < 8.4.0) && !flag(profiling)
- condition: "impl(ghc < 8.1)"
then:
dependencies:
- lens-family == 1.2.1
- lens-family-core == 1.2.1
else:
dependencies:
- lens-family >= 1.2.2
- lens-family-core >= 1.2.2
- condition: "impl(ghc < 8.4.0) && !flag(profiling)"
dependencies:
- ghc-datasize
- condition: "impl(ghcjs)"
then:
dependencies:
- hashable >= 1.2.4 && < 1.3
else:
exposed-modules:
- Nix.Options.Parser
dependencies:
- hashable >= 1.2.5 && < 1.3
- haskeline
- pretty-show
executables:
hnix:
source-dirs: main
@ -111,12 +159,21 @@ executables:
- pretty-show
- repline
- haskeline
when:
- condition: "impl(ghcjs)"
then:
buildable: false
else:
buildable: true
tests:
hnix-tests:
source-dirs: tests
main: Main.hs
ghc-options: -threaded
verbatim:
build-tool-depends:
hspec-discover:hspec-discover == 2.*
dependencies:
- hnix
- Glob
@ -125,16 +182,22 @@ tests:
- process
- split
- tasty
- tasty-hedgehog
- tasty-hunit
- tasty-th
- unix
- QuickCheck
- quickcheck-instances
- hedgehog
- generic-random
- Diff
- megaparsec
- tasty-quickcheck
- pretty-show
when:
- condition: "impl(ghcjs)"
then:
buildable: false
else:
buildable: true
benchmarks:
hnix-benchmarks:
@ -143,3 +206,9 @@ benchmarks:
dependencies:
- hnix
- criterion
when:
- condition: "impl(ghcjs)"
then:
buildable: false
else:
buildable: true

View file

@ -1,3 +1,4 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
@ -5,7 +6,9 @@
module Nix.Atoms where
#ifdef MIN_VERSION_serialise
import Codec.Serialise
#endif
import Control.DeepSeq
import Data.Data
import Data.Hashable
@ -25,10 +28,12 @@ data NAtom
| NBool Bool
-- | Null values. There's only one of this variant.
| NNull
-- | URIs, which are just string literals, but do not need quotes.
| NUri Text
deriving (Eq, Ord, Generic, Typeable, Data, Show, Read, NFData,
Serialise, Hashable)
Hashable)
#ifdef MIN_VERSION_serialise
instance Serialise NAtom
#endif
-- | Translate an atom into its nix representation.
atomText :: NAtom -> Text
@ -36,4 +41,3 @@ atomText (NInt i) = pack (show i)
atomText (NFloat f) = pack (show f)
atomText (NBool b) = if b then "true" else "false"
atomText NNull = "null"
atomText (NUri uri) = uri

View file

@ -1,3 +1,4 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
@ -7,7 +8,9 @@
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}
@ -24,17 +27,30 @@ import Control.Monad
import Control.Monad.Catch
import Control.Monad.ListM (sortByM)
import Control.Monad.Reader (asks)
import qualified Crypto.Hash.MD5 as MD5
import qualified Crypto.Hash.SHA1 as SHA1
import qualified Crypto.Hash.SHA256 as SHA256
import qualified Crypto.Hash.SHA512 as SHA512
-- Using package imports here because there is a bug in cabal2nix that forces
-- us to put the hashing package in the unconditional dependency list.
-- See https://github.com/NixOS/cabal2nix/issues/348 for more info
#if MIN_VERSION_hashing(0, 1, 0)
import Crypto.Hash
import qualified "hashing" Crypto.Hash.MD5 as MD5
import qualified "hashing" Crypto.Hash.SHA1 as SHA1
import qualified "hashing" Crypto.Hash.SHA256 as SHA256
import qualified "hashing" Crypto.Hash.SHA512 as SHA512
#else
import Data.ByteString.Base16 as Base16
import qualified "cryptohash-md5" Crypto.Hash.MD5 as MD5
import qualified "cryptohash-sha1" Crypto.Hash.SHA1 as SHA1
import qualified "cryptohash-sha256" Crypto.Hash.SHA256 as SHA256
import qualified "cryptohash-sha512" Crypto.Hash.SHA512 as SHA512
#endif
import qualified Data.Aeson as A
import qualified Data.Aeson.Encoding as A
import Data.Align (alignWith)
import Data.Array
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Data.ByteString.Base16 as Base16
import qualified Data.ByteString.Lazy as LBS
import Data.Char (isDigit)
import Data.Coerce
@ -46,6 +62,7 @@ import Data.Maybe
import Data.Semigroup
import Data.Set (Set)
import qualified Data.Set as S
import Data.String.Interpolate.IsString
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Text.Encoding
@ -54,7 +71,6 @@ import qualified Data.Text.Lazy.Builder as Builder
import Data.These (fromThese)
import qualified Data.Time.Clock.POSIX as Time
import Data.Traversable (mapM)
import Language.Haskell.TH.Syntax (addDependentFile, runIO)
import Nix.Atoms
import Nix.Convert
import Nix.Effects
@ -128,12 +144,41 @@ builtinsList = sequence [
, add0 Normal "currentSystem" currentSystem
, add0 Normal "currentTime" currentTime_
, add2 Normal "deepSeq" deepSeq
, add0 TopLevel "derivation" $(do
let f = "data/nix/corepkgs/derivation.nix"
addDependentFile f
Success expr <- runIO $ parseNixFile f
, add0 TopLevel "derivation" $(do
-- This is compiled in so that we only parse and evaluate it once,
-- at compile-time.
let Success expr = parseNixText [i|
/* This is the implementation of the derivation builtin function.
It's actually a wrapper around the derivationStrict primop. */
drvAttrs @ { outputs ? [ "out" ], ... }:
let
strict = derivationStrict drvAttrs;
commonAttrs = drvAttrs // (builtins.listToAttrs outputsList) //
{ all = map (x: x.value) outputsList;
inherit drvAttrs;
};
outputToAttrListElement = outputName:
{ name = outputName;
value = commonAttrs // {
outPath = builtins.getAttr outputName strict;
drvPath = strict.drvPath;
type = "derivation";
inherit outputName;
};
};
outputsList = map outputToAttrListElement outputs;
in (builtins.head outputsList).value|]
[| cata Eval.eval expr |]
)
, add TopLevel "derivationStrict" derivationStrict_
, add TopLevel "dirOf" dirOf
, add2 Normal "div" div_
@ -178,6 +223,7 @@ builtinsList = sequence [
, add TopLevel "placeholder" placeHolder
, add Normal "readDir" readDir_
, add Normal "readFile" readFile_
, add2 Normal "findFile" findFile_
, add2 TopLevel "removeAttrs" removeAttrs
, add3 Normal "replaceStrings" replaceStrings
, add2 TopLevel "scopedImport" scopedImport
@ -195,6 +241,7 @@ builtinsList = sequence [
, add' Normal "toJSON"
(arity1 $ decodeUtf8 . LBS.toStrict . A.encodingToLazyByteString
. toEncodingSorted)
, add2 Normal "toFile" toFile
, add Normal "toPath" toPath
, add TopLevel "toString" toString
, add Normal "toXML" toXML_
@ -250,8 +297,7 @@ nixPath = fmap nvList $ flip foldNixPath [] $ \p mn rest ->
nvStr (makeNixStringWithoutContext $ Text.pack (fromMaybe "" mn))) ]) : rest
toString :: MonadNix e m => m (NValue m) -> m (NValue m)
toString str =
str >>= normalForm >>= valueText False >>= toNix @NixString
toString str = str >>= coerceToString False >>= toNix @NixString . Text.pack
hasAttr :: forall e m. MonadNix e m => m (NValue m) -> m (NValue m) -> m (NValue m)
hasAttr x y =
@ -269,26 +315,19 @@ hasContext :: MonadNix e m => m (NValue m) -> m (NValue m)
hasContext =
toNix . not . null . stringContextOnly <=< fromValue
getAttr :: MonadNix e m => m (NValue m) -> m (NValue m) -> m (NValue m)
getAttr x y = x >>= \x' -> y >>= \y' -> case (x', y') of
(NVStr ns, NVSet aset _) ->
case stringNoContext ns of
Just key -> attrsetGet key aset >>= force'
Nothing -> throwError $ ErrorCall $ "Invalid NixString for builtin.getAttr: "
++ show (ns, aset)
(x, y) -> throwError $ ErrorCall $ "Invalid types for builtin.getAttr: "
++ show (x, y)
getAttr :: forall e m. MonadNix e m => m (NValue m) -> m (NValue m) -> m (NValue m)
getAttr x y =
fromValue @Text x >>= \key ->
fromValue @(AttrSet (NThunk m), AttrSet SourcePos) y >>= \(aset, _) ->
attrsetGet key aset >>= force'
unsafeGetAttrPos :: forall e m. MonadNix e m
=> m (NValue m) -> m (NValue m) -> m (NValue m)
unsafeGetAttrPos x y = x >>= \x' -> y >>= \y' -> case (x', y') of
(NVStr ns, NVSet _ apos) ->
case stringNoContext ns of
Just key -> case M.lookup key apos of
Nothing -> pure $ nvConstant NNull
Just delta -> toValue delta
Nothing -> throwError $ ErrorCall $ "Invalid NixString for unsafeGetAttrPos " ++ show apos
(x, y) -> throwError $ ErrorCall $ "Invalid types for builtin.unsafeGetAttrPos: "
(NVStr ns _, NVSet _ apos) -> case M.lookup (stringIntentionallyDropContext key) apos of
Nothing -> pure $ nvConstant NNull
Just delta -> toValue delta
(x, y) -> throwError $ ErrorCall $ "Invalid types for builtins.unsafeGetAttrPos: "
++ show (x, y)
-- This function is a bit special in that it doesn't care about the contents
@ -318,11 +357,14 @@ mul_ x y = x >>= \x' -> y >>= \y' -> case (x', y') of
div_ :: MonadNix e m => m (NValue m) -> m (NValue m) -> m (NValue m)
div_ x y = x >>= \x' -> y >>= \y' -> case (x', y') of
(NVConstant (NInt x), NVConstant (NInt y)) ->
(NVConstant (NInt x), NVConstant (NInt y)) | y /= 0 ->
toNix (floor (fromInteger x / fromInteger y :: Double) :: Integer)
(NVConstant (NFloat x), NVConstant (NInt y)) -> toNix (x / fromInteger y)
(NVConstant (NInt x), NVConstant (NFloat y)) -> toNix (fromInteger x / y)
(NVConstant (NFloat x), NVConstant (NFloat y)) -> toNix (x / y)
(NVConstant (NFloat x), NVConstant (NInt y)) | y /= 0 ->
toNix (x / fromInteger y)
(NVConstant (NInt x), NVConstant (NFloat y)) | y /= 0 ->
toNix (fromInteger x / y)
(NVConstant (NFloat x), NVConstant (NFloat y)) | y /= 0 ->
toNix (x / y)
(_, _) ->
throwError $ Division x' y'
@ -668,6 +710,13 @@ functionArgs fun = fun >>= \case
v -> throwError $ ErrorCall $
"builtins.functionArgs: expected function, got " ++ show v
toFile :: MonadNix e m => m (NValue m) -> m (NValue m) -> m (NValue m)
toFile name s = do
name' <- fromValue name
s' <- fromValue s
mres <- toFile_ (Text.unpack name') (Text.unpack s')
toNix $ Text.pack $ unStorePath mres
toPath :: MonadNix e m => m (NValue m) -> m (NValue m)
toPath = fromValue @Path >=> toNix @Path
@ -771,14 +820,33 @@ listToAttrs = fromValue @[NThunk m] >=> \l ->
hashString :: MonadNix e m => Text -> Text -> Prim m Text
hashString algo s = Prim $ do
hash <- case algo of
"md5" -> pure MD5.hash
"sha1" -> pure SHA1.hash
"sha256" -> pure SHA256.hash
"sha512" -> pure SHA512.hash
case algo of
"md5" -> pure $
#if MIN_VERSION_hashing(0, 1, 0)
Text.pack $ show (hash (encodeUtf8 s) :: MD5.MD5)
#else
decodeUtf8 $ Base16.encode $ MD5.hash $ encodeUtf8 s
#endif
"sha1" -> pure $
#if MIN_VERSION_hashing(0, 1, 0)
Text.pack $ show (hash (encodeUtf8 s) :: SHA1.SHA1)
#else
decodeUtf8 $ Base16.encode $ SHA1.hash $ encodeUtf8 s
#endif
"sha256" -> pure $
#if MIN_VERSION_hashing(0, 1, 0)
Text.pack $ show (hash (encodeUtf8 s) :: SHA256.SHA256)
#else
decodeUtf8 $ Base16.encode $ SHA256.hash $ encodeUtf8 s
#endif
"sha512" -> pure $
#if MIN_VERSION_hashing(0, 1, 0)
Text.pack $ show (hash (encodeUtf8 s) :: SHA512.SHA512)
#else
decodeUtf8 $ Base16.encode $ SHA512.hash $ encodeUtf8 s
#endif
_ -> throwError $ ErrorCall $ "builtins.hashString: "
++ "expected \"md5\", \"sha1\", \"sha256\", or \"sha512\", got " ++ show algo
pure $ decodeUtf8 $ Base16.encode $ hash $ encodeUtf8 s
placeHolder :: MonadNix e m => m (NValue m) -> m (NValue m)
placeHolder = fromValue @Text >=> \_ -> do
@ -799,6 +867,19 @@ readFile_ :: MonadNix e m => m (NValue m) -> m (NValue m)
readFile_ path =
path >>= absolutePathFromValue >>= Nix.Render.readFile >>= toNix
findFile_ :: forall e m. MonadNix e m
=> m (NValue m) -> m (NValue m) -> m (NValue m)
findFile_ aset filePath =
aset >>= \aset' ->
filePath >>= \filePath' ->
case (aset', filePath') of
(NVList x, NVStr ns) -> do
mres <- findPath x (Text.unpack (stringIntentionallyDropContext ns))
pure $ nvPath mres
(NVList _, y) -> throwError $ ErrorCall $ "expected a string, got " ++ show y
(x, NVStr _ _) -> throwError $ ErrorCall $ "expected a list, got " ++ show x
(x, y) -> throwError $ ErrorCall $ "Invalid types for builtins.findFile: " ++ show (x, y)
data FileType
= FileTypeRegular
| FileTypeDirectory
@ -845,8 +926,7 @@ typeOf v = v >>= toNix @Text . \case
NFloat _ -> "float"
NBool _ -> "bool"
NNull -> "null"
NUri _ -> "string"
NVStr _ -> "string"
NVStr _ -> "string"
NVList _ -> "list"
NVSet _ _ -> "set"
NVClosure {} -> "lambda"
@ -886,14 +966,12 @@ fetchTarball v = v >>= \case
"builtins.fetchTarball: Missing url attribute"
Just url -> force url $ go (M.lookup "sha256" s)
v@NVStr {} -> go Nothing v
v@(NVConstant (NUri _)) -> go Nothing v
v -> throwError $ ErrorCall $
"builtins.fetchTarball: Expected URI or set, got " ++ show v
where
go :: Maybe (NThunk m) -> NValue m -> m (NValue m)
go msha = \case
NVStr ns -> fetch (stringIntentionallyDropContext ns) msha
NVConstant (NUri uri) -> fetch uri msha
v -> throwError $ ErrorCall $
"builtins.fetchTarball: Expected URI or string, got " ++ show v
@ -922,14 +1000,12 @@ fetchurl :: forall e m. MonadNix e m => m (NValue m) -> m (NValue m)
fetchurl v = v >>= \case
NVSet s _ -> attrsetGet "url" s >>= force ?? (go (M.lookup "sha256" s))
v@NVStr {} -> go Nothing v
v@(NVConstant (NUri _)) -> go Nothing v
v -> throwError $ ErrorCall $ "builtins.fetchurl: Expected URI or set, got "
++ show v
where
go :: Maybe (NThunk m) -> NValue m -> m (NValue m)
go _msha = \case
NVStr ns -> getURL (stringIntentionallyDropContext ns) -- msha
NVConstant (NUri uri) -> getURL uri -- msha
v -> throwError $ ErrorCall $
"builtins.fetchurl: Expected URI or string, got " ++ show v

View file

@ -148,7 +148,6 @@ instance Convertible e m
instance (Convertible e m, MonadEffects m)
=> FromValue Text m (NValueNF m) where
fromValueMay = \case
Fix (NVConstantF (NUri u)) -> pure $ Just u
Fix (NVStrF ns) -> pure $ stringNoContext ns
Fix (NVPathF p) -> Just . Text.pack . unStorePath <$> addPath p
Fix (NVSetF s _) -> case M.lookup "outPath" s of
@ -162,7 +161,6 @@ instance (Convertible e m, MonadEffects m)
instance (Convertible e m, MonadThunk (NValue m) (NThunk m) m, MonadEffects m)
=> FromValue Text m (NValue m) where
fromValueMay = \case
NVConstant (NUri u) -> pure $ Just u
NVStr ns -> pure $ stringNoContext ns
NVPath p -> Just . Text.pack . unStorePath <$> addPath p
NVSet s _ -> case M.lookup "outPath" s of
@ -176,7 +174,6 @@ instance (Convertible e m, MonadThunk (NValue m) (NThunk m) m, MonadEffects m)
instance (Convertible e m, MonadEffects m)
=> FromValue NixString m (NValueNF m) where
fromValueMay = \case
Fix (NVConstantF (NUri u)) -> pure $ Just (makeNixStringWithoutContext u)
Fix (NVStrF ns) -> pure $ Just ns
Fix (NVPathF p) -> Just . makeNixStringWithoutContext . Text.pack . unStorePath <$> addPath p
Fix (NVSetF s _) -> case M.lookup "outPath" s of
@ -190,7 +187,6 @@ instance (Convertible e m, MonadEffects m)
instance (Convertible e m, MonadThunk (NValue m) (NThunk m) m, MonadEffects m)
=> FromValue NixString m (NValue m) where
fromValueMay = \case
NVConstant (NUri u) -> pure $ Just (makeNixStringWithoutContext u)
NVStr ns -> pure $ Just ns
NVPath p -> Just . makeNixStringWithoutContext . Text.pack . unStorePath <$> addPath p
NVSet s _ -> case M.lookup "outPath" s of
@ -224,7 +220,6 @@ newtype Path = Path { getPath :: FilePath }
instance Convertible e m => FromValue Path m (NValueNF m) where
fromValueMay = \case
Fix (NVConstantF (NUri u)) -> pure $ Just (Path (Text.unpack u))
Fix (NVPathF p) -> pure $ Just (Path p)
Fix (NVStrF ns) -> pure $ Path . Text.unpack <$> stringNoContext ns
Fix (NVSetF s _) -> case M.lookup "outPath" s of
@ -238,7 +233,6 @@ instance Convertible e m => FromValue Path m (NValueNF m) where
instance (Convertible e m, MonadThunk (NValue m) (NThunk m) m)
=> FromValue Path m (NValue m) where
fromValueMay = \case
NVConstant (NUri u) -> pure $ Just (Path (Text.unpack u))
NVPath p -> pure $ Just (Path p)
NVStr ns -> pure $ Path . Text.unpack <$> stringNoContext ns
NVSet s _ -> case M.lookup "outPath" s of
@ -328,7 +322,6 @@ instance (Convertible e m, MonadEffects m)
NFloat n -> toJSON n
NBool b -> toJSON b
NNull -> A.Null
NUri u -> toJSON u
Fix (NVStrF ns) -> pure $ toJSON <$> stringNoContext ns
Fix (NVListF l) -> fmap (A.Array . V.fromList) . sequence
<$> traverse fromValueMay l

View file

@ -1,40 +1,33 @@
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
module Nix.Eval where
import Control.Arrow (first)
import Control.Monad
import Control.Monad.Fix
import Control.Monad.Reader
import Control.Monad.State.Strict
import Data.Align.Key
import Data.Fix
import Data.Align.Key (alignWithKey)
import Data.Either (isRight)
import Data.Fix (Fix(Fix))
import Data.HashMap.Lazy (HashMap)
import qualified Data.HashMap.Lazy as M
import Data.List (partition, foldl')
import Data.List (partition)
import Data.List.NonEmpty (NonEmpty(..))
import qualified Data.List.NonEmpty as NE
import Data.Maybe (fromMaybe, catMaybes)
import Data.Text (Text)
import qualified Data.Text as Text
import Data.These
import Data.These (These(..))
import Data.Traversable (for)
import Nix.Atoms
import Nix.Convert
@ -47,13 +40,12 @@ import Nix.Thunk
import Nix.Utils
class (Show v, Monad m) => MonadEval v m | v -> m where
freeVariable :: Text -> m v
attrMissing :: NonEmpty Text -> Maybe v -> m v
evaledSym :: Text -> v -> m v
freeVariable :: Text -> m v
attrMissing :: NonEmpty Text -> Maybe v -> m v
evaledSym :: Text -> v -> m v
evalCurPos :: m v
evalConstant :: NAtom -> m v
evalString :: NixString -> m v
evalString :: NString (m v) -> m v
evalLiteralPath :: FilePath -> m v
evalEnvPath :: FilePath -> m v
evalUnary :: NUnaryOp -> v -> m v
@ -67,7 +59,6 @@ class (Show v, Monad m) => MonadEval v m | v -> m where
evalAbs :: Params (m v)
-> (forall a. m v -> (AttrSet (m v) -> m v -> m (a, v)) -> m (a, v))
-> m v
{-
evalSelect :: v -> NonEmpty Text -> Maybe (m v) -> m v
evalHasAttr :: v -> NonEmpty Text -> m v
@ -83,7 +74,6 @@ class (Show v, Monad m) => MonadEval v m | v -> m where
evalLetElem :: Text -> m v -> m v
evalLet :: m v -> m v
-}
evalError :: Exception s => s -> m a
type MonadNixEval e v t m =
@ -112,54 +102,35 @@ eval (NSym "__curPos") = evalCurPos
eval (NSym var) =
lookupVar var >>= maybe (freeVariable var) (force ?? evaledSym var)
eval (NConstant x) = evalConstant x
eval (NStr str) = assembleString str >>= \case
Nothing -> evalError @v $ ErrorCall "failed to evaluate string"
Just e -> evalString e
eval (NLiteralPath p) = evalLiteralPath p
eval (NEnvPath p) = evalEnvPath p
eval (NUnary op arg) = evalUnary op =<< arg
eval (NConstant x) = evalConstant x
eval (NStr str) = evalString str
eval (NLiteralPath p) = evalLiteralPath p
eval (NEnvPath p) = evalEnvPath p
eval (NUnary op arg) = evalUnary op =<< arg
eval (NBinary NApp fun arg) = do
scope <- currentScopes @_ @t
evalApp ?? withScopes scope arg =<< fun
fun >>= (`evalApp` withScopes scope arg)
eval (NBinary op larg rarg) = larg >>= \lval -> evalBinary op lval rarg
eval (NBinary op larg rarg) = larg >>= evalBinary op ?? rarg
eval (NSelect aset attr alt) = do
traceM "NSelect"
mres <- evalSelect aset attr
traceM "NSelect..2"
case mres of
Right v -> v
Left (s, ks) -> fromMaybe (attrMissing ks (Just s)) alt
eval (NSelect aset attr alt) = evalSelect aset attr >>= either go id
where
go (s, ks) = fromMaybe (attrMissing ks (Just s)) alt
eval (NHasAttr aset attr) =
toValue . either (const False) (const True) =<< evalSelect aset attr
eval (NHasAttr aset attr) = evalSelect aset attr >>= toValue . isRight
eval (NList l) = do
scope <- currentScopes
toValue =<< for l (thunk . withScopes @t scope)
for l (thunk . withScopes @t scope) >>= toValue
eval (NSet binds) = do
traceM "NSet..1"
(s, p) <- evalBinds True False binds
traceM $ "NSet..2: s = " ++ show (void s)
traceM $ "NSet..2: p = " ++ show (void p)
toValue (s, p)
eval (NSet binds) =
evalBinds False (desugarBinds (eval . NSet) binds) >>= toValue
eval (NRecSet binds) = do
traceM "NRecSet..1"
(s, p) <- evalBinds True True (desugarBinds (eval . NRecSet) binds)
traceM $ "NRecSet..2: s = " ++ show (void s)
traceM $ "NRecSet..2: p = " ++ show (void p)
toValue (s, p)
eval (NRecSet binds) =
evalBinds True (desugarBinds (eval . NRecSet) binds) >>= toValue
eval (NLet binds body) = do
traceM "Let..1"
(s, _) <- evalBinds True True binds
traceM $ "Let..2: s = " ++ show (void s)
pushScope s body
eval (NLet binds body) = evalBinds True binds >>= (pushScope ?? body) . fst
eval (NIf cond t f) = cond >>= \v -> evalIf v t f
@ -173,99 +144,99 @@ eval (NAbs params body) = do
-- we defer here so the present scope is restored when the parameters and
-- body are forced during application.
scope <- currentScopes @_ @t
evalAbs params $ \arg k ->
withScopes @t scope $ do
args <- buildArgument params arg
pushScope args (k (M.map (`force` pure) args) body)
evalAbs params $ \arg k -> withScopes @t scope $ do
args <- buildArgument params arg
pushScope args (k (M.map (`force` pure) args) body)
-- | If you know that the 'scope' action will result in an 'AttrSet t', then
-- this implementation may be used as an implementation for 'evalWith'.
evalWithAttrSet :: forall e v t m. MonadNixEval e v t m => m v -> m v -> m v
evalWithAttrSet scope body = do
evalWithAttrSet aset body = do
-- The scope is deliberately wrapped in a thunk here, since it is
-- evaluated each time a name is looked up within the weak scope, and
-- we want to be sure the action it evaluates is to force a thunk, so
-- its value is only computed once.
cur <- currentScopes @_ @t
s <- thunk $ withScopes cur scope
scope <- currentScopes @_ @t
s <- thunk $ withScopes scope aset
pushWeakScope ?? body $ force s $
fmap fst . fromValue @(AttrSet t, AttrSet SourcePos)
attrSetAlter :: forall e v t m. MonadNixEval e v t m
=> [Text]
-> SourcePos
-> AttrSet (m v)
-> AttrSet SourcePos
-> m v
-> m (AttrSet (m v))
attrSetAlter [] _ _ =
-> m (AttrSet (m v), AttrSet SourcePos)
attrSetAlter [] _ _ _ _ =
evalError @v $ ErrorCall "invalid selector with no components"
attrSetAlter (p:ps) m val = case M.lookup p m of
Nothing
| null ps -> go
| otherwise -> recurse M.empty
Just x
| null ps -> go
| otherwise ->
x >>= fromValue @(AttrSet t, AttrSet SourcePos)
>>= \(s, _) -> recurse (force ?? pure <$> s)
where
go = return $ M.insert p val m
recurse s = attrSetAlter ps s val <&> \m' ->
M.insert p (toValue @(AttrSet t, AttrSet SourcePos)
=<< fmap (, mempty)
(fmap (value @_ @_ @m) <$> sequence m')) m
attrSetAlter (k:ks) pos m p val = case M.lookup k m of
Nothing | null ks -> go
| otherwise -> recurse M.empty M.empty
Just x | null ks -> go
| otherwise ->
x >>= fromValue @(AttrSet t, AttrSet SourcePos)
>>= \(st, sp) -> recurse (force ?? pure <$> st) sp
where
go = return (M.insert k val m, M.insert k pos p)
recurse st sp = attrSetAlter ks pos st sp val <&> \(st', _) ->
( M.insert k (toValue @(AttrSet t, AttrSet SourcePos)
=<< (, mempty) . fmap value <$> sequence st') st
, M.insert k pos sp )
desugarBinds :: forall r. ([Binding r] -> r) -> [Binding r] -> [Binding r]
desugarBinds embed binds = evalState (mapM (go <=< collect) binds) M.empty
where
collect :: Binding r
-> State (HashMap VarName (Maybe SourcePos, [Binding r]))
-> State (HashMap VarName (SourcePos, [Binding r]))
(Either VarName (Binding r))
collect (NamedVar (StaticKey x p:|y:ys) val) = do
collect (NamedVar (StaticKey x :| y:ys) val p) = do
m <- get
let v = case M.lookup x m of
Nothing -> (p, [NamedVar (y:|ys) val])
Just (p, v) -> (p, NamedVar (y:|ys) val : v)
put $ M.insert x v m
put $ M.insert x ?? m $ case M.lookup x m of
Nothing -> (p, [NamedVar (y:|ys) val p])
Just (q, v) -> (q, NamedVar (y:|ys) val q : v)
pure $ Left x
collect x = pure $ Right x
go :: Either VarName (Binding r)
-> State (HashMap VarName (Maybe SourcePos, [Binding r]))
-> State (HashMap VarName (SourcePos, [Binding r]))
(Binding r)
go (Right x) = pure x
go (Left x) = do
Just (p, v) <- gets $ M.lookup x
pure $ NamedVar (StaticKey x p :| []) (embed v)
pure $ NamedVar (StaticKey x :| []) (embed v) p
evalBinds :: forall e v t m. MonadNixEval e v t m
=> Bool
-> Bool
-> [Binding (m v)]
-> m (AttrSet t, AttrSet SourcePos)
evalBinds allowDynamic recursive binds = do
evalBinds recursive binds = do
scope <- currentScopes @_ @t
buildResult scope . concat =<< mapM (go scope) (moveOverridesLast binds)
where
moveOverridesLast = (\(x, y) -> y ++ x) .
partition (\case NamedVar (StaticKey "__overrides" _ :| []) _ -> True
partition (\case NamedVar (StaticKey "__overrides" :| []) _ _pos -> True
_ -> False)
go :: Scopes m t -> Binding (m v) -> m [([Text], Maybe SourcePos, m v)]
go _ (NamedVar (StaticKey "__overrides" _ :| []) finalValue) =
go :: Scopes m t -> Binding (m v) -> m [([Text], SourcePos, m v)]
go _ (NamedVar (StaticKey "__overrides" :| []) finalValue pos) =
finalValue >>= fromValue >>= \(o', p') ->
return $ map (\(k, v) -> ([k], M.lookup k p', force v pure))
-- jww (2018-05-09): What to do with the key position here?
return $ map (\(k, v) -> ([k], fromMaybe pos (M.lookup k p'),
force v pure))
(M.toList o')
go _ (NamedVar pathExpr finalValue) = do
let go :: NAttrPath (m v) -> m ([Text], Maybe SourcePos, m v)
go _ (NamedVar pathExpr finalValue pos) = do
let go :: NAttrPath (m v) -> m ([Text], SourcePos, m v)
go = \case
h :| t -> evalSetterKeyName allowDynamic h >>= \case
(Nothing, _) ->
pure ([], Nothing,
h :| t -> evalSetterKeyName h >>= \case
Nothing ->
pure ([], nullPos,
toValue @(AttrSet t, AttrSet SourcePos)
(mempty, mempty))
(Just k, pos) -> case t of
Just k -> case t of
[] -> pure ([k], pos, finalValue)
x:xs -> do
(restOfPath, _, v) <- go (x:|xs)
@ -276,10 +247,10 @@ evalBinds allowDynamic recursive binds = do
([], _, _) -> []
result -> [result]
go scope (Inherit ms names) = fmap catMaybes $ forM names $ \name ->
evalSetterKeyName allowDynamic name >>= \case
(Nothing, _) -> return Nothing
(Just key, pos) -> return $ Just ([key], pos, do
go scope (Inherit ms names pos) = fmap catMaybes $ forM names $
evalSetterKeyName >=> \case
Nothing -> pure Nothing
Just key -> pure $ Just ([key], pos, do
mv <- case ms of
Nothing -> withScopes scope $ lookupVar key
Just s -> s
@ -291,97 +262,55 @@ evalBinds allowDynamic recursive binds = do
Just v -> force v pure)
buildResult :: Scopes m t
-> [([Text], Maybe SourcePos, m v)]
-> [([Text], SourcePos, m v)]
-> m (AttrSet t, AttrSet SourcePos)
buildResult scope bindings = do
s <- foldM insert M.empty bindings
(s, p) <- foldM insert (M.empty, M.empty) bindings
res <- if recursive
then loebM (encapsulate <$> s)
else traverse (thunk . withScopes scope) s
return (res, foldl' go M.empty bindings)
else traverse mkThunk s
return (res, p)
where
go m ([k], Just pos, _) = M.insert k pos m
go m _ = m
mkThunk = thunk . withScopes scope
encapsulate f attrs =
thunk . withScopes scope . pushScope attrs $ f
encapsulate f attrs = mkThunk . pushScope attrs $ f
insert m (path, _, value) = attrSetAlter path m value
insert (m, p) (path, pos, value) = attrSetAlter path pos m p value
evalSelect :: forall e v t m. MonadNixEval e v t m
=> m v
-> NAttrPath (m v)
-> m (Either (v, NonEmpty Text) (m v))
evalSelect aset attr = do
traceM "evalSelect"
s <- aset
traceM "evalSelect..2"
path <- evalSelector True attr
traceM $ "evalSelect..3: " ++ show path
res <- extract s path
traceM "evalSelect..4"
return res
path <- traverse evalGetterKeyName attr
extract s path
where
extract x path@(k:|ks) = fromValueMay x >>= \case
Just (s :: AttrSet t, p :: AttrSet SourcePos) ->
case M.lookup k s of
Just t -> do
traceM $ "Forcing value at selector " ++ Text.unpack k
case ks of
[] -> pure $ Right $ force t pure
y:ys -> force t $ extract ?? (y:|ys)
Nothing ->
Left . (, path) <$> toValue (s, p)
Nothing ->
return $ Left (x, path)
evalSelector :: (MonadEval v m, FromValue NixString m v)
=> Bool -> NAttrPath (m v) -> m (NonEmpty Text)
evalSelector allowDynamic binds =
NE.map fst <$> traverse (evalGetterKeyName allowDynamic) binds
Just (s :: AttrSet t, p :: AttrSet SourcePos)
| Just t <- M.lookup k s -> case ks of
[] -> pure $ Right $ force t pure
y:ys -> force t $ extract ?? (y:|ys)
| otherwise -> Left . (, path) <$> toValue (s, p)
Nothing -> return $ Left (x, path)
-- | Evaluate a component of an attribute path in a context where we are
-- *retrieving* a value
evalGetterKeyName :: (MonadEval v m, FromValue NixString m v)
=> Bool -> NKeyName (m v) -> m (Text, Maybe SourcePos)
evalGetterKeyName canBeDynamic
| canBeDynamic = evalKeyNameDynamicNotNull
| otherwise = evalKeyNameStatic
evalKeyNameStatic :: forall v m. MonadEval v m
=> NKeyName (m v) -> m (Text, Maybe SourcePos)
evalKeyNameStatic = \case
StaticKey k p -> pure (k, p)
DynamicKey _ ->
evalError @v $ ErrorCall "dynamic attribute not allowed in this context"
evalKeyNameDynamicNotNull
:: forall v m. (MonadEval v m, FromValue NixString m v)
=> NKeyName (m v) -> m (Text, Maybe SourcePos)
evalKeyNameDynamicNotNull = evalKeyNameDynamicNullable >=> \case
(Nothing, _) ->
evalError @v $ ErrorCall "value is null while a string was expected"
(Just k, p) -> pure (k, p)
evalGetterKeyName :: forall v m. (MonadEval v m, FromValue NixString m v)
=> NKeyName (m v) -> m Text
evalGetterKeyName = evalSetterKeyName >=> \case
Just k -> pure k
Nothing -> evalError @v $ ErrorCall "value is null while a string was expected"
-- | Evaluate a component of an attribute path in a context where we are
-- *binding* a value
evalSetterKeyName :: (MonadEval v m, FromValue NixString m v)
=> Bool -> NKeyName (m v) -> m (Maybe Text, Maybe SourcePos)
evalSetterKeyName canBeDynamic
| canBeDynamic = evalKeyNameDynamicNullable
| otherwise = fmap (first Just) . evalKeyNameStatic
-- | Returns Nothing iff the key value is null
evalKeyNameDynamicNullable
:: forall v m. (MonadEval v m, FromValue NixString m v)
=> NKeyName (m v)
-> m (Maybe Text, Maybe SourcePos)
evalKeyNameDynamicNullable = \case
StaticKey k p -> pure (Just k, p)
DynamicKey k ->
runAntiquoted "\n" assembleString (>>= fromValueMay) k
<&> \case Just ns -> (stringNoContext ns, Nothing)
_ -> (Nothing, Nothing)
=> NKeyName (m v) -> m (Maybe Text)
evalSetterKeyName = \case
StaticKey k -> pure (Just k)
DynamicKey k -> runAntiquoted "\n" assembleString (>>= fromValueMay) k
<&> \case Just (t, _) -> Just t
_ -> Nothing
assembleString :: forall v m. (MonadEval v m, FromValue NixString m v)
=> NString (m v) -> m (Maybe NixString)
@ -389,17 +318,16 @@ assembleString = \case
Indented _ parts -> fromParts parts
DoubleQuoted parts -> fromParts parts
where
go = runAntiquoted "\n" (pure . Just . makeNixStringWithoutContext) (>>= fromValueMay)
fromParts = fmap (fmap mconcat . sequence) . traverse go
fromParts parts = fmap mconcat . sequence <$> mapM go parts
go = runAntiquoted "\n" (pure . Just . makeNixStringWithoutContext) (>>= fromValueMay)
buildArgument :: forall e v t m. MonadNixEval e v t m
=> Params (m v) -> m v -> m (AttrSet t)
buildArgument params arg = do
scope <- currentScopes @_ @t
case params of
Param name -> M.singleton name
<$> thunk (withScopes scope arg)
Param name -> M.singleton name <$> thunk (withScopes scope arg)
ParamSet s isVariadic m ->
arg >>= fromValue @(AttrSet t, AttrSet SourcePos)
>>= \(args, _) -> do

View file

@ -44,6 +44,7 @@ import Data.IORef
import Data.List
import qualified Data.List.NonEmpty as NE
import Data.List.Split
import Data.Monoid
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Typeable
@ -67,7 +68,9 @@ import Nix.Scope
import Nix.Thunk
import Nix.Utils
import Nix.Value
#ifdef MIN_VERSION_haskeline
import System.Console.Haskeline.MonadException hiding (catch)
#endif
import System.Directory
import System.Environment
import System.Exit (ExitCode (ExitSuccess))
@ -77,6 +80,9 @@ import System.Posix.Files
import System.Process (readProcessWithExitCode)
import Text.PrettyPrint.ANSI.Leijen (text)
import qualified Text.PrettyPrint.ANSI.Leijen as P
#ifdef MIN_VERSION_pretty_show
import qualified Text.Show.Pretty as PS
#endif
#ifdef MIN_VERSION_ghc_datasize
#if MIN_VERSION_ghc_datasize(0,2,0) && __GLASGOW_HASKELL__ >= 804
@ -179,11 +185,13 @@ instance MonadNix e m => MonadEval (NValue m) m where
span <- currentPos
pure $ nvConstantP (Provenance scope (NConstant_ span c)) c
evalString ns = do
scope <- currentScopes
span <- currentPos
pure $ nvStrP (Provenance scope
(NStr_ span (DoubleQuoted [Plain (stringIntentionallyDropContext ns)]))) ns
evalString = assembleString >=> \case
Just (s, c) -> do
scope <- currentScopes
span <- currentPos
pure $ nvStrP (Provenance scope
(NStr_ span (DoubleQuoted [Plain s]))) s c
Nothing -> nverr $ ErrorCall "Failed to assemble string"
evalLiteralPath p = do
scope <- currentScopes
@ -323,8 +331,8 @@ execBinaryOp scope span op lval rarg = do
NBool l, NBool r) -> toBool $ not l || r
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(NVStr ls, NVStr rs) -> case op of
NPlus -> pure $ bin nvStrP (ls `mappend` rs)
(NVStr ls lc, NVStr rs rc) -> case op of
NPlus -> pure $ bin nvStrP (ls `mappend` rs) (lc `mappend` rc)
NEq -> toBool =<< valueEq lval rval
NNEq -> toBool . not =<< valueEq lval rval
NLt -> toBool $ ls < rs
@ -361,6 +369,20 @@ execBinaryOp scope span op lval rarg = do
NNEq -> toBool . not =<< valueEq (nvSet M.empty M.empty) rval
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(ls@NVSet {}, NVStr rs) -> case op of
NPlus -> (\lx -> bin nvStrP (modifyNixContents (Text.pack lx `mappend`) rs))
<$> coerceToString False ls
NEq -> toBool =<< valueEq lval rval
NNEq -> toBool . not =<< valueEq lval rval
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(NVStr ls, rs@NVSet {}) -> case op of
NPlus -> (\rx -> bin nvStrP (modifyNixContents (`mappend` Text.pack rx) ls))
<$> coerceToString False rs
NEq -> toBool =<< valueEq lval rval
NNEq -> toBool . not =<< valueEq lval rval
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(NVList ls, NVList rs) -> case op of
NConcat -> pure $ bin nvListP $ ls ++ rs
NEq -> toBool =<< valueEq lval rval
@ -382,9 +404,7 @@ execBinaryOp scope span op lval rarg = do
(NVPath p, NVStr ns) -> case op of
NEq -> toBool $ Just p == fmap Text.unpack (stringNoContext ns)
NNEq -> toBool $ Just p /= fmap Text.unpack (stringNoContext ns)
NPlus -> case stringNoContext ns of
Just s -> bin nvPathP <$> makeAbsolutePath (p `mappend` Text.unpack s)
Nothing -> nverr $ ErrorCall $ unsupportedTypes lval rval
NPlus -> bin nvPathP <$> makeAbsolutePath (p `mappend` Text.unpack (stringIntentionallyDropContext ns))
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(NVPath ls, NVPath rs) -> case op of
@ -419,27 +439,29 @@ execBinaryOp scope span op lval rarg = do
toInt = pure . bin nvConstantP . NInt
toFloat = pure . bin nvConstantP . NFloat
coerceToString :: MonadNix e m => NValue m -> m String
coerceToString = \case
NVConstant (NBool b)
| b -> pure "1"
| otherwise -> pure ""
NVConstant (NInt n) -> pure $ show n
NVConstant (NFloat n) -> pure $ show n
NVConstant (NUri u) -> pure $ show u
NVConstant NNull -> pure ""
coerceToString :: MonadNix e m => Bool -> NValue m -> m String
coerceToString copyToStore = go
where
go = \case
NVConstant (NBool b)
| b -> pure "1"
| otherwise -> pure ""
NVConstant (NInt n) -> pure $ show n
NVConstant (NFloat n) -> pure $ show n
NVConstant NNull -> pure ""
NVStr ns -> pure $ Text.unpack $ stringIntentionallyDropContext ns
NVPath p -> unStorePath <$> addPath p
NVList l -> unwords <$> traverse (`force` coerceToString) l
NVStr ns _ -> pure $ Text.unpack (stringIntentionallyDropContext ns)
NVPath p | copyToStore -> unStorePath <$> addPath p
| otherwise -> pure p
NVList l -> unwords <$> traverse (`force` go) l
v@(NVSet s _) | Just p <- M.lookup "__toString" s ->
force p $ (`callFunc` pure v) >=> coerceToString
v@(NVSet s _) | Just p <- M.lookup "__toString" s ->
force p $ (`callFunc` pure v) >=> go
NVSet s _ | Just p <- M.lookup "outPath" s ->
force p coerceToString
NVSet s _ | Just p <- M.lookup "outPath" s ->
force p go
v -> throwError $ ErrorCall $ "Expected a string, but saw: " ++ show v
v -> throwError $ ErrorCall $ "Expected a string, but saw: " ++ show v
newtype Lazy m a = Lazy
{ runLazy :: ReaderT (Context (Lazy m) (NThunk (Lazy m)))
@ -466,10 +488,12 @@ instance MonadCatch m => MonadCatch (Lazy m) where
instance MonadThrow m => MonadThrow (Lazy m) where
throwM = Lazy . throwM
#ifdef MIN_VERSION_haskeline
instance MonadException m => MonadException (Lazy m) where
controlIO f = Lazy $ controlIO $ \(RunIO run) ->
let run' = RunIO (fmap Lazy . run . runLazy)
in runLazy <$> f run'
#endif
instance (MonadFix m, MonadCatch m, MonadIO m, Alternative m,
MonadPlus m, Typeable m)
@ -484,6 +508,12 @@ instance (MonadFix m, MonadCatch m, MonadIO m, Alternative m,
_ -> throwError $ ErrorCall $
"addPath: failed: nix-store --add " ++ show path
toFile_ filepath content = do
liftIO $ writeFile filepath content
storepath <- addPath filepath
liftIO $ removeFile filepath
return storepath
makeAbsolutePath origPath = do
origPathExpanded <- liftIO $ expandHomePath origPath
absPath <- if isAbsolute origPathExpanded then pure origPathExpanded else do
@ -502,6 +532,8 @@ instance (MonadFix m, MonadCatch m, MonadIO m, Alternative m,
findEnvPath = findEnvPathM
findPath = findPathM
pathExists = liftIO . fileExist
importPath scope origPath = do
@ -570,7 +602,7 @@ instance (MonadFix m, MonadCatch m, MonadIO m, Alternative m,
"__ignoreNulls" -> pure Nothing
_ -> force v $ \case
NVConstant NNull | ignoreNulls -> pure Nothing
v' -> Just <$> (toNix =<< Text.pack <$> coerceToString v')
v' -> Just <$> (toNix =<< Text.pack <$> coerceToString True v')
nixInstantiateExpr expr = do
traceM $ "Executing: "
@ -613,12 +645,12 @@ instance (MonadFix m, MonadCatch m, MonadIO m, Alternative m,
-- return response
let status = statusCode (responseStatus response)
if status /= 200
then throwError $ ErrorCall $
then throwError $ ErrorCall $
"fail, got " ++ show status ++ " when fetching url:" ++ urlstr
else -- do
-- let bstr = responseBody response
-- liftIO $ print bstr
throwError $ ErrorCall $
throwError $ ErrorCall $
"success in downloading but hnix-store is not yet ready; url = " ++ urlstr
traceEffect = liftIO . putStrLn
@ -676,49 +708,66 @@ x <///> y | isAbsolute y || "." `isPrefixOf` y = x </> y
joinPath $ head [ xs ++ drop (length tx) ys
| tx <- tails xs, tx `elem` inits ys ]
nixFilePath :: (MonadEffects m, MonadIO m) => FilePath -> m (Maybe FilePath)
nixFilePath path = do
path <- makeAbsolutePath path
exists <- liftIO $ doesDirectoryExist path
path' <- if exists
then makeAbsolutePath $ path </> "default.nix"
else return path
exists <- liftIO $ doesFileExist path'
return $ if exists then Just path' else Nothing
findEnvPathM :: forall e m. (MonadNix e m, MonadIO m)
=> FilePath -> m FilePath
findEnvPathM name = do
mres <- lookupVar @_ @(NThunk m) "__nixPath"
mpath <- case mres of
Nothing -> error "impossible"
Just x -> force x $ fromValue >=> \(l :: [NThunk m]) ->
foldM go Nothing l
findPathBy :: forall e m. (MonadNix e m, MonadIO m) =>
(FilePath -> m (Maybe FilePath)) ->
[NThunk m] -> FilePath -> m FilePath
findPathBy finder l name = do
mpath <- foldM go Nothing l
case mpath of
Nothing ->
throwError $ ErrorCall $ "file '" ++ name
++ "' was not found in the Nix search path"
++ " (add it using $NIX_PATH or -I)"
Just path -> return path
where
go :: Maybe FilePath -> NThunk m -> m (Maybe FilePath)
go p@(Just _) _ = pure p
go Nothing l = force l $ fromValue >=> \(s :: HashMap Text (NThunk m)) ->
case M.lookup "path" s of
Just p -> force p $ fromValue >=> \(Path path) ->
case M.lookup "prefix" s of
Nothing -> tryPath path Nothing
Just pf -> force pf $ fromValueMay >=> \case
Just (pfx :: Text) | not (Text.null pfx) ->
tryPath path (Just (Text.unpack pfx))
_ -> tryPath path Nothing
Nothing ->
throwError $ ErrorCall $ "__nixPath must be a list of attr sets"
++ " with 'path' elements, but saw: " ++ show s
where
go :: Maybe FilePath -> NThunk m -> m (Maybe FilePath)
go p@(Just _) _ = pure p
go Nothing l = force l $ fromValue >=>
\(s :: HashMap Text (NThunk m)) ->
case M.lookup "path" s of
Just p -> force p $ fromValue >=> \(Path path) ->
case M.lookup "prefix" s of
Nothing -> tryPath path Nothing
Just pf -> force pf $ fromValueMay >=> \case
Just (pfx :: Text) | not (Text.null pfx) ->
tryPath path (Just (Text.unpack pfx))
_ -> tryPath path Nothing
Nothing ->
throwError $ ErrorCall $ "__nixPath must be a list of attr sets"
++ " with 'path' elements, but saw: " ++ show s
tryPath p (Just n) | n':ns <- splitDirectories name, n == n' =
nixFilePath $ p <///> joinPath ns
tryPath p _ = nixFilePath $ p <///> name
tryPath p (Just n) | n':ns <- splitDirectories name, n == n' =
finder $ p <///> joinPath ns
tryPath p _ = finder $ p <///> name
findPathM :: forall e m. (MonadNix e m, MonadIO m) =>
[NThunk m] -> FilePath -> m FilePath
findPathM l name = findPathBy path l name
where
path :: (MonadEffects m, MonadIO m) => FilePath -> m (Maybe FilePath)
path path = do
path <- makeAbsolutePath path
exists <- liftIO $ doesPathExist path
return $ if exists then Just path else Nothing
findEnvPathM :: forall e m. (MonadNix e m, MonadIO m)
=> FilePath -> m FilePath
findEnvPathM name = do
mres <- lookupVar @_ @(NThunk m) "__nixPath"
case mres of
Nothing -> error "impossible"
Just x -> force x $ fromValue >=> \(l :: [NThunk m]) ->
findPathBy nixFilePath l name
where
nixFilePath :: (MonadEffects m, MonadIO m) => FilePath -> m (Maybe FilePath)
nixFilePath path = do
path <- makeAbsolutePath path
exists <- liftIO $ doesDirectoryExist path
path' <- if exists
then makeAbsolutePath $ path </> "default.nix"
else return path
exists <- liftIO $ doesFileExist path'
return $ if exists then Just path' else Nothing
addTracing :: (MonadNix e m, Has e Options, MonadIO m,
MonadReader Int n, Alternative n)
@ -732,13 +781,17 @@ addTracing k v = do
opts :: Options <- asks (view hasLens)
let rendered =
if verbose opts >= Chatty
then show (void x)
else show (prettyNix (Fix (Fix (NSym "?") <$ x)))
msg x = "eval: " ++ replicate depth ' ' ++ x
loc <- renderLocation span (text (msg rendered ++ " ..."))
#ifdef MIN_VERSION_pretty_show
then text $ PS.ppShow (void x)
#else
then text $ show (void x)
#endif
else prettyNix (Fix (Fix (NSym "?") <$ x))
msg x = text ("eval: " ++ replicate depth ' ') <> x
loc <- renderLocation span (msg rendered <> text " ...\n")
liftIO $ putStr $ show loc
res <- k v'
liftIO $ putStrLn $ msg (rendered ++ " ...done")
liftIO $ print $ msg rendered <> text " ...done"
return res
evalExprLoc :: forall e m. (MonadNix e m, Has e Options, MonadIO m)

View file

@ -13,6 +13,7 @@ import Data.Monoid
import Data.Text (Text)
import Nix.Atoms
import Nix.Expr.Types
import Text.Megaparsec.Pos (SourcePos)
-- | Make an integer literal expression.
mkInt :: Integer -> NExpr
@ -40,13 +41,6 @@ mkIndentedStr w = Fix . NStr . Indented w . \case
"" -> []
x -> [Plain x]
-- | Make a literal URI expression.
mkUri :: Text -> NExpr
mkUri = Fix . mkUriF
mkUriF :: Text -> NExprF a
mkUriF = NConstant . NUri
-- | Make a path. Use 'True' if the path should be read from the
-- environment, else 'False'.
mkPath :: Bool -> FilePath -> NExpr
@ -78,7 +72,7 @@ mkSymF :: Text -> NExprF a
mkSymF = NSym
mkSelector :: Text -> NAttrPath NExpr
mkSelector = (:| []) . flip StaticKey Nothing
mkSelector = (:| []) . StaticKey
mkBool :: Bool -> NExpr
mkBool = Fix . mkBoolF
@ -140,16 +134,16 @@ mkDots e keys = Fix $ NSelect e (map (StaticKey ?? Nothing) keys) Nothing
-}
-- | An `inherit` clause without an expression to pull from.
inherit :: [NKeyName e] -> Binding e
inherit :: [NKeyName e] -> SourcePos -> Binding e
inherit = Inherit Nothing
-- | An `inherit` clause with an expression to pull from.
inheritFrom :: e -> [NKeyName e] -> Binding e
inheritFrom :: e -> [NKeyName e] -> SourcePos -> Binding e
inheritFrom expr = Inherit (Just expr)
-- | Shorthand for producing a binding of a name to an expression.
bindTo :: Text -> NExpr -> Binding NExpr
bindTo name = NamedVar (mkSelector name)
bindTo name x = NamedVar (mkSelector name) x nullPos
-- | Infix version of bindTo.
($=) :: Text -> NExpr -> Binding NExpr
@ -233,5 +227,5 @@ infixl 1 @@
infixr 1 ==>
(@.) :: NExpr -> Text -> NExpr
obj @. name = Fix (NSelect obj (StaticKey name Nothing :| []) Nothing)
obj @. name = Fix (NSelect obj (StaticKey name :| []) Nothing)
infixl 2 @.

View file

@ -1,5 +1,6 @@
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFoldable #-}
@ -26,8 +27,10 @@
-- | The nix expression type and supporting types.
module Nix.Expr.Types where
#ifdef MIN_VERSION_serialise
import Codec.Serialise (Serialise)
import qualified Codec.Serialise as Ser
#endif
import Control.Applicative
import Control.DeepSeq
import Control.Monad
@ -40,7 +43,9 @@ import Data.Eq.Deriving
import Data.Fix
import Data.Functor.Classes
import Data.Hashable
#if MIN_VERSION_hashable(1, 2, 5)
import Data.Hashable.Lifted
#endif
import Data.List (inits, tails)
import Data.List.NonEmpty (NonEmpty(..))
import qualified Data.List.NonEmpty as NE
@ -53,19 +58,37 @@ import GHC.Exts
import GHC.Generics
import Language.Haskell.TH.Syntax
import Lens.Family2
import Lens.Family2.Stock (_1)
import Lens.Family2.TH
import Nix.Atoms
import Nix.Utils
import Text.Megaparsec.Pos
import Text.Read.Deriving
import Text.Show.Deriving
#if MIN_VERSION_base(4, 10, 0)
import Type.Reflection (eqTypeRep)
import qualified Type.Reflection as Reflection
#endif
type VarName = Text
instance Hashable1 NonEmpty -- an unfortunate orphan
-- unfortunate orphans
#if MIN_VERSION_hashable(1, 2, 5)
instance Hashable1 NonEmpty
#endif
#if !MIN_VERSION_base(4, 10, 0)
instance Eq1 NonEmpty where
liftEq eq (a NE.:| as) (b NE.:| bs) = eq a b && liftEq eq as bs
instance Show1 NonEmpty where
liftShowsPrec shwP shwL p (a NE.:| as) = showParen (p > 5) $
shwP 6 a . showString " :| " . shwL as
#endif
#if !MIN_VERSION_binary(0, 8, 4)
instance Binary a => Binary (NE.NonEmpty a) where
get = fmap NE.fromList Bin.get
put = Bin.put . NE.toList
#endif
-- | The main nix expression type. This is polymorphic so that it can be made
-- a functor, which allows us to traverse expressions and map functions over
@ -113,35 +136,71 @@ data NExprF r
| NAssert !r !r
-- ^ Assert that the first returns true before evaluating the second.
deriving (Ord, Eq, Generic, Generic1, Typeable, Data, Functor,
Foldable, Traversable, Show, NFData, NFData1, Serialise,
Hashable, Hashable1)
Foldable, Traversable, Show, NFData,
Hashable)
#if MIN_VERSION_hashable(1, 2, 5)
instance Hashable1 NExprF
#endif
#if MIN_VERSION_deepseq(1, 4, 3)
instance NFData1 NExprF
#endif
#ifdef MIN_VERSION_serialise
instance Serialise r => Serialise (NExprF r)
#endif
-- | We make an `IsString` for expressions, where the string is interpreted
-- as an identifier. This is the most common use-case...
instance IsString NExpr where
fromString = Fix . NSym . fromString
#if MIN_VERSION_base(4, 10, 0)
instance Lift (Fix NExprF) where
lift = dataToExpQ $ \b ->
case Reflection.typeOf b `eqTypeRep` Reflection.typeRep @Text of
Just HRefl -> Just [| pack $(liftString $ unpack b) |]
Nothing -> Nothing
#else
instance Lift (Fix NExprF) where
lift = dataToExpQ $ \b -> case cast b of
Just t -> Just [| pack $(liftString $ unpack t) |]
Nothing -> Nothing
#endif
-- | The monomorphic expression type is a fixed point of the polymorphic one.
type NExpr = Fix NExprF
#ifdef MIN_VERSION_serialise
instance Serialise NExpr
#endif
-- | A single line of the bindings section of a let expression or of a set.
data Binding r
= NamedVar !(NAttrPath r) !r
= NamedVar !(NAttrPath r) !r !SourcePos
-- ^ An explicit naming, such as @x = y@ or @x.y = z@.
| Inherit !(Maybe r) ![NKeyName r]
| Inherit !(Maybe r) ![NKeyName r] !SourcePos
-- ^ Using a name already in scope, such as @inherit x;@ which is shorthand
-- for @x = x;@ or @inherit (x) y;@ which means @y = x.y;@.
-- for @x = x;@ or @inherit (x) y;@ which means @y = x.y;@. The
-- unsafeGetAttrPos for every name so inherited is the position of the
-- first name, whether that be the first argument to this constructor, or
-- the first member of the list in the second argument.
deriving (Generic, Generic1, Typeable, Data, Ord, Eq, Functor,
Foldable, Traversable, Show, NFData, NFData1, Serialise,
Hashable, Hashable1)
Foldable, Traversable, Show, NFData,
Hashable)
#if MIN_VERSION_hashable(1, 2, 5)
instance Hashable1 Binding
#endif
#if MIN_VERSION_deepseq(1, 4, 3)
instance NFData1 Binding
#endif
#ifdef MIN_VERSION_serialise
instance Serialise r => Serialise (Binding r)
#endif
-- | @Params@ represents all the ways the formal parameters to a
-- function can be represented.
@ -153,8 +212,19 @@ data Params r
-- bind to the set in the function body. The bool indicates whether it is
-- variadic or not.
deriving (Ord, Eq, Generic, Generic1, Typeable, Data, Functor, Show,
Foldable, Traversable, NFData, NFData1, Serialise,
Hashable, Hashable1)
Foldable, Traversable, NFData, Hashable)
#if MIN_VERSION_hashable(1, 2, 5)
instance Hashable1 Params
#endif
#if MIN_VERSION_deepseq(1, 4, 3)
instance NFData1 Params
#endif
#ifdef MIN_VERSION_serialise
instance Serialise r => Serialise (Params r)
#endif
-- This uses an association list because nix XML serialization preserves the
-- order of the param set.
@ -167,8 +237,10 @@ instance IsString (Params r) where
-- antiquoted (surrounded by ${...}) or plain (not antiquoted).
data Antiquoted (v :: *) (r :: *) = Plain !v | EscapedNewline | Antiquoted !r
deriving (Ord, Eq, Generic, Generic1, Typeable, Data, Functor, Foldable,
Traversable, Show, Read, NFData, NFData1, Serialise,
Hashable, Hashable1)
Traversable, Show, Read, NFData, Hashable)
#if MIN_VERSION_hashable(1, 2, 5)
instance Hashable v => Hashable1 (Antiquoted v)
instance Hashable2 Antiquoted where
liftHashWithSalt2 ha _ salt (Plain a) =
@ -177,6 +249,15 @@ instance Hashable2 Antiquoted where
salt `hashWithSalt` (1 :: Int)
liftHashWithSalt2 _ hb salt (Antiquoted b) =
hb (salt `hashWithSalt` (2 :: Int)) b
#endif
#if MIN_VERSION_deepseq(1, 4, 3)
instance NFData v => NFData1 (Antiquoted v)
#endif
#ifdef MIN_VERSION_serialise
instance (Serialise v, Serialise r) => Serialise (Antiquoted v r)
#endif
-- | An 'NString' is a list of things that are either a plain string
-- or an antiquoted expression. After the antiquotes have been evaluated,
@ -190,8 +271,19 @@ data NString r
-- their indentation will be stripped, but the amount stripped is
-- remembered.
deriving (Eq, Ord, Generic, Generic1, Typeable, Data, Functor, Foldable,
Traversable, Show, Read, NFData, NFData1, Serialise,
Hashable, Hashable1)
Traversable, Show, Read, NFData, Hashable)
#if MIN_VERSION_hashable(1, 2, 5)
instance Hashable1 NString
#endif
#if MIN_VERSION_deepseq(1, 4, 3)
instance NFData1 NString
#endif
#ifdef MIN_VERSION_serialise
instance Serialise r => Serialise (NString r)
#endif
-- | For the the 'IsString' instance, we use a plain doublequoted string.
instance IsString (NString r) where
@ -219,9 +311,12 @@ instance IsString (NString r) where
-- parser still considers it a 'DynamicKey' for simplicity.
data NKeyName r
= DynamicKey !(Antiquoted (NString r) r)
| StaticKey !VarName !(Maybe SourcePos)
| StaticKey !VarName
deriving (Eq, Ord, Generic, Typeable, Data, Show, Read, NFData,
Serialise, Hashable)
Hashable)
#ifdef MIN_VERSION_serialise
instance Serialise r => Serialise (NKeyName r)
instance Serialise Pos where
encode x = Ser.encode (unPos x)
@ -230,6 +325,7 @@ instance Serialise Pos where
instance Serialise SourcePos where
encode (SourcePos f l c) = Ser.encode f <> Ser.encode l <> Ser.encode c
decode = SourcePos <$> Ser.decode <*> Ser.decode <*> Ser.decode
#endif
instance Hashable Pos where
hashWithSalt salt x = hashWithSalt salt (unPos x)
@ -243,33 +339,37 @@ instance Generic1 NKeyName where
from1 = id
to1 = id
#if MIN_VERSION_deepseq(1, 4, 3)
instance NFData1 NKeyName where
liftRnf _ (StaticKey !_ !_) = ()
liftRnf _ (StaticKey !_) = ()
liftRnf _ (DynamicKey (Plain !_)) = ()
liftRnf _ (DynamicKey EscapedNewline) = ()
liftRnf k (DynamicKey (Antiquoted r)) = k r
#endif
-- | Most key names are just static text, so this instance is convenient.
instance IsString (NKeyName r) where
fromString = flip StaticKey Nothing . fromString
fromString = StaticKey . fromString
instance Eq1 NKeyName where
liftEq eq (DynamicKey a) (DynamicKey b) = liftEq2 (liftEq eq) eq a b
liftEq _ (StaticKey a _) (StaticKey b _) = a == b
liftEq _ (StaticKey a) (StaticKey b) = a == b
liftEq _ _ _ = False
#if MIN_VERSION_hashable(1, 2, 5)
instance Hashable1 NKeyName where
liftHashWithSalt h salt (DynamicKey a) =
liftHashWithSalt2 (liftHashWithSalt h) h (salt `hashWithSalt` (0 :: Int)) a
liftHashWithSalt _ salt (StaticKey n p) =
salt `hashWithSalt` (1 :: Int) `hashWithSalt` n `hashWithSalt` p
liftHashWithSalt _ salt (StaticKey n) =
salt `hashWithSalt` (1 :: Int) `hashWithSalt` n
#endif
-- Deriving this instance automatically is not possible because @r@
-- occurs not only as last argument in @Antiquoted (NString r) r@
instance Show1 NKeyName where
liftShowsPrec sp sl p = \case
DynamicKey a -> showsUnaryWith (liftShowsPrec2 (liftShowsPrec sp sl) (liftShowList sp sl) sp sl) "DynamicKey" p a
StaticKey t _ -> showsUnaryWith showsPrec "StaticKey" p t
StaticKey t -> showsUnaryWith showsPrec "StaticKey" p t
-- Deriving this instance automatically is not possible because @r@
-- occurs not only as last argument in @Antiquoted (NString r) r@
@ -286,9 +386,9 @@ instance Foldable NKeyName where
instance Traversable NKeyName where
traverse f = \case
DynamicKey (Plain str) -> DynamicKey . Plain <$> traverse f str
DynamicKey EscapedNewline -> pure $ DynamicKey EscapedNewline
DynamicKey (Antiquoted e) -> DynamicKey . Antiquoted <$> f e
StaticKey key pos -> pure (StaticKey key pos)
DynamicKey EscapedNewline -> pure $ DynamicKey EscapedNewline
StaticKey key -> pure (StaticKey key)
-- | A selector (for example in a @let@ or an attribute set) is made up
-- of strung-together key names.
@ -296,8 +396,12 @@ type NAttrPath r = NonEmpty (NKeyName r)
-- | There are two unary operations: logical not and integer negation.
data NUnaryOp = NNeg | NNot
deriving (Eq, Ord, Generic, Typeable, Data, Show, Read, NFData,
Serialise, Hashable)
deriving (Eq, Ord, Enum, Bounded, Generic, Typeable, Data, Show, Read,
NFData, Hashable)
#ifdef MIN_VERSION_serialise
instance Serialise NUnaryOp
#endif
-- | Binary operators expressible in the nix language.
data NBinaryOp
@ -317,14 +421,22 @@ data NBinaryOp
| NDiv -- ^ Division (/)
| NConcat -- ^ List concatenation (++)
| NApp -- ^ Apply a function to an argument.
deriving (Eq, Ord, Generic, Typeable, Data, Show, Read, NFData,
Serialise, Hashable)
deriving (Eq, Ord, Enum, Bounded, Generic, Typeable, Data, Show, Read,
NFData, Hashable)
#ifdef MIN_VERSION_serialise
instance Serialise NBinaryOp
#endif
-- | Get the name out of the parameter (there might be none).
paramName :: Params r -> Maybe VarName
paramName (Param n) = Just n
paramName (ParamSet _ _ n) = n
#if !MIN_VERSION_deepseq(1, 4, 3)
instance NFData NExpr
#endif
$(deriveEq1 ''NExprF)
$(deriveEq1 ''NString)
$(deriveEq1 ''Binding)
@ -415,16 +527,17 @@ class NExprAnn ann g | g -> ann where
ekey :: NExprAnn ann g
=> NonEmpty Text
-> SourcePos
-> Lens' (Fix g) (Maybe (Fix g))
ekey keys f e@(Fix x) | (NSet xs, ann) <- fromNExpr x =
ekey keys pos f e@(Fix x) | (NSet xs, ann) <- fromNExpr x =
case go xs of
((v, []):_) -> fromMaybe e <$> f (Just v)
((v, r:rest):_) -> ekey (r :| rest) f v
((v, r:rest):_) -> ekey (r :| rest) pos f v
_ -> f Nothing <&> \case
Nothing -> e
Just v ->
let entry = NamedVar (NE.map (StaticKey ?? Nothing) keys) v
let entry = NamedVar (NE.map StaticKey keys) v pos
in Fix (toNExpr (NSet (entry : xs), ann))
where
go xs = do
@ -433,11 +546,11 @@ ekey keys f e@(Fix x) | (NSet xs, ann) <- fromNExpr x =
case ks of
[] -> empty
j:js -> do
NamedVar ns v <- xs
guard $ (j:js) == (NE.toList ns ^.. traverse._StaticKey._1)
NamedVar ns v _p <- xs
guard $ (j:js) == (NE.toList ns ^.. traverse._StaticKey)
return (v, rest)
ekey _ f e = fromMaybe e <$> f Nothing
ekey _ _ f e = fromMaybe e <$> f Nothing
stripPositionInfo :: NExpr -> NExpr
stripPositionInfo = transport phi
@ -445,11 +558,10 @@ stripPositionInfo = transport phi
phi (NSet binds) = NSet (map go binds)
phi (NRecSet binds) = NRecSet (map go binds)
phi (NLet binds body) = NLet (map go binds) body
phi (NSelect s attr alt) = NSelect s (NE.map clear attr) alt
phi x = x
go (NamedVar path r) = NamedVar (NE.map clear path) r
go (Inherit ms names) = Inherit ms (map clear names)
go (NamedVar path r _pos) = NamedVar path r nullPos
go (Inherit ms names _pos) = Inherit ms names nullPos
clear (StaticKey name _) = StaticKey name Nothing
clear k = k
nullPos :: SourcePos
nullPos = SourcePos "<string>" (mkPos 1) (mkPos 1)

View file

@ -1,4 +1,5 @@
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
@ -20,7 +21,9 @@ module Nix.Expr.Types.Annotated
, SourcePos(..), unPos, mkPos
)where
#ifdef MIN_VERSION_serialise
import Codec.Serialise
#endif
import Control.DeepSeq
import Data.Aeson (ToJSON(..), FromJSON(..))
import Data.Aeson.TH
@ -31,7 +34,9 @@ import Data.Fix
import Data.Function (on)
import Data.Functor.Compose
import Data.Hashable
#if MIN_VERSION_hashable(1, 2, 5)
import Data.Hashable.Lifted
#endif
import Data.Ord.Deriving
import Data.Semigroup
import Data.Text (Text, pack)
@ -48,9 +53,13 @@ data SrcSpan = SrcSpan
{ spanBegin :: SourcePos
, spanEnd :: SourcePos
}
deriving (Ord, Eq, Generic, Typeable, Data, Show, NFData, Serialise,
deriving (Ord, Eq, Generic, Typeable, Data, Show, NFData,
Hashable)
#ifdef MIN_VERSION_serialise
instance Serialise SrcSpan
#endif
-- | A type constructor applied to a type along with an annotation
--
-- Intended to be used with 'Fix':
@ -60,8 +69,19 @@ data Ann ann a = Ann
, annotated :: a
}
deriving (Ord, Eq, Data, Generic, Generic1, Typeable, Functor, Foldable,
Traversable, Read, Show, NFData, NFData1, Serialise,
Hashable, Hashable1)
Traversable, Read, Show, NFData, Hashable)
#if MIN_VERSION_hashable(1, 2, 5)
instance Hashable ann => Hashable1 (Ann ann)
#endif
#ifdef MIN_VERSION_serialise
instance (Serialise ann, Serialise a) => Serialise (Ann ann a)
#endif
#if MIN_VERSION_deepseq(1, 4, 3)
instance NFData ann => NFData1 (Ann ann)
#endif
$(deriveEq1 ''Ann)
$(deriveEq2 ''Ann)
@ -88,9 +108,19 @@ type NExprLocF = AnnF SrcSpan NExprF
-- | A nix expression with source location at each subexpression.
type NExprLoc = Fix NExprLocF
#if !MIN_VERSION_deepseq(1, 4, 3)
instance (NFData (f (g a)), NFData (g a)) => NFData (Compose f g a)
#endif
instance NFData NExprLoc
#ifdef MIN_VERSION_serialise
instance Serialise NExprLoc
#endif
#if MIN_VERSION_hashable(1, 2, 5)
instance Hashable NExprLoc
#endif
instance Binary SrcSpan
instance (Binary ann, Binary a) => Binary (Ann ann a)
@ -100,9 +130,11 @@ instance Binary NExprLoc
instance ToJSON SrcSpan
instance FromJSON SrcSpan
#ifdef MIN_VERSION_serialise
instance Serialise r => Serialise (Compose (Ann SrcSpan) NExprF r) where
encode (Compose (Ann ann a)) = encode ann <> encode a
decode = (Compose .) . Ann <$> decode <*> decode
#endif
pattern AnnE :: forall ann (g :: * -> *). ann
-> g (Fix (Compose (Ann ann) g)) -> Fix (Compose (Ann ann) g)
@ -155,9 +187,6 @@ nNull = Fix (Compose (Ann nullSpan (NConstant NNull)))
nullSpan :: SrcSpan
nullSpan = SrcSpan nullPos nullPos
nullPos :: SourcePos
nullPos = SourcePos "<string>" (mkPos 1) (mkPos 1)
-- | Pattern systems for matching on NExprLocF constructions.
pattern NSym_ :: SrcSpan -> VarName -> NExprLocF r

View file

@ -76,7 +76,7 @@ valueText :: forall e m. (Framed e m, MonadEffects m, Typeable m)
=> Bool -> NValueNF m -> m NixString
valueText addPathsToStore = cata phi
where
--phi :: () -- NValueF m (m ns) -> m ns
phi :: NValueF m NixString -> m NixString
phi (NVConstantF a) = pure (makeNixStringWithoutContext (atomText a))
phi (NVStrF ns) = pure ns
phi v@(NVListF _) = coercionFailed v

127
src/Nix/Options/Parser.hs Normal file
View file

@ -0,0 +1,127 @@
module Nix.Options.Parser where
import Control.Arrow (second)
import Data.Char (isDigit)
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Time
import Nix.Options
import Options.Applicative hiding (ParserResult(..))
import Text.PrettyPrint.ANSI.Leijen hiding ((<$>))
decodeVerbosity :: Int -> Verbosity
decodeVerbosity 0 = ErrorsOnly
decodeVerbosity 1 = Informational
decodeVerbosity 2 = Talkative
decodeVerbosity 3 = Chatty
decodeVerbosity 4 = DebugInfo
decodeVerbosity _ = Vomit
argPair :: Mod OptionFields (Text, Text) -> Parser (Text, Text)
argPair = option $ str >>= \s ->
case Text.findIndex (== '=') s of
Nothing -> errorWithoutStackTrace
"Format of --arg/--argstr in hnix is: name=expr"
Just i -> return $ second Text.tail $ Text.splitAt i s
nixOptions :: UTCTime -> Parser Options
nixOptions current = Options
<$> (fromMaybe ErrorsOnly <$>
optional
(option (do a <- str
if all isDigit a
then pure $ decodeVerbosity (read a)
else fail "Argument to -v/--verbose must be a number")
( short 'v'
<> long "verbose"
<> help "Verbose output")))
<*> switch
( long "trace"
<> help "Enable tracing code (even more can be seen if built with --flags=tracing)")
<*> switch
( long "thunks"
<> help "Enable reporting of thunk tracing as well as regular evaluation")
<*> switch
( long "values"
<> help "Enable reporting of value provenance in error messages")
<*> optional (strOption
( long "reduce"
<> help "When done evaluating, output the evaluated part of the expression to FILE"))
<*> switch
( long "reduce-sets"
<> help "Reduce set members that aren't used; breaks if hasAttr is used")
<*> switch
( long "reduce-lists"
<> help "Reduce list members that aren't used; breaks if elemAt is used")
<*> switch
( long "parse"
<> help "Whether to parse the file (also the default right now)")
<*> switch
( long "parse-only"
<> help "Whether to parse only, no pretty printing or checking")
<*> switch
( long "find"
<> help "If selected, find paths within attr trees")
<*> optional (strOption
( long "find-file"
<> help "Look up the given files in Nix's search path"))
<*> switch
( long "strict"
<> help "When used with --eval, recursively evaluate list elements and attributes")
<*> switch
( long "eval"
<> help "Whether to evaluate, or just pretty-print")
<*> switch
( long "json"
<> help "Print the resulting value as an JSON representation")
<*> switch
( long "xml"
<> help "Print the resulting value as an XML representation")
<*> optional (strOption
( short 'A'
<> long "attr"
<> help "Select an attribute from the top-level Nix expression being evaluated"))
<*> many (strOption
( short 'I'
<> long "include"
<> help "Add a path to the Nix expression search path"))
<*> switch
( long "check"
<> help "Whether to check for syntax errors after parsing")
<*> optional (strOption
( long "read"
<> help "Read in an expression tree from a binary cache"))
<*> switch
( long "cache"
<> help "Write out the parsed expression tree to a binary cache")
<*> switch
( long "repl"
<> help "After performing any indicated actions, enter the REPL")
<*> switch
( long "ignore-errors"
<> help "Continue parsing files, even if there are errors")
<*> optional (strOption
( short 'E'
<> long "expr"
<> help "Expression to parse or evaluate"))
<*> many (argPair
( long "arg"
<> help "Argument to pass to an evaluated lambda"))
<*> many (argPair
( long "argstr"
<> help "Argument string to pass to an evaluated lambda"))
<*> optional (strOption
( short 'f'
<> long "file"
<> help "Parse all of the files given in FILE; - means stdin"))
<*> option (parseTimeOrError True defaultTimeLocale "%Y/%m/%d %H:%M:%S" <$> str)
( long "now"
<> value current
<> help "Set current time for testing purposes")
<*> many (strArgument (metavar "FILE" <> help "Path of file to parse"))
nixOptionsInfo :: UTCTime -> ParserInfo Options
nixOptionsInfo current =
info (helper <*> nixOptions current)
(fullDesc <> progDesc "" <> header "hnix")

View file

@ -1,3 +1,4 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
@ -29,7 +30,9 @@ import Nix.Value
import Text.Megaparsec.Pos
import qualified Text.PrettyPrint.ANSI.Leijen as P
import Text.PrettyPrint.ANSI.Leijen hiding ((<$>))
#ifdef MIN_VERSION_pretty_show
import qualified Text.Show.Pretty as PS
#endif
renderFrames :: forall v e m.
(MonadReader e m, Has e Options,
@ -111,7 +114,11 @@ renderExpr _level longLabel shortLabel e@(Fix (Compose (Ann _ x))) = do
opts :: Options <- asks (view hasLens)
let rendered
| verbose opts >= DebugInfo =
#ifdef MIN_VERSION_pretty_show
text (PS.ppShow (stripAnnotation e))
#else
text (show (stripAnnotation e))
#endif
| verbose opts >= Chatty =
prettyNix (stripAnnotation e)
| otherwise =

View file

@ -99,7 +99,7 @@ instance Substitutable Type where
apply s (TSet b a) = TSet b (M.map (apply s) a)
apply s (TList a) = TList (map (apply s) a)
apply (Subst s) t@(TVar a) = Map.findWithDefault t a s
apply s (t1 `TArr` t2) = apply s t1 `TArr` apply s t2
apply s (t1 :~> t2) = apply s t1 :~> apply s t2
apply s (TMany ts) = TMany (map (apply s) ts)
instance Substitutable Scheme where
@ -122,12 +122,12 @@ class FreeTypeVars a where
ftv :: a -> Set.Set TVar
instance FreeTypeVars Type where
ftv TCon{} = Set.empty
ftv (TVar a) = Set.singleton a
ftv (TSet _ a) = Set.unions (map ftv (M.elems a))
ftv (TList a) = Set.unions (map ftv a)
ftv (t1 `TArr` t2) = ftv t1 `Set.union` ftv t2
ftv (TMany ts) = Set.unions (map ftv ts)
ftv TCon{} = Set.empty
ftv (TVar a) = Set.singleton a
ftv (TSet _ a) = Set.unions (map ftv (M.elems a))
ftv (TList a) = Set.unions (map ftv a)
ftv (t1 :~> t2) = ftv t1 `Set.union` ftv t2
ftv (TMany ts) = Set.unions (map ftv ts)
instance FreeTypeVars TVar where
ftv = Set.singleton
@ -359,7 +359,6 @@ instance MonadEval (Judgment s) (Infer s) where
NFloat _ -> typeFloat
NBool _ -> typeBool
NNull -> typeNull
NUri _ -> typeUri
evalString = const $ return $ Judgment As.empty [] typeString
evalLiteralPath = const $ return $ Judgment As.empty [] typePath
@ -367,14 +366,14 @@ instance MonadEval (Judgment s) (Infer s) where
evalUnary op (Judgment as1 cs1 t1) = do
tv <- fresh
return $ Judgment as1 (cs1 ++ unops (t1 `TArr` tv) op) tv
return $ Judgment as1 (cs1 ++ unops (t1 :~> tv) op) tv
evalBinary op (Judgment as1 cs1 t1) e2 = do
Judgment as2 cs2 t2 <- e2
tv <- fresh
return $ Judgment
(as1 `As.merge` as2)
(cs1 ++ cs2 ++ binops (t1 `TArr` (t2 `TArr` tv)) op)
(cs1 ++ cs2 ++ binops (t1 :~> t2 :~> tv) op)
tv
evalWith = Eval.evalWithAttrSet
@ -399,7 +398,7 @@ instance MonadEval (Judgment s) (Infer s) where
tv <- fresh
return $ Judgment
(as1 `As.merge` as2)
(cs1 ++ cs2 ++ [EqConst t1 (t2 `TArr` tv)])
(cs1 ++ cs2 ++ [EqConst t1 (t2 :~> tv)])
tv
evalAbs (Param x) k = do
@ -410,7 +409,7 @@ instance MonadEval (Judgment s) (Infer s) where
return $ Judgment
(as `As.remove` x)
(cs ++ [EqConst t' tv | t' <- As.lookup x as])
(tv `TArr` t)
(tv :~> t)
evalAbs (ParamSet ps variadic _mname) k = do
js <- fmap concat $ forM ps $ \(name, _) -> do
@ -434,7 +433,7 @@ instance MonadEval (Judgment s) (Infer s) where
(cs ++ [ EqConst t' (tys M.! x)
| x <- names
, t' <- As.lookup x as])
(ty `TArr` t)
(ty :~> t)
evalError = throwError . EvaluationError
@ -492,18 +491,18 @@ normalize (Forall _ body) = Forall (map snd ord) (normtype body)
ord = zip (nub $ fv body) (map TV letters)
fv (TVar a) = [a]
fv (TArr a b) = fv a ++ fv b
fv (a :~> b) = fv a ++ fv b
fv (TCon _) = []
fv (TSet _ a) = concatMap fv (M.elems a)
fv (TList a) = concatMap fv a
fv (TMany ts) = concatMap fv ts
normtype (TArr a b) = TArr (normtype a) (normtype b)
normtype (TCon a) = TCon a
normtype (TSet b a) = TSet b (M.map normtype a)
normtype (TList a) = TList (map normtype a)
normtype (TMany ts) = TMany (map normtype ts)
normtype (TVar a) =
normtype (a :~> b) = normtype a :~> normtype b
normtype (TCon a) = TCon a
normtype (TSet b a) = TSet b (M.map normtype a)
normtype (TList a) = TList (map normtype a)
normtype (TMany ts) = TMany (map normtype ts)
normtype (TVar a) =
case Prelude.lookup a ord of
Just x -> TVar x
Nothing -> error "type variable not in signature"
@ -571,7 +570,7 @@ unifies (TSet True s) (TSet False b)
| M.keys b `intersect` M.keys s == M.keys b = return emptySubst
unifies (TSet False s) (TSet False b)
| null (M.keys b \\ M.keys s) = return emptySubst
unifies (TArr t1 t2) (TArr t3 t4) = unifyMany [t1, t2] [t3, t4]
unifies (t1 :~> t2) (t3 :~> t4) = unifyMany [t1, t2] [t3, t4]
unifies (TMany t1s) t2 = considering t1s >>- unifies ?? t2
unifies t1 (TMany t2s) = considering t2s >>- unifies t1
unifies t1 t2 = throwError $ UnificationFail t1 t2

View file

@ -12,7 +12,7 @@ data Type
| TCon String -- known type
| TSet Bool (AttrSet Type) -- heterogenous map, bool if variadic
| TList [Type] -- heterogenous list
| TArr Type Type -- type -> type
| (:~>) Type Type -- type -> type
| TMany [Type] -- variant type
deriving (Show, Eq, Ord)
@ -26,16 +26,17 @@ typeSet = TSet True M.empty
typeList :: Type
typeList = TList []
typeFun :: [Type] -> Type
typeFun = foldr1 TArr
infixr 1 :~>
typeInt, typeFloat, typeBool, typeString, typePath, typeUri, typeNull :: Type
typeFun :: [Type] -> Type
typeFun = foldr1 (:~>)
typeInt, typeFloat, typeBool, typeString, typePath, typeNull :: Type
typeInt = TCon "integer"
typeFloat = TCon "float"
typeBool = TCon "boolean"
typeString = TCon "string"
typePath = TCon "path"
typeUri = TCon "uri"
typeNull = TCon "null"
type Name = Text

View file

@ -8,6 +8,7 @@
module EvalTests (tests, genEvalCompareTests) where
import Control.Monad.Catch
import Control.Monad (when)
import Control.Monad.IO.Class
import qualified Data.HashMap.Lazy as M
@ -30,6 +31,12 @@ case_basic_sum =
case_basic_div =
constantEqualText "3" "builtins.div 6 2"
case_zero_div = do
assertNixEvalThrows "builtins.div 1 0"
assertNixEvalThrows "builtins.div 1.0 0"
assertNixEvalThrows "builtins.div 1 0.0"
assertNixEvalThrows "builtins.div 1.0 0.0"
case_basic_function =
constantEqualText "2" "(a: a) 2"
@ -52,9 +59,11 @@ case_function_definition_uses_environment =
constantEqualText "3" "let f = (let a=1; in x: x+a); in f 2"
case_function_atpattern =
-- jww (2018-05-09): This should be constantEqualText
constantEqualText' "2" "(({a}@attrs:attrs) {a=2;}).a"
case_function_ellipsis =
-- jww (2018-05-09): This should be constantEqualText
constantEqualText' "2" "(({a, ...}@attrs:attrs) {a=0; b=2;}).b"
case_function_default_value_not_in_atpattern =
@ -67,7 +76,7 @@ case_function_recursive_args =
constantEqualText "2" "({ x ? 1, y ? x * 3}: y - x) {}"
case_function_recursive_sets =
constantEqualText' "[ [ 6 4 100 ] 4 ]" [i|
constantEqualText "[ [ 6 4 100 ] 4 ]" [i|
let x = rec {
y = 2;
@ -86,6 +95,30 @@ case_nested_with =
case_match_failure_null =
constantEqualText "null" "builtins.match \"ab\" \"abc\""
case_find_file_success_no_prefix =
constantEqualText "./tests/files/findFile.nix"
"builtins.findFile [{ path=\"./tests/files\"; prefix=\"\"; }] \"findFile.nix\""
case_find_file_success_with_prefix =
constantEqualText "./tests/files/findFile.nix"
"builtins.findFile [{ path=\"./tests/files\"; prefix=\"nix\"; }] \"nix/findFile.nix\""
case_find_file_success_folder =
constantEqualText "./tests/files"
"builtins.findFile [{ path=\"./tests\"; prefix=\"\"; }] \"files\""
case_find_file_failure_not_found =
assertNixEvalThrows "builtins.findFile [{ path=\"./tests/files\"; prefix=\"\"; }] \"not_found.nix\""
case_find_file_failure_invalid_arg_1 =
assertNixEvalThrows "builtins.findFile 1 \"files\""
case_find_file_failure_invalid_arg_2 =
assertNixEvalThrows "builtins.findFile [{ path=\"./tests/files\"; prefix=\"\"; }] 2"
case_find_file_failure_invalid_arg_no_path =
assertNixEvalThrows "builtins.findFile [{ prefix=\"\"; }] \"files\""
case_inherit_in_rec_set =
constantEqualText "1" "let x = 1; in (rec { inherit x; }).x"
@ -109,8 +142,100 @@ case_inherit_from_set_has_no_scope =
)).success
|]
case_unsafegetattrpos1 =
constantEqualText "[ 6 20 ]" [i|
let e = 1;
f = 1;
t = {};
s = {
inherit t e f;
a = 1;
"b" = 2;
c.d = 3;
};
p = builtins.unsafeGetAttrPos "e" s; in
[ p.line p.column ]
|]
case_unsafegetattrpos2 =
constantEqualText "[ 6 20 ]" [i|
let e = 1;
f = 1;
t = {};
s = {
inherit t e f;
a = 1;
"b" = 2;
c.d = 3;
};
p = builtins.unsafeGetAttrPos "f" s; in
[ p.line p.column ]
|]
case_unsafegetattrpos3 =
constantEqualText "[ 7 13 ]" [i|
let e = 1;
f = 1;
t = {};
s = {
inherit t e f;
a = 1;
"b" = 2;
c.d = 3;
};
p = builtins.unsafeGetAttrPos "a" s; in
[ p.line p.column ]
|]
case_unsafegetattrpos4 =
constantEqualText "[ 8 13 ]" [i|
let e = 1;
f = 1;
t = {};
s = {
inherit t e f;
a = 1;
"b" = 2;
c.d = 3;
};
p = builtins.unsafeGetAttrPos "b" s; in
[ p.line p.column ]
|]
-- jww (2018-05-09): These two are failing but they shouldn't be
-- case_unsafegetattrpos5 =
-- constantEqualText "[ 7 13 ]" [i|
-- let e = 1;
-- f = 1;
-- t = {};
-- s = {
-- inherit t e f;
-- a = 1;
-- "b" = 2;
-- c.d = 3;
-- };
-- p = builtins.unsafeGetAttrPos "c.d" s; in
-- [ p.line p.column ]
-- |]
-- case_unsafegetattrpos6 =
-- constantEqualText "[ 7 13 ]" [i|
-- let e = 1;
-- f = 1;
-- t = {};
-- s = {
-- inherit t e f;
-- a = 1;
-- "b" = 2;
-- c.d = 3;
-- };
-- p = builtins.unsafeGetAttrPos "d" s; in
-- [ p.line p.column ]
-- |]
case_fixed_points =
constantEqualText' [i|[
constantEqualText [i|[
{
foobar = "foobar";
foo = "foo";
@ -134,7 +259,7 @@ case_fixed_points =
|]
case_fixed_points_and_fold =
constantEqualText' [i|[ {} {} ]|] [i|
constantEqualText [i|[ {} {} ]|] [i|
let
extends = f: rattrs: self:
let super = rattrs self; in super // f self super;
@ -145,6 +270,13 @@ let
in [ (fix toFixFold) (fix toFix) ]
|]
case_fixed_points_attrsets =
constantEqualText "{ x = { y = { z = 100; }; z = { y = 100; }; }; }" [i|
let fix = f: let x = f x; in x;
f = self: { x.z.y = 100; x.y.z = self.x.z.y; };
in fix f
|]
-- jww (2018-05-02): This constantly changes!
-- case_placeholder =
-- constantEqualText
@ -170,6 +302,7 @@ instance (Show r, Show (NValueF m r), Eq r) => Eq (NValueF m r) where
NVSetF x _ == NVSetF y _ =
M.keys x == M.keys y &&
and (zipWith (==) (M.elems x) (M.elems y))
NVPathF x == NVPathF y = x == y
x == y = error $ "Need to add comparison for values: "
++ show x ++ " == " ++ show y
@ -195,3 +328,17 @@ constantEqualText a b = do
mres <- liftIO $ lookupEnv "MATCHING_TESTS"
when (isJust mres) $
assertEvalMatchesNix b
assertNixEvalThrows :: Text -> Assertion
assertNixEvalThrows a = do
let Success a' = parseNixTextLoc a
time <- liftIO getCurrentTime
let opts = defaultOptions time
errored <- catch ((runLazyM opts $ normalForm =<< nixEvalExprLoc Nothing a') >> pure False) handler
if errored then
pure ()
else
assertFailure "Did not catch nix exception"
where
handler :: NixException -> IO Bool
handler _ = pure True

View file

@ -7,11 +7,12 @@ module Main where
import Control.DeepSeq
import qualified Control.Exception as Exc
import Control.Applicative ((<|>))
import Control.Monad
import Control.Monad.IO.Class
import Data.Fix
import Data.List (isInfixOf)
import Data.Maybe (isJust, fromMaybe)
import Data.Maybe
import Data.String.Interpolate.IsString
import Data.Text (unpack)
import Data.Time
@ -26,7 +27,9 @@ import Nix.Value
import qualified NixLanguageTests
import qualified ParserTests
import qualified PrettyTests
import qualified PrettyParseTests
import qualified ReduceExprTests
-- import qualified PrettyParseTests
import System.Directory
import System.Environment
import System.FilePath.Glob
import System.Posix.Files
@ -84,17 +87,23 @@ main :: IO ()
main = do
nixLanguageTests <- NixLanguageTests.genTests
evalComparisonTests <- EvalTests.genEvalCompareTests
nixpkgsTestsEnv <- lookupEnv "NIXPKGS_TESTS"
prettyTestsEnv <- lookupEnv "PRETTY_TESTS"
let allOrLookup var = lookupEnv "ALL_TESTS" <|> lookupEnv var
nixpkgsTestsEnv <- allOrLookup "NIXPKGS_TESTS"
-- prettyTestsEnv <- lookupEnv "PRETTY_TESTS"
hpackTestsEnv <- allOrLookup "HPACK_TESTS"
setEnv "NIX_REMOTE" "local?root=/tmp"
pwd <- getCurrentDirectory
setEnv "NIX_REMOTE" ("local?root=" ++ pwd ++ "/")
defaultMain $ testGroup "hnix" $
[ testCase "hnix.cabal correctly generated" cabalCorrectlyGenerated ] ++
[ testCase "hnix.cabal correctly generated" cabalCorrectlyGenerated
| isJust hpackTestsEnv ] ++
[ ParserTests.tests
, EvalTests.tests
, PrettyTests.tests ] ++
[ PrettyParseTests.tests (read (fromMaybe "0" prettyTestsEnv)) ] ++
, PrettyTests.tests
, ReduceExprTests.tests] ++
-- [ PrettyParseTests.tests
-- (fromIntegral (read (fromMaybe "0" prettyTestsEnv) :: Int)) ] ++
[ evalComparisonTests ] ++
[ testCase "Nix language tests present" ensureLangTestsPresent
, nixLanguageTests ] ++

View file

@ -19,6 +19,7 @@ import Data.Time
import GHC.Exts
import Nix.Lint
import Nix.Options
import Nix.Options.Parser
import Nix.Parser
import Nix.Pretty
import Nix.Utils

View file

@ -59,11 +59,11 @@ case_constant_path = do
assertParseText "~/a/b" $ mkPath False "~/a/b"
case_constant_uri = do
assertParseText "a:a" $ mkUri "a:a"
assertParseText "http://foo.bar" $ mkUri "http://foo.bar"
assertParseText "a+de+.adA+-:%%%ads%5asdk&/" $ mkUri "a+de+.adA+-:%%%ads%5asdk&/"
assertParseText "rec+def:c" $ mkUri "rec+def:c"
assertParseText "f.foo:bar" $ mkUri "f.foo:bar"
assertParseText "a:a" $ mkStr "a:a"
assertParseText "http://foo.bar" $ mkStr "http://foo.bar"
assertParseText "a+de+.adA+-:%%%ads%5asdk&/" $ mkStr "a+de+.adA+-:%%%ads%5asdk&/"
assertParseText "rec+def:c" $ mkStr "rec+def:c"
assertParseText "f.foo:bar" $ mkStr "f.foo:bar"
assertParseFail "http://foo${\"bar\"}"
assertParseFail ":bcdef"
assertParseFail "a%20:asda"
@ -72,51 +72,51 @@ case_constant_uri = do
case_simple_set = do
assertParseText "{ a = 23; b = 4; }" $ Fix $ NSet
[ NamedVar (mkSelector "a") $ mkInt 23
, NamedVar (mkSelector "b") $ mkInt 4
[ NamedVar (mkSelector "a") (mkInt 23) nullPos
, NamedVar (mkSelector "b") (mkInt 4) nullPos
]
assertParseFail "{ a = 23 }"
case_set_inherit = do
assertParseText "{ e = 3; inherit a b; }" $ Fix $ NSet
[ NamedVar (mkSelector "e") $ mkInt 3
, Inherit Nothing $ flip StaticKey Nothing <$> ["a", "b"]
[ NamedVar (mkSelector "e") (mkInt 3) nullPos
, Inherit Nothing (StaticKey <$> ["a", "b"]) nullPos
]
assertParseText "{ inherit; }" $ Fix $ NSet [ Inherit Nothing [] ]
assertParseText "{ inherit; }" $ Fix $ NSet [ Inherit Nothing [] nullPos ]
case_set_scoped_inherit = assertParseText "{ inherit (a) b c; e = 4; inherit(a)b c; }" $ Fix $ NSet
[ Inherit (Just (mkSym "a")) $ flip StaticKey Nothing <$> ["b", "c"]
, NamedVar (mkSelector "e") $ mkInt 4
, Inherit (Just (mkSym "a")) $ flip StaticKey Nothing <$> ["b", "c"]
[ Inherit (Just (mkSym "a")) (StaticKey <$> ["b", "c"]) nullPos
, NamedVar (mkSelector "e") (mkInt 4) nullPos
, Inherit (Just (mkSym "a")) (StaticKey <$> ["b", "c"]) nullPos
]
case_set_rec = assertParseText "rec { a = 3; b = a; }" $ Fix $ NRecSet
[ NamedVar (mkSelector "a") $ mkInt 3
, NamedVar (mkSelector "b") $ mkSym "a"
[ NamedVar (mkSelector "a") (mkInt 3) nullPos
, NamedVar (mkSelector "b") (mkSym "a") nullPos
]
case_set_complex_keynames = do
assertParseText "{ \"\" = null; }" $ Fix $ NSet
[ NamedVar (DynamicKey (Plain "") :| []) mkNull ]
[ NamedVar (DynamicKey (Plain (DoubleQuoted [])) :| []) mkNull nullPos ]
assertParseText "{ a.b = 3; a.c = 4; }" $ Fix $ NSet
[ NamedVar (StaticKey "a" Nothing :| [StaticKey "b" Nothing]) $ mkInt 3
, NamedVar (StaticKey "a" Nothing :| [StaticKey "c" Nothing]) $ mkInt 4
[ NamedVar (StaticKey "a" :| [StaticKey "b"]) (mkInt 3) nullPos
, NamedVar (StaticKey "a" :| [StaticKey "c"]) (mkInt 4) nullPos
]
assertParseText "{ ${let a = \"b\"; in a} = 4; }" $ Fix $ NSet
[ NamedVar (DynamicKey (Antiquoted letExpr) :| []) $ mkInt 4 ]
[ NamedVar (DynamicKey (Antiquoted letExpr) :| []) (mkInt 4) nullPos ]
assertParseText "{ \"a${let a = \"b\"; in a}c\".e = 4; }" $ Fix $ NSet
[ NamedVar (DynamicKey (Plain str) :| [StaticKey "e" Nothing]) $ mkInt 4 ]
[ NamedVar (DynamicKey (Plain str) :| [StaticKey "e"]) (mkInt 4) nullPos ]
where
letExpr = Fix $ NLet [NamedVar (mkSelector "a") (mkStr "b")] (mkSym "a")
letExpr = Fix $ NLet [NamedVar (mkSelector "a") (mkStr "b") nullPos] (mkSym "a")
str = DoubleQuoted [Plain "a", Antiquoted letExpr, Plain "c"]
case_set_inherit_direct = assertParseText "{ inherit ({a = 3;}); }" $ Fix $ NSet
[ flip Inherit [] $ Just $ Fix $ NSet [NamedVar (mkSelector "a") $ mkInt 3]
[ Inherit (Just $ Fix $ NSet [NamedVar (mkSelector "a") (mkInt 3) nullPos]) [] nullPos
]
case_inherit_selector = do
assertParseText "{ inherit \"a\"; }" $ Fix $ NSet
[Inherit Nothing [DynamicKey (Plain "a")]]
[Inherit Nothing [DynamicKey (Plain (DoubleQuoted [Plain "a"]))] nullPos]
assertParseFail "{ inherit a.x; }"
case_int_list = assertParseText "[1 2 3]" $ Fix $ NList
@ -126,7 +126,8 @@ case_int_null_list = assertParseText "[1 2 3 null 4]" $ Fix (NList (map (Fix . N
case_mixed_list = do
assertParseText "[{a = 3;}.a (if true then null else false) null false 4 [] c.d or null]" $ Fix $ NList
[ Fix (NSelect (Fix (NSet [NamedVar (mkSelector "a") (mkInt 3)])) (mkSelector "a") Nothing)
[ Fix (NSelect (Fix (NSet [NamedVar (mkSelector "a") (mkInt 3) nullPos]))
(mkSelector "a") Nothing)
, Fix (NIf (mkBool True) mkNull (mkBool False))
, mkNull, mkBool False, mkInt 4, Fix (NList [])
, Fix (NSelect (mkSym "c") (mkSelector "d") (Just mkNull))
@ -140,8 +141,8 @@ case_simple_lambda = assertParseText "a: a" $ Fix $ NAbs (Param "a") (mkSym "a")
case_lambda_or_uri = do
assertParseText "a :b" $ Fix $ NAbs (Param "a") (mkSym "b")
assertParseText "a c:def" $ Fix $ NBinary NApp (mkSym "a") (mkUri "c:def")
assertParseText "c:def: c" $ Fix $ NBinary NApp (mkUri "c:def:") (mkSym "c")
assertParseText "a c:def" $ Fix $ NBinary NApp (mkSym "a") (mkStr "c:def")
assertParseText "c:def: c" $ Fix $ NBinary NApp (mkStr "c:def:") (mkSym "c")
assertParseText "a:{}" $ Fix $ NAbs (Param "a") $ Fix $ NSet []
assertParseText "a:[a]" $ Fix $ NAbs (Param "a") $ Fix $ NList [mkSym "a"]
assertParseFail "def:"
@ -178,23 +179,23 @@ case_simple_let = do
assertParseText "let a = 4; in a" $ Fix (NLet binds $ mkSym "a")
assertParseFail "let a = 4 in a"
where
binds = [NamedVar (mkSelector "a") $ mkInt 4]
binds = [NamedVar (mkSelector "a") (mkInt 4) nullPos]
case_let_body = assertParseText "let { body = 1; }" letBody
where
letBody = Fix $ NSelect aset (mkSelector "body") Nothing
aset = Fix $ NRecSet [NamedVar (mkSelector "body") (mkInt 1)]
aset = Fix $ NRecSet [NamedVar (mkSelector "body") (mkInt 1) nullPos]
case_nested_let = do
assertParseText "let a = 4; in let b = 5; in a" $ Fix $ NLet
[NamedVar (mkSelector "a") (mkInt 4)]
(Fix $ NLet [NamedVar (mkSelector "b") (mkInt 5)] $ mkSym "a")
[NamedVar (mkSelector "a") (mkInt 4) nullPos]
(Fix $ NLet [NamedVar (mkSelector "b") (mkInt 5) nullPos] $ mkSym "a")
assertParseFail "let a = 4; let b = 3; in b"
case_let_scoped_inherit = do
assertParseText "let a = null; inherit (b) c; in c" $ Fix $ NLet
[ NamedVar (mkSelector "a") mkNull
, Inherit (Just $ mkSym "b") [StaticKey "c" Nothing] ]
[ NamedVar (mkSelector "a") mkNull nullPos
, Inherit (Just $ mkSym "b") [StaticKey "c"] nullPos ]
(mkSym "c")
assertParseFail "let inherit (b) c in c"
@ -249,18 +250,19 @@ case_string_antiquote = do
case_select = do
assertParseText "a . e .di. f" $ Fix $ NSelect (mkSym "a")
(StaticKey "e" Nothing :| [StaticKey "di" Nothing, StaticKey "f" Nothing])
(StaticKey "e" :| [StaticKey "di", StaticKey "f"])
Nothing
assertParseText "a.e . d or null" $ Fix $ NSelect (mkSym "a")
(StaticKey "e" Nothing :| [StaticKey "d" Nothing])
(StaticKey "e" :| [StaticKey "d"])
(Just mkNull)
assertParseText "{}.\"\"or null" $ Fix $ NSelect (Fix (NSet []))
(DynamicKey (Plain "") :| []) (Just mkNull)
(DynamicKey (Plain (DoubleQuoted [])) :| []) (Just mkNull)
assertParseText "{ a = [1]; }.a or [2] ++ [3]" $ Fix $ NBinary NConcat
(Fix (NSelect
(Fix (NSet [NamedVar (StaticKey "a" Nothing :| [])
(Fix (NList [Fix (NConstant (NInt 1))]))]))
(StaticKey "a" Nothing :| [])
(Fix (NSet [NamedVar (StaticKey "a" :| [])
(Fix (NList [Fix (NConstant (NInt 1))]))
nullPos]))
(StaticKey "a" :| [])
(Just (Fix (NList [Fix (NConstant (NInt 2))])))))
(Fix (NList [Fix (NConstant (NInt 3))]))
@ -269,7 +271,7 @@ case_select_path = do
assertParseText "f.b ../a" $ Fix $ NBinary NApp select (mkPath False "../a")
assertParseText "{}./def" $ Fix $ NBinary NApp (Fix (NSet [])) (mkPath False "./def")
assertParseText "{}.\"\"./def" $ Fix $ NBinary NApp
(Fix $ NSelect (Fix (NSet [])) (DynamicKey (Plain "") :| []) Nothing)
(Fix $ NSelect (Fix (NSet [])) (DynamicKey (Plain (DoubleQuoted [])) :| []) Nothing)
(mkPath False "./def")
where select = Fix $ NSelect (mkSym "f") (mkSelector "b") Nothing
@ -308,8 +310,8 @@ case_operators = do
assertParseText "1 + (if true then 2 else 3)" $ mkOper2 NPlus (mkInt 1) $ Fix $ NIf
(mkBool True) (mkInt 2) (mkInt 3)
assertParseText "{ a = 3; } // rec { b = 4; }" $ mkOper2 NUpdate
(Fix $ NSet [NamedVar (mkSelector "a") (mkInt 3)])
(Fix $ NRecSet [NamedVar (mkSelector "b") (mkInt 4)])
(Fix $ NSet [NamedVar (mkSelector "a") (mkInt 3) nullPos])
(Fix $ NRecSet [NamedVar (mkSelector "b") (mkInt 4) nullPos])
assertParseText "--a" $ mkOper NNeg $ mkOper NNeg $ mkSym "a"
assertParseText "a - b - c" $ mkOper2 NMinus
(mkOper2 NMinus (mkSym "a") (mkSym "b")) $

View file

@ -11,151 +11,137 @@
module PrettyParseTests where
import Control.Monad
import Data.Algorithm.Diff
import Data.Algorithm.DiffOutput
import Data.Char
import Data.Fix
import qualified Data.List.NonEmpty as NE
import Data.Text (Text, pack)
import qualified Data.Text as Text
import Generic.Random
import Hedgehog
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import Nix.Atoms
import Nix.Expr
import Nix.Parser
import Nix.Pretty
import Test.QuickCheck.Instances.Semigroup ()
import Test.QuickCheck.Instances.Text ()
import qualified Test.QuickCheck.Property as P
import Test.Tasty
import Test.Tasty.QuickCheck hiding (Success, Failure)
import Test.Tasty.Hedgehog
import Text.Megaparsec (Pos, SourcePos, mkPos)
import Text.PrettyPrint.ANSI.Leijen ((</>), text)
import qualified Text.PrettyPrint.ANSI.Leijen as P
import qualified Text.Show.Pretty as PS
-- Instead of using the Generic arbitrary instance (which doesn't exist anyway
-- for Text), we use a different generator which just prints sensible looking
-- variable names
custom :: GenList '[Text]
custom = asciiText :@ Nil
asciiString :: Gen String
asciiString = do
n <- choose (1, 15)
replicateM n (elements ['a'..'z'])
asciiString :: MonadGen m => m String
asciiString = Gen.list (Range.linear 1 15) Gen.lower
asciiText :: Gen Text
asciiText = pack <$> asciiString
pcustom :: GenList '[Pos]
pcustom = arbitrary :@ Nil
-- | This generator generates selects one of the constructors uniformly and
-- also decreases the size of the generator by dividing by the branching
-- factor. This ensures sensible termination.
genArb :: (GArbitrary (Options 'Sized '[Text]) a, GUniformWeight a) => Gen a
genArb = genericArbitraryWith (setGenerators custom sizedOpts) uniform
-- Might want to replace this instance with a constant value
instance Arbitrary Pos where
arbitrary = mkPos <$> (getSmall <$> arbitrary `suchThat` (> 0))
genPos :: Gen Pos
genPos = mkPos <$> Gen.int (Range.linear 1 256)
instance Arbitrary (f (Fix f)) => Arbitrary (Fix f) where
arbitrary = genArb
genSourcePos :: Gen SourcePos
genSourcePos = SourcePos <$> asciiString <*> genPos <*> genPos
instance Arbitrary f => Arbitrary (NString f) where
arbitrary = genArb
genKeyName :: Gen (NKeyName NExpr)
genKeyName = Gen.choice [ DynamicKey <$> genAntiquoted genString
, StaticKey <$> asciiText ]
instance Arbitrary SourcePos where
arbitrary = genericArbitraryWith (setGenerators pcustom sizedOpts) uniform
genAntiquoted :: Gen a -> Gen (Antiquoted a NExpr)
genAntiquoted gen = Gen.choice
[ Plain <$> gen
, pure EscapedNewline
, Antiquoted <$> genExpr
]
instance Arbitrary f => Arbitrary (Binding f) where
arbitrary = genArb
genBinding :: Gen (Binding NExpr)
genBinding = Gen.choice
[ NamedVar <$> genAttrPath <*> genExpr <*> genSourcePos
, Inherit <$> Gen.maybe genExpr
<*> Gen.list (Range.linear 0 5) genKeyName
<*> genSourcePos
]
instance Arbitrary f => Arbitrary (NKeyName f) where
arbitrary = oneof [ DynamicKey <$> arbitrary
, StaticKey <$> asciiText <*> arbitrary ]
genString :: Gen (NString NExpr)
genString = Gen.choice
[ DoubleQuoted <$> Gen.list (Range.linear 0 5) (genAntiquoted asciiText)
, Indented <$> Gen.int (Range.linear 0 10)
<*> Gen.list (Range.linear 0 5) (genAntiquoted asciiText)
]
instance Arbitrary f => Arbitrary (Params f) where
arbitrary =
oneof [ Param <$> asciiText
, ParamSet <$> listOf ((,) <$> asciiText <*> arbitrary) <*> arbitrary
<*> oneof [pure Nothing, Just <$> asciiText]
]
genAttrPath :: Gen (NAttrPath NExpr)
genAttrPath = (NE.:|) <$> genKeyName
<*> Gen.list (Range.linear 0 4) genKeyName
instance Arbitrary NAtom where
arbitrary =
oneof [ NInt <$> arbitrary `suchThat` (>= 0)
, NFloat <$> arbitrary `suchThat` (>= 0)
, NBool <$> arbitrary
, pure NNull
, NUri <$> asciiText `suchThat` (\x -> Text.length x > 0) ]
genParams :: Gen (Params NExpr)
genParams = Gen.choice
[ Param <$> asciiText
, ParamSet <$> Gen.list (Range.linear 0 10) ((,) <$> asciiText
<*> Gen.maybe genExpr)
<*> Gen.bool
<*> Gen.choice [pure Nothing, Just <$> asciiText]
]
instance Arbitrary NUnaryOp where
arbitrary = genArb
instance Arbitrary NBinaryOp where
arbitrary = genArb
instance (Arbitrary f) => Arbitrary (Antiquoted Text f) where
arbitrary = genArb
instance (Arbitrary f) => Arbitrary (Antiquoted (NString f) f) where
arbitrary = genArb
genAtom :: Gen NAtom
genAtom = Gen.choice
[ NInt <$> Gen.integral (Range.linear 0 1000)
, NFloat <$> Gen.float (Range.linearFrac 0.0 1000.0)
, NBool <$> Gen.bool
, pure NNull ]
-- This is written by hand so we can use `fairList` rather than the normal
-- list Arbitrary instance which makes the generator terminate. The
-- distribution is not scientifically chosen.
instance Arbitrary f => Arbitrary (NExprF f) where
arbitrary =
sized $ \n ->
if n < 2
then oneof [genConstant, genStr, genSym, genLiteralPath, genEnvPath ]
else
frequency
[ ( 1, genConstant)
, ( 1, genSym)
, ( 4, resize (n `div` 3) genIf)
, (10, genRecSet )
, (20, genSet )
, ( 5, genList )
, ( 2, genUnary )
, ( 2, resize (n `div` 3) genBinary )
, ( 3, resize (n `div` 3) genSelect )
, (20, resize (n `div` 2) genAbs )
, ( 2, resize (n `div` 2) genHasAttr )
, (10, resize (n `div` 2) genLet )
, (10, resize (n `div` 2) genWith )
, ( 1, resize (n `div` 2) genAssert)
]
where
genConstant = NConstant <$> arbitrary
genStr = NStr <$> arbitrary
genSym = NSym <$> asciiText
genList = NList <$> fairList arbitrary
genSet = NSet <$> fairList arbitrary
genRecSet = NRecSet <$> fairList arbitrary
genLiteralPath = NLiteralPath . ("./" ++) <$> asciiString
genEnvPath = NEnvPath <$> asciiString
genUnary = NUnary <$> arbitrary <*> arbitrary
genBinary = NBinary <$> arbitrary <*> arbitrary <*> arbitrary
genSelect = NSelect <$> arbitrary <*> arbitrary <*> arbitrary
genHasAttr = NHasAttr <$> arbitrary <*> arbitrary
genAbs = NAbs <$> arbitrary <*> arbitrary
genLet = NLet <$> fairList arbitrary <*> arbitrary
genIf = NIf <$> arbitrary <*> arbitrary <*> arbitrary
genWith = NWith <$> arbitrary <*> arbitrary
genAssert = NAssert <$> arbitrary <*> arbitrary
genExpr :: Gen NExpr
genExpr = Gen.sized $ \(Size n) ->
Fix <$>
if n < 2
then Gen.choice
[genConstant, genStr, genSym, genLiteralPath, genEnvPath ]
else
Gen.frequency
[ ( 1, genConstant)
, ( 1, genSym)
, ( 4, Gen.resize (Size (n `div` 3)) genIf)
, (10, genRecSet )
, (20, genSet )
, ( 5, genList )
, ( 2, genUnary )
, ( 2, Gen.resize (Size (n `div` 3)) genBinary )
, ( 3, Gen.resize (Size (n `div` 3)) genSelect )
, (20, Gen.resize (Size (n `div` 2)) genAbs )
, ( 2, Gen.resize (Size (n `div` 2)) genHasAttr )
, (10, Gen.resize (Size (n `div` 2)) genLet )
, (10, Gen.resize (Size (n `div` 2)) genWith )
, ( 1, Gen.resize (Size (n `div` 2)) genAssert)
]
where
genConstant = NConstant <$> genAtom
genStr = NStr <$> genString
genSym = NSym <$> asciiText
genList = NList <$> fairList genExpr
genSet = NSet <$> fairList genBinding
genRecSet = NRecSet <$> fairList genBinding
genLiteralPath = NLiteralPath . ("./" ++) <$> asciiString
genEnvPath = NEnvPath <$> asciiString
genUnary = NUnary <$> Gen.enumBounded <*> genExpr
genBinary = NBinary <$> Gen.enumBounded <*> genExpr <*> genExpr
genSelect = NSelect <$> genExpr <*> genAttrPath <*> Gen.maybe genExpr
genHasAttr = NHasAttr <$> genExpr <*> genAttrPath
genAbs = NAbs <$> genParams <*> genExpr
genLet = NLet <$> fairList genBinding <*> genExpr
genIf = NIf <$> genExpr <*> genExpr <*> genExpr
genWith = NWith <$> genExpr <*> genExpr
genAssert = NAssert <$> genExpr <*> genExpr
-- | Useful when there are recursive positions at each element of the list as
-- it divides the size by the length of the generated list.
fairList :: Gen a -> Gen [a]
fairList g = do
s <- getSize
k <- choose (0, s)
fairList g = Gen.sized $ \s -> do
k <- Gen.int (Range.linear 0 (unSize s))
-- Use max here to avoid dividing by zero when there is the empty list
resize (s `div` max 1 k) $ vectorOf k g
Gen.resize (Size (unSize s `div` max 1 k)) $ Gen.list (Range.singleton k) g
equivUpToNormalization :: NExpr -> NExpr -> Bool
equivUpToNormalization x y = normalize x == normalize y
@ -174,11 +160,11 @@ normalize = cata $ \case
r -> Fix r
where
normBinding (NamedVar path r) = NamedVar (NE.map normKey path) r
normBinding (Inherit mr names) = Inherit mr (map normKey names)
normBinding (NamedVar path r pos) = NamedVar (NE.map normKey path) r pos
normBinding (Inherit mr names pos) = Inherit mr (map normKey names) pos
normKey (DynamicKey quoted) = DynamicKey (normAntiquotedString quoted)
normKey (StaticKey name _) = StaticKey name Nothing
normKey (StaticKey name) = StaticKey name
normAntiquotedString :: Antiquoted (NString NExpr) NExpr
-> Antiquoted (NString NExpr) NExpr
@ -200,38 +186,38 @@ normalize = cata $ \case
normParams r = r
-- | Test that parse . pretty == id up to attribute position information.
prop_prettyparse :: NExpr -> P.Result
prop_prettyparse p =
prop_prettyparse :: Monad m => NExpr -> PropertyT m ()
prop_prettyparse p = do
let prog = show (pretty p)
in case parse (pack prog) of
Failure s -> P.rejected
{ P.reason = show $
case parse (pack prog) of
Failure s -> do
footnote $ show $
text "Parse failed:" </> text (show s)
P.<$> P.indent 2 (pretty p) }
P.<$> P.indent 2 (pretty p)
discard
Success v
| equivUpToNormalization p v -> P.succeeded
| otherwise ->
| equivUpToNormalization p v -> success
| otherwise -> do
let pp = normalise prog
pv = normalise (show (pretty v))
in (P.liftBool (pp == pv))
{ P.reason = show $
text "----------------------------------------"
P.<$> text "Expr before:" P.<$> P.indent 2 (text (PS.ppShow p))
P.<$> text "----------------------------------------"
P.<$> text "Expr after:" P.<$> P.indent 2 (text (PS.ppShow v))
P.<$> text "----------------------------------------"
P.<$> text "Pretty before:" P.<$> P.indent 2 (text prog)
P.<$> text "----------------------------------------"
P.<$> text "Pretty after:" P.<$> P.indent 2 (pretty v)
P.<$> text "----------------------------------------"
P.<$> text "Normalised before:" P.<$> P.indent 2 (text pp)
P.<$> text "----------------------------------------"
P.<$> text "Normalised after:" P.<$> P.indent 2 (text pv)
P.<$> text "========================================"
P.<$> text "Normalised diff:"
P.<$> text (ppDiff (diff pp pv))
P.<$> text "========================================"
}
footnote $ show $
text "----------------------------------------"
P.<$> text "Expr before:" P.<$> P.indent 2 (text (PS.ppShow p))
P.<$> text "----------------------------------------"
P.<$> text "Expr after:" P.<$> P.indent 2 (text (PS.ppShow v))
P.<$> text "----------------------------------------"
P.<$> text "Pretty before:" P.<$> P.indent 2 (text prog)
P.<$> text "----------------------------------------"
P.<$> text "Pretty after:" P.<$> P.indent 2 (pretty v)
P.<$> text "----------------------------------------"
P.<$> text "Normalised before:" P.<$> P.indent 2 (text pp)
P.<$> text "----------------------------------------"
P.<$> text "Normalised after:" P.<$> P.indent 2 (text pv)
P.<$> text "========================================"
P.<$> text "Normalised diff:"
P.<$> text (ppDiff (diff pp pv))
P.<$> text "========================================"
assert (pp == pv)
where
pretty = prettyNix
parse = parseNixText
@ -241,6 +227,7 @@ prop_prettyparse p =
diff :: String -> String -> [Diff [String]]
diff s1 s2 = getDiff (map (:[]) (lines s1)) (map (:[]) (lines s2))
tests :: Int -> TestTree
tests n = testProperty "Pretty/Parse Property" $
withMaxSuccess n prop_prettyparse
tests :: TestLimit -> TestTree
tests n = testProperty "Pretty/Parse Property" $ withTests n $ property $ do
x <- forAll genExpr
prop_prettyparse x

59
tests/ReduceExprTests.hs Normal file
View file

@ -0,0 +1,59 @@
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
module ReduceExprTests (tests) where
import Data.Fix
import Test.Tasty
import Test.Tasty.HUnit
import Nix.Atoms
import Nix.Expr.Types
import Nix.Expr.Types.Annotated
import Nix.Parser
import Nix.Reduce (reduceExpr)
tests :: TestTree
tests = testGroup "Expr Reductions"
[ testCase "Non nested NSelect on set should be reduced" $
cmpReduceResult selectBasic selectBasicExpect,
testCase "Nested NSelect on set should be reduced" $
cmpReduceResult selectNested selectNestedExpect,
testCase "Non nested NSelect with incorrect attrpath shouldn't be reduced" $
shouldntReduce selectIncorrectAttrPath,
testCase "Nested NSelect with incorrect attrpath shouldn't be reduced" $
shouldntReduce selectNestedIncorrectAttrPath
]
assertSucc :: Result a -> IO a
assertSucc (Success a) = pure a
assertSucc (Failure d) = assertFailure $ show d
cmpReduceResult :: Result NExprLoc -> NExpr -> Assertion
cmpReduceResult r e = do
r <- assertSucc r
r <- stripAnnotation <$> reduceExpr Nothing r
r @?= e
shouldntReduce :: Result NExprLoc -> Assertion
shouldntReduce r = do
r <- assertSucc r
rReduced <- reduceExpr Nothing r
r @?= rReduced
selectBasic :: Result NExprLoc
selectBasic = parseNixTextLoc "{b=2;a=42;}.a"
selectBasicExpect :: NExpr
selectBasicExpect = Fix . NConstant $ NInt 42
selectNested :: Result NExprLoc
selectNested = parseNixTextLoc "{a={b=2;a=42;};b={a=2;};}.a.a"
selectNestedExpect :: NExpr
selectNestedExpect = Fix . NConstant $ NInt 42
selectIncorrectAttrPath :: Result NExprLoc
selectIncorrectAttrPath = parseNixTextLoc "{a=42;}.b"
selectNestedIncorrectAttrPath :: Result NExprLoc
selectNestedIncorrectAttrPath = parseNixTextLoc "{a={a=42;};}.a.b"

View file

@ -48,8 +48,7 @@ nixEvalString expr = do
return res
nixEvalFile :: FilePath -> IO String
nixEvalFile fp = readProcess "nix-instantiate"
["--store", "local?root=/tmp", "--eval", fp] ""
nixEvalFile fp = readProcess "nix-instantiate" ["--eval", "--strict", fp] ""
assertEvalFileMatchesNix :: FilePath -> Assertion
assertEvalFileMatchesNix fp = do

0
tests/files/findFile.nix Normal file
View file