Merge branch 'master' of https://github.com/haskell-nix/hnix into string_context_255

This commit is contained in:
gb 2018-05-08 14:44:47 -04:00
commit fbcb258eb4
24 changed files with 521 additions and 1183 deletions

7
.dockerignore Normal file
View file

@ -0,0 +1,7 @@
.git
Dockerfile
Makefile
README.md
cabal.sandbox.config
.cabal-sandbox/
hnix.cabal

View file

@ -1,22 +1,45 @@
language: nix
sudo: true
# sudo: false
cache:
directories:
- /nix/store
# cache:
# directories:
# - /nix
# timeout: 100000
cache:
timeout: 10000
git:
depth: 1
install:
- nix-shell --argstr compiler ghc822 --run true
- nix-shell --argstr compiler ghc842 --run true
env:
- GHCVERSION=ghc822 NIXPKGS_TESTS=yes MATCHING_TESTS=yes TRACING=true STRICT=true PROFILING=false
- GHCVERSION=ghc822 NIXPKGS_TESTS=yes MATCHING_TESTS=yes TRACING=false STRICT=true PROFILING=true
- GHCVERSION=ghc842 NIXPKGS_TESTS=yes MATCHING_TESTS=yes TRACING=false STRICT=false PROFILING=false
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
matrix:
allow_failures:
- env: GHCVERSION=ghc842 STRICT=true
exclude:
- env: GHCVERSION=ghc822 STRICT=false
script:
- nix-build --argstr compiler $GHCVERSION --arg doProfiling $PROFILING --arg doTracing $TRACING --arg doStrict $STRICT
branches:
only:
- master
- pending
notifications:
webhooks:
urls:

16
Dockerfile Normal file
View file

@ -0,0 +1,16 @@
FROM lnl7/nix:2.0
WORKDIR /tmp/build
COPY default.nix /tmp/build
COPY package.yaml /tmp/build
# Install tools needed by builtins.fetchTarball, and then install all
# dependencies into its own layer, which doesn't change.
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"]

190
PLAN.org
View file

@ -1,190 +0,0 @@
#+TITLE: hnix Project Plan
* Goals
The purpose of hnix is provide a fully featured, compatible implementation of
the Nix language, sufficient to evaluate and make use of the nixpkgs package
repository definitions. The intent is to provide a convenient basis for
creating new tooling in Haskell for the Nix ecosystem, and to provide an
second implementation apart from the current C++ code base used by the Nix
project.
* Architecture
#+name: dot-figure
#+begin_src dot :file diagram4.svg :cmdline -Tsvg :results file
digraph {
bgcolor=transparent;
rankdir=LR;
{
rank=same;
parser [label="Parser",style=filled,fillcolor=yellow,shape=box,width=2];
value [label="Values",style=filled,fillcolor=yellow,shape=box,width=2];
eval [label="Core Evaluator",style=filled,fillcolor=yellow,shape=box,width=2];
expr [label="Expressions",style=filled,fillcolor=yellow,shape=box,width=2];
pretty [label="Pretty Printer",style=filled,fillcolor=yellow,shape=box,width=2];
}
{
rank=same;
exec [label="Executing Evaluator",style=filled,fillcolor=yellow,shape=box,width=2];
builtins [label="Built-in Functions",style=filled,fillcolor=yellow,shape=box,width=2];
}
support [label="Supporting Abstractions",style=filled,fillcolor=yellow,shape=box,width=2];
features [label="Other Features",style=filled,fillcolor=yellow,shape=box,width=2];
main [label="Top-Level Driver",style=filled,fillcolor=green,shape=box,width=2];
expr -> {
"Atoms.hs" [color=transparent]
"Expr.hs" [color=transparent]
"Expr/Shorthands.hs" [color=transparent]
"Expr/Types.hs" [color=transparent]
"Expr/Types/Annotated.hs" [color=transparent]
"Strings.hs" [color=transparent]
};
pretty -> {
"Pretty.hs" [color=transparent]
"Render.hs" [color=transparent]
"Render/Frame.hs" [color=transparent]
};
parser -> {
"Parser.hs" [color=transparent]
"Parser/Library.hs" [color=transparent]
"Parser/Operators.hs" [color=transparent]
};
value -> {
"Convert.hs" [color=transparent]
"Normal.hs" [color=transparent]
"Value.hs" [color=transparent]
};
builtins -> {
"Builtins.hs" [color=transparent]
"XML.hs" [color=transparent]
};
support -> {
"Frames.hs" [color=transparent]
"Scope.hs" [color=transparent]
"Thunk.hs" [color=transparent]
"Options.hs" [color=transparent]
"Utils.hs" [color=transparent]
};
features -> {
"Cache.hs" [color=transparent]
"Lint.hs" [color=transparent]
"Reduce.hs" [color=transparent]
"Type/..." [color=transparent]
"TH.hs" [color=transparent]
};
"Cache.hs" -> { expr parser };
"Lint.hs" -> { expr eval };
"Reduce.hs" -> { expr parser };
"Type/..." -> { expr eval };
"TH.hs" -> { expr parser };
eval -> {
"Context.hs" [color=transparent]
"Eval.hs" [color=transparent]
};
exec -> {
"Effects.hs" [color=transparent]
"Exec.hs" [color=transparent]
};
{ eval parser pretty value expr builtins exec } -> support;
{ eval parser pretty value } -> expr;
exec -> { eval value builtins };
{ pretty builtins } -> value;
main -> { parser pretty exec features };
}
#+end_src
#+ATTR_LATEX: :height 3cm
#+results: dot-figure
[[file:diagram4.svg]]
* What needs to be done
** PROJECT Pass the Nix language tests
:PROPERTIES:
:ID: BB4190F1-695D-454A-8E14-492651B4EC9F
:CREATED: [2018-04-23 Mon 17:12]
:URL: https://github.com/jwiegley/hnix/milestone/1
:END:
We currently have just two tests remaining that need to be fixed to pass the
Nix language tests:
*** TODO builtins.path:
https://github.com/jwiegley/hnix/issues/128
*** TODO builtins.genericClosure:
https://github.com/jwiegley/hnix/issues/144
** PROJECT Successfully evaluate all of nixpkgs
:PROPERTIES:
:ID: E4A330E7-70C1-4E79-A94C-D63B2533EBE1
:CREATED: [2018-04-23 Mon 17:10]
:URL: https://github.com/jwiegley/hnix/milestone/2
:END:
There are still a few problems in the evaluator with process the contents of
the nixpkgs repository:
*** TODO https://github.com/jwiegley/hnix/issues/157
*** TODO https://github.com/jwiegley/hnix/issues/193
** PROJECT Increase test coverage
:PROPERTIES:
:ID: 5998F757-F30B-4987-89BE-4E44A1BE57BF
:CREATED: [2018-04-23 Mon 17:17]
:END:
*** TODO https://github.com/jwiegley/hnix/issues/158
** PROJECT Improve the hnix REPL
:PROPERTIES:
:ID: F824236D-7D7E-43D0-8DE6-AD66055B8935
:CREATED: [2018-04-23 Mon 17:17]
:END:
*** TODO https://github.com/jwiegley/hnix/issues/164
** PROJECT Support concurrent evaluation
:PROPERTIES:
:ID: AE9B3606-009D-43FF-A1E0-0E9A5494BFAC
:CREATED: [2018-04-23 Mon 17:18]
:END:
*** TODO https://github.com/jwiegley/hnix/issues/170
** PROJECT Type checker
:PROPERTIES:
:ID: F42B3AAB-3BA8-40DC-8B29-F534019F5832
:CREATED: [2018-04-23 Mon 17:16]
:END:
** PROJECT Haskell integration using a quasi-quoter
:PROPERTIES:
:ID: 7800EF09-5083-4819-ACD4-877B85E98C07
:CREATED: [2018-04-23 Mon 17:16]
:END:
* Colophon
#+STARTUP: content fninline hidestars
#+OPTIONS: ^:{}
#+ARCHIVE: PLAN-archive.txt::
#+SEQ_TODO: STARTED TODO APPT WAITING(@) DELEGATED(@) DEFERRED(@) SOMEDAY(@) PROJECT | DONE(@) CANCELED(@) NOTE
#+TAGS: P1(1) P2(2) P3(3) Call(c) Errand(e) Home(h) Net(n) Reply(r) Waiting(w)
#+DRAWERS: PROPERTIES LOGBOOK OUTPUT SCRIPT SOURCE DATA
#+PROPERTY: OVERLAY (face (:background "#e8eff9"))

View file

@ -24,6 +24,20 @@ $ cabal test
$ LANGUAGE_TESTS=yes NIXPKGS_TESTS=yes cabal test
$ ./dist/build/hnix/hnix --help
```
## Building a Docker container
If you don't have Nix installed, or you'd just like to play around with `hnix`
completely separately from your main system, you can build a Docker container:
```bash
$ docker build -t hnix .
$ docker run hnix hnix --eval --expr '1 + 2'
# In order to refer to files under the current directory:
$ docker run -v $PWD/:/tmp/build run hnix hnix default.nix
```
## Building with full debug info
To build `hnix` for debugging, and with full tracing output and stack traces,
@ -33,7 +47,7 @@ use:
$ nix-shell --arg doProfiling true
$ cabal configure --enable-tests --enable-profiling --flags=tracing
$ cabal build
$ ./dist/build/hnix/hnix -v5 <args> +RTS -xc
$ ./dist/build/hnix/hnix -v5 --trace <args> +RTS -xc
```
Note that this will run quite slowly, but will give the most information as to

View file

@ -1,624 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.40.1 (0)
-->
<!-- Title: %3 Pages: 1 -->
<svg width="1006pt" height="962pt"
viewBox="0.00 0.00 1005.78 962.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 958)">
<title>%3</title>
<!-- parser -->
<g id="node1" class="node">
<title>parser</title>
<polygon fill="#ffff00" stroke="#000000" points="684,-457 540,-457 540,-421 684,-421 684,-457"/>
<text text-anchor="middle" x="612" y="-435.3" font-family="Times,serif" font-size="14.00" fill="#000000">Parser</text>
</g>
<!-- expr -->
<g id="node4" class="node">
<title>expr</title>
<polygon fill="#ffff00" stroke="#000000" points="684,-349 540,-349 540,-313 684,-313 684,-349"/>
<text text-anchor="middle" x="612" y="-327.3" font-family="Times,serif" font-size="14.00" fill="#000000">Expressions</text>
</g>
<!-- parser&#45;&gt;expr -->
<g id="edge49" class="edge">
<title>parser&#45;&gt;expr</title>
<path fill="none" stroke="#000000" d="M539.9728,-420.762C532.7984,-416.1215 526.5121,-410.3008 522,-403 513.5883,-389.3896 513.5883,-380.6104 522,-367 524.6791,-362.6651 527.9836,-358.8521 531.7281,-355.4981"/>
<polygon fill="#000000" stroke="#000000" points="534.125,-358.0728 539.9728,-349.238 529.8919,-352.4977 534.125,-358.0728"/>
</g>
<!-- support -->
<g id="node8" class="node">
<title>support</title>
<polygon fill="#ffff00" stroke="#000000" points="872.5928,-630 728.5928,-630 728.5928,-594 872.5928,-594 872.5928,-630"/>
<text text-anchor="middle" x="800.5928" y="-608.3" font-family="Times,serif" font-size="14.00" fill="#000000">Supporting Abstractions</text>
</g>
<!-- parser&#45;&gt;support -->
<g id="edge42" class="edge">
<title>parser&#45;&gt;support</title>
<path fill="none" stroke="#000000" d="M673.1876,-457.0009C677.1381,-459.6085 680.8052,-462.5904 684,-466 721.7816,-506.321 681.5085,-545.3561 720,-585 720.9251,-585.9528 721.8848,-586.872 722.8755,-587.7588"/>
<polygon fill="#000000" stroke="#000000" points="720.9373,-590.6817 731.0181,-593.9415 725.1704,-585.1067 720.9373,-590.6817"/>
</g>
<!-- Parser.hs -->
<g id="node20" class="node">
<title>Parser.hs</title>
<ellipse fill="none" stroke="transparent" cx="800.5928" cy="-450" rx="35.9954" ry="18"/>
<text text-anchor="middle" x="800.5928" y="-446.3" font-family="Times,serif" font-size="14.00" fill="#000000">Parser.hs</text>
</g>
<!-- parser&#45;&gt;Parser.hs -->
<g id="edge10" class="edge">
<title>parser&#45;&gt;Parser.hs</title>
<path fill="none" stroke="#000000" d="M684.1061,-443.2057C707.8765,-444.5922 733.6414,-446.0949 754.8815,-447.3338"/>
<polygon fill="#000000" stroke="#000000" points="754.7296,-450.8308 764.9165,-447.9191 755.1373,-443.8427 754.7296,-450.8308"/>
</g>
<!-- Parser/Library.hs -->
<g id="node21" class="node">
<title>Parser/Library.hs</title>
<ellipse fill="none" stroke="transparent" cx="800.5928" cy="-558" rx="57.6901" ry="18"/>
<text text-anchor="middle" x="800.5928" y="-554.3" font-family="Times,serif" font-size="14.00" fill="#000000">Parser/Library.hs</text>
</g>
<!-- parser&#45;&gt;Parser/Library.hs -->
<g id="edge11" class="edge">
<title>parser&#45;&gt;Parser/Library.hs</title>
<path fill="none" stroke="#000000" d="M671.3559,-457.1823C675.872,-459.7609 680.1591,-462.686 684,-466 709.0033,-487.5732 694.6177,-509.874 720,-531 726.603,-536.4957 734.3708,-540.8882 742.4323,-544.395"/>
<polygon fill="#000000" stroke="#000000" points="741.39,-547.7453 751.9783,-548.1179 743.9334,-541.2237 741.39,-547.7453"/>
</g>
<!-- Parser/Operators.hs -->
<g id="node22" class="node">
<title>Parser/Operators.hs</title>
<ellipse fill="none" stroke="transparent" cx="800.5928" cy="-504" rx="64.189" ry="18"/>
<text text-anchor="middle" x="800.5928" y="-500.3" font-family="Times,serif" font-size="14.00" fill="#000000">Parser/Operators.hs</text>
</g>
<!-- parser&#45;&gt;Parser/Operators.hs -->
<g id="edge12" class="edge">
<title>parser&#45;&gt;Parser/Operators.hs</title>
<path fill="none" stroke="#000000" d="M662.7655,-457.0389C680.8149,-463.4057 701.2949,-470.5764 720,-477 729.4204,-480.2351 739.476,-483.6413 749.177,-486.9034"/>
<polygon fill="#000000" stroke="#000000" points="748.1292,-490.2436 758.7231,-490.1059 750.3556,-483.6071 748.1292,-490.2436"/>
</g>
<!-- value -->
<g id="node2" class="node">
<title>value</title>
<polygon fill="#ffff00" stroke="#000000" points="684,-695 540,-695 540,-659 684,-659 684,-695"/>
<text text-anchor="middle" x="612" y="-673.3" font-family="Times,serif" font-size="14.00" fill="#000000">Values</text>
</g>
<!-- value&#45;&gt;expr -->
<g id="edge50" class="edge">
<title>value&#45;&gt;expr</title>
<path fill="none" stroke="#000000" d="M577.1364,-658.7272C556.9771,-646.0631 533.4053,-627.1432 522,-603 510.7995,-579.2902 508.2142,-389.306 522,-367 524.6791,-362.6651 527.9836,-358.8521 531.7281,-355.4981"/>
<polygon fill="#000000" stroke="#000000" points="534.125,-358.0728 539.9728,-349.238 529.8919,-352.4977 534.125,-358.0728"/>
</g>
<!-- value&#45;&gt;support -->
<g id="edge43" class="edge">
<title>value&#45;&gt;support</title>
<path fill="none" stroke="#000000" d="M662.7655,-658.9611C680.8149,-652.5943 701.2949,-645.4236 720,-639 725.4538,-637.1271 731.1205,-635.1968 736.808,-633.2704"/>
<polygon fill="#000000" stroke="#000000" points="738.0111,-636.5584 746.3655,-630.0427 735.7713,-629.9264 738.0111,-636.5584"/>
</g>
<!-- Convert.hs -->
<g id="node23" class="node">
<title>Convert.hs</title>
<ellipse fill="none" stroke="transparent" cx="800.5928" cy="-774" rx="40.0939" ry="18"/>
<text text-anchor="middle" x="800.5928" y="-770.3" font-family="Times,serif" font-size="14.00" fill="#000000">Convert.hs</text>
</g>
<!-- value&#45;&gt;Convert.hs -->
<g id="edge13" class="edge">
<title>value&#45;&gt;Convert.hs</title>
<path fill="none" stroke="#000000" d="M668.8595,-695.0797C674.1734,-697.7169 679.3155,-700.682 684,-704 704.3394,-718.4063 699.4081,-732.957 720,-747 730.191,-753.9499 742.3199,-759.2399 754.0344,-763.213"/>
<polygon fill="#000000" stroke="#000000" points="753.1365,-766.5994 763.7255,-766.2483 755.2287,-759.9194 753.1365,-766.5994"/>
</g>
<!-- Normal.hs -->
<g id="node24" class="node">
<title>Normal.hs</title>
<ellipse fill="none" stroke="transparent" cx="800.5928" cy="-720" rx="39.7935" ry="18"/>
<text text-anchor="middle" x="800.5928" y="-716.3" font-family="Times,serif" font-size="14.00" fill="#000000">Normal.hs</text>
</g>
<!-- value&#45;&gt;Normal.hs -->
<g id="edge14" class="edge">
<title>value&#45;&gt;Normal.hs</title>
<path fill="none" stroke="#000000" d="M684.1061,-693.4405C707.8765,-698.8603 733.6414,-704.7348 754.8815,-709.5776"/>
<polygon fill="#000000" stroke="#000000" points="754.3886,-713.055 764.9165,-711.8656 755.9448,-706.2301 754.3886,-713.055"/>
</g>
<!-- Value.hs -->
<g id="node25" class="node">
<title>Value.hs</title>
<ellipse fill="none" stroke="transparent" cx="800.5928" cy="-666" rx="35.194" ry="18"/>
<text text-anchor="middle" x="800.5928" y="-662.3" font-family="Times,serif" font-size="14.00" fill="#000000">Value.hs</text>
</g>
<!-- value&#45;&gt;Value.hs -->
<g id="edge15" class="edge">
<title>value&#45;&gt;Value.hs</title>
<path fill="none" stroke="#000000" d="M684.1061,-672.7943C708.1446,-671.3922 734.2229,-669.8711 755.5985,-668.6244"/>
<polygon fill="#000000" stroke="#000000" points="755.9054,-672.1125 765.6846,-668.0361 755.4978,-665.1244 755.9054,-672.1125"/>
</g>
<!-- eval -->
<g id="node3" class="node">
<title>eval</title>
<polygon fill="#ffff00" stroke="#000000" points="684,-403 540,-403 540,-367 684,-367 684,-403"/>
<text text-anchor="middle" x="612" y="-381.3" font-family="Times,serif" font-size="14.00" fill="#000000">Core Evaluator</text>
</g>
<!-- eval&#45;&gt;expr -->
<g id="edge51" class="edge">
<title>eval&#45;&gt;expr</title>
<path fill="none" stroke="#000000" d="M612,-366.7902C612,-364.3082 612,-361.8262 612,-359.3443"/>
<polygon fill="#000000" stroke="#000000" points="615.5001,-359.1406 612,-349.1406 608.5001,-359.1407 615.5001,-359.1406"/>
</g>
<!-- eval&#45;&gt;support -->
<g id="edge44" class="edge">
<title>eval&#45;&gt;support</title>
<path fill="none" stroke="#000000" d="M674.1928,-403.1551C677.8088,-405.7237 681.133,-408.655 684,-412 735.1087,-471.6306 667.8408,-526.2861 720,-585 720.6431,-585.724 721.3063,-586.4285 721.9879,-587.1143"/>
<polygon fill="#000000" stroke="#000000" points="720.0194,-590.0308 729.935,-593.7634 724.5113,-584.662 720.0194,-590.0308"/>
</g>
<!-- Context.hs -->
<g id="node38" class="node">
<title>Context.hs</title>
<ellipse fill="none" stroke="transparent" cx="800.5928" cy="-396" rx="40.0939" ry="18"/>
<text text-anchor="middle" x="800.5928" y="-392.3" font-family="Times,serif" font-size="14.00" fill="#000000">Context.hs</text>
</g>
<!-- eval&#45;&gt;Context.hs -->
<g id="edge38" class="edge">
<title>eval&#45;&gt;Context.hs</title>
<path fill="none" stroke="#000000" d="M684.1061,-389.2057C706.1551,-390.4918 729.9201,-391.8779 750.1982,-393.0606"/>
<polygon fill="#000000" stroke="#000000" points="750.2701,-396.5707 760.457,-393.659 750.6778,-389.5826 750.2701,-396.5707"/>
</g>
<!-- Eval.hs -->
<g id="node39" class="node">
<title>Eval.hs</title>
<ellipse fill="none" stroke="transparent" cx="800.5928" cy="-342" rx="31.3957" ry="18"/>
<text text-anchor="middle" x="800.5928" y="-338.3" font-family="Times,serif" font-size="14.00" fill="#000000">Eval.hs</text>
</g>
<!-- eval&#45;&gt;Eval.hs -->
<g id="edge39" class="edge">
<title>eval&#45;&gt;Eval.hs</title>
<path fill="none" stroke="#000000" d="M684.1061,-368.5595C710.4372,-362.5559 739.2156,-355.9943 761.5801,-350.8951"/>
<polygon fill="#000000" stroke="#000000" points="762.4008,-354.2979 771.3725,-348.6624 760.8446,-347.473 762.4008,-354.2979"/>
</g>
<!-- expr&#45;&gt;support -->
<g id="edge45" class="edge">
<title>expr&#45;&gt;support</title>
<path fill="none" stroke="#000000" d="M674.377,-349.0011C677.9498,-351.6058 681.2139,-354.5873 684,-358 748.5999,-437.1291 654.0056,-507.0301 720,-585 720.6256,-585.7391 721.2721,-586.4581 721.938,-587.1575"/>
<polygon fill="#000000" stroke="#000000" points="719.8985,-590.0215 729.7478,-593.9257 724.483,-584.7316 719.8985,-590.0215"/>
</g>
<!-- Atoms.hs -->
<g id="node11" class="node">
<title>Atoms.hs</title>
<ellipse fill="none" stroke="transparent" cx="800.5928" cy="-288" rx="36.2938" ry="18"/>
<text text-anchor="middle" x="800.5928" y="-284.3" font-family="Times,serif" font-size="14.00" fill="#000000">Atoms.hs</text>
</g>
<!-- expr&#45;&gt;Atoms.hs -->
<g id="edge1" class="edge">
<title>expr&#45;&gt;Atoms.hs</title>
<path fill="none" stroke="#000000" d="M684.1061,-314.5595C708.8823,-308.9104 735.8253,-302.7673 757.5528,-297.8133"/>
<polygon fill="#000000" stroke="#000000" points="758.4823,-301.1913 767.4541,-295.5558 756.9262,-294.3664 758.4823,-301.1913"/>
</g>
<!-- Expr.hs -->
<g id="node12" class="node">
<title>Expr.hs</title>
<ellipse fill="none" stroke="transparent" cx="800.5928" cy="-234" rx="31.6951" ry="18"/>
<text text-anchor="middle" x="800.5928" y="-230.3" font-family="Times,serif" font-size="14.00" fill="#000000">Expr.hs</text>
</g>
<!-- expr&#45;&gt;Expr.hs -->
<g id="edge2" class="edge">
<title>expr&#45;&gt;Expr.hs</title>
<path fill="none" stroke="#000000" d="M636.2632,-312.7258C657.4936,-297.3718 689.6073,-275.6502 720,-261 733.1346,-254.6687 748.1368,-249.2024 761.608,-244.8766"/>
<polygon fill="#000000" stroke="#000000" points="763.0382,-248.0977 771.5548,-241.7955 760.967,-241.4112 763.0382,-248.0977"/>
</g>
<!-- Expr/Shorthands.hs -->
<g id="node13" class="node">
<title>Expr/Shorthands.hs</title>
<ellipse fill="none" stroke="transparent" cx="800.5928" cy="-180" rx="64.189" ry="18"/>
<text text-anchor="middle" x="800.5928" y="-176.3" font-family="Times,serif" font-size="14.00" fill="#000000">Expr/Shorthands.hs</text>
</g>
<!-- expr&#45;&gt;Expr/Shorthands.hs -->
<g id="edge3" class="edge">
<title>expr&#45;&gt;Expr/Shorthands.hs</title>
<path fill="none" stroke="#000000" d="M623.3126,-312.6959C640.8324,-285.8004 677.0276,-235.6953 720,-207 726.805,-202.4559 734.4434,-198.6135 742.2349,-195.3821"/>
<polygon fill="#000000" stroke="#000000" points="743.6781,-198.5775 751.769,-191.7374 741.1785,-192.039 743.6781,-198.5775"/>
</g>
<!-- Expr/Types.hs -->
<g id="node14" class="node">
<title>Expr/Types.hs</title>
<ellipse fill="none" stroke="transparent" cx="800.5928" cy="-126" rx="50.0912" ry="18"/>
<text text-anchor="middle" x="800.5928" y="-122.3" font-family="Times,serif" font-size="14.00" fill="#000000">Expr/Types.hs</text>
</g>
<!-- expr&#45;&gt;Expr/Types.hs -->
<g id="edge4" class="edge">
<title>expr&#45;&gt;Expr/Types.hs</title>
<path fill="none" stroke="#000000" d="M618.0957,-312.9934C631.0988,-277.2519 664.9433,-197.0101 720,-153 727.8666,-146.7117 737.267,-141.8573 746.8242,-138.1192"/>
<polygon fill="#000000" stroke="#000000" points="748.1202,-141.3734 756.3826,-134.7414 745.7879,-134.7734 748.1202,-141.3734"/>
</g>
<!-- Expr/Types/Annotated.hs -->
<g id="node15" class="node">
<title>Expr/Types/Annotated.hs</title>
<ellipse fill="none" stroke="transparent" cx="800.5928" cy="-72" rx="80.6858" ry="18"/>
<text text-anchor="middle" x="800.5928" y="-68.3" font-family="Times,serif" font-size="14.00" fill="#000000">Expr/Types/Annotated.hs</text>
</g>
<!-- expr&#45;&gt;Expr/Types/Annotated.hs -->
<g id="edge5" class="edge">
<title>expr&#45;&gt;Expr/Types/Annotated.hs</title>
<path fill="none" stroke="#000000" d="M615.3887,-312.6914C624.3824,-269.2176 652.9103,-158.9419 720,-99 724.1952,-95.2517 728.9284,-92.0211 733.9461,-89.237"/>
<polygon fill="#000000" stroke="#000000" points="735.7877,-92.2324 743.2308,-84.6925 732.7103,-85.9451 735.7877,-92.2324"/>
</g>
<!-- Strings.hs -->
<g id="node16" class="node">
<title>Strings.hs</title>
<ellipse fill="none" stroke="transparent" cx="800.5928" cy="-18" rx="37.8943" ry="18"/>
<text text-anchor="middle" x="800.5928" y="-14.3" font-family="Times,serif" font-size="14.00" fill="#000000">Strings.hs</text>
</g>
<!-- expr&#45;&gt;Strings.hs -->
<g id="edge6" class="edge">
<title>expr&#45;&gt;Strings.hs</title>
<path fill="none" stroke="#000000" d="M613.5796,-312.6825C618.8819,-262.5194 640.4767,-121.5725 720,-45 729.3938,-35.9547 741.9064,-29.9369 754.2649,-25.9342"/>
<polygon fill="#000000" stroke="#000000" points="755.4229,-29.2437 764.0807,-23.1369 753.5043,-22.5117 755.4229,-29.2437"/>
</g>
<!-- pretty -->
<g id="node5" class="node">
<title>pretty</title>
<polygon fill="#ffff00" stroke="#000000" points="684,-857 540,-857 540,-821 684,-821 684,-857"/>
<text text-anchor="middle" x="612" y="-835.3" font-family="Times,serif" font-size="14.00" fill="#000000">Pretty Printer</text>
</g>
<!-- pretty&#45;&gt;value -->
<g id="edge56" class="edge">
<title>pretty&#45;&gt;value</title>
<path fill="none" stroke="#000000" d="M539.9728,-820.762C532.7984,-816.1215 526.5121,-810.3008 522,-803 500.9708,-768.974 500.9708,-747.026 522,-713 524.6791,-708.6651 527.9836,-704.8521 531.7281,-701.4981"/>
<polygon fill="#000000" stroke="#000000" points="534.125,-704.0728 539.9728,-695.238 529.8919,-698.4977 534.125,-704.0728"/>
</g>
<!-- pretty&#45;&gt;expr -->
<g id="edge52" class="edge">
<title>pretty&#45;&gt;expr</title>
<path fill="none" stroke="#000000" d="M539.9728,-820.762C532.7984,-816.1215 526.5121,-810.3008 522,-803 509.2656,-782.3953 509.2656,-387.6047 522,-367 524.6791,-362.6651 527.9836,-358.8521 531.7281,-355.4981"/>
<polygon fill="#000000" stroke="#000000" points="534.125,-358.0728 539.9728,-349.238 529.8919,-352.4977 534.125,-358.0728"/>
</g>
<!-- pretty&#45;&gt;support -->
<g id="edge46" class="edge">
<title>pretty&#45;&gt;support</title>
<path fill="none" stroke="#000000" d="M674.1928,-820.8449C677.8088,-818.2763 681.133,-815.345 684,-812 735.1087,-752.3694 667.8408,-697.7139 720,-639 720.6431,-638.276 721.3063,-637.5715 721.9879,-636.8857"/>
<polygon fill="#000000" stroke="#000000" points="724.5113,-639.338 729.935,-630.2366 720.0194,-633.9692 724.5113,-639.338"/>
</g>
<!-- Pretty.hs -->
<g id="node17" class="node">
<title>Pretty.hs</title>
<ellipse fill="none" stroke="transparent" cx="800.5928" cy="-936" rx="35.194" ry="18"/>
<text text-anchor="middle" x="800.5928" y="-932.3" font-family="Times,serif" font-size="14.00" fill="#000000">Pretty.hs</text>
</g>
<!-- pretty&#45;&gt;Pretty.hs -->
<g id="edge7" class="edge">
<title>pretty&#45;&gt;Pretty.hs</title>
<path fill="none" stroke="#000000" d="M636.2632,-857.2742C657.4936,-872.6282 689.6073,-894.3498 720,-909 732.4991,-915.0249 746.6894,-920.2666 759.6422,-924.4875"/>
<polygon fill="#000000" stroke="#000000" points="758.6681,-927.8501 769.2575,-927.5123 760.7687,-921.1727 758.6681,-927.8501"/>
</g>
<!-- Render.hs -->
<g id="node18" class="node">
<title>Render.hs</title>
<ellipse fill="none" stroke="transparent" cx="800.5928" cy="-882" rx="38.1938" ry="18"/>
<text text-anchor="middle" x="800.5928" y="-878.3" font-family="Times,serif" font-size="14.00" fill="#000000">Render.hs</text>
</g>
<!-- pretty&#45;&gt;Render.hs -->
<g id="edge8" class="edge">
<title>pretty&#45;&gt;Render.hs</title>
<path fill="none" stroke="#000000" d="M684.1061,-855.4405C708.3436,-860.9668 734.6548,-866.9658 756.1285,-871.8619"/>
<polygon fill="#000000" stroke="#000000" points="755.4115,-875.2882 765.9394,-874.0989 756.9677,-868.4634 755.4115,-875.2882"/>
</g>
<!-- Render/Frame.hs -->
<g id="node19" class="node">
<title>Render/Frame.hs</title>
<ellipse fill="none" stroke="transparent" cx="800.5928" cy="-828" rx="57.6901" ry="18"/>
<text text-anchor="middle" x="800.5928" y="-824.3" font-family="Times,serif" font-size="14.00" fill="#000000">Render/Frame.hs</text>
</g>
<!-- pretty&#45;&gt;Render/Frame.hs -->
<g id="edge9" class="edge">
<title>pretty&#45;&gt;Render/Frame.hs</title>
<path fill="none" stroke="#000000" d="M684.1061,-834.7943C700.2279,-833.854 717.2672,-832.8601 733.19,-831.9314"/>
<polygon fill="#000000" stroke="#000000" points="733.6923,-835.4081 743.4715,-831.3317 733.2846,-828.42 733.6923,-835.4081"/>
</g>
<!-- exec -->
<g id="node6" class="node">
<title>exec</title>
<polygon fill="#ffff00" stroke="#000000" points="504,-657 360,-657 360,-621 504,-621 504,-657"/>
<text text-anchor="middle" x="432" y="-635.3" font-family="Times,serif" font-size="14.00" fill="#000000">Executing Evaluator</text>
</g>
<!-- exec&#45;&gt;value -->
<g id="edge53" class="edge">
<title>exec&#45;&gt;value</title>
<path fill="none" stroke="#000000" d="M504.1757,-654.2371C512.6116,-656.018 521.2652,-657.8449 529.8236,-659.6517"/>
<polygon fill="#000000" stroke="#000000" points="529.3008,-663.1183 539.8081,-661.7595 530.7467,-656.2693 529.3008,-663.1183"/>
</g>
<!-- exec&#45;&gt;eval -->
<g id="edge54" class="edge">
<title>exec&#45;&gt;eval</title>
<path fill="none" stroke="#000000" d="M494.2965,-620.9325C497.8881,-618.3433 501.1786,-615.3835 504,-612 561.8421,-542.6348 482.1579,-481.3652 540,-412 540.6172,-411.2599 541.2568,-410.54 541.9171,-409.8398"/>
<polygon fill="#000000" stroke="#000000" points="544.4551,-412.271 549.7035,-403.0675 539.8612,-406.9893 544.4551,-412.271"/>
</g>
<!-- builtins -->
<g id="node7" class="node">
<title>builtins</title>
<polygon fill="#ffff00" stroke="#000000" points="504,-603 360,-603 360,-567 504,-567 504,-603"/>
<text text-anchor="middle" x="432" y="-581.3" font-family="Times,serif" font-size="14.00" fill="#000000">Built&#45;in Functions</text>
</g>
<!-- exec&#45;&gt;builtins -->
<g id="edge55" class="edge">
<title>exec&#45;&gt;builtins</title>
<path fill="none" stroke="#000000" d="M432,-620.7902C432,-618.3082 432,-615.8262 432,-613.3443"/>
<polygon fill="#000000" stroke="#000000" points="435.5001,-613.1406 432,-603.1406 428.5001,-613.1407 435.5001,-613.1406"/>
</g>
<!-- exec&#45;&gt;support -->
<g id="edge47" class="edge">
<title>exec&#45;&gt;support</title>
<path fill="none" stroke="#000000" d="M504.3823,-633.6979C565.8763,-629.1934 654.1242,-622.7291 718.2921,-618.0287"/>
<polygon fill="#000000" stroke="#000000" points="718.6585,-621.5113 728.3761,-617.29 718.1471,-614.53 718.6585,-621.5113"/>
</g>
<!-- Effects.hs -->
<g id="node40" class="node">
<title>Effects.hs</title>
<ellipse fill="none" stroke="transparent" cx="612" cy="-785" rx="37.8943" ry="18"/>
<text text-anchor="middle" x="612" y="-781.3" font-family="Times,serif" font-size="14.00" fill="#000000">Effects.hs</text>
</g>
<!-- exec&#45;&gt;Effects.hs -->
<g id="edge40" class="edge">
<title>exec&#45;&gt;Effects.hs</title>
<path fill="none" stroke="#000000" d="M444.2194,-657.2095C462.3405,-682.949 498.6783,-729.9545 540,-758 548.8731,-764.0223 559.2668,-768.9236 569.3532,-772.8145"/>
<polygon fill="#000000" stroke="#000000" points="568.2755,-776.1463 578.8699,-776.2389 570.6456,-769.5597 568.2755,-776.1463"/>
</g>
<!-- Exec.hs -->
<g id="node41" class="node">
<title>Exec.hs</title>
<ellipse fill="none" stroke="transparent" cx="612" cy="-731" rx="31.6951" ry="18"/>
<text text-anchor="middle" x="612" y="-727.3" font-family="Times,serif" font-size="14.00" fill="#000000">Exec.hs</text>
</g>
<!-- exec&#45;&gt;Exec.hs -->
<g id="edge41" class="edge">
<title>exec&#45;&gt;Exec.hs</title>
<path fill="none" stroke="#000000" d="M459.1923,-657.2744C480.5772,-671.171 511.4311,-690.1899 540,-704 550.9696,-709.3026 563.2686,-714.256 574.5551,-718.4353"/>
<polygon fill="#000000" stroke="#000000" points="573.5596,-721.7969 584.154,-721.8979 575.935,-715.2122 573.5596,-721.7969"/>
</g>
<!-- builtins&#45;&gt;value -->
<g id="edge57" class="edge">
<title>builtins&#45;&gt;value</title>
<path fill="none" stroke="#000000" d="M483.6174,-603.0424C490.5536,-605.8473 497.5095,-608.8625 504,-612 528.9978,-624.0838 555.745,-640.1556 576.3675,-653.2944"/>
<polygon fill="#000000" stroke="#000000" points="574.5044,-656.2575 584.8077,-658.7256 578.2923,-650.3709 574.5044,-656.2575"/>
</g>
<!-- builtins&#45;&gt;support -->
<g id="edge48" class="edge">
<title>builtins&#45;&gt;support</title>
<path fill="none" stroke="#000000" d="M484.0458,-566.9223C535.9823,-551.7496 617.4208,-535.8069 684,-558 702.9737,-564.3246 702.2914,-575.7045 720,-585 723.2623,-586.7124 726.6549,-588.3477 730.1213,-589.9045"/>
<polygon fill="#000000" stroke="#000000" points="728.8574,-593.17 739.4309,-593.8415 731.5839,-586.7228 728.8574,-593.17"/>
</g>
<!-- Builtins.hs -->
<g id="node26" class="node">
<title>Builtins.hs</title>
<ellipse fill="none" stroke="transparent" cx="612" cy="-493" rx="40.0939" ry="18"/>
<text text-anchor="middle" x="612" y="-489.3" font-family="Times,serif" font-size="14.00" fill="#000000">Builtins.hs</text>
</g>
<!-- builtins&#45;&gt;Builtins.hs -->
<g id="edge16" class="edge">
<title>builtins&#45;&gt;Builtins.hs</title>
<path fill="none" stroke="#000000" d="M477.4037,-566.8372C486.4195,-562.6739 495.668,-557.9903 504,-553 521.3894,-542.5849 522.6106,-535.4151 540,-525 549.8832,-519.0806 561.056,-513.5927 571.6003,-508.893"/>
<polygon fill="#000000" stroke="#000000" points="573.2609,-511.9883 581.0522,-504.8087 570.4842,-505.5625 573.2609,-511.9883"/>
</g>
<!-- XML.hs -->
<g id="node27" class="node">
<title>XML.hs</title>
<ellipse fill="none" stroke="transparent" cx="612" cy="-585" rx="33.5952" ry="18"/>
<text text-anchor="middle" x="612" y="-581.3" font-family="Times,serif" font-size="14.00" fill="#000000">XML.hs</text>
</g>
<!-- builtins&#45;&gt;XML.hs -->
<g id="edge17" class="edge">
<title>builtins&#45;&gt;XML.hs</title>
<path fill="none" stroke="#000000" d="M504.1757,-585C525.6322,-585 548.4967,-585 567.6236,-585"/>
<polygon fill="#000000" stroke="#000000" points="567.8324,-588.5001 577.8324,-585 567.8324,-581.5001 567.8324,-588.5001"/>
</g>
<!-- Frames.hs -->
<g id="node28" class="node">
<title>Frames.hs</title>
<ellipse fill="none" stroke="transparent" cx="957.4821" cy="-720" rx="38.1938" ry="18"/>
<text text-anchor="middle" x="957.4821" y="-716.3" font-family="Times,serif" font-size="14.00" fill="#000000">Frames.hs</text>
</g>
<!-- support&#45;&gt;Frames.hs -->
<g id="edge18" class="edge">
<title>support&#45;&gt;Frames.hs</title>
<path fill="none" stroke="#000000" d="M867.1013,-630.1879C872.0829,-632.7602 876.8493,-635.6827 881.1857,-639 904.0953,-656.5257 896.8134,-672.5802 917.1857,-693 919.6391,-695.4591 922.3481,-697.8268 925.1667,-700.0686"/>
<polygon fill="#000000" stroke="#000000" points="923.1467,-702.9276 933.2787,-706.0249 927.2896,-697.2852 923.1467,-702.9276"/>
</g>
<!-- Scope.hs -->
<g id="node29" class="node">
<title>Scope.hs</title>
<ellipse fill="none" stroke="transparent" cx="957.4821" cy="-666" rx="35.194" ry="18"/>
<text text-anchor="middle" x="957.4821" y="-662.3" font-family="Times,serif" font-size="14.00" fill="#000000">Scope.hs</text>
</g>
<!-- support&#45;&gt;Scope.hs -->
<g id="edge19" class="edge">
<title>support&#45;&gt;Scope.hs</title>
<path fill="none" stroke="#000000" d="M854.8248,-630.0293C863.6444,-633.0014 872.6751,-636.0688 881.1857,-639 893.4328,-643.2182 906.7532,-647.8996 918.7859,-652.1637"/>
<polygon fill="#000000" stroke="#000000" points="917.9438,-655.5788 928.5385,-655.6281 920.2869,-648.9826 917.9438,-655.5788"/>
</g>
<!-- Thunk.hs -->
<g id="node30" class="node">
<title>Thunk.hs</title>
<ellipse fill="none" stroke="transparent" cx="957.4821" cy="-612" rx="36.2938" ry="18"/>
<text text-anchor="middle" x="957.4821" y="-608.3" font-family="Times,serif" font-size="14.00" fill="#000000">Thunk.hs</text>
</g>
<!-- support&#45;&gt;Thunk.hs -->
<g id="edge20" class="edge">
<title>support&#45;&gt;Thunk.hs</title>
<path fill="none" stroke="#000000" d="M872.609,-612C885.5681,-612 898.7628,-612 910.7655,-612"/>
<polygon fill="#000000" stroke="#000000" points="910.9708,-615.5001 920.9707,-612 910.9707,-608.5001 910.9708,-615.5001"/>
</g>
<!-- Options.hs -->
<g id="node31" class="node">
<title>Options.hs</title>
<ellipse fill="none" stroke="transparent" cx="957.4821" cy="-558" rx="40.0939" ry="18"/>
<text text-anchor="middle" x="957.4821" y="-554.3" font-family="Times,serif" font-size="14.00" fill="#000000">Options.hs</text>
</g>
<!-- support&#45;&gt;Options.hs -->
<g id="edge21" class="edge">
<title>support&#45;&gt;Options.hs</title>
<path fill="none" stroke="#000000" d="M854.8248,-593.9707C863.6444,-590.9986 872.6751,-587.9312 881.1857,-585 892.591,-581.0717 904.9273,-576.7417 916.2874,-572.7211"/>
<polygon fill="#000000" stroke="#000000" points="917.7172,-575.9277 925.9713,-569.2854 915.3766,-569.3306 917.7172,-575.9277"/>
</g>
<!-- Utils.hs -->
<g id="node32" class="node">
<title>Utils.hs</title>
<ellipse fill="none" stroke="transparent" cx="957.4821" cy="-504" rx="31.6951" ry="18"/>
<text text-anchor="middle" x="957.4821" y="-500.3" font-family="Times,serif" font-size="14.00" fill="#000000">Utils.hs</text>
</g>
<!-- support&#45;&gt;Utils.hs -->
<g id="edge22" class="edge">
<title>support&#45;&gt;Utils.hs</title>
<path fill="none" stroke="#000000" d="M867.1013,-593.8121C872.0829,-591.2398 876.8493,-588.3173 881.1857,-585 904.0953,-567.4743 896.8134,-551.4198 917.1857,-531 920.048,-528.131 923.2582,-525.3865 926.5844,-522.8211"/>
<polygon fill="#000000" stroke="#000000" points="928.8527,-525.5022 934.9591,-516.8441 924.7863,-519.8045 928.8527,-525.5022"/>
</g>
<!-- features -->
<g id="node9" class="node">
<title>features</title>
<polygon fill="#ffff00" stroke="#000000" points="324,-430 180,-430 180,-394 324,-394 324,-430"/>
<text text-anchor="middle" x="252" y="-408.3" font-family="Times,serif" font-size="14.00" fill="#000000">Other Features</text>
</g>
<!-- Cache.hs -->
<g id="node33" class="node">
<title>Cache.hs</title>
<ellipse fill="none" stroke="transparent" cx="432" cy="-493" rx="35.9954" ry="18"/>
<text text-anchor="middle" x="432" y="-489.3" font-family="Times,serif" font-size="14.00" fill="#000000">Cache.hs</text>
</g>
<!-- features&#45;&gt;Cache.hs -->
<g id="edge23" class="edge">
<title>features&#45;&gt;Cache.hs</title>
<path fill="none" stroke="#000000" d="M286.0972,-430.1996C307.1371,-441.1401 334.8275,-455.0231 360,-466 370.4933,-470.5758 382.0353,-475.1156 392.746,-479.13"/>
<polygon fill="#000000" stroke="#000000" points="391.7014,-482.4752 402.2947,-482.655 394.1257,-475.9084 391.7014,-482.4752"/>
</g>
<!-- Lint.hs -->
<g id="node34" class="node">
<title>Lint.hs</title>
<ellipse fill="none" stroke="transparent" cx="432" cy="-331" rx="29.795" ry="18"/>
<text text-anchor="middle" x="432" y="-327.3" font-family="Times,serif" font-size="14.00" fill="#000000">Lint.hs</text>
</g>
<!-- features&#45;&gt;Lint.hs -->
<g id="edge24" class="edge">
<title>features&#45;&gt;Lint.hs</title>
<path fill="none" stroke="#000000" d="M286.0972,-393.8004C307.1371,-382.8599 334.8275,-368.9769 360,-358 371.7591,-352.8722 384.8351,-347.7897 396.5824,-343.441"/>
<polygon fill="#000000" stroke="#000000" points="397.8851,-346.6915 406.0796,-339.9758 395.4857,-340.1155 397.8851,-346.6915"/>
</g>
<!-- Reduce.hs -->
<g id="node35" class="node">
<title>Reduce.hs</title>
<ellipse fill="none" stroke="transparent" cx="432" cy="-439" rx="38.9931" ry="18"/>
<text text-anchor="middle" x="432" y="-435.3" font-family="Times,serif" font-size="14.00" fill="#000000">Reduce.hs</text>
</g>
<!-- features&#45;&gt;Reduce.hs -->
<g id="edge25" class="edge">
<title>features&#45;&gt;Reduce.hs</title>
<path fill="none" stroke="#000000" d="M324.1757,-422.8264C344.6419,-425.8963 366.3892,-429.1584 384.9519,-431.9428"/>
<polygon fill="#000000" stroke="#000000" points="384.4999,-435.4141 394.9085,-433.4363 385.5384,-428.4915 384.4999,-435.4141"/>
</g>
<!-- Type/... -->
<g id="node36" class="node">
<title>Type/...</title>
<ellipse fill="none" stroke="transparent" cx="432" cy="-277" rx="31.6951" ry="18"/>
<text text-anchor="middle" x="432" y="-273.3" font-family="Times,serif" font-size="14.00" fill="#000000">Type/...</text>
</g>
<!-- features&#45;&gt;Type/... -->
<g id="edge26" class="edge">
<title>features&#45;&gt;Type/...</title>
<path fill="none" stroke="#000000" d="M265.8767,-393.9507C284.8423,-370.3662 320.9848,-329.1224 360,-304 370.1572,-297.4597 382.13,-292.1695 393.3658,-288.0628"/>
<polygon fill="#000000" stroke="#000000" points="394.6661,-291.3169 402.9858,-284.7569 392.3911,-284.6969 394.6661,-291.3169"/>
</g>
<!-- TH.hs -->
<g id="node37" class="node">
<title>TH.hs</title>
<ellipse fill="none" stroke="transparent" cx="432" cy="-385" rx="27.0966" ry="18"/>
<text text-anchor="middle" x="432" y="-381.3" font-family="Times,serif" font-size="14.00" fill="#000000">TH.hs</text>
</g>
<!-- features&#45;&gt;TH.hs -->
<g id="edge27" class="edge">
<title>features&#45;&gt;TH.hs</title>
<path fill="none" stroke="#000000" d="M324.1757,-401.1736C348.4962,-397.5256 374.6255,-393.6062 395.0705,-390.5394"/>
<polygon fill="#000000" stroke="#000000" points="395.8339,-393.9642 405.2041,-389.0194 394.7955,-387.0416 395.8339,-393.9642"/>
</g>
<!-- main -->
<g id="node10" class="node">
<title>main</title>
<polygon fill="#00ff00" stroke="#000000" points="144,-607 0,-607 0,-571 144,-571 144,-607"/>
<text text-anchor="middle" x="72" y="-585.3" font-family="Times,serif" font-size="14.00" fill="#000000">Top&#45;Level Driver</text>
</g>
<!-- main&#45;&gt;parser -->
<g id="edge58" class="edge">
<title>main&#45;&gt;parser</title>
<path fill="none" stroke="#000000" d="M144.105,-580.6785C259.1187,-566.9314 474.2378,-539.1014 504,-520 528.275,-504.4203 517.4051,-483.9297 540,-466 541.5686,-464.7552 543.2006,-463.5637 544.8832,-462.4233"/>
<polygon fill="#000000" stroke="#000000" points="546.8969,-465.2963 553.658,-457.1392 543.2857,-459.2997 546.8969,-465.2963"/>
</g>
<!-- main&#45;&gt;pretty -->
<g id="edge59" class="edge">
<title>main&#45;&gt;pretty</title>
<path fill="none" stroke="#000000" d="M107.3382,-607.1245C184.7581,-646.5457 375.5679,-742.1457 540,-812 544.1201,-813.7503 548.3961,-815.5053 552.7165,-817.2337"/>
<polygon fill="#000000" stroke="#000000" points="551.615,-820.5614 562.2022,-820.963 554.1763,-814.0468 551.615,-820.5614"/>
</g>
<!-- main&#45;&gt;exec -->
<g id="edge60" class="edge">
<title>main&#45;&gt;exec</title>
<path fill="none" stroke="#000000" d="M144.4183,-599.0581C203.7397,-607.2972 287.6847,-618.9562 349.5743,-627.552"/>
<polygon fill="#000000" stroke="#000000" points="349.311,-631.0489 359.6974,-628.958 350.274,-624.1155 349.311,-631.0489"/>
</g>
<!-- main&#45;&gt;features -->
<g id="edge61" class="edge">
<title>main&#45;&gt;features</title>
<path fill="none" stroke="#000000" d="M90.5852,-570.7246C122.8708,-538.977 189.3763,-473.5799 226.1444,-437.4247"/>
<polygon fill="#000000" stroke="#000000" points="228.8396,-439.683 233.5159,-430.176 223.9316,-434.6919 228.8396,-439.683"/>
</g>
<!-- Cache.hs&#45;&gt;parser -->
<g id="edge28" class="edge">
<title>Cache.hs&#45;&gt;parser</title>
<path fill="none" stroke="#000000" d="M462.9431,-483.7171C484.6076,-477.2177 514.4463,-468.2661 541.738,-460.0786"/>
<polygon fill="#000000" stroke="#000000" points="543.0853,-463.3286 551.6578,-457.1027 541.0738,-456.6238 543.0853,-463.3286"/>
</g>
<!-- Cache.hs&#45;&gt;expr -->
<g id="edge29" class="edge">
<title>Cache.hs&#45;&gt;expr</title>
<path fill="none" stroke="#000000" d="M465.9687,-486.881C479.3511,-482.9283 493.9027,-476.4782 504,-466 539.1088,-429.5668 504.8912,-394.4332 540,-358 540.9119,-357.0537 541.8601,-356.1403 542.8405,-355.2586"/>
<polygon fill="#000000" stroke="#000000" points="545.0868,-357.9472 550.9184,-349.1016 540.8435,-352.38 545.0868,-357.9472"/>
</g>
<!-- Lint.hs&#45;&gt;eval -->
<g id="edge30" class="edge">
<title>Lint.hs&#45;&gt;eval</title>
<path fill="none" stroke="#000000" d="M458.9478,-339.0843C480.8881,-345.6664 512.875,-355.2625 541.9404,-363.9821"/>
<polygon fill="#000000" stroke="#000000" points="541.0826,-367.3788 551.6666,-366.9 543.0941,-360.674 541.0826,-367.3788"/>
</g>
<!-- Lint.hs&#45;&gt;expr -->
<g id="edge31" class="edge">
<title>Lint.hs&#45;&gt;expr</title>
<path fill="none" stroke="#000000" d="M462.128,-331C480.693,-331 505.4786,-331 529.5403,-331"/>
<polygon fill="#000000" stroke="#000000" points="529.7879,-334.5001 539.7878,-331 529.7878,-327.5001 529.7879,-334.5001"/>
</g>
<!-- Reduce.hs&#45;&gt;parser -->
<g id="edge32" class="edge">
<title>Reduce.hs&#45;&gt;parser</title>
<path fill="none" stroke="#000000" d="M471.0573,-439C488.2172,-439 509.0613,-439 529.3981,-439"/>
<polygon fill="#000000" stroke="#000000" points="529.6421,-442.5001 539.6421,-439 529.6421,-435.5001 529.6421,-442.5001"/>
</g>
<!-- Reduce.hs&#45;&gt;expr -->
<g id="edge33" class="edge">
<title>Reduce.hs&#45;&gt;expr</title>
<path fill="none" stroke="#000000" d="M466.8705,-430.7543C479.4696,-426.5875 493.2287,-420.5473 504,-412 526.5949,-394.0703 517.4051,-375.9297 540,-358 541.5686,-356.7552 543.2006,-355.5637 544.8832,-354.4233"/>
<polygon fill="#000000" stroke="#000000" points="546.8969,-357.2963 553.658,-349.1392 543.2857,-351.2997 546.8969,-357.2963"/>
</g>
<!-- Type/...&#45;&gt;eval -->
<g id="edge34" class="edge">
<title>Type/...&#45;&gt;eval</title>
<path fill="none" stroke="#000000" d="M461.8077,-283.6488C475.674,-287.8169 491.7647,-294.2909 504,-304 526.5949,-321.9297 517.4051,-340.0703 540,-358 541.5686,-359.2448 543.2006,-360.4363 544.8832,-361.5767"/>
<polygon fill="#000000" stroke="#000000" points="543.2857,-364.7003 553.658,-366.8608 546.8969,-358.7037 543.2857,-364.7003"/>
</g>
<!-- Type/...&#45;&gt;expr -->
<g id="edge35" class="edge">
<title>Type/...&#45;&gt;expr</title>
<path fill="none" stroke="#000000" d="M460.5215,-285.5565C482.4245,-292.1273 513.6329,-301.4899 542.0365,-310.0109"/>
<polygon fill="#000000" stroke="#000000" points="541.3623,-313.4627 551.9463,-312.9839 543.3738,-306.758 541.3623,-313.4627"/>
</g>
<!-- TH.hs&#45;&gt;parser -->
<g id="edge36" class="edge">
<title>TH.hs&#45;&gt;parser</title>
<path fill="none" stroke="#000000" d="M457.0282,-392.5085C479.0813,-399.1244 512.2785,-409.0836 542.2975,-418.0892"/>
<polygon fill="#000000" stroke="#000000" points="541.33,-421.4531 551.914,-420.9742 543.3415,-414.7483 541.33,-421.4531"/>
</g>
<!-- TH.hs&#45;&gt;expr -->
<g id="edge37" class="edge">
<title>TH.hs&#45;&gt;expr</title>
<path fill="none" stroke="#000000" d="M457.0282,-377.4915C479.0813,-370.8756 512.2785,-360.9164 542.2975,-351.9108"/>
<polygon fill="#000000" stroke="#000000" points="543.3415,-355.2517 551.914,-349.0258 541.33,-348.5469 543.3415,-355.2517"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 36 KiB

View file

@ -2,7 +2,7 @@
--
-- see: https://github.com/sol/hpack
--
-- hash: 5eba5b1b4e2ae5297dcad048052e5bc02fc2f42aa37016fbea866ecbf3f4a380
-- hash: 5dd7aaae46b28fedd3791ab641dc5093bf7e2bca549578aea96ec23ed791ed22
name: hnix
version: 0.5.0
@ -60,8 +60,6 @@ library
Nix.Normal
Nix.Options
Nix.Parser
Nix.Parser.Library
Nix.Parser.Operators
Nix.Pretty
Nix.Reduce
Nix.Render
@ -206,6 +204,7 @@ test-suite hnix-tests
, megaparsec
, mtl
, optparse-applicative
, pretty-show
, process
, quickcheck-instances
, serialise

View file

@ -116,7 +116,7 @@ main = do
A.encodeToLazyText (stripAnnotation expr)
| verbose opts >= DebugInfo =
liftIO $ print $ stripAnnotation expr
liftIO $ putStr $ PS.ppShow $ stripAnnotation expr
| cache opts, Just path <- mpath =
liftIO $ writeCache (addExtension (dropExtension path) "nixc") expr

View file

@ -134,6 +134,7 @@ tests:
- Diff
- megaparsec
- tasty-quickcheck
- pretty-show
benchmarks:
hnix-benchmarks:

View file

@ -37,7 +37,6 @@ import Nix.Frames
import Nix.Normal
import Nix.Options
import Nix.Parser
import Nix.Parser.Library (Result(..))
import Nix.Pretty
import Nix.Reduce
import Nix.Render.Frame

View file

@ -56,7 +56,6 @@ import Lens.Family2
import Lens.Family2.Stock (_1)
import Lens.Family2.TH
import Nix.Atoms
import Nix.Parser.Library (SourcePos(..))
import Nix.Utils
import Text.Megaparsec.Pos
import Text.Read.Deriving

View file

@ -17,7 +17,6 @@
module Nix.Expr.Types.Annotated
( module Nix.Expr.Types.Annotated
, module Data.Functor.Compose
, module Nix.Parser.Library
, SourcePos(..), unPos, mkPos
)where
@ -39,8 +38,8 @@ import Data.Text (Text, pack)
import GHC.Generics
import Nix.Atoms
import Nix.Expr.Types
import Nix.Parser.Library (SourcePos(..))
import Text.Megaparsec (unPos, mkPos)
import Text.Megaparsec.Pos (SourcePos(..))
import Text.Read.Deriving
import Text.Show.Deriving

View file

@ -1,28 +1,58 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
{-# OPTIONS_GHC -Wno-missing-signatures #-}
module Nix.Parser (
parseNixFile,
parseNixFileLoc,
parseNixText,
parseNixTextLoc,
Result(..)
) where
module Nix.Parser
( parseNixFile
, parseNixFileLoc
, parseNixText
, parseNixTextLoc
, parseFromFileEx
, parseFromText
, Result(..)
, reservedNames
, OperatorInfo(..)
, NSpecialOp(..)
, NAssoc(..)
, NOperatorDef
, getUnaryOperator
, getBinaryOperator
, getSpecialOperator
) where
import Control.Applicative hiding (many, some)
import Control.DeepSeq
import Control.Monad
import Control.Monad.IO.Class
import Data.Char (isAlpha, isDigit, isSpace)
import Data.Data (Data(..))
import Data.Foldable (concat)
import Data.Functor
import Data.Functor.Identity
import Data.HashSet (HashSet)
import qualified Data.HashSet as HashSet
import Data.List.NonEmpty (NonEmpty(..))
import qualified Data.List.NonEmpty as NE
import Data.Text hiding (map)
import qualified Data.Map as Map
import Data.Text (Text)
import Data.Text hiding (map, foldr1, concat, concatMap, zipWith)
import qualified Data.Text.IO as T
import Data.Typeable (Typeable)
import Data.Void
import GHC.Generics hiding (Prefix)
import Nix.Expr hiding (($>))
import Nix.Parser.Library
import Nix.Parser.Operators
import Nix.Strings
import Text.Megaparsec
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as L
import Text.Megaparsec.Expr
import Text.PrettyPrint.ANSI.Leijen (Doc, text)
infixl 3 <+>
(<+>) :: MonadPlus m => m a -> m a -> m a
@ -315,3 +345,202 @@ parseNixText =
parseNixTextLoc :: Text -> Result NExprLoc
parseNixTextLoc = parseFromText (whiteSpace *> nixToplevelForm <* eof)
{- Parser.Library -}
skipLineComment' :: Tokens Text -> Parser ()
skipLineComment' prefix =
string prefix
*> void (takeWhileP (Just "character") (\x -> x /= '\n' && x /= '\r'))
whiteSpace :: Parser ()
whiteSpace = L.space space1 lineCmnt blockCmnt
where
lineCmnt = skipLineComment' "#"
blockCmnt = L.skipBlockComment "/*" "*/"
lexeme :: Parser a -> Parser a
lexeme p = p <* whiteSpace
symbol :: Text -> Parser Text
symbol = lexeme . string
reservedEnd :: Char -> Bool
reservedEnd x = isSpace x ||
x == '{' || x == '(' || x == '[' ||
x == '}' || x == ')' || x == ']' ||
x == ';' || x == ':' || x == '.' ||
x == '"' || x == '\'' || x == ','
reserved :: Text -> Parser ()
reserved n = lexeme $ try $
string n *> lookAhead (void (satisfy reservedEnd) <|> eof)
identifier = lexeme $ try $ do
ident <- cons <$> satisfy (\x -> isAlpha x || x == '_')
<*> takeWhileP Nothing identLetter
guard (not (ident `HashSet.member` reservedNames))
return ident
where
identLetter x = isAlpha x || isDigit x || x == '_' || x == '\'' || x == '-'
parens = between (symbol "(") (symbol ")")
braces = between (symbol "{") (symbol "}")
-- angles = between (symbol "<") (symbol ">")
brackets = between (symbol "[") (symbol "]")
semi = symbol ";"
comma = symbol ","
-- colon = symbol ":"
-- dot = symbol "."
equals = symbol "="
question = symbol "?"
integer :: Parser Integer
integer = lexeme L.decimal
float :: Parser Double
float = lexeme L.float
reservedNames :: HashSet Text
reservedNames = HashSet.fromList
[ "let", "in"
, "if", "then", "else"
, "assert"
, "with"
, "rec"
, "inherit"
, "true", "false" ]
type Parser = ParsecT Void Text Identity
data Result a = Success a | Failure Doc deriving Show
parseFromFileEx :: MonadIO m => Parser a -> FilePath -> m (Result a)
parseFromFileEx p path = do
txt <- liftIO (T.readFile path)
return $ either (Failure . text . parseErrorPretty' txt) Success
$ parse p path txt
parseFromText :: Parser a -> Text -> Result a
parseFromText p txt =
either (Failure . text . parseErrorPretty' txt) Success $
parse p "<string>" txt
{- Parser.Operators -}
data NSpecialOp = NHasAttrOp | NSelectOp
deriving (Eq, Ord, Generic, Typeable, Data, Show, NFData)
data NAssoc = NAssocNone | NAssocLeft | NAssocRight
deriving (Eq, Ord, Generic, Typeable, Data, Show, NFData)
data NOperatorDef
= NUnaryDef Text NUnaryOp
| NBinaryDef Text NBinaryOp NAssoc
| NSpecialDef Text NSpecialOp NAssoc
deriving (Eq, Ord, Generic, Typeable, Data, Show, NFData)
annotateLocation :: Parser a -> Parser (Ann SrcSpan a)
annotateLocation p = do
begin <- getPosition
res <- p
end <- getPosition
pure $ Ann (SrcSpan begin end) res
annotateLocation1 :: Parser (NExprF NExprLoc) -> Parser NExprLoc
annotateLocation1 = fmap annToAnnF . annotateLocation
manyUnaryOp f = foldr1 (.) <$> some f
operator "-" = lexeme . try $ string "-" <* notFollowedBy (char '>')
operator "/" = lexeme . try $ string "/" <* notFollowedBy (char '/')
operator "<" = lexeme . try $ string "<" <* notFollowedBy (char '=')
operator ">" = lexeme . try $ string ">" <* notFollowedBy (char '=')
operator n = symbol n
opWithLoc :: Text -> o -> (Ann SrcSpan o -> a) -> Parser a
opWithLoc name op f = do
Ann ann _ <- annotateLocation $ {- dbg (unpack name) $ -} operator name
return $ f (Ann ann op)
binaryN name op = (NBinaryDef name op NAssocNone,
InfixN (opWithLoc name op nBinary))
binaryL name op = (NBinaryDef name op NAssocLeft,
InfixL (opWithLoc name op nBinary))
binaryR name op = (NBinaryDef name op NAssocRight,
InfixR (opWithLoc name op nBinary))
prefix name op = (NUnaryDef name op,
Prefix (manyUnaryOp (opWithLoc name op nUnary)))
-- postfix name op = (NUnaryDef name op,
-- Postfix (opWithLoc name op nUnary))
nixOperators
:: Parser (Ann SrcSpan (NAttrPath NExprLoc))
-> [[(NOperatorDef, Operator Parser NExprLoc)]]
nixOperators selector =
[ -- This is not parsed here, even though technically it's part of the
-- expression table. The problem is that in some cases, such as list
-- membership, it's also a term. And since terms are effectively the
-- highest precedence entities parsed by the expression parser, it ends up
-- working out that we parse them as a kind of "meta-term".
-- {- 1 -} [ (NSpecialDef "." NSelectOp NAssocLeft,
-- Postfix $ do
-- sel <- seldot *> selector
-- mor <- optional (reserved "or" *> term)
-- return $ \x -> nSelectLoc x sel mor) ]
{- 2 -} [ (NBinaryDef " " NApp NAssocLeft,
-- Thanks to Brent Yorgey for showing me this trick!
InfixL $ nApp <$ symbol "") ]
, {- 3 -} [ prefix "-" NNeg ]
, {- 4 -} [ (NSpecialDef "?" NHasAttrOp NAssocLeft,
Postfix $ symbol "?" *> (flip nHasAttr <$> selector)) ]
, {- 5 -} [ binaryR "++" NConcat ]
, {- 6 -} [ binaryL "*" NMult
, binaryL "/" NDiv ]
, {- 7 -} [ binaryL "+" NPlus
, binaryL "-" NMinus ]
, {- 8 -} [ prefix "!" NNot ]
, {- 9 -} [ binaryR "//" NUpdate ]
, {- 10 -} [ binaryL "<" NLt
, binaryL ">" NGt
, binaryL "<=" NLte
, binaryL ">=" NGte ]
, {- 11 -} [ binaryN "==" NEq
, binaryN "!=" NNEq ]
, {- 12 -} [ binaryL "&&" NAnd ]
, {- 13 -} [ binaryL "||" NOr ]
, {- 14 -} [ binaryN "->" NImpl ]
]
data OperatorInfo = OperatorInfo
{ precedence :: Int
, associativity :: NAssoc
, operatorName :: Text
} deriving (Eq, Ord, Generic, Typeable, Data, Show)
getUnaryOperator :: NUnaryOp -> OperatorInfo
getUnaryOperator = (m Map.!) where
m = Map.fromList $ concat $ zipWith buildEntry [1..]
(nixOperators (error "unused"))
buildEntry i = concatMap $ \case
(NUnaryDef name op, _) -> [(op, OperatorInfo i NAssocNone name)]
_ -> []
getBinaryOperator :: NBinaryOp -> OperatorInfo
getBinaryOperator = (m Map.!) where
m = Map.fromList $ concat $ zipWith buildEntry [1..]
(nixOperators (error "unused"))
buildEntry i = concatMap $ \case
(NBinaryDef name op assoc, _) -> [(op, OperatorInfo i assoc name)]
_ -> []
getSpecialOperator :: NSpecialOp -> OperatorInfo
getSpecialOperator NSelectOp = OperatorInfo 1 NAssocLeft "."
getSpecialOperator o = m Map.! o where
m = Map.fromList $ concat $ zipWith buildEntry [1..]
(nixOperators (error "unused"))
buildEntry i = concatMap $ \case
(NSpecialDef name op assoc, _) -> [(op, OperatorInfo i assoc name)]
_ -> []

View file

@ -1,101 +0,0 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wno-missing-signatures #-}
module Nix.Parser.Library
( module Nix.Parser.Library
, module X
) where
import Control.Applicative hiding (many)
import Control.Monad
import Control.Monad.IO.Class
import Data.Char (isAlpha, isDigit, isSpace)
import Data.Functor.Identity
import Data.HashSet (HashSet)
import qualified Data.HashSet as HashSet
import Data.Text
import qualified Data.Text.IO as T
import Data.Void
import Text.Megaparsec as X
import Text.Megaparsec.Char as X
import qualified Text.Megaparsec.Char.Lexer as L
import Text.PrettyPrint.ANSI.Leijen as X (Doc, text)
skipLineComment' :: Tokens Text -> Parser ()
skipLineComment' prefix =
string prefix
*> void (takeWhileP (Just "character") (\x -> x /= '\n' && x /= '\r'))
whiteSpace :: Parser ()
whiteSpace = L.space space1 lineCmnt blockCmnt
where
lineCmnt = skipLineComment' "#"
blockCmnt = L.skipBlockComment "/*" "*/"
lexeme :: Parser a -> Parser a
lexeme p = p <* whiteSpace
symbol = lexeme . string
reservedEnd :: Char -> Bool
reservedEnd x = isSpace x ||
x == '{' || x == '(' || x == '[' ||
x == '}' || x == ')' || x == ']' ||
x == ';' || x == ':' || x == '.' ||
x == '"' || x == '\'' || x == ','
reserved :: Text -> Parser ()
reserved n = lexeme $ try $
string n *> lookAhead (void (satisfy reservedEnd) <|> eof)
identifier = lexeme $ try $ do
ident <- cons <$> satisfy (\x -> isAlpha x || x == '_')
<*> takeWhileP Nothing identLetter
guard (not (ident `HashSet.member` reservedNames))
return ident
where
identLetter x = isAlpha x || isDigit x || x == '_' || x == '\'' || x == '-'
parens = between (symbol "(") (symbol ")")
braces = between (symbol "{") (symbol "}")
angles = between (symbol "<") (symbol ">")
brackets = between (symbol "[") (symbol "]")
semi = symbol ";"
comma = symbol ","
colon = symbol ":"
dot = symbol "."
equals = symbol "="
question = symbol "?"
integer :: Parser Integer
integer = lexeme L.decimal
float :: Parser Double
float = lexeme L.float
reservedNames :: HashSet Text
reservedNames = HashSet.fromList
[ "let", "in"
, "if", "then", "else"
, "assert"
, "with"
, "rec"
, "inherit"
, "true", "false" ]
type Parser = ParsecT Void Text Identity
data Result a = Success a | Failure Doc deriving Show
parseFromFileEx :: MonadIO m => Parser a -> FilePath -> m (Result a)
parseFromFileEx p path = do
txt <- liftIO (T.readFile path)
return $ either (Failure . text . parseErrorPretty' txt) Success
$ parse p path txt
parseFromText :: Parser a -> Text -> Result a
parseFromText p txt =
either (Failure . text . parseErrorPretty' txt) Success $
parse p "<string>" txt

View file

@ -1,137 +0,0 @@
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -Wno-missing-signatures #-}
module Nix.Parser.Operators where
import Control.DeepSeq
import Data.Data (Data(..))
import Data.Foldable (concat)
import qualified Data.Map as Map
import Data.Text (Text)
import Data.Typeable (Typeable)
import GHC.Generics hiding (Prefix)
import Nix.Expr
import Nix.Parser.Library
import Text.Megaparsec.Expr
data NSpecialOp = NHasAttrOp | NSelectOp
deriving (Eq, Ord, Generic, Typeable, Data, Show, NFData)
data NAssoc = NAssocNone | NAssocLeft | NAssocRight
deriving (Eq, Ord, Generic, Typeable, Data, Show, NFData)
data NOperatorDef
= NUnaryDef Text NUnaryOp
| NBinaryDef Text NBinaryOp NAssoc
| NSpecialDef Text NSpecialOp NAssoc
deriving (Eq, Ord, Generic, Typeable, Data, Show, NFData)
annotateLocation :: Parser a -> Parser (Ann SrcSpan a)
annotateLocation p = do
begin <- getPosition
res <- p
end <- getPosition
pure $ Ann (SrcSpan begin end) res
annotateLocation1 :: Parser (NExprF NExprLoc) -> Parser NExprLoc
annotateLocation1 = fmap annToAnnF . annotateLocation
manyUnaryOp f = foldr1 (.) <$> some f
operator "-" = lexeme . try $ string "-" <* notFollowedBy (char '>')
operator "/" = lexeme . try $ string "/" <* notFollowedBy (char '/')
operator "<" = lexeme . try $ string "<" <* notFollowedBy (char '=')
operator ">" = lexeme . try $ string ">" <* notFollowedBy (char '=')
operator n = symbol n
opWithLoc :: Text -> o -> (Ann SrcSpan o -> a) -> Parser a
opWithLoc name op f = do
Ann ann _ <- annotateLocation $ {- dbg (unpack name) $ -} operator name
return $ f (Ann ann op)
binaryN name op = (NBinaryDef name op NAssocNone,
InfixN (opWithLoc name op nBinary))
binaryL name op = (NBinaryDef name op NAssocLeft,
InfixL (opWithLoc name op nBinary))
binaryR name op = (NBinaryDef name op NAssocRight,
InfixR (opWithLoc name op nBinary))
prefix name op = (NUnaryDef name op,
Prefix (manyUnaryOp (opWithLoc name op nUnary)))
postfix name op = (NUnaryDef name op,
Postfix (opWithLoc name op nUnary))
nixOperators
:: Parser (Ann SrcSpan (NAttrPath NExprLoc))
-> [[(NOperatorDef, Operator Parser NExprLoc)]]
nixOperators selector =
[ -- This is not parsed here, even though technically it's part of the
-- expression table. The problem is that in some cases, such as list
-- membership, it's also a term. And since terms are effectively the
-- highest precedence entities parsed by the expression parser, it ends up
-- working out that we parse them as a kind of "meta-term".
-- {- 1 -} [ (NSpecialDef "." NSelectOp NAssocLeft,
-- Postfix $ do
-- sel <- seldot *> selector
-- mor <- optional (reserved "or" *> term)
-- return $ \x -> nSelectLoc x sel mor) ]
{- 2 -} [ (NBinaryDef " " NApp NAssocLeft,
-- Thanks to Brent Yorgey for showing me this trick!
InfixL $ nApp <$ symbol "") ]
, {- 3 -} [ prefix "-" NNeg ]
, {- 4 -} [ (NSpecialDef "?" NHasAttrOp NAssocLeft,
Postfix $ symbol "?" *> (flip nHasAttr <$> selector)) ]
, {- 5 -} [ binaryR "++" NConcat ]
, {- 6 -} [ binaryL "*" NMult
, binaryL "/" NDiv ]
, {- 7 -} [ binaryL "+" NPlus
, binaryL "-" NMinus ]
, {- 8 -} [ prefix "!" NNot ]
, {- 9 -} [ binaryR "//" NUpdate ]
, {- 10 -} [ binaryL "<" NLt
, binaryL ">" NGt
, binaryL "<=" NLte
, binaryL ">=" NGte ]
, {- 11 -} [ binaryN "==" NEq
, binaryN "!=" NNEq ]
, {- 12 -} [ binaryL "&&" NAnd ]
, {- 13 -} [ binaryL "||" NOr ]
, {- 14 -} [ binaryN "->" NImpl ]
]
data OperatorInfo = OperatorInfo
{ precedence :: Int
, associativity :: NAssoc
, operatorName :: Text
} deriving (Eq, Ord, Generic, Typeable, Data, Show)
getUnaryOperator :: NUnaryOp -> OperatorInfo
getUnaryOperator = (m Map.!) where
m = Map.fromList $ concat $ zipWith buildEntry [1..]
(nixOperators (error "unused"))
buildEntry i = concatMap $ \case
(NUnaryDef name op, _) -> [(op, OperatorInfo i NAssocNone name)]
_ -> []
getBinaryOperator :: NBinaryOp -> OperatorInfo
getBinaryOperator = (m Map.!) where
m = Map.fromList $ concat $ zipWith buildEntry [1..]
(nixOperators (error "unused"))
buildEntry i = concatMap $ \case
(NBinaryDef name op assoc, _) -> [(op, OperatorInfo i assoc name)]
_ -> []
getSpecialOperator :: NSpecialOp -> OperatorInfo
getSpecialOperator NSelectOp = OperatorInfo 1 NAssocLeft "."
getSpecialOperator o = m Map.! o where
m = Map.fromList $ concat $ zipWith buildEntry [1..]
(nixOperators (error "unused"))
buildEntry i = concatMap $ \case
(NSpecialDef name op assoc, _) -> [(op, OperatorInfo i assoc name)]
_ -> []

View file

@ -25,8 +25,7 @@ import Data.Text (pack, unpack, replace, strip)
import qualified Data.Text as Text
import Nix.Atoms
import Nix.Expr
import Nix.Parser.Library (reservedNames)
import Nix.Parser.Operators
import Nix.Parser
import Nix.Strings
import Nix.Thunk
#if ENABLE_TRACING
@ -124,7 +123,8 @@ prettyParams :: Params NixDoc -> Doc
prettyParams (Param n) = text $ unpack n
prettyParams (ParamSet s v mname) = prettyParamSet s v <> case mname of
Nothing -> empty
Just name -> text "@" <> text (unpack name)
Just name | Text.null name -> empty
| otherwise -> text "@" <> text (unpack name)
prettyParamSet :: ParamSet NixDoc -> Bool -> Doc
prettyParamSet args var =

View file

@ -14,7 +14,9 @@ import Data.List.NonEmpty (NonEmpty((:|)))
import qualified Data.Set as Set
import Data.Void
import Nix.Expr.Types.Annotated
import Nix.Parser.Library
import Text.Megaparsec.Error
import Text.Megaparsec.Pos (SourcePos(..))
import Text.PrettyPrint.ANSI.Leijen
class Monad m => MonadFile m where
readFile :: FilePath -> m ByteString

View file

@ -21,12 +21,12 @@ import Nix.Expr
import Nix.Frames
import Nix.Normal
import Nix.Options
import Nix.Parser.Library hiding (colon)
import Nix.Pretty
import Nix.Render
import Nix.Thunk
import Nix.Utils
import Nix.Value
import Text.Megaparsec.Pos
import qualified Text.PrettyPrint.ANSI.Leijen as P
import Text.PrettyPrint.ANSI.Leijen hiding ((<$>))
import qualified Text.Show.Pretty as PS

View file

@ -11,7 +11,7 @@ import Control.Monad
import Control.Monad.IO.Class
import Data.Fix
import Data.List (isInfixOf)
import Data.Maybe (isJust)
import Data.Maybe (isJust, fromMaybe)
import Data.String.Interpolate.IsString
import Data.Text (unpack)
import Data.Time
@ -25,7 +25,7 @@ import Nix.Value
import qualified NixLanguageTests
import qualified ParserTests
import qualified PrettyTests
-- import qualified PrettyParseTests
import qualified PrettyParseTests
import System.Environment
import System.FilePath.Glob
import System.Posix.Files
@ -83,21 +83,20 @@ main :: IO ()
main = do
nixLanguageTests <- NixLanguageTests.genTests
evalComparisonTests <- EvalTests.genEvalCompareTests
langTestsEnv <- lookupEnv "LANGUAGE_TESTS"
nixpkgsTestsEnv <- lookupEnv "NIXPKGS_TESTS"
let runLangTests = isJust langTestsEnv
let runNixpkgsTests = isJust nixpkgsTestsEnv
prettyTestsEnv <- lookupEnv "PRETTY_TESTS"
setEnv "NIX_REMOTE" "local?root=/tmp"
defaultMain $ testGroup "hnix" $
[ testCase "hnix.cabal correctly generated" cabalCorrectlyGenerated ] ++
[ ParserTests.tests
, EvalTests.tests
, PrettyTests.tests
-- , PrettyParseTests.tests
, evalComparisonTests ] ++
, PrettyTests.tests ] ++
[ PrettyParseTests.tests (read (fromMaybe "0" prettyTestsEnv)) ] ++
[ evalComparisonTests ] ++
[ testCase "Nix language tests present" ensureLangTestsPresent
| runLangTests ] ++
[ nixLanguageTests | runLangTests ] ++
, nixLanguageTests ] ++
[ testCase "Nixpkgs parses without errors" ensureNixpkgsCanParse
| runNixpkgsTests ]
| isJust nixpkgsTestsEnv ]

View file

@ -56,7 +56,10 @@ groupBy key = Map.fromListWith (++) . map (key &&& pure)
genTests :: IO TestTree
genTests = do
testFiles <- sort . filter ((/= ".xml") . takeExtension)
testFiles <- sort
-- jww (2018-05-07): Temporarily disable this test until #128 is fixed.
. filter ((/= "eval-okay-path") . takeBaseName)
. filter ((/= ".xml") . takeExtension)
<$> globDir1 (compile "*-*-*.*") "data/nix/tests/lang"
let testsByName = groupBy (takeFileName . dropExtensions) testFiles
let testsByType = groupBy testType (Map.toList testsByName)

View file

@ -1,38 +1,42 @@
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS -Wno-orphans#-}
module PrettyParseTests where
import Test.Tasty.QuickCheck hiding (Success, Failure)
import Test.Tasty
import Test.QuickCheck.Instances.Text ()
import Test.QuickCheck.Instances.Semigroup ()
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 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 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
import Nix.Expr (NExpr, NExprF(..), NString(..), NUnaryOp(..), NBinaryOp(..)
, Params(..), NKeyName(..), Antiquoted(..), Binding(..))
import Nix.Atoms
import Nix.Pretty
import Nix.Parser
import Generic.Random
import Data.Fix
import Data.Text (Text, pack, unpack)
import Text.Megaparsec (Pos, SourcePos, mkPos)
import Control.Monad
import Data.Algorithm.Diff
import Data.Algorithm.DiffOutput
import Data.Char
-- 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
-- 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
@ -45,11 +49,11 @@ asciiText :: Gen Text
asciiText = pack <$> asciiString
pcustom :: GenList '[Pos]
pcustom = (arbitrary) :@ Nil
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.
-- | 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
@ -70,13 +74,23 @@ instance Arbitrary f => Arbitrary (Binding f) where
arbitrary = genArb
instance Arbitrary f => Arbitrary (NKeyName f) where
arbitrary = genArb
arbitrary = oneof [ DynamicKey <$> arbitrary
, StaticKey <$> asciiText <*> arbitrary ]
instance Arbitrary f => Arbitrary (Params f) where
arbitrary = genArb
arbitrary =
oneof [ Param <$> asciiText
, ParamSet <$> listOf ((,) <$> asciiText <*> arbitrary) <*> arbitrary
<*> oneof [pure Nothing, Just <$> asciiText]
]
instance Arbitrary NAtom where
arbitrary = genArb
arbitrary =
oneof [ NInt <$> arbitrary `suchThat` (>= 0)
, NFloat <$> arbitrary `suchThat` (>= 0)
, NBool <$> arbitrary
, pure NNull
, NUri <$> asciiText `suchThat` (\x -> Text.length x > 0) ]
instance Arbitrary NUnaryOp where
arbitrary = genArb
@ -90,76 +104,143 @@ instance (Arbitrary f) => Arbitrary (Antiquoted Text f) where
instance (Arbitrary f) => Arbitrary (Antiquoted (NString f) f) where
arbitrary = genArb
-- 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.
-- 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 [nConstant, nStr, nSym, nLiteralPath, nEnvPath ]
then oneof [genConstant, genStr, genSym, genLiteralPath, genEnvPath ]
else
frequency
[ (1, nConstant)
, (1, nSym)
, (4, resize (n `div` 3) nIf)
, (10, nRecSet )
, (20, nSet )
, (5, nList )
, (2, nUnary )
, (2, resize (n `div` 3) nBinary )
, (3, resize (n `div` 3) nSelect )
, (20, resize (n `div` 2) nAbs )
, (2, resize (n `div` 2) nHasAttr )
, (10, resize (n `div` 2) nLet )
, (10, resize (n `div` 2) nWith )
, (1, resize (n `div` 2) nAssert)
[ ( 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
nConstant = NConstant <$> arbitrary
nStr = NStr <$> arbitrary
nSym = NSym <$> asciiText
nList = NList <$> fairList arbitrary
nSet = NSet <$> fairList arbitrary
nRecSet = NRecSet <$> fairList arbitrary
nLiteralPath = NLiteralPath <$> asciiString
nEnvPath = NEnvPath <$> asciiString
nUnary = NUnary <$> arbitrary <*> arbitrary
nBinary = NBinary <$> arbitrary <*> arbitrary <*> arbitrary
nSelect = NSelect <$> arbitrary <*> arbitrary <*> arbitrary
nHasAttr = NHasAttr <$> arbitrary <*> arbitrary
nAbs = NAbs <$> arbitrary <*> arbitrary
nLet = NLet <$> arbitrary <*> arbitrary
nIf = NIf <$> arbitrary <*> arbitrary <*> arbitrary
nWith = NWith <$> arbitrary <*> arbitrary
nAssert = NAssert <$> arbitrary <*> arbitrary
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
-- | Useful when there are recursive positions at each element of the list
-- as it divides the size by the length of the generated list.
-- | 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)
-- Use max here to avoid dividing by zero when there is the empty list
resize (s `div` (max 1 k)) $ vectorOf k g
resize (s `div` max 1 k) $ vectorOf k g
-- | Test that pretty . parse . pretty == pretty
equivUpToNormalization :: NExpr -> NExpr -> Bool
equivUpToNormalization x y = normalize x == normalize y
normalize :: NExpr -> NExpr
normalize = cata $ \case
NConstant (NInt n) | n < 0 -> Fix (NUnary NNeg (Fix (NConstant (NInt (negate n)))))
NConstant (NFloat n) | n < 0 -> Fix (NUnary NNeg (Fix (NConstant (NFloat (negate n)))))
NSet binds -> Fix (NSet (map normBinding binds))
NRecSet binds -> Fix (NRecSet (map normBinding binds))
NLet binds r -> Fix (NLet (map normBinding binds) r)
NAbs params r -> Fix (NAbs (normParams params) r)
r -> Fix r
where
normBinding (NamedVar path r) = NamedVar (NE.map normKey path) r
normBinding (Inherit mr names) = Inherit mr (map normKey names)
normKey (DynamicKey quoted) = DynamicKey (normAntiquotedString quoted)
normKey (StaticKey name _) = StaticKey name Nothing
normAntiquotedString :: Antiquoted (NString NExpr) NExpr
-> Antiquoted (NString NExpr) NExpr
normAntiquotedString (Plain (DoubleQuoted [EscapedNewline])) =
EscapedNewline
normAntiquotedString (Plain (DoubleQuoted strs)) =
let strs' = map normAntiquotedText strs
in if strs == strs'
then Plain (DoubleQuoted strs)
else normAntiquotedString (Plain (DoubleQuoted strs'))
normAntiquotedString r = r
normAntiquotedText :: Antiquoted Text NExpr -> Antiquoted Text NExpr
normAntiquotedText (Plain "\n") = EscapedNewline
normAntiquotedText (Plain "''\n") = EscapedNewline
normAntiquotedText r = r
normParams (ParamSet binds var (Just "")) = ParamSet binds var Nothing
normParams r = r
-- | Test that parse . pretty == id up to attribute position information.
prop_prettyparse :: NExpr -> P.Result
prop_prettyparse p =
case parse (pretty p) of
Failure s -> P.rejected { P.reason = show s ++ show (pretty p) }
Success v ->
let pp = normalise (unpack (pretty p))
pv = normalise (unpack (pretty v))
in (P.liftBool (pp == pv)) { P.reason = "Bad parse:" ++ pp ++ pv ++ ppDiff (diff pp pv) ++ show p ++ show v}
let prog = show (pretty p)
in case parse (pack prog) of
Failure s -> P.rejected
{ P.reason = show $
text "Parse failed:" </> text (show s)
P.<$> P.indent 2 (pretty p) }
Success v
| equivUpToNormalization p v -> P.succeeded
| otherwise ->
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 "========================================"
}
where
pretty = pack . show . prettyNix
parse = parseNixText
pretty = prettyNix
parse = parseNixText
normalise = unlines . map (reverse . dropWhile isSpace . reverse) . lines
diff :: String -> String -> [Diff [String]]
diff s1 s2 = getDiff (map (:[]) (lines s1)) (map (:[]) (lines s2))
diff :: String -> String -> [Diff [String]]
diff s1 s2 = getDiff (map (:[]) (lines s1)) (map (:[]) (lines s2))
tests :: TestTree
tests = testProperty "Pretty Parse Property" prop_prettyparse
tests :: Int -> TestTree
tests n = testProperty "Pretty/Parse Property" $
withMaxSuccess n prop_prettyparse

View file

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

12
tests/files/attrs.nix Normal file
View file

@ -0,0 +1,12 @@
rec {
y = 2;
z = { w = 4; };
v = rec {
u = 6;
t = [ u z.w s.q ];
};
s = { r = import ./goodbye.nix; q = 10; };
p = import ./hello.nix;
}

6
tests/files/if-then.nix Normal file
View file

@ -0,0 +1,6 @@
# [ ({ a = 1; b = 2; } // { c = 1; d = 2; })
# ([1 2 3] ++ [4.0 5.0 6.0])
# (x: y: x + y)
# ]
({ x, y ? x + 1 }: x + x)