diff --git a/Changelog.md b/Changelog.md
new file mode 100644
--- /dev/null
+++ b/Changelog.md
@@ -0,0 +1,6 @@
+# 0.2
+
+- Implement defining and using _blocks_
+- Add function application
+- Allow passing functions as template arguments
+- remove `!` (pass in your own `not`)
diff --git a/fixtures/bools.hs b/fixtures/bools.hs
new file mode 100644
--- /dev/null
+++ b/fixtures/bools.hs
@@ -0,0 +1,62 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+module DemoBools (render) where
+import Prelude (String, fst, snd, ($), return)
+import Control.Monad (forM_)
+import Bools
+type Writer a = (String, a)
+tell :: String -> Writer (); tell x = (x, ())
+execWriter :: Writer a -> String; execWriter = fst
+render :: Bools -> String
+render z_root = execWriter $ do
+  tell "Not\n"
+  tell "===\n"
+  tell "\n"
+  forM_ (boolsBools z_root) $ \z_var0_b -> do
+    tell "not "
+    if z_var0_b
+    then do
+      tell "1"
+      return ()
+    else do
+      tell "0"
+      return ()
+    tell " = "
+    if (boolsNot z_root z_var0_b)
+    then do
+      tell "1"
+      return ()
+    else do
+      tell "0"
+      return ()
+    tell "\n"
+  tell "\n"
+  tell "And\n"
+  tell "===\n"
+  tell "\n"
+  forM_ (boolsBools z_root) $ \z_var1_x -> do
+    forM_ (boolsBools z_root) $ \z_var2_y -> do
+      tell "and "
+      if z_var1_x
+      then do
+        tell "1"
+        return ()
+      else do
+        tell "0"
+        return ()
+      tell " "
+      if z_var2_y
+      then do
+        tell "1"
+        return ()
+      else do
+        tell "0"
+        return ()
+      tell " = "
+      if (boolsAnd z_root z_var1_x z_var2_y)
+      then do
+        tell "1"
+        return ()
+      else do
+        tell "0"
+        return ()
+      tell "\n"
diff --git a/fixtures/bools.txt b/fixtures/bools.txt
new file mode 100644
--- /dev/null
+++ b/fixtures/bools.txt
@@ -0,0 +1,13 @@
+Not
+===
+
+not 0 = 1
+not 1 = 0
+
+And
+===
+
+and 0 0 = 0
+and 0 1 = 0
+and 1 0 = 0
+and 1 1 = 1
diff --git a/fixtures/bools.zinza b/fixtures/bools.zinza
new file mode 100644
--- /dev/null
+++ b/fixtures/bools.zinza
@@ -0,0 +1,15 @@
+Not
+===
+
+{% for b in bools %}
+not {% if b %}1{% else %}0{% endif %} = {% if not b %}1{% else %}0{% endif %}
+{% endfor %}
+
+And
+===
+
+{% for x in bools %}
+{% for y in bools %}
+and {% if x %}1{% else %}0{% endif %} {% if y %}1{% else %}0{% endif %} = {% if and x y %}1{% else %}0{% endif %}
+{% endfor %}
+{% endfor %}
diff --git a/fixtures/cabal-install.hs b/fixtures/cabal-install.hs
new file mode 100644
--- /dev/null
+++ b/fixtures/cabal-install.hs
@@ -0,0 +1,675 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+module DemoCabalInstall (render) where
+import Prelude (String, fst, snd, ($), return)
+import Control.Monad (forM_)
+import CabalInstall
+type Writer a = (String, a)
+tell :: String -> Writer (); tell x = (x, ())
+execWriter :: Writer a -> String; execWriter = fst
+render :: CabalInstall -> String
+render z_root = execWriter $ do
+  if (ciLib z_root)
+  then do
+    tell "Cabal-Version:      2.0\n"
+    return ()
+  else do
+    tell "Cabal-Version:      >= 1.10\n"
+    return ()
+  tell "-- NOTE: This file is autogenerated from 'cabal-install.cabal.zinza'.\n"
+  tell "-- DO NOT EDIT MANUALLY.\n"
+  tell "-- To update this file, edit 'cabal-install.cabal.pp' and run\n"
+  tell "-- 'make cabal-install-prod' in the project's root folder.\n"
+  tell "Name:               cabal-install\n"
+  tell "Version:            3.3.0.0\n"
+  let
+    z_var0_cabal_BUILDDEPENDS = do
+      tell "    build-depends:\n"
+      tell "        async      >= 2.0      && < 2.3,\n"
+      tell "        array      >= 0.4      && < 0.6,\n"
+      tell "        base       >= 4.8      && < 4.14,\n"
+      tell "        base16-bytestring >= 0.1.1 && < 0.2,\n"
+      tell "        binary     >= 0.7.3    && < 0.9,\n"
+      tell "        bytestring >= 0.10.6.0 && < 0.11,\n"
+      tell "        Cabal      == 3.3.*,\n"
+      tell "        containers >= 0.5.6.2  && < 0.7,\n"
+      tell "        cryptohash-sha256 >= 0.11 && < 0.12,\n"
+      tell "        deepseq    >= 1.4.1.1  && < 1.5,\n"
+      tell "        directory  >= 1.2.2.0  && < 1.4,\n"
+      tell "        echo       >= 0.1.3    && < 0.2,\n"
+      tell "        edit-distance >= 0.2.2 && < 0.3,\n"
+      tell "        filepath   >= 1.4.0.0  && < 1.5,\n"
+      tell "        hashable   >= 1.0      && < 1.4,\n"
+      tell "        HTTP       >= 4000.1.5 && < 4000.4,\n"
+      tell "        mtl        >= 2.0      && < 2.3,\n"
+      tell "        network-uri >= 2.6.0.2 && < 2.7,\n"
+      tell "        network    >= 2.6      && < 3.2,\n"
+      tell "        pretty     >= 1.1      && < 1.2,\n"
+      tell "        process    >= 1.2.3.0  && < 1.7,\n"
+      tell "        random     >= 1        && < 1.2,\n"
+      tell "        stm        >= 2.0      && < 2.6,\n"
+      tell "        tar        >= 0.5.0.3  && < 0.6,\n"
+      tell "        time       >= 1.5.0.1  && < 1.10,\n"
+      tell "        transformers >= 0.4.2.0 && < 0.6,\n"
+      tell "        zlib       >= 0.5.3    && < 0.7,\n"
+      tell "        hackage-security >= 0.6.0.0 && < 0.7,\n"
+      tell "        text       >= 1.2.3    && < 1.3,\n"
+      tell "        parsec     >= 3.1.13.0 && < 3.2\n"
+      tell "\n"
+      tell "    if !impl(ghc >= 8.0)\n"
+      tell "        build-depends: fail        == 4.9.*\n"
+      tell "        build-depends: semigroups  >= 0.18.3 && <0.20\n"
+      tell "\n"
+      tell "    if flag(native-dns)\n"
+      tell "      if os(windows)\n"
+      tell "        build-depends: windns      >= 0.1.0 && < 0.2\n"
+      tell "      else\n"
+      tell "        build-depends: resolv      >= 0.1.1 && < 0.2\n"
+      tell "\n"
+      tell "    if os(windows)\n"
+      tell "      build-depends: Win32 >= 2 && < 3\n"
+      tell "    else\n"
+      tell "      build-depends: unix >= 2.5 && < 2.9\n"
+      tell "\n"
+      tell "    if flag(lukko)\n"
+      tell "      build-depends: lukko >= 0.1 && <0.2\n"
+      tell "    else\n"
+      tell "      build-depends: base >= 4.10\n"
+      return ()
+  let
+    z_var1_cabal_COMPONENTCOMMON = do
+      tell "    default-language: Haskell2010\n"
+      tell "    ghc-options:    -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns\n"
+      tell "    if impl(ghc >= 8.0)\n"
+      tell "        ghc-options: -Wcompat\n"
+      tell "                     -Wnoncanonical-monad-instances\n"
+      tell "      if impl(ghc < 8.8)\n"
+      tell "        ghc-options: -Wnoncanonical-monadfail-instances\n"
+      tell "\n"
+      return ()
+  let
+    z_var2_cabal_BUILDINFO = do
+      if (ciLib z_root)
+      then do
+        tell "    exposed-modules:\n"
+        return ()
+      else do
+        tell "    other-modules:\n"
+        return ()
+      tell "        -- this modules are moved from Cabal\n"
+      tell "        -- they are needed for as long until cabal-install moves to parsec parser\n"
+      tell "        Distribution.Deprecated.ParseUtils\n"
+      tell "        Distribution.Deprecated.ReadP\n"
+      tell "        Distribution.Deprecated.Text\n"
+      tell "        Distribution.Deprecated.ViewAsFieldDescr\n"
+      tell "\n"
+      tell "        Distribution.Client.BuildReports.Anonymous\n"
+      tell "        Distribution.Client.BuildReports.Storage\n"
+      tell "        Distribution.Client.BuildReports.Types\n"
+      tell "        Distribution.Client.BuildReports.Upload\n"
+      tell "        Distribution.Client.Check\n"
+      tell "        Distribution.Client.CmdBench\n"
+      tell "        Distribution.Client.CmdBuild\n"
+      tell "        Distribution.Client.CmdClean\n"
+      tell "        Distribution.Client.CmdConfigure\n"
+      tell "        Distribution.Client.CmdUpdate\n"
+      tell "        Distribution.Client.CmdErrorMessages\n"
+      tell "        Distribution.Client.CmdExec\n"
+      tell "        Distribution.Client.CmdFreeze\n"
+      tell "        Distribution.Client.CmdHaddock\n"
+      tell "        Distribution.Client.CmdInstall\n"
+      tell "        Distribution.Client.CmdInstall.ClientInstallFlags\n"
+      tell "        Distribution.Client.CmdRepl\n"
+      tell "        Distribution.Client.CmdRun\n"
+      tell "        Distribution.Client.CmdTest\n"
+      tell "        Distribution.Client.CmdLegacy\n"
+      tell "        Distribution.Client.CmdSdist\n"
+      tell "        Distribution.Client.Compat.Directory\n"
+      tell "        Distribution.Client.Compat.ExecutablePath\n"
+      tell "        Distribution.Client.Compat.FilePerms\n"
+      tell "        Distribution.Client.Compat.Orphans\n"
+      tell "        Distribution.Client.Compat.Prelude\n"
+      tell "        Distribution.Client.Compat.Process\n"
+      tell "        Distribution.Client.Compat.Semaphore\n"
+      tell "        Distribution.Client.Config\n"
+      tell "        Distribution.Client.Configure\n"
+      tell "        Distribution.Client.Dependency\n"
+      tell "        Distribution.Client.Dependency.Types\n"
+      tell "        Distribution.Client.DistDirLayout\n"
+      tell "        Distribution.Client.Exec\n"
+      tell "        Distribution.Client.Fetch\n"
+      tell "        Distribution.Client.FetchUtils\n"
+      tell "        Distribution.Client.FileMonitor\n"
+      tell "        Distribution.Client.Freeze\n"
+      tell "        Distribution.Client.GZipUtils\n"
+      tell "        Distribution.Client.GenBounds\n"
+      tell "        Distribution.Client.Get\n"
+      tell "        Distribution.Client.Glob\n"
+      tell "        Distribution.Client.GlobalFlags\n"
+      tell "        Distribution.Client.Haddock\n"
+      tell "        Distribution.Client.HttpUtils\n"
+      tell "        Distribution.Client.IndexUtils\n"
+      tell "        Distribution.Client.IndexUtils.Timestamp\n"
+      tell "        Distribution.Client.Init\n"
+      tell "        Distribution.Client.Init.Heuristics\n"
+      tell "        Distribution.Client.Init.Licenses\n"
+      tell "        Distribution.Client.Init.Types\n"
+      tell "        Distribution.Client.Install\n"
+      tell "        Distribution.Client.InstallPlan\n"
+      tell "        Distribution.Client.InstallSymlink\n"
+      tell "        Distribution.Client.JobControl\n"
+      tell "        Distribution.Client.List\n"
+      tell "        Distribution.Client.Manpage\n"
+      tell "        Distribution.Client.Nix\n"
+      tell "        Distribution.Client.Outdated\n"
+      tell "        Distribution.Client.PackageHash\n"
+      tell "        Distribution.Client.PackageUtils\n"
+      tell "        Distribution.Client.ParseUtils\n"
+      tell "        Distribution.Client.ProjectBuilding\n"
+      tell "        Distribution.Client.ProjectBuilding.Types\n"
+      tell "        Distribution.Client.ProjectConfig\n"
+      tell "        Distribution.Client.ProjectConfig.Legacy\n"
+      tell "        Distribution.Client.ProjectConfig.Types\n"
+      tell "        Distribution.Client.ProjectOrchestration\n"
+      tell "        Distribution.Client.ProjectPlanOutput\n"
+      tell "        Distribution.Client.ProjectPlanning\n"
+      tell "        Distribution.Client.ProjectPlanning.Types\n"
+      tell "        Distribution.Client.RebuildMonad\n"
+      tell "        Distribution.Client.Reconfigure\n"
+      tell "        Distribution.Client.Run\n"
+      tell "        Distribution.Client.Sandbox\n"
+      tell "        Distribution.Client.Sandbox.Index\n"
+      tell "        Distribution.Client.Sandbox.PackageEnvironment\n"
+      tell "        Distribution.Client.Sandbox.Timestamp\n"
+      tell "        Distribution.Client.Sandbox.Types\n"
+      tell "        Distribution.Client.SavedFlags\n"
+      tell "        Distribution.Client.Security.DNS\n"
+      tell "        Distribution.Client.Security.HTTP\n"
+      tell "        Distribution.Client.Setup\n"
+      tell "        Distribution.Client.SetupWrapper\n"
+      tell "        Distribution.Client.SolverInstallPlan\n"
+      tell "        Distribution.Client.SourceFiles\n"
+      tell "        Distribution.Client.SourceRepo\n"
+      tell "        Distribution.Client.SrcDist\n"
+      tell "        Distribution.Client.Store\n"
+      tell "        Distribution.Client.Tar\n"
+      tell "        Distribution.Client.TargetSelector\n"
+      tell "        Distribution.Client.Targets\n"
+      tell "        Distribution.Client.Types\n"
+      tell "        Distribution.Client.Update\n"
+      tell "        Distribution.Client.Upload\n"
+      tell "        Distribution.Client.Utils\n"
+      tell "        Distribution.Client.Utils.Assertion\n"
+      tell "        Distribution.Client.Utils.Json\n"
+      tell "        Distribution.Client.Utils.Parsec\n"
+      tell "        Distribution.Client.VCS\n"
+      tell "        Distribution.Client.Win32SelfUpgrade\n"
+      tell "        Distribution.Client.World\n"
+      tell "        Distribution.Solver.Compat.Prelude\n"
+      tell "        Distribution.Solver.Modular\n"
+      tell "        Distribution.Solver.Modular.Assignment\n"
+      tell "        Distribution.Solver.Modular.Builder\n"
+      tell "        Distribution.Solver.Modular.Configured\n"
+      tell "        Distribution.Solver.Modular.ConfiguredConversion\n"
+      tell "        Distribution.Solver.Modular.ConflictSet\n"
+      tell "        Distribution.Solver.Modular.Cycles\n"
+      tell "        Distribution.Solver.Modular.Dependency\n"
+      tell "        Distribution.Solver.Modular.Explore\n"
+      tell "        Distribution.Solver.Modular.Flag\n"
+      tell "        Distribution.Solver.Modular.Index\n"
+      tell "        Distribution.Solver.Modular.IndexConversion\n"
+      tell "        Distribution.Solver.Modular.LabeledGraph\n"
+      tell "        Distribution.Solver.Modular.Linking\n"
+      tell "        Distribution.Solver.Modular.Log\n"
+      tell "        Distribution.Solver.Modular.Message\n"
+      tell "        Distribution.Solver.Modular.PSQ\n"
+      tell "        Distribution.Solver.Modular.Package\n"
+      tell "        Distribution.Solver.Modular.Preference\n"
+      tell "        Distribution.Solver.Modular.RetryLog\n"
+      tell "        Distribution.Solver.Modular.Solver\n"
+      tell "        Distribution.Solver.Modular.Tree\n"
+      tell "        Distribution.Solver.Modular.Validate\n"
+      tell "        Distribution.Solver.Modular.Var\n"
+      tell "        Distribution.Solver.Modular.Version\n"
+      tell "        Distribution.Solver.Modular.WeightedPSQ\n"
+      tell "        Distribution.Solver.Types.ComponentDeps\n"
+      tell "        Distribution.Solver.Types.ConstraintSource\n"
+      tell "        Distribution.Solver.Types.DependencyResolver\n"
+      tell "        Distribution.Solver.Types.Flag\n"
+      tell "        Distribution.Solver.Types.InstSolverPackage\n"
+      tell "        Distribution.Solver.Types.InstalledPreference\n"
+      tell "        Distribution.Solver.Types.LabeledPackageConstraint\n"
+      tell "        Distribution.Solver.Types.OptionalStanza\n"
+      tell "        Distribution.Solver.Types.PackageConstraint\n"
+      tell "        Distribution.Solver.Types.PackageFixedDeps\n"
+      tell "        Distribution.Solver.Types.PackageIndex\n"
+      tell "        Distribution.Solver.Types.PackagePath\n"
+      tell "        Distribution.Solver.Types.PackagePreferences\n"
+      tell "        Distribution.Solver.Types.PkgConfigDb\n"
+      tell "        Distribution.Solver.Types.Progress\n"
+      tell "        Distribution.Solver.Types.ResolverPackage\n"
+      tell "        Distribution.Solver.Types.Settings\n"
+      tell "        Distribution.Solver.Types.SolverId\n"
+      tell "        Distribution.Solver.Types.SolverPackage\n"
+      tell "        Distribution.Solver.Types.SourcePackage\n"
+      tell "        Distribution.Solver.Types.Variable\n"
+      tell "        Paths_cabal_install\n"
+      tell "\n"
+      if (ciLib z_root)
+      then do
+        tell "    autogen-modules:\n"
+        tell "        Paths_cabal_install\n"
+        return ()
+      else do
+        return ()
+      z_var0_cabal_BUILDDEPENDS
+      tell "    if flag(debug-expensive-assertions)\n"
+      tell "      cpp-options: -DDEBUG_EXPENSIVE_ASSERTIONS\n"
+      tell "\n"
+      tell "    if flag(debug-conflict-sets)\n"
+      tell "      cpp-options: -DDEBUG_CONFLICT_SETS\n"
+      tell "      build-depends: base >= 4.8\n"
+      tell "\n"
+      tell "    if flag(debug-tracetree)\n"
+      tell "      cpp-options: -DDEBUG_TRACETREE\n"
+      tell "      build-depends: tracetree >= 0.1 && < 0.2\n"
+      return ()
+  tell "Synopsis:           The command-line interface for Cabal and Hackage.\n"
+  tell "Description:\n"
+  tell "    The \\'cabal\\' command-line program simplifies the process of managing\n"
+  tell "    Haskell software by automating the fetching, configuration, compilation\n"
+  tell "    and installation of Haskell libraries and programs.\n"
+  tell "homepage:           http://www.haskell.org/cabal/\n"
+  tell "bug-reports:        https://github.com/haskell/cabal/issues\n"
+  tell "License:            BSD3\n"
+  tell "License-File:       LICENSE\n"
+  tell "Author:             Cabal Development Team (see AUTHORS file)\n"
+  tell "Maintainer:         Cabal Development Team <cabal-devel@haskell.org>\n"
+  tell "Copyright:          2003-2019, Cabal Development Team\n"
+  tell "Category:           Distribution\n"
+  if (ciLib z_root)
+  then do
+    tell "Build-type:         Simple\n"
+    return ()
+  else do
+    tell "Build-type:         Custom\n"
+    return ()
+  tell "Extra-Source-Files:\n"
+  tell "  README.md bash-completion/cabal bootstrap.sh changelog\n"
+  tell "\n"
+  tell "  -- Generated with 'make gen-extra-source-files'\n"
+  tell "  -- Do NOT edit this section manually; instead, run the script.\n"
+  tell "  -- BEGIN gen-extra-source-files\n"
+  tell "  tests/IntegrationTests2/build/keep-going/cabal.project\n"
+  tell "  tests/IntegrationTests2/build/keep-going/p/P.hs\n"
+  tell "  tests/IntegrationTests2/build/keep-going/p/p.cabal\n"
+  tell "  tests/IntegrationTests2/build/keep-going/q/Q.hs\n"
+  tell "  tests/IntegrationTests2/build/keep-going/q/q.cabal\n"
+  tell "  tests/IntegrationTests2/build/local-tarball/cabal.project\n"
+  tell "  tests/IntegrationTests2/build/local-tarball/q/Q.hs\n"
+  tell "  tests/IntegrationTests2/build/local-tarball/q/q.cabal\n"
+  tell "  tests/IntegrationTests2/build/setup-custom1/A.hs\n"
+  tell "  tests/IntegrationTests2/build/setup-custom1/Setup.hs\n"
+  tell "  tests/IntegrationTests2/build/setup-custom1/a.cabal\n"
+  tell "  tests/IntegrationTests2/build/setup-custom2/A.hs\n"
+  tell "  tests/IntegrationTests2/build/setup-custom2/Setup.hs\n"
+  tell "  tests/IntegrationTests2/build/setup-custom2/a.cabal\n"
+  tell "  tests/IntegrationTests2/build/setup-simple/A.hs\n"
+  tell "  tests/IntegrationTests2/build/setup-simple/Setup.hs\n"
+  tell "  tests/IntegrationTests2/build/setup-simple/a.cabal\n"
+  tell "  tests/IntegrationTests2/exception/bad-config/cabal.project\n"
+  tell "  tests/IntegrationTests2/exception/build/Main.hs\n"
+  tell "  tests/IntegrationTests2/exception/build/a.cabal\n"
+  tell "  tests/IntegrationTests2/exception/configure/a.cabal\n"
+  tell "  tests/IntegrationTests2/exception/no-pkg/empty.in\n"
+  tell "  tests/IntegrationTests2/exception/no-pkg2/cabal.project\n"
+  tell "  tests/IntegrationTests2/regression/3324/cabal.project\n"
+  tell "  tests/IntegrationTests2/regression/3324/p/P.hs\n"
+  tell "  tests/IntegrationTests2/regression/3324/p/p.cabal\n"
+  tell "  tests/IntegrationTests2/regression/3324/q/Q.hs\n"
+  tell "  tests/IntegrationTests2/regression/3324/q/q.cabal\n"
+  tell "  tests/IntegrationTests2/targets/all-disabled/cabal.project\n"
+  tell "  tests/IntegrationTests2/targets/all-disabled/p.cabal\n"
+  tell "  tests/IntegrationTests2/targets/benchmarks-disabled/cabal.project\n"
+  tell "  tests/IntegrationTests2/targets/benchmarks-disabled/p.cabal\n"
+  tell "  tests/IntegrationTests2/targets/benchmarks-disabled/q/q.cabal\n"
+  tell "  tests/IntegrationTests2/targets/complex/cabal.project\n"
+  tell "  tests/IntegrationTests2/targets/complex/q/Q.hs\n"
+  tell "  tests/IntegrationTests2/targets/complex/q/q.cabal\n"
+  tell "  tests/IntegrationTests2/targets/empty-pkg/cabal.project\n"
+  tell "  tests/IntegrationTests2/targets/empty-pkg/p.cabal\n"
+  tell "  tests/IntegrationTests2/targets/empty/cabal.project\n"
+  tell "  tests/IntegrationTests2/targets/empty/foo.hs\n"
+  tell "  tests/IntegrationTests2/targets/exes-disabled/cabal.project\n"
+  tell "  tests/IntegrationTests2/targets/exes-disabled/p/p.cabal\n"
+  tell "  tests/IntegrationTests2/targets/exes-disabled/q/q.cabal\n"
+  tell "  tests/IntegrationTests2/targets/lib-only/p.cabal\n"
+  tell "  tests/IntegrationTests2/targets/libs-disabled/cabal.project\n"
+  tell "  tests/IntegrationTests2/targets/libs-disabled/p/p.cabal\n"
+  tell "  tests/IntegrationTests2/targets/libs-disabled/q/q.cabal\n"
+  tell "  tests/IntegrationTests2/targets/multiple-exes/cabal.project\n"
+  tell "  tests/IntegrationTests2/targets/multiple-exes/p.cabal\n"
+  tell "  tests/IntegrationTests2/targets/multiple-libs/cabal.project\n"
+  tell "  tests/IntegrationTests2/targets/multiple-libs/p/p.cabal\n"
+  tell "  tests/IntegrationTests2/targets/multiple-libs/q/q.cabal\n"
+  tell "  tests/IntegrationTests2/targets/multiple-tests/cabal.project\n"
+  tell "  tests/IntegrationTests2/targets/multiple-tests/p.cabal\n"
+  tell "  tests/IntegrationTests2/targets/simple/P.hs\n"
+  tell "  tests/IntegrationTests2/targets/simple/cabal.project\n"
+  tell "  tests/IntegrationTests2/targets/simple/p.cabal\n"
+  tell "  tests/IntegrationTests2/targets/simple/q/QQ.hs\n"
+  tell "  tests/IntegrationTests2/targets/simple/q/q.cabal\n"
+  tell "  tests/IntegrationTests2/targets/test-only/p.cabal\n"
+  tell "  tests/IntegrationTests2/targets/tests-disabled/cabal.project\n"
+  tell "  tests/IntegrationTests2/targets/tests-disabled/p.cabal\n"
+  tell "  tests/IntegrationTests2/targets/tests-disabled/q/q.cabal\n"
+  tell "  tests/IntegrationTests2/targets/variety/cabal.project\n"
+  tell "  tests/IntegrationTests2/targets/variety/p.cabal\n"
+  tell "  -- END gen-extra-source-files\n"
+  tell "\n"
+  tell "  -- Additional manual extra-source-files:\n"
+  tell "  tests/IntegrationTests2/build/local-tarball/p-0.1.tar.gz\n"
+  tell "\n"
+  tell "\n"
+  tell "source-repository head\n"
+  tell "  type:     git\n"
+  tell "  location: https://github.com/haskell/cabal/\n"
+  tell "  subdir:   cabal-install\n"
+  tell "\n"
+  tell "Flag native-dns\n"
+  tell "  description:  Enable use of the [resolv](https://hackage.haskell.org/package/resolv) & [windns](https://hackage.haskell.org/package/windns) packages for performing DNS lookups\n"
+  tell "  default:      True\n"
+  tell "  manual:       True\n"
+  tell "\n"
+  tell "Flag debug-expensive-assertions\n"
+  tell "  description:  Enable expensive assertions for testing or debugging\n"
+  tell "  default:      False\n"
+  tell "  manual:       True\n"
+  tell "\n"
+  tell "Flag debug-conflict-sets\n"
+  tell "  description:  Add additional information to ConflictSets\n"
+  tell "  default:      False\n"
+  tell "  manual:       True\n"
+  tell "\n"
+  tell "Flag debug-tracetree\n"
+  tell "  description:  Compile in support for tracetree (used to debug the solver)\n"
+  tell "  default:      False\n"
+  tell "  manual:       True\n"
+  tell "\n"
+  tell "Flag lukko\n"
+  tell "  description:  Use @lukko@ for file-locking\n"
+  tell "  default:      True\n"
+  tell "  manual:       True\n"
+  tell "\n"
+  if (ciLib z_root)
+  then do
+    return ()
+  else do
+    tell "custom-setup\n"
+    tell "   setup-depends:\n"
+    tell "       Cabal     >= 2.2,\n"
+    tell "       base,\n"
+    tell "       process   >= 1.1.0.1  && < 1.7,\n"
+    tell "       filepath  >= 1.3      && < 1.5\n"
+    tell "\n"
+    return ()
+  if (ciLib z_root)
+  then do
+    tell "library cabal-lib-client\n"
+    z_var1_cabal_COMPONENTCOMMON
+    tell "    hs-source-dirs: .\n"
+    z_var2_cabal_BUILDINFO
+    tell "\n"
+    tell "library cabal-install-solver-dsl\n"
+    z_var1_cabal_COMPONENTCOMMON
+    tell "    hs-source-dirs: solver-dsl\n"
+    tell "    exposed-modules:\n"
+    tell "        UnitTests.Distribution.Solver.Modular.DSL\n"
+    tell "    build-depends:\n"
+    tell "        base,\n"
+    tell "        Cabal,\n"
+    tell "        containers,\n"
+    tell "        -- TODO: depend on cabal-install-solver only\n"
+    tell "        cabal-lib-client\n"
+    return ()
+  else do
+    return ()
+  tell "executable cabal\n"
+  tell "    main-is:        Main.hs\n"
+  tell "    hs-source-dirs: main\n"
+  z_var1_cabal_COMPONENTCOMMON
+  tell "\n"
+  tell "    ghc-options: -rtsopts -threaded\n"
+  tell "\n"
+  tell "    -- On AIX, some legacy BSD operations such as flock(2) are provided by libbsd.a\n"
+  tell "    if os(aix)\n"
+  tell "        extra-libraries: bsd\n"
+  if (ciLib z_root)
+  then do
+    tell "    build-depends:\n"
+    tell "        cabal-lib-client,\n"
+    tell "        Cabal,\n"
+    tell "        base,\n"
+    tell "        directory,\n"
+    tell "        filepath\n"
+    tell "\n"
+    tell "    other-modules: Paths_cabal_install\n"
+    return ()
+  else do
+    tell "    hs-source-dirs: .\n"
+    z_var2_cabal_BUILDINFO
+    return ()
+  if (ciLib z_root)
+  then do
+    if (ciMonolithic z_root)
+    then do
+      tell "    -- Monolithic: tests fused into executable\n"
+      tell "    hs-source-dirs: tests\n"
+      tell "    other-modules:\n"
+      tell "        UnitTests\n"
+      tell "        MemoryUsageTests\n"
+      tell "        SolverQuickCheck\n"
+      tell "        IntegrationTests2\n"
+      tell "\n"
+      tell "        UnitTests.Distribution.Client.ArbitraryInstances\n"
+      tell "        UnitTests.Distribution.Client.FileMonitor\n"
+      tell "        UnitTests.Distribution.Client.Get\n"
+      tell "        UnitTests.Distribution.Client.GenericInstances\n"
+      tell "        UnitTests.Distribution.Client.GZipUtils\n"
+      tell "        UnitTests.Distribution.Client.Glob\n"
+      tell "        UnitTests.Distribution.Client.IndexUtils.Timestamp\n"
+      tell "        UnitTests.Distribution.Client.InstallPlan\n"
+      tell "        UnitTests.Distribution.Client.JobControl\n"
+      tell "        UnitTests.Distribution.Client.ProjectConfig\n"
+      tell "        UnitTests.Distribution.Client.Sandbox\n"
+      tell "        UnitTests.Distribution.Client.Sandbox.Timestamp\n"
+      tell "        UnitTests.Distribution.Client.Store\n"
+      tell "        UnitTests.Distribution.Client.Tar\n"
+      tell "        UnitTests.Distribution.Client.Targets\n"
+      tell "        UnitTests.Distribution.Client.TreeDiffInstances\n"
+      tell "        UnitTests.Distribution.Client.UserConfig\n"
+      tell "        UnitTests.Distribution.Client.VCS\n"
+      tell "        UnitTests.Distribution.Solver.Modular.Builder\n"
+      tell "        UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils\n"
+      tell "        UnitTests.Distribution.Solver.Modular.MemoryUsage\n"
+      tell "        UnitTests.Distribution.Solver.Modular.QuickCheck\n"
+      tell "        UnitTests.Distribution.Solver.Modular.QuickCheck.Utils\n"
+      tell "        UnitTests.Distribution.Solver.Modular.RetryLog\n"
+      tell "        UnitTests.Distribution.Solver.Modular.Solver\n"
+      tell "        UnitTests.Distribution.Solver.Modular.WeightedPSQ\n"
+      tell "        UnitTests.Options\n"
+      tell "        UnitTests.TempTestDir\n"
+      tell "\n"
+      tell "    cpp-options: -DMONOLITHIC\n"
+      tell "    build-depends:\n"
+      tell "        Cabal      == 3.3.*,\n"
+      tell "        cabal-install-solver-dsl,\n"
+      tell "        QuickCheck >= 2.8.2,\n"
+      tell "        array,\n"
+      tell "        async,\n"
+      tell "        bytestring,\n"
+      tell "        containers,\n"
+      tell "        deepseq,\n"
+      tell "        directory,\n"
+      tell "        edit-distance,\n"
+      tell "        filepath,\n"
+      tell "        hashable,\n"
+      tell "        mtl,\n"
+      tell "        network,\n"
+      tell "        network-uri,\n"
+      tell "        pretty-show >= 1.6.15,\n"
+      tell "        random,\n"
+      tell "        tagged,\n"
+      tell "        tar,\n"
+      tell "        tasty >= 1.2.3 && < 1.3,\n"
+      tell "        tasty-hunit >= 0.10,\n"
+      tell "        tasty-quickcheck,\n"
+      tell "        tree-diff,\n"
+      tell "        time,\n"
+      tell "        zlib\n"
+      return ()
+    else do
+      return ()
+    return ()
+  else do
+    return ()
+  if (ciLib z_root)
+  then do
+    tell "Test-Suite unit-tests\n"
+    tell "  type: exitcode-stdio-1.0\n"
+    tell "  main-is: UnitTests.hs\n"
+    tell "  hs-source-dirs: tests\n"
+    tell "  ghc-options: -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns -main-is UnitTests\n"
+    tell "  other-modules:\n"
+    tell "    UnitTests.Distribution.Client.ArbitraryInstances\n"
+    tell "    UnitTests.Distribution.Client.Targets\n"
+    tell "    UnitTests.Distribution.Client.FileMonitor\n"
+    tell "    UnitTests.Distribution.Client.Get\n"
+    tell "    UnitTests.Distribution.Client.GenericInstances\n"
+    tell "    UnitTests.Distribution.Client.Glob\n"
+    tell "    UnitTests.Distribution.Client.GZipUtils\n"
+    tell "    UnitTests.Distribution.Client.Sandbox\n"
+    tell "    UnitTests.Distribution.Client.Sandbox.Timestamp\n"
+    tell "    UnitTests.Distribution.Client.Store\n"
+    tell "    UnitTests.Distribution.Client.Tar\n"
+    tell "    UnitTests.Distribution.Client.TreeDiffInstances\n"
+    tell "    UnitTests.Distribution.Client.UserConfig\n"
+    tell "    UnitTests.Distribution.Client.ProjectConfig\n"
+    tell "    UnitTests.Distribution.Client.JobControl\n"
+    tell "    UnitTests.Distribution.Client.IndexUtils.Timestamp\n"
+    tell "    UnitTests.Distribution.Client.InstallPlan\n"
+    tell "    UnitTests.Distribution.Client.VCS\n"
+    tell "    UnitTests.Distribution.Solver.Modular.Builder\n"
+    tell "    UnitTests.Distribution.Solver.Modular.RetryLog\n"
+    tell "    UnitTests.Distribution.Solver.Modular.Solver\n"
+    tell "    UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils\n"
+    tell "    UnitTests.Distribution.Solver.Modular.WeightedPSQ\n"
+    tell "    UnitTests.Options\n"
+    tell "    UnitTests.TempTestDir\n"
+    tell "  build-depends:\n"
+    tell "        array,\n"
+    tell "        base,\n"
+    tell "        async,\n"
+    tell "        bytestring,\n"
+    tell "        cabal-lib-client,\n"
+    tell "        cabal-install-solver-dsl,\n"
+    tell "        Cabal,\n"
+    tell "        containers,\n"
+    tell "        deepseq,\n"
+    tell "        mtl,\n"
+    tell "        random,\n"
+    tell "        directory,\n"
+    tell "        filepath,\n"
+    tell "        tar,\n"
+    tell "        time,\n"
+    tell "        zlib,\n"
+    tell "        network-uri < 2.6.2.0,\n"
+    tell "        network,\n"
+    tell "        tasty >= 1.2.3 && <1.3,\n"
+    tell "        tasty-hunit >= 0.10,\n"
+    tell "        tasty-quickcheck,\n"
+    tell "        tagged,\n"
+    tell "        tree-diff,\n"
+    tell "        QuickCheck >= 2.8.2\n"
+    tell "\n"
+    tell "  ghc-options: -threaded\n"
+    tell "\n"
+    tell "  default-language: Haskell2010\n"
+    tell "\n"
+    tell "Test-Suite memory-usage-tests\n"
+    tell "  type: exitcode-stdio-1.0\n"
+    tell "  main-is: MemoryUsageTests.hs\n"
+    tell "  hs-source-dirs: tests\n"
+    tell "  ghc-options: -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns \"-with-rtsopts=-M4M -K1K\" -main-is MemoryUsageTests\n"
+    tell "  other-modules:\n"
+    tell "    UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils\n"
+    tell "    UnitTests.Distribution.Solver.Modular.MemoryUsage\n"
+    tell "    UnitTests.Options\n"
+    tell "  build-depends:\n"
+    tell "        base,\n"
+    tell "        async,\n"
+    tell "        Cabal,\n"
+    tell "        cabal-lib-client,\n"
+    tell "        cabal-install-solver-dsl,\n"
+    tell "        containers,\n"
+    tell "        deepseq,\n"
+    tell "        tagged,\n"
+    tell "        tasty >= 1.2.3 && <1.3,\n"
+    tell "        tasty-hunit >= 0.10\n"
+    tell "\n"
+    tell "  ghc-options: -threaded\n"
+    tell "\n"
+    tell "  default-language: Haskell2010\n"
+    tell "\n"
+    tell "Test-Suite solver-quickcheck\n"
+    tell "  type: exitcode-stdio-1.0\n"
+    tell "  main-is: SolverQuickCheck.hs\n"
+    tell "  hs-source-dirs: tests\n"
+    tell "  ghc-options: -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns -main-is SolverQuickCheck\n"
+    tell "  other-modules:\n"
+    tell "    UnitTests.Distribution.Solver.Modular.QuickCheck\n"
+    tell "    UnitTests.Distribution.Solver.Modular.QuickCheck.Utils\n"
+    tell "  build-depends:\n"
+    tell "        base,\n"
+    tell "        async,\n"
+    tell "        Cabal,\n"
+    tell "        cabal-lib-client,\n"
+    tell "        cabal-install-solver-dsl,\n"
+    tell "        containers,\n"
+    tell "        deepseq >= 1.2,\n"
+    tell "        hashable,\n"
+    tell "        random,\n"
+    tell "        tagged,\n"
+    tell "        tasty >= 1.2.3 && <1.3,\n"
+    tell "        tasty-quickcheck,\n"
+    tell "        QuickCheck >= 2.8.2,\n"
+    tell "        pretty-show >= 1.6.15\n"
+    tell "\n"
+    tell "  ghc-options: -threaded\n"
+    tell "\n"
+    tell "  default-language: Haskell2010\n"
+    tell "\n"
+    tell "test-suite integration-tests2\n"
+    tell "  type: exitcode-stdio-1.0\n"
+    tell "  main-is: IntegrationTests2.hs\n"
+    tell "  hs-source-dirs: tests\n"
+    tell "  ghc-options: -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns -main-is IntegrationTests2\n"
+    tell "  other-modules:\n"
+    tell "  build-depends:\n"
+    tell "        base,\n"
+    tell "        Cabal,\n"
+    tell "        cabal-lib-client,\n"
+    tell "        containers,\n"
+    tell "        deepseq,\n"
+    tell "        directory,\n"
+    tell "        edit-distance,\n"
+    tell "        filepath,\n"
+    tell "        tasty >= 1.2.3 && <1.3,\n"
+    tell "        tasty-hunit >= 0.10,\n"
+    tell "        tagged\n"
+    tell "\n"
+    tell "  ghc-options: -threaded\n"
+    tell "  default-language: Haskell2010\n"
+    tell "\n"
+    return ()
+  else do
+    return ()
diff --git a/fixtures/cabal-install.txt b/fixtures/cabal-install.txt
new file mode 100644
--- /dev/null
+++ b/fixtures/cabal-install.txt
@@ -0,0 +1,374 @@
+Cabal-Version:      >= 1.10
+-- NOTE: This file is autogenerated from 'cabal-install.cabal.zinza'.
+-- DO NOT EDIT MANUALLY.
+-- To update this file, edit 'cabal-install.cabal.pp' and run
+-- 'make cabal-install-prod' in the project's root folder.
+Name:               cabal-install
+Version:            3.3.0.0
+Synopsis:           The command-line interface for Cabal and Hackage.
+Description:
+    The \'cabal\' command-line program simplifies the process of managing
+    Haskell software by automating the fetching, configuration, compilation
+    and installation of Haskell libraries and programs.
+homepage:           http://www.haskell.org/cabal/
+bug-reports:        https://github.com/haskell/cabal/issues
+License:            BSD3
+License-File:       LICENSE
+Author:             Cabal Development Team (see AUTHORS file)
+Maintainer:         Cabal Development Team <cabal-devel@haskell.org>
+Copyright:          2003-2019, Cabal Development Team
+Category:           Distribution
+Build-type:         Custom
+Extra-Source-Files:
+  README.md bash-completion/cabal bootstrap.sh changelog
+
+  -- Generated with 'make gen-extra-source-files'
+  -- Do NOT edit this section manually; instead, run the script.
+  -- BEGIN gen-extra-source-files
+  tests/IntegrationTests2/build/keep-going/cabal.project
+  tests/IntegrationTests2/build/keep-going/p/P.hs
+  tests/IntegrationTests2/build/keep-going/p/p.cabal
+  tests/IntegrationTests2/build/keep-going/q/Q.hs
+  tests/IntegrationTests2/build/keep-going/q/q.cabal
+  tests/IntegrationTests2/build/local-tarball/cabal.project
+  tests/IntegrationTests2/build/local-tarball/q/Q.hs
+  tests/IntegrationTests2/build/local-tarball/q/q.cabal
+  tests/IntegrationTests2/build/setup-custom1/A.hs
+  tests/IntegrationTests2/build/setup-custom1/Setup.hs
+  tests/IntegrationTests2/build/setup-custom1/a.cabal
+  tests/IntegrationTests2/build/setup-custom2/A.hs
+  tests/IntegrationTests2/build/setup-custom2/Setup.hs
+  tests/IntegrationTests2/build/setup-custom2/a.cabal
+  tests/IntegrationTests2/build/setup-simple/A.hs
+  tests/IntegrationTests2/build/setup-simple/Setup.hs
+  tests/IntegrationTests2/build/setup-simple/a.cabal
+  tests/IntegrationTests2/exception/bad-config/cabal.project
+  tests/IntegrationTests2/exception/build/Main.hs
+  tests/IntegrationTests2/exception/build/a.cabal
+  tests/IntegrationTests2/exception/configure/a.cabal
+  tests/IntegrationTests2/exception/no-pkg/empty.in
+  tests/IntegrationTests2/exception/no-pkg2/cabal.project
+  tests/IntegrationTests2/regression/3324/cabal.project
+  tests/IntegrationTests2/regression/3324/p/P.hs
+  tests/IntegrationTests2/regression/3324/p/p.cabal
+  tests/IntegrationTests2/regression/3324/q/Q.hs
+  tests/IntegrationTests2/regression/3324/q/q.cabal
+  tests/IntegrationTests2/targets/all-disabled/cabal.project
+  tests/IntegrationTests2/targets/all-disabled/p.cabal
+  tests/IntegrationTests2/targets/benchmarks-disabled/cabal.project
+  tests/IntegrationTests2/targets/benchmarks-disabled/p.cabal
+  tests/IntegrationTests2/targets/benchmarks-disabled/q/q.cabal
+  tests/IntegrationTests2/targets/complex/cabal.project
+  tests/IntegrationTests2/targets/complex/q/Q.hs
+  tests/IntegrationTests2/targets/complex/q/q.cabal
+  tests/IntegrationTests2/targets/empty-pkg/cabal.project
+  tests/IntegrationTests2/targets/empty-pkg/p.cabal
+  tests/IntegrationTests2/targets/empty/cabal.project
+  tests/IntegrationTests2/targets/empty/foo.hs
+  tests/IntegrationTests2/targets/exes-disabled/cabal.project
+  tests/IntegrationTests2/targets/exes-disabled/p/p.cabal
+  tests/IntegrationTests2/targets/exes-disabled/q/q.cabal
+  tests/IntegrationTests2/targets/lib-only/p.cabal
+  tests/IntegrationTests2/targets/libs-disabled/cabal.project
+  tests/IntegrationTests2/targets/libs-disabled/p/p.cabal
+  tests/IntegrationTests2/targets/libs-disabled/q/q.cabal
+  tests/IntegrationTests2/targets/multiple-exes/cabal.project
+  tests/IntegrationTests2/targets/multiple-exes/p.cabal
+  tests/IntegrationTests2/targets/multiple-libs/cabal.project
+  tests/IntegrationTests2/targets/multiple-libs/p/p.cabal
+  tests/IntegrationTests2/targets/multiple-libs/q/q.cabal
+  tests/IntegrationTests2/targets/multiple-tests/cabal.project
+  tests/IntegrationTests2/targets/multiple-tests/p.cabal
+  tests/IntegrationTests2/targets/simple/P.hs
+  tests/IntegrationTests2/targets/simple/cabal.project
+  tests/IntegrationTests2/targets/simple/p.cabal
+  tests/IntegrationTests2/targets/simple/q/QQ.hs
+  tests/IntegrationTests2/targets/simple/q/q.cabal
+  tests/IntegrationTests2/targets/test-only/p.cabal
+  tests/IntegrationTests2/targets/tests-disabled/cabal.project
+  tests/IntegrationTests2/targets/tests-disabled/p.cabal
+  tests/IntegrationTests2/targets/tests-disabled/q/q.cabal
+  tests/IntegrationTests2/targets/variety/cabal.project
+  tests/IntegrationTests2/targets/variety/p.cabal
+  -- END gen-extra-source-files
+
+  -- Additional manual extra-source-files:
+  tests/IntegrationTests2/build/local-tarball/p-0.1.tar.gz
+
+
+source-repository head
+  type:     git
+  location: https://github.com/haskell/cabal/
+  subdir:   cabal-install
+
+Flag native-dns
+  description:  Enable use of the [resolv](https://hackage.haskell.org/package/resolv) & [windns](https://hackage.haskell.org/package/windns) packages for performing DNS lookups
+  default:      True
+  manual:       True
+
+Flag debug-expensive-assertions
+  description:  Enable expensive assertions for testing or debugging
+  default:      False
+  manual:       True
+
+Flag debug-conflict-sets
+  description:  Add additional information to ConflictSets
+  default:      False
+  manual:       True
+
+Flag debug-tracetree
+  description:  Compile in support for tracetree (used to debug the solver)
+  default:      False
+  manual:       True
+
+Flag lukko
+  description:  Use @lukko@ for file-locking
+  default:      True
+  manual:       True
+
+custom-setup
+   setup-depends:
+       Cabal     >= 2.2,
+       base,
+       process   >= 1.1.0.1  && < 1.7,
+       filepath  >= 1.3      && < 1.5
+
+executable cabal
+    main-is:        Main.hs
+    hs-source-dirs: main
+    default-language: Haskell2010
+    ghc-options:    -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns
+    if impl(ghc >= 8.0)
+        ghc-options: -Wcompat
+                     -Wnoncanonical-monad-instances
+      if impl(ghc < 8.8)
+        ghc-options: -Wnoncanonical-monadfail-instances
+
+
+    ghc-options: -rtsopts -threaded
+
+    -- On AIX, some legacy BSD operations such as flock(2) are provided by libbsd.a
+    if os(aix)
+        extra-libraries: bsd
+    hs-source-dirs: .
+    other-modules:
+        -- this modules are moved from Cabal
+        -- they are needed for as long until cabal-install moves to parsec parser
+        Distribution.Deprecated.ParseUtils
+        Distribution.Deprecated.ReadP
+        Distribution.Deprecated.Text
+        Distribution.Deprecated.ViewAsFieldDescr
+
+        Distribution.Client.BuildReports.Anonymous
+        Distribution.Client.BuildReports.Storage
+        Distribution.Client.BuildReports.Types
+        Distribution.Client.BuildReports.Upload
+        Distribution.Client.Check
+        Distribution.Client.CmdBench
+        Distribution.Client.CmdBuild
+        Distribution.Client.CmdClean
+        Distribution.Client.CmdConfigure
+        Distribution.Client.CmdUpdate
+        Distribution.Client.CmdErrorMessages
+        Distribution.Client.CmdExec
+        Distribution.Client.CmdFreeze
+        Distribution.Client.CmdHaddock
+        Distribution.Client.CmdInstall
+        Distribution.Client.CmdInstall.ClientInstallFlags
+        Distribution.Client.CmdRepl
+        Distribution.Client.CmdRun
+        Distribution.Client.CmdTest
+        Distribution.Client.CmdLegacy
+        Distribution.Client.CmdSdist
+        Distribution.Client.Compat.Directory
+        Distribution.Client.Compat.ExecutablePath
+        Distribution.Client.Compat.FilePerms
+        Distribution.Client.Compat.Orphans
+        Distribution.Client.Compat.Prelude
+        Distribution.Client.Compat.Process
+        Distribution.Client.Compat.Semaphore
+        Distribution.Client.Config
+        Distribution.Client.Configure
+        Distribution.Client.Dependency
+        Distribution.Client.Dependency.Types
+        Distribution.Client.DistDirLayout
+        Distribution.Client.Exec
+        Distribution.Client.Fetch
+        Distribution.Client.FetchUtils
+        Distribution.Client.FileMonitor
+        Distribution.Client.Freeze
+        Distribution.Client.GZipUtils
+        Distribution.Client.GenBounds
+        Distribution.Client.Get
+        Distribution.Client.Glob
+        Distribution.Client.GlobalFlags
+        Distribution.Client.Haddock
+        Distribution.Client.HttpUtils
+        Distribution.Client.IndexUtils
+        Distribution.Client.IndexUtils.Timestamp
+        Distribution.Client.Init
+        Distribution.Client.Init.Heuristics
+        Distribution.Client.Init.Licenses
+        Distribution.Client.Init.Types
+        Distribution.Client.Install
+        Distribution.Client.InstallPlan
+        Distribution.Client.InstallSymlink
+        Distribution.Client.JobControl
+        Distribution.Client.List
+        Distribution.Client.Manpage
+        Distribution.Client.Nix
+        Distribution.Client.Outdated
+        Distribution.Client.PackageHash
+        Distribution.Client.PackageUtils
+        Distribution.Client.ParseUtils
+        Distribution.Client.ProjectBuilding
+        Distribution.Client.ProjectBuilding.Types
+        Distribution.Client.ProjectConfig
+        Distribution.Client.ProjectConfig.Legacy
+        Distribution.Client.ProjectConfig.Types
+        Distribution.Client.ProjectOrchestration
+        Distribution.Client.ProjectPlanOutput
+        Distribution.Client.ProjectPlanning
+        Distribution.Client.ProjectPlanning.Types
+        Distribution.Client.RebuildMonad
+        Distribution.Client.Reconfigure
+        Distribution.Client.Run
+        Distribution.Client.Sandbox
+        Distribution.Client.Sandbox.Index
+        Distribution.Client.Sandbox.PackageEnvironment
+        Distribution.Client.Sandbox.Timestamp
+        Distribution.Client.Sandbox.Types
+        Distribution.Client.SavedFlags
+        Distribution.Client.Security.DNS
+        Distribution.Client.Security.HTTP
+        Distribution.Client.Setup
+        Distribution.Client.SetupWrapper
+        Distribution.Client.SolverInstallPlan
+        Distribution.Client.SourceFiles
+        Distribution.Client.SourceRepo
+        Distribution.Client.SrcDist
+        Distribution.Client.Store
+        Distribution.Client.Tar
+        Distribution.Client.TargetSelector
+        Distribution.Client.Targets
+        Distribution.Client.Types
+        Distribution.Client.Update
+        Distribution.Client.Upload
+        Distribution.Client.Utils
+        Distribution.Client.Utils.Assertion
+        Distribution.Client.Utils.Json
+        Distribution.Client.Utils.Parsec
+        Distribution.Client.VCS
+        Distribution.Client.Win32SelfUpgrade
+        Distribution.Client.World
+        Distribution.Solver.Compat.Prelude
+        Distribution.Solver.Modular
+        Distribution.Solver.Modular.Assignment
+        Distribution.Solver.Modular.Builder
+        Distribution.Solver.Modular.Configured
+        Distribution.Solver.Modular.ConfiguredConversion
+        Distribution.Solver.Modular.ConflictSet
+        Distribution.Solver.Modular.Cycles
+        Distribution.Solver.Modular.Dependency
+        Distribution.Solver.Modular.Explore
+        Distribution.Solver.Modular.Flag
+        Distribution.Solver.Modular.Index
+        Distribution.Solver.Modular.IndexConversion
+        Distribution.Solver.Modular.LabeledGraph
+        Distribution.Solver.Modular.Linking
+        Distribution.Solver.Modular.Log
+        Distribution.Solver.Modular.Message
+        Distribution.Solver.Modular.PSQ
+        Distribution.Solver.Modular.Package
+        Distribution.Solver.Modular.Preference
+        Distribution.Solver.Modular.RetryLog
+        Distribution.Solver.Modular.Solver
+        Distribution.Solver.Modular.Tree
+        Distribution.Solver.Modular.Validate
+        Distribution.Solver.Modular.Var
+        Distribution.Solver.Modular.Version
+        Distribution.Solver.Modular.WeightedPSQ
+        Distribution.Solver.Types.ComponentDeps
+        Distribution.Solver.Types.ConstraintSource
+        Distribution.Solver.Types.DependencyResolver
+        Distribution.Solver.Types.Flag
+        Distribution.Solver.Types.InstSolverPackage
+        Distribution.Solver.Types.InstalledPreference
+        Distribution.Solver.Types.LabeledPackageConstraint
+        Distribution.Solver.Types.OptionalStanza
+        Distribution.Solver.Types.PackageConstraint
+        Distribution.Solver.Types.PackageFixedDeps
+        Distribution.Solver.Types.PackageIndex
+        Distribution.Solver.Types.PackagePath
+        Distribution.Solver.Types.PackagePreferences
+        Distribution.Solver.Types.PkgConfigDb
+        Distribution.Solver.Types.Progress
+        Distribution.Solver.Types.ResolverPackage
+        Distribution.Solver.Types.Settings
+        Distribution.Solver.Types.SolverId
+        Distribution.Solver.Types.SolverPackage
+        Distribution.Solver.Types.SourcePackage
+        Distribution.Solver.Types.Variable
+        Paths_cabal_install
+
+    build-depends:
+        async      >= 2.0      && < 2.3,
+        array      >= 0.4      && < 0.6,
+        base       >= 4.8      && < 4.14,
+        base16-bytestring >= 0.1.1 && < 0.2,
+        binary     >= 0.7.3    && < 0.9,
+        bytestring >= 0.10.6.0 && < 0.11,
+        Cabal      == 3.3.*,
+        containers >= 0.5.6.2  && < 0.7,
+        cryptohash-sha256 >= 0.11 && < 0.12,
+        deepseq    >= 1.4.1.1  && < 1.5,
+        directory  >= 1.2.2.0  && < 1.4,
+        echo       >= 0.1.3    && < 0.2,
+        edit-distance >= 0.2.2 && < 0.3,
+        filepath   >= 1.4.0.0  && < 1.5,
+        hashable   >= 1.0      && < 1.4,
+        HTTP       >= 4000.1.5 && < 4000.4,
+        mtl        >= 2.0      && < 2.3,
+        network-uri >= 2.6.0.2 && < 2.7,
+        network    >= 2.6      && < 3.2,
+        pretty     >= 1.1      && < 1.2,
+        process    >= 1.2.3.0  && < 1.7,
+        random     >= 1        && < 1.2,
+        stm        >= 2.0      && < 2.6,
+        tar        >= 0.5.0.3  && < 0.6,
+        time       >= 1.5.0.1  && < 1.10,
+        transformers >= 0.4.2.0 && < 0.6,
+        zlib       >= 0.5.3    && < 0.7,
+        hackage-security >= 0.6.0.0 && < 0.7,
+        text       >= 1.2.3    && < 1.3,
+        parsec     >= 3.1.13.0 && < 3.2
+
+    if !impl(ghc >= 8.0)
+        build-depends: fail        == 4.9.*
+        build-depends: semigroups  >= 0.18.3 && <0.20
+
+    if flag(native-dns)
+      if os(windows)
+        build-depends: windns      >= 0.1.0 && < 0.2
+      else
+        build-depends: resolv      >= 0.1.1 && < 0.2
+
+    if os(windows)
+      build-depends: Win32 >= 2 && < 3
+    else
+      build-depends: unix >= 2.5 && < 2.9
+
+    if flag(lukko)
+      build-depends: lukko >= 0.1 && <0.2
+    else
+      build-depends: base >= 4.10
+    if flag(debug-expensive-assertions)
+      cpp-options: -DDEBUG_EXPENSIVE_ASSERTIONS
+
+    if flag(debug-conflict-sets)
+      cpp-options: -DDEBUG_CONFLICT_SETS
+      build-depends: base >= 4.8
+
+    if flag(debug-tracetree)
+      cpp-options: -DDEBUG_TRACETREE
+      build-depends: tracetree >= 0.1 && < 0.2
diff --git a/fixtures/cabal-install.zinza b/fixtures/cabal-install.zinza
new file mode 100644
--- /dev/null
+++ b/fixtures/cabal-install.zinza
@@ -0,0 +1,662 @@
+{# lib enabled means that we have internal library: cabal-lib-client #}
+{% if lib %}
+Cabal-Version:      2.0
+{% else %}
+Cabal-Version:      >= 1.10
+{% endif %}
+-- NOTE: This file is autogenerated from 'cabal-install.cabal.zinza'.
+-- DO NOT EDIT MANUALLY.
+-- To update this file, edit 'cabal-install.cabal.pp' and run
+-- 'make cabal-install-prod' in the project's root folder.
+Name:               cabal-install
+Version:            3.3.0.0
+{# NOTE: when updating build-depends, don't forget to update version regexps in bootstrap.sh. #}
+{% defblock cabal_BUILDDEPENDS %}
+    build-depends:
+        async      >= 2.0      && < 2.3,
+        array      >= 0.4      && < 0.6,
+        base       >= 4.8      && < 4.14,
+        base16-bytestring >= 0.1.1 && < 0.2,
+        binary     >= 0.7.3    && < 0.9,
+        bytestring >= 0.10.6.0 && < 0.11,
+        Cabal      == 3.3.*,
+        containers >= 0.5.6.2  && < 0.7,
+        cryptohash-sha256 >= 0.11 && < 0.12,
+        deepseq    >= 1.4.1.1  && < 1.5,
+        directory  >= 1.2.2.0  && < 1.4,
+        echo       >= 0.1.3    && < 0.2,
+        edit-distance >= 0.2.2 && < 0.3,
+        filepath   >= 1.4.0.0  && < 1.5,
+        hashable   >= 1.0      && < 1.4,
+        HTTP       >= 4000.1.5 && < 4000.4,
+        mtl        >= 2.0      && < 2.3,
+        network-uri >= 2.6.0.2 && < 2.7,
+        network    >= 2.6      && < 3.2,
+        pretty     >= 1.1      && < 1.2,
+        process    >= 1.2.3.0  && < 1.7,
+        random     >= 1        && < 1.2,
+        stm        >= 2.0      && < 2.6,
+        tar        >= 0.5.0.3  && < 0.6,
+        time       >= 1.5.0.1  && < 1.10,
+        transformers >= 0.4.2.0 && < 0.6,
+        zlib       >= 0.5.3    && < 0.7,
+        hackage-security >= 0.6.0.0 && < 0.7,
+        text       >= 1.2.3    && < 1.3,
+        parsec     >= 3.1.13.0 && < 3.2
+
+    if !impl(ghc >= 8.0)
+        build-depends: fail        == 4.9.*
+        build-depends: semigroups  >= 0.18.3 && <0.20
+
+    if flag(native-dns)
+      if os(windows)
+        build-depends: windns      >= 0.1.0 && < 0.2
+      else
+        build-depends: resolv      >= 0.1.1 && < 0.2
+
+    if os(windows)
+      build-depends: Win32 >= 2 && < 3
+    else
+      build-depends: unix >= 2.5 && < 2.9
+
+    if flag(lukko)
+      build-depends: lukko >= 0.1 && <0.2
+    else
+      build-depends: base >= 4.10
+{% endblock %}
+{% defblock cabal_COMPONENTCOMMON %}
+    default-language: Haskell2010
+    ghc-options:    -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns
+    if impl(ghc >= 8.0)
+        ghc-options: -Wcompat
+                     -Wnoncanonical-monad-instances
+      if impl(ghc < 8.8)
+        ghc-options: -Wnoncanonical-monadfail-instances
+
+{% endblock %}
+{% defblock cabal_BUILDINFO %}
+{% if lib %}
+    exposed-modules:
+{% else %}
+    other-modules:
+{% endif %}
+        -- this modules are moved from Cabal
+        -- they are needed for as long until cabal-install moves to parsec parser
+        Distribution.Deprecated.ParseUtils
+        Distribution.Deprecated.ReadP
+        Distribution.Deprecated.Text
+        Distribution.Deprecated.ViewAsFieldDescr
+
+        Distribution.Client.BuildReports.Anonymous
+        Distribution.Client.BuildReports.Storage
+        Distribution.Client.BuildReports.Types
+        Distribution.Client.BuildReports.Upload
+        Distribution.Client.Check
+        Distribution.Client.CmdBench
+        Distribution.Client.CmdBuild
+        Distribution.Client.CmdClean
+        Distribution.Client.CmdConfigure
+        Distribution.Client.CmdUpdate
+        Distribution.Client.CmdErrorMessages
+        Distribution.Client.CmdExec
+        Distribution.Client.CmdFreeze
+        Distribution.Client.CmdHaddock
+        Distribution.Client.CmdInstall
+        Distribution.Client.CmdInstall.ClientInstallFlags
+        Distribution.Client.CmdRepl
+        Distribution.Client.CmdRun
+        Distribution.Client.CmdTest
+        Distribution.Client.CmdLegacy
+        Distribution.Client.CmdSdist
+        Distribution.Client.Compat.Directory
+        Distribution.Client.Compat.ExecutablePath
+        Distribution.Client.Compat.FilePerms
+        Distribution.Client.Compat.Orphans
+        Distribution.Client.Compat.Prelude
+        Distribution.Client.Compat.Process
+        Distribution.Client.Compat.Semaphore
+        Distribution.Client.Config
+        Distribution.Client.Configure
+        Distribution.Client.Dependency
+        Distribution.Client.Dependency.Types
+        Distribution.Client.DistDirLayout
+        Distribution.Client.Exec
+        Distribution.Client.Fetch
+        Distribution.Client.FetchUtils
+        Distribution.Client.FileMonitor
+        Distribution.Client.Freeze
+        Distribution.Client.GZipUtils
+        Distribution.Client.GenBounds
+        Distribution.Client.Get
+        Distribution.Client.Glob
+        Distribution.Client.GlobalFlags
+        Distribution.Client.Haddock
+        Distribution.Client.HttpUtils
+        Distribution.Client.IndexUtils
+        Distribution.Client.IndexUtils.Timestamp
+        Distribution.Client.Init
+        Distribution.Client.Init.Heuristics
+        Distribution.Client.Init.Licenses
+        Distribution.Client.Init.Types
+        Distribution.Client.Install
+        Distribution.Client.InstallPlan
+        Distribution.Client.InstallSymlink
+        Distribution.Client.JobControl
+        Distribution.Client.List
+        Distribution.Client.Manpage
+        Distribution.Client.Nix
+        Distribution.Client.Outdated
+        Distribution.Client.PackageHash
+        Distribution.Client.PackageUtils
+        Distribution.Client.ParseUtils
+        Distribution.Client.ProjectBuilding
+        Distribution.Client.ProjectBuilding.Types
+        Distribution.Client.ProjectConfig
+        Distribution.Client.ProjectConfig.Legacy
+        Distribution.Client.ProjectConfig.Types
+        Distribution.Client.ProjectOrchestration
+        Distribution.Client.ProjectPlanOutput
+        Distribution.Client.ProjectPlanning
+        Distribution.Client.ProjectPlanning.Types
+        Distribution.Client.RebuildMonad
+        Distribution.Client.Reconfigure
+        Distribution.Client.Run
+        Distribution.Client.Sandbox
+        Distribution.Client.Sandbox.Index
+        Distribution.Client.Sandbox.PackageEnvironment
+        Distribution.Client.Sandbox.Timestamp
+        Distribution.Client.Sandbox.Types
+        Distribution.Client.SavedFlags
+        Distribution.Client.Security.DNS
+        Distribution.Client.Security.HTTP
+        Distribution.Client.Setup
+        Distribution.Client.SetupWrapper
+        Distribution.Client.SolverInstallPlan
+        Distribution.Client.SourceFiles
+        Distribution.Client.SourceRepo
+        Distribution.Client.SrcDist
+        Distribution.Client.Store
+        Distribution.Client.Tar
+        Distribution.Client.TargetSelector
+        Distribution.Client.Targets
+        Distribution.Client.Types
+        Distribution.Client.Update
+        Distribution.Client.Upload
+        Distribution.Client.Utils
+        Distribution.Client.Utils.Assertion
+        Distribution.Client.Utils.Json
+        Distribution.Client.Utils.Parsec
+        Distribution.Client.VCS
+        Distribution.Client.Win32SelfUpgrade
+        Distribution.Client.World
+        Distribution.Solver.Compat.Prelude
+        Distribution.Solver.Modular
+        Distribution.Solver.Modular.Assignment
+        Distribution.Solver.Modular.Builder
+        Distribution.Solver.Modular.Configured
+        Distribution.Solver.Modular.ConfiguredConversion
+        Distribution.Solver.Modular.ConflictSet
+        Distribution.Solver.Modular.Cycles
+        Distribution.Solver.Modular.Dependency
+        Distribution.Solver.Modular.Explore
+        Distribution.Solver.Modular.Flag
+        Distribution.Solver.Modular.Index
+        Distribution.Solver.Modular.IndexConversion
+        Distribution.Solver.Modular.LabeledGraph
+        Distribution.Solver.Modular.Linking
+        Distribution.Solver.Modular.Log
+        Distribution.Solver.Modular.Message
+        Distribution.Solver.Modular.PSQ
+        Distribution.Solver.Modular.Package
+        Distribution.Solver.Modular.Preference
+        Distribution.Solver.Modular.RetryLog
+        Distribution.Solver.Modular.Solver
+        Distribution.Solver.Modular.Tree
+        Distribution.Solver.Modular.Validate
+        Distribution.Solver.Modular.Var
+        Distribution.Solver.Modular.Version
+        Distribution.Solver.Modular.WeightedPSQ
+        Distribution.Solver.Types.ComponentDeps
+        Distribution.Solver.Types.ConstraintSource
+        Distribution.Solver.Types.DependencyResolver
+        Distribution.Solver.Types.Flag
+        Distribution.Solver.Types.InstSolverPackage
+        Distribution.Solver.Types.InstalledPreference
+        Distribution.Solver.Types.LabeledPackageConstraint
+        Distribution.Solver.Types.OptionalStanza
+        Distribution.Solver.Types.PackageConstraint
+        Distribution.Solver.Types.PackageFixedDeps
+        Distribution.Solver.Types.PackageIndex
+        Distribution.Solver.Types.PackagePath
+        Distribution.Solver.Types.PackagePreferences
+        Distribution.Solver.Types.PkgConfigDb
+        Distribution.Solver.Types.Progress
+        Distribution.Solver.Types.ResolverPackage
+        Distribution.Solver.Types.Settings
+        Distribution.Solver.Types.SolverId
+        Distribution.Solver.Types.SolverPackage
+        Distribution.Solver.Types.SourcePackage
+        Distribution.Solver.Types.Variable
+        Paths_cabal_install
+
+{% if lib %}
+    autogen-modules:
+        Paths_cabal_install
+{% endif %}
+{% useblock cabal_BUILDDEPENDS %}
+    if flag(debug-expensive-assertions)
+      cpp-options: -DDEBUG_EXPENSIVE_ASSERTIONS
+
+    if flag(debug-conflict-sets)
+      cpp-options: -DDEBUG_CONFLICT_SETS
+      build-depends: base >= 4.8
+
+    if flag(debug-tracetree)
+      cpp-options: -DDEBUG_TRACETREE
+      build-depends: tracetree >= 0.1 && < 0.2
+{% endblock %}
+{#                     #}
+{# Package Description #}
+{#                     #}
+Synopsis:           The command-line interface for Cabal and Hackage.
+Description:
+    The \'cabal\' command-line program simplifies the process of managing
+    Haskell software by automating the fetching, configuration, compilation
+    and installation of Haskell libraries and programs.
+homepage:           http://www.haskell.org/cabal/
+bug-reports:        https://github.com/haskell/cabal/issues
+License:            BSD3
+License-File:       LICENSE
+Author:             Cabal Development Team (see AUTHORS file)
+Maintainer:         Cabal Development Team <cabal-devel@haskell.org>
+Copyright:          2003-2019, Cabal Development Team
+Category:           Distribution
+{% if lib %}
+Build-type:         Simple
+{% else %}
+Build-type:         Custom
+{% endif %}
+Extra-Source-Files:
+  README.md bash-completion/cabal bootstrap.sh changelog
+
+  -- Generated with 'make gen-extra-source-files'
+  -- Do NOT edit this section manually; instead, run the script.
+  -- BEGIN gen-extra-source-files
+  tests/IntegrationTests2/build/keep-going/cabal.project
+  tests/IntegrationTests2/build/keep-going/p/P.hs
+  tests/IntegrationTests2/build/keep-going/p/p.cabal
+  tests/IntegrationTests2/build/keep-going/q/Q.hs
+  tests/IntegrationTests2/build/keep-going/q/q.cabal
+  tests/IntegrationTests2/build/local-tarball/cabal.project
+  tests/IntegrationTests2/build/local-tarball/q/Q.hs
+  tests/IntegrationTests2/build/local-tarball/q/q.cabal
+  tests/IntegrationTests2/build/setup-custom1/A.hs
+  tests/IntegrationTests2/build/setup-custom1/Setup.hs
+  tests/IntegrationTests2/build/setup-custom1/a.cabal
+  tests/IntegrationTests2/build/setup-custom2/A.hs
+  tests/IntegrationTests2/build/setup-custom2/Setup.hs
+  tests/IntegrationTests2/build/setup-custom2/a.cabal
+  tests/IntegrationTests2/build/setup-simple/A.hs
+  tests/IntegrationTests2/build/setup-simple/Setup.hs
+  tests/IntegrationTests2/build/setup-simple/a.cabal
+  tests/IntegrationTests2/exception/bad-config/cabal.project
+  tests/IntegrationTests2/exception/build/Main.hs
+  tests/IntegrationTests2/exception/build/a.cabal
+  tests/IntegrationTests2/exception/configure/a.cabal
+  tests/IntegrationTests2/exception/no-pkg/empty.in
+  tests/IntegrationTests2/exception/no-pkg2/cabal.project
+  tests/IntegrationTests2/regression/3324/cabal.project
+  tests/IntegrationTests2/regression/3324/p/P.hs
+  tests/IntegrationTests2/regression/3324/p/p.cabal
+  tests/IntegrationTests2/regression/3324/q/Q.hs
+  tests/IntegrationTests2/regression/3324/q/q.cabal
+  tests/IntegrationTests2/targets/all-disabled/cabal.project
+  tests/IntegrationTests2/targets/all-disabled/p.cabal
+  tests/IntegrationTests2/targets/benchmarks-disabled/cabal.project
+  tests/IntegrationTests2/targets/benchmarks-disabled/p.cabal
+  tests/IntegrationTests2/targets/benchmarks-disabled/q/q.cabal
+  tests/IntegrationTests2/targets/complex/cabal.project
+  tests/IntegrationTests2/targets/complex/q/Q.hs
+  tests/IntegrationTests2/targets/complex/q/q.cabal
+  tests/IntegrationTests2/targets/empty-pkg/cabal.project
+  tests/IntegrationTests2/targets/empty-pkg/p.cabal
+  tests/IntegrationTests2/targets/empty/cabal.project
+  tests/IntegrationTests2/targets/empty/foo.hs
+  tests/IntegrationTests2/targets/exes-disabled/cabal.project
+  tests/IntegrationTests2/targets/exes-disabled/p/p.cabal
+  tests/IntegrationTests2/targets/exes-disabled/q/q.cabal
+  tests/IntegrationTests2/targets/lib-only/p.cabal
+  tests/IntegrationTests2/targets/libs-disabled/cabal.project
+  tests/IntegrationTests2/targets/libs-disabled/p/p.cabal
+  tests/IntegrationTests2/targets/libs-disabled/q/q.cabal
+  tests/IntegrationTests2/targets/multiple-exes/cabal.project
+  tests/IntegrationTests2/targets/multiple-exes/p.cabal
+  tests/IntegrationTests2/targets/multiple-libs/cabal.project
+  tests/IntegrationTests2/targets/multiple-libs/p/p.cabal
+  tests/IntegrationTests2/targets/multiple-libs/q/q.cabal
+  tests/IntegrationTests2/targets/multiple-tests/cabal.project
+  tests/IntegrationTests2/targets/multiple-tests/p.cabal
+  tests/IntegrationTests2/targets/simple/P.hs
+  tests/IntegrationTests2/targets/simple/cabal.project
+  tests/IntegrationTests2/targets/simple/p.cabal
+  tests/IntegrationTests2/targets/simple/q/QQ.hs
+  tests/IntegrationTests2/targets/simple/q/q.cabal
+  tests/IntegrationTests2/targets/test-only/p.cabal
+  tests/IntegrationTests2/targets/tests-disabled/cabal.project
+  tests/IntegrationTests2/targets/tests-disabled/p.cabal
+  tests/IntegrationTests2/targets/tests-disabled/q/q.cabal
+  tests/IntegrationTests2/targets/variety/cabal.project
+  tests/IntegrationTests2/targets/variety/p.cabal
+  -- END gen-extra-source-files
+
+  -- Additional manual extra-source-files:
+  tests/IntegrationTests2/build/local-tarball/p-0.1.tar.gz
+
+
+source-repository head
+  type:     git
+  location: https://github.com/haskell/cabal/
+  subdir:   cabal-install
+
+Flag native-dns
+  description:  Enable use of the [resolv](https://hackage.haskell.org/package/resolv) & [windns](https://hackage.haskell.org/package/windns) packages for performing DNS lookups
+  default:      True
+  manual:       True
+
+Flag debug-expensive-assertions
+  description:  Enable expensive assertions for testing or debugging
+  default:      False
+  manual:       True
+
+Flag debug-conflict-sets
+  description:  Add additional information to ConflictSets
+  default:      False
+  manual:       True
+
+Flag debug-tracetree
+  description:  Compile in support for tracetree (used to debug the solver)
+  default:      False
+  manual:       True
+
+Flag lukko
+  description:  Use @lukko@ for file-locking
+  default:      True
+  manual:       True
+
+{% if lib %}
+{% else %}
+custom-setup
+   setup-depends:
+       Cabal     >= 2.2,
+       base,
+       process   >= 1.1.0.1  && < 1.7,
+       filepath  >= 1.3      && < 1.5
+
+{% endif %}
+{# Libraries, if cabal_lib #}
+{% if lib %}
+library cabal-lib-client
+{% useblock cabal_COMPONENTCOMMON %}
+    hs-source-dirs: .
+{% useblock cabal_BUILDINFO %}
+
+library cabal-install-solver-dsl
+{% useblock cabal_COMPONENTCOMMON %}
+    hs-source-dirs: solver-dsl
+    exposed-modules:
+        UnitTests.Distribution.Solver.Modular.DSL
+    build-depends:
+        base,
+        Cabal,
+        containers,
+        -- TODO: depend on cabal-install-solver only
+        cabal-lib-client
+{% endif %}
+{# Executable #}
+executable cabal
+    main-is:        Main.hs
+    hs-source-dirs: main
+{% useblock cabal_COMPONENTCOMMON %}
+
+    ghc-options: -rtsopts -threaded
+
+    -- On AIX, some legacy BSD operations such as flock(2) are provided by libbsd.a
+    if os(aix)
+        extra-libraries: bsd
+{% if lib %}
+    build-depends:
+        cabal-lib-client,
+        Cabal,
+        base,
+        directory,
+        filepath
+
+    other-modules: Paths_cabal_install
+{% else %}
+    hs-source-dirs: .
+{% useblock cabal_BUILDINFO %}
+{% endif %}
+{# MONOLITHIC exe additions #}
+{% if lib %}
+{% if monolithic %}
+    -- Monolithic: tests fused into executable
+    hs-source-dirs: tests
+    other-modules:
+        UnitTests
+        MemoryUsageTests
+        SolverQuickCheck
+        IntegrationTests2
+
+        UnitTests.Distribution.Client.ArbitraryInstances
+        UnitTests.Distribution.Client.FileMonitor
+        UnitTests.Distribution.Client.Get
+        UnitTests.Distribution.Client.GenericInstances
+        UnitTests.Distribution.Client.GZipUtils
+        UnitTests.Distribution.Client.Glob
+        UnitTests.Distribution.Client.IndexUtils.Timestamp
+        UnitTests.Distribution.Client.InstallPlan
+        UnitTests.Distribution.Client.JobControl
+        UnitTests.Distribution.Client.ProjectConfig
+        UnitTests.Distribution.Client.Sandbox
+        UnitTests.Distribution.Client.Sandbox.Timestamp
+        UnitTests.Distribution.Client.Store
+        UnitTests.Distribution.Client.Tar
+        UnitTests.Distribution.Client.Targets
+        UnitTests.Distribution.Client.TreeDiffInstances
+        UnitTests.Distribution.Client.UserConfig
+        UnitTests.Distribution.Client.VCS
+        UnitTests.Distribution.Solver.Modular.Builder
+        UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils
+        UnitTests.Distribution.Solver.Modular.MemoryUsage
+        UnitTests.Distribution.Solver.Modular.QuickCheck
+        UnitTests.Distribution.Solver.Modular.QuickCheck.Utils
+        UnitTests.Distribution.Solver.Modular.RetryLog
+        UnitTests.Distribution.Solver.Modular.Solver
+        UnitTests.Distribution.Solver.Modular.WeightedPSQ
+        UnitTests.Options
+        UnitTests.TempTestDir
+
+    cpp-options: -DMONOLITHIC
+    build-depends:
+        Cabal      == 3.3.*,
+        cabal-install-solver-dsl,
+        QuickCheck >= 2.8.2,
+        array,
+        async,
+        bytestring,
+        containers,
+        deepseq,
+        directory,
+        edit-distance,
+        filepath,
+        hashable,
+        mtl,
+        network,
+        network-uri,
+        pretty-show >= 1.6.15,
+        random,
+        tagged,
+        tar,
+        tasty >= 1.2.3 && < 1.3,
+        tasty-hunit >= 0.10,
+        tasty-quickcheck,
+        tree-diff,
+        time,
+        zlib
+{% endif %}
+{% endif %}
+{#                                                        #}
+{# Test-suites                                            #}
+{# disable if we don't configure with an (internal) libs  #}
+{#                                                        #}
+{%if lib %}
+{#                            #}
+{# Small, fast running tests. #}
+{#                            #}
+Test-Suite unit-tests
+  type: exitcode-stdio-1.0
+  main-is: UnitTests.hs
+  hs-source-dirs: tests
+  ghc-options: -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns -main-is UnitTests
+  other-modules:
+    UnitTests.Distribution.Client.ArbitraryInstances
+    UnitTests.Distribution.Client.Targets
+    UnitTests.Distribution.Client.FileMonitor
+    UnitTests.Distribution.Client.Get
+    UnitTests.Distribution.Client.GenericInstances
+    UnitTests.Distribution.Client.Glob
+    UnitTests.Distribution.Client.GZipUtils
+    UnitTests.Distribution.Client.Sandbox
+    UnitTests.Distribution.Client.Sandbox.Timestamp
+    UnitTests.Distribution.Client.Store
+    UnitTests.Distribution.Client.Tar
+    UnitTests.Distribution.Client.TreeDiffInstances
+    UnitTests.Distribution.Client.UserConfig
+    UnitTests.Distribution.Client.ProjectConfig
+    UnitTests.Distribution.Client.JobControl
+    UnitTests.Distribution.Client.IndexUtils.Timestamp
+    UnitTests.Distribution.Client.InstallPlan
+    UnitTests.Distribution.Client.VCS
+    UnitTests.Distribution.Solver.Modular.Builder
+    UnitTests.Distribution.Solver.Modular.RetryLog
+    UnitTests.Distribution.Solver.Modular.Solver
+    UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils
+    UnitTests.Distribution.Solver.Modular.WeightedPSQ
+    UnitTests.Options
+    UnitTests.TempTestDir
+  build-depends:
+        array,
+        base,
+        async,
+        bytestring,
+        cabal-lib-client,
+        cabal-install-solver-dsl,
+        Cabal,
+        containers,
+        deepseq,
+        mtl,
+        random,
+        directory,
+        filepath,
+        tar,
+        time,
+        zlib,
+        network-uri < 2.6.2.0,
+        network,
+        tasty >= 1.2.3 && <1.3,
+        tasty-hunit >= 0.10,
+        tasty-quickcheck,
+        tagged,
+        tree-diff,
+        QuickCheck >= 2.8.2
+
+  ghc-options: -threaded
+
+  default-language: Haskell2010
+
+{#                                                 #}
+{# Tests to run with a limited stack and heap size #}
+{#                                                 #}
+Test-Suite memory-usage-tests
+  type: exitcode-stdio-1.0
+  main-is: MemoryUsageTests.hs
+  hs-source-dirs: tests
+  ghc-options: -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns "-with-rtsopts=-M4M -K1K" -main-is MemoryUsageTests
+  other-modules:
+    UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils
+    UnitTests.Distribution.Solver.Modular.MemoryUsage
+    UnitTests.Options
+  build-depends:
+        base,
+        async,
+        Cabal,
+        cabal-lib-client,
+        cabal-install-solver-dsl,
+        containers,
+        deepseq,
+        tagged,
+        tasty >= 1.2.3 && <1.3,
+        tasty-hunit >= 0.10
+
+  ghc-options: -threaded
+
+  default-language: Haskell2010
+
+{#                   #}
+{# Slow solver tests #}
+{#                   #}
+Test-Suite solver-quickcheck
+  type: exitcode-stdio-1.0
+  main-is: SolverQuickCheck.hs
+  hs-source-dirs: tests
+  ghc-options: -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns -main-is SolverQuickCheck
+  other-modules:
+    UnitTests.Distribution.Solver.Modular.QuickCheck
+    UnitTests.Distribution.Solver.Modular.QuickCheck.Utils
+  build-depends:
+        base,
+        async,
+        Cabal,
+        cabal-lib-client,
+        cabal-install-solver-dsl,
+        containers,
+        deepseq >= 1.2,
+        hashable,
+        random,
+        tagged,
+        tasty >= 1.2.3 && <1.3,
+        tasty-quickcheck,
+        QuickCheck >= 2.8.2,
+        pretty-show >= 1.6.15
+
+  ghc-options: -threaded
+
+  default-language: Haskell2010
+
+{#                                                            #}
+{# Integration tests that use the cabal-install code directly #}
+{# but still build whole projects                             #}
+{#                                                            #}
+test-suite integration-tests2
+  type: exitcode-stdio-1.0
+  main-is: IntegrationTests2.hs
+  hs-source-dirs: tests
+  ghc-options: -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns -main-is IntegrationTests2
+  other-modules:
+  build-depends:
+        base,
+        Cabal,
+        cabal-lib-client,
+        containers,
+        deepseq,
+        directory,
+        edit-distance,
+        filepath,
+        tasty >= 1.2.3 && <1.3,
+        tasty-hunit >= 0.10,
+        tagged
+
+  ghc-options: -threaded
+  default-language: Haskell2010
+
+{% endif %}
diff --git a/fixtures/fancy.hs b/fixtures/fancy.hs
--- a/fixtures/fancy.hs
+++ b/fixtures/fancy.hs
@@ -1,5 +1,6 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 module DemoFancy (render) where
-import Prelude (String, fst, snd, ($))
+import Prelude (String, fst, snd, ($), return)
 import Control.Monad (forM_)
 import Fancy
 import qualified Data.Map.Strict as Map
@@ -7,19 +8,23 @@
 tell :: String -> Writer (); tell x = (x, ())
 execWriter :: Writer a -> String; execWriter = fst
 render :: Fancy -> String
-render (z_root) = execWriter $ do
-  tell (fancyString $ z_root)
+render z_root = execWriter $ do
+  tell (fancyString z_root)
   tell "\n"
-  if (fancyBoolA $ z_root)
+  if (fancyBoolA z_root)
   then do
-    forM_ (Map.toList $ fancyMap $ z_root) $ \z_var0_kv -> do
-      tell (fst $ z_var0_kv)
+    forM_ (Map.toList (fancyMap z_root)) $ \z_var0_kv -> do
+      tell (fst z_var0_kv)
       tell " -- "
-      tell (snd $ z_var0_kv)
+      tell (snd z_var0_kv)
       tell "\n"
+    return ()
   else do
-    if (fancyBoolB $ z_root)
+    if (fancyNot z_root (fancyNot z_root (fancyBoolB z_root)))
     then do
       tell "Another output\n"
+      return ()
     else do
       tell "Third branch\n"
+      return ()
+    return ()
diff --git a/fixtures/fancy.zinza b/fixtures/fancy.zinza
--- a/fixtures/fancy.zinza
+++ b/fixtures/fancy.zinza
@@ -5,7 +5,7 @@
 {% for kv in map %}
 {{ kv.key }} -- {{ kv.val }}
 {% endfor %}
-{% elif boolB %}
+{% elif not (not boolB) %}
 Another output
 {% else %}
 Third branch
diff --git a/fixtures/licenses.hs b/fixtures/licenses.hs
--- a/fixtures/licenses.hs
+++ b/fixtures/licenses.hs
@@ -1,15 +1,16 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 module DemoLicenses (render) where
-import Prelude (String, fst, snd, ($))
+import Prelude (String, fst, snd, ($), return)
 import Control.Monad (forM_)
 import Licenses
 type Writer a = (String, a)
 tell :: String -> Writer (); tell x = (x, ())
 execWriter :: Writer a -> String; execWriter = fst
 render :: Licenses -> String
-render (z_root) = execWriter $ do
-  forM_ (licenses $ z_root) $ \z_var0_license -> do
+render z_root = execWriter $ do
+  forM_ (licenses z_root) $ \z_var0_license -> do
     tell "licenseName "
-    tell (licenseCon $ z_var0_license)
+    tell (licenseCon z_var0_license)
     tell " = "
-    tell (licenseName $ z_var0_license)
+    tell (licenseName z_var0_license)
     tell "\n"
diff --git a/fixtures/regression-c.hs b/fixtures/regression-c.hs
--- a/fixtures/regression-c.hs
+++ b/fixtures/regression-c.hs
@@ -1,15 +1,16 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 module DemoLicenses (render) where
-import Prelude (String, fst, snd, ($))
+import Prelude (String, fst, snd, ($), return)
 import Control.Monad (forM_)
 import Licenses
 type Writer a = (String, a)
 tell :: String -> Writer (); tell x = (x, ())
 execWriter :: Writer a -> String; execWriter = fst
 render :: Licenses -> String
-render (z_root) = execWriter $ do
+render z_root = execWriter $ do
   tell "int main() {\n"
-  forM_ (licenses $ z_root) $ \z_var0_license -> do
+  forM_ (licenses z_root) $ \z_var0_license -> do
     tell "  printf(\""
-    tell (licenseCon $ z_var0_license)
+    tell (licenseCon z_var0_license)
     tell "\\n\");\n"
   tell "}\n"
diff --git a/src/Zinza.hs b/src/Zinza.hs
--- a/src/Zinza.hs
+++ b/src/Zinza.hs
@@ -1,6 +1,6 @@
 {-# LANGUAGE ScopedTypeVariables #-}
 -- |
--- SPDX-Identifier-Id: GPL-2.0-or-later AND BSD-3-Clause
+-- License: GPL-2.0-or-later AND BSD-3-Clause
 --
 -- Zinza - a small jinja-syntax-inspired typed-template compiler.
 --
@@ -23,25 +23,27 @@
 --
 -- @
 -- newtype Licenses = Licenses { licenses :: [License] }
---   deriving (Generic)
+--   deriving ('GHC.Generics.Generic')
 --
 -- data License = License
 --     { licenseCon  :: String
 --     , licenseName :: String
 --     }
---   deriving (Generic)
+--   deriving ('GHC.Generics.Generic')
 -- @
 --
 -- We can (generically) derive 'Zinza' instances for @Licenses@ and @License@
 --
 -- @
 -- instance 'Zinza' Licenses where
---     'toType'  = 'genericToType'  id
---     'toValue' = 'genericToValue' id
+--     'toType'    = 'genericToType'    id
+--     'toValue'   = 'genericToValue'   id
+--     'fromValue' = 'genericFromValue' id
 --
 -- instance 'Zinza' License where
---     'toType'  = 'genericToTypeSFP'
---     'toValue' = 'genericToValueSFP'
+--     'toType'    = 'genericToTypeSFP'
+--     'toValue'   = 'genericToValueSFP'
+--     'fromValue' = 'genericFromValueSFP'
 -- @
 --
 -- Then the example of run-time usage is
@@ -76,8 +78,9 @@
 -- prints a Haskell module source code:
 --
 -- @
+-- {-# OPTIONS_GHC -fno-warn-unused-imports #-}
 -- module DemoLicenses (render) where
--- import Prelude (String, fst, snd, ($))
+-- import Prelude (String, fst, snd, ($), not, return)
 -- import Control.Monad (forM_)
 -- import Licenses
 -- type Writer a = (String, a)
@@ -102,12 +105,17 @@
 -- {{ expression }}
 -- @
 --
--- Expression syntax has only two structures
---
--- * negated: @!foo@
+-- Expression syntax has only few constructions:
 --
 -- * field access @foo.bar@
 --
+-- * function application @fun bar@ (though function can only be @not@)
+--
+-- /Note:/ you can provide your own /Prelude/ of functions. See @Bools.hs@
+-- and @Bools.zinza@ in tests for an example.
+-- You cannot define new functions in templates, but you can pass
+-- them as template arguments.
+--
 -- === Control structures
 --
 -- The __for__ and __if__ statements are supported:
@@ -132,6 +140,24 @@
 -- trailing new line feed is stripped. This way full-line control tags
 -- don't introduce new lines in the output.
 --
+-- === Blocks
+--
+-- It's possible to define blocks to be used (possibly multiple times) later:
+--
+-- @
+-- {% defblock blockname %}
+-- ...
+-- {% endblock %}
+-- @
+--
+-- And the block can be used later with:
+--
+-- @
+-- {% useblock blockname %}
+-- @
+--
+-- Blocks follow scopes of @if@ and @for@ control structures
+--
 -- === Comments
 --
 -- @
@@ -151,10 +177,12 @@
     -- ** Generic deriving
     genericToType,
     genericToValue,
+    genericFromValue,
     genericToTypeSFP,
     genericToValueSFP,
+    genericFromValueSFP,
     stripFieldPrefix,
-    GZinzaType, GZinzaValue, GFieldNames,
+    GZinzaType, GZinzaValue, GZinzaFrom, GFieldNames,
     -- * Templates
     Node (..), Nodes, Expr (..), LExpr,
     -- * Types
@@ -244,8 +272,9 @@
 simpleConfig moduleName imports = ModuleConfig
     { mcRender = "render"
     , mcHeader =
-        [ "module " ++ moduleName ++ " (render) where"
-        , "import Prelude (String, fst, snd, ($))"
+        [ "{-# OPTIONS_GHC -fno-warn-unused-imports #-}"
+        , "module " ++ moduleName ++ " (render) where"
+        , "import Prelude (String, fst, snd, ($), return)"
         , "import Control.Monad (forM_)"
         ] ++
         [ "import " ++ i
diff --git a/src/Zinza/Check.hs b/src/Zinza/Check.hs
--- a/src/Zinza/Check.hs
+++ b/src/Zinza/Check.hs
@@ -6,6 +6,8 @@
 import Data.Functor.Identity (Identity (..))
 import Data.Proxy            (Proxy (..))
 import Data.Traversable      (for)
+import Control.Monad.Trans.State (StateT (..), evalStateT, put, get)
+import Control.Monad.Trans.Class (lift)
 
 import qualified Data.Map.Strict as M
 
@@ -20,6 +22,12 @@
 import Zinza.Var
 
 -------------------------------------------------------------------------------
+-- Type
+-------------------------------------------------------------------------------
+
+type Check v m = StateT (M.Map Var (v Value -> m ShowS)) (Either CompileError)
+
+-------------------------------------------------------------------------------
 -- Nodes
 -------------------------------------------------------------------------------
 
@@ -31,28 +39,31 @@
                 Nothing -> Left (UnboundTopLevelVar loc var)
                 Just _  -> Right (EField (L loc (EVar (L loc (Identity rootTy)))) (L loc var))
 
-        run <- checkNodes (map (>>== id) nodes')
+        run <- evalStateT (checkNodes (map (>>== id) nodes')) M.empty
         return $ fmap ($ "") . run . Identity . toValue
 
     rootTy -> throwRuntime (NotRecord zeroLoc rootTy)
 
 checkNodes
     :: (Indexing v i, ThrowRuntime m)
-    => Nodes (i Ty)  -- ^ nodes with root object
-    -> Either CompileError (v Value -> m ShowS)
+    => Nodes (i Ty)                    -- ^ nodes with root object
+    -> Check v m (v Value -> m ShowS)
 checkNodes nodes = do
     nodes' <- traverse checkNode nodes
     return $ \val -> do
         ss <- traverse ($ val) nodes'
         return (foldr (.) id ss)
 
-checkNode :: (Indexing v i, ThrowRuntime m) => Node (i Ty) -> Either CompileError (v Value -> m ShowS)
+checkNode
+    :: (Indexing v i, ThrowRuntime m)
+    => Node (i Ty)
+    -> Check v m (v Value -> m ShowS)
 checkNode NComment = return $ \_val -> return id
 checkNode (NRaw s) = return $ \_val -> return (showString s)
 checkNode (NIf expr xs ys) = do
     b' <- checkBool expr
-    xs' <- checkNodes xs
-    ys' <- checkNodes ys
+    xs' <- resetingState $ checkNodes xs
+    ys' <- resetingState $ checkNodes ys
     return $ \ctx -> do
         b'' <- b' ctx
         if b''
@@ -65,17 +76,40 @@
         return $ showString s
 checkNode (NFor _v expr nodes) = do
     (expr', ty) <- checkList expr
-    nodes' <- checkNodes (fmap (fmap (maybe (Here ty) There)) nodes)
+    blocks <- get
+    nodes' <- lift $ evalStateT
+        (checkNodes (fmap (fmap (maybe (Here ty) There)) nodes))
+        (M.map (\f (_ ::: xs) -> f xs) blocks)
     return $ \ctx -> do
         xs <- expr' ctx
         pieces <- for xs $ \x -> nodes' (x ::: ctx)
         return $ foldr (.) id pieces
+checkNode (NDefBlock l n nodes) = do
+    blocks <- get
+    if M.member n blocks
+    then lift (Left (ShadowingBlock l n))
+    else do
+        nodes' <- checkNodes nodes
+        put $ M.insert n nodes' blocks
+    return $ \_ -> return id
+checkNode (NUseBlock l n) = do
+    blocks <- get
+    case M.lookup n blocks of
+        Nothing -> lift (Left (UnboundUseBlock l n))
+        Just block -> return block
 
+resetingState :: Monad m => StateT s m a -> StateT s m a
+resetingState m = do
+    s <- get
+    x <- m
+    put s
+    return x
+
 -------------------------------------------------------------------------------
 -- Expressions
 -------------------------------------------------------------------------------
 
-checkList :: (Indexing v i, ThrowRuntime m) => LExpr (i Ty) -> Either CompileError (v Value -> m [Value], Ty)
+checkList :: (Indexing v i, ThrowRuntime m) => LExpr (i Ty) -> Check v m (v Value -> m [Value], Ty)
 checkList e@(L l _) = do
     (e', ty) <- checkType e
     case ty of
@@ -85,7 +119,7 @@
     go (VList xs) = return xs
     go x          = throwRuntime (NotList l (valueType x))
 
-checkBool :: (Indexing v i, ThrowRuntime m) => LExpr (i Ty) -> Either CompileError (v Value -> m Bool)
+checkBool :: (Indexing v i, ThrowRuntime m) => LExpr (i Ty) -> Check v m (v Value -> m Bool)
 checkBool e@(L l _) = do
     (e', ty) <- checkType e
     case ty of
@@ -95,7 +129,7 @@
     go (VBool b) = return b
     go x         = throwRuntime (NotBool l (valueType x))
 
-checkString :: (Indexing v i, ThrowRuntime m) => LExpr (i Ty) -> Either CompileError (v Value -> m String)
+checkString :: (Indexing v i, ThrowRuntime m) => LExpr (i Ty) -> Check v m (v Value -> m String)
 checkString e@(L l _) = do
     (e', ty) <- checkType e
     case ty of
@@ -105,11 +139,9 @@
     go (VString b) = return b
     go x           = throwRuntime (NotString l (valueType x))
 
-checkType :: (Indexing v i, ThrowRuntime m) => LExpr (i Ty) -> Either CompileError (v Value -> m Value, Ty)
-checkType (L _ (EVar (L _ i))) = return (\v -> return (fst (index v i)), extract i)
-checkType (L _ (ENot b)) = do
-    b' <- checkBool b
-    return (fmap (VBool . not) . b', TyBool)
+checkType :: (Indexing v i, ThrowRuntime m) => LExpr (i Ty) -> Check v m (v Value -> m Value, Ty)
+checkType (L _ (EVar (L _ i))) =
+    return (\v -> return (fst (index v i)), extract i)
 checkType (L eLoc (EField e (L nameLoc name))) = do
     (e', ty) <- checkType e
     case ty of
@@ -122,3 +154,18 @@
         Just y  -> return y
         Nothing -> throwRuntime (FieldNotInRecord nameLoc name (valueType x))
     go x = throwRuntime (NotRecord eLoc (valueType x))
+checkType (L eLoc (EApp f@(L fLoc _) x)) = do
+    (f', fTy) <- checkType f
+    (x', xTy) <- checkType x
+    case fTy of
+        TyFun xTy' yTy | xTy == xTy' -> do
+            return (go f' x', yTy)
+        TyFun xTy' _ -> throwRuntime (FunArgDontMatch fLoc xTy xTy')
+        _            -> throwRuntime (NotFunction eLoc fTy)
+  where
+    go f' x' ctx = do
+        f2 <- f' ctx
+        x2 <- x' ctx
+        case f2 of
+            VFun f3 -> either throwRuntime return $ f3 x2
+            _    -> throwRuntime (NotFunction eLoc (valueType f2))
diff --git a/src/Zinza/Class.hs b/src/Zinza/Class.hs
--- a/src/Zinza/Class.hs
+++ b/src/Zinza/Class.hs
@@ -3,8 +3,9 @@
     Zinza (..),
     ) where
 
-import Data.Foldable (toList)
-import Data.Proxy    (Proxy (..))
+import Control.Exception (throw)
+import Data.Foldable     (toList)
+import Data.Proxy        (Proxy (..))
 
 import qualified Data.List.NonEmpty as NE
 import qualified Data.Map.Lazy      as Map
@@ -12,7 +13,9 @@
 import qualified Data.Text          as T
 import qualified Data.Text.Lazy     as LT
 
+import Zinza.Errors
 import Zinza.Type
+import Zinza.Pos
 import Zinza.Value
 --
 -- $setup
@@ -27,7 +30,7 @@
 -- Class can be auto-derived for product types.
 --
 -- >>> data R = R { recFoo :: String, recBar :: Char } deriving Generic
--- >>> instance Zinza R where toType = genericToTypeSFP; toValue = genericToValueSFP
+-- >>> instance Zinza R where toType = genericToTypeSFP; toValue = genericToValueSFP; fromValue = genericFromValueSFP
 -- >>> displayTy $ toType (Proxy :: Proxy R)
 -- "{bar: String, foo: String}"
 --
@@ -40,14 +43,25 @@
     toValueList :: [a] -> Value
     toValueList = VList . map toValue
 
+    fromValue     :: Loc -> Value -> Either RuntimeError a
+    fromValueList :: Loc -> Value -> Either RuntimeError [a]
+    fromValueList l (VList xs) = traverse (fromValue l) xs
+    fromValueList l v          = throwRuntime $ NotList l (valueType v)
+
 instance Zinza () where
-    toType _  = tyUnit
-    toValue _ = VRecord mempty
+    toType _    = tyUnit
+    toValue _   = VRecord mempty
 
+    -- we can be strict, but it's easy to just eat some errors.
+    fromValue _ _ = return ()
+
 instance Zinza Bool where
     toType _ = TyBool
     toValue = VBool
 
+    fromValue _ (VBool b) = return b
+    fromValue l v         = throwRuntime (NotBool l (valueType v))
+
 instance Zinza Char where
     toType     _ = TyString (Just "return")
     toTypeList _ = TyString Nothing
@@ -55,9 +69,16 @@
     toValue     = VString . return
     toValueList = VString
 
+    fromValue _ (VString [c]) = return c
+    fromValue l v             = throwRuntime $ CustomError l "Not Char" (valueType v)
+
+    fromValueList _ (VString s) = return s
+    fromValueList l v           = throwRuntime (NotString l (valueType v))
+
 instance Zinza a => Zinza [a] where
-    toType _ = toTypeList (Proxy :: Proxy a)
-    toValue = toValueList
+    toType _  = toTypeList (Proxy :: Proxy a)
+    toValue   = toValueList
+    fromValue = fromValueList
 
 instance (Zinza a, Zinza b) => Zinza (a, b) where
     toType _ = TyRecord $ Map.fromList
@@ -70,24 +91,52 @@
         , ("snd", toValue b)
         ]
 
+    fromValue l (VRecord m)
+        | [("fst", x), ("snd", y)] <- Map.toList m
+        = (,) <$> fromValue l x <*> fromValue l y
+    fromValue l v = throwRuntime $ CustomError l "Not pair" (valueType v)
+
 -------------------------------------------------------------------------------
+-- Functions
+-------------------------------------------------------------------------------
+
+-- | The 'fromValue' for function produces partial functions.
+-- Use with care.
+--
+-- This means that higher order functions in templates might throw
+-- pure exception. They wont, if they are well-typed.
+--
+instance (Zinza a, Zinza b) => Zinza (a -> b) where
+    toType _ = TyFun (toType (Proxy :: Proxy a)) (toType (Proxy :: Proxy b))
+    toValue f = VFun $ fmap (toValue . f) . fromValue zeroLoc
+    fromValue l (VFun f) = return $ 
+        either throw id . (>>= fromValue l) . f . toValue
+    fromValue l v = throwRuntime $ NotFunction l (valueType v)
+
+-------------------------------------------------------------------------------
 -- semigroups
 -------------------------------------------------------------------------------
 
 instance Zinza a => Zinza (NE.NonEmpty a) where
     toType _ = TyList Nothing (toType (Proxy :: Proxy a))
     toValue  = VList . map toValue . toList
+    fromValue l v = do
+        xs <- fromValue l v
+        case xs of
+            []     -> throwRuntime $ CustomError l "Not NonEmpty" (valueType v)
+            (y:ys) -> return (y NE.:| ys)
 
 -------------------------------------------------------------------------------
 -- containers
 -------------------------------------------------------------------------------
 
-instance Zinza a => Zinza (Set.Set a) where
-    toType _ = TyList Nothing (toType (Proxy :: Proxy a))
-    toValue  = VList . map toValue . toList
+instance (Zinza a, Ord a) => Zinza (Set.Set a) where
+    toType _    = TyList Nothing (toType (Proxy :: Proxy a))
+    toValue     = VList . map toValue . toList
+    fromValue l = fmap Set.fromList . fromValue l
 
--- | Pairs are encoded as @{key: k, val: v }@
-instance (Zinza k, Zinza v) => Zinza (Map.Map k v) where
+-- | Pairs are encoded as @{ key: k, val: v }@
+instance (Zinza k, Zinza v, Ord k) => Zinza (Map.Map k v) where
     toType _ = TyList (Just "Map.toList") $ TyRecord $ Map.fromList
         [ ("key", ("fst", toType (Proxy :: Proxy k)))
         , ("val", ("snd", toType (Proxy :: Proxy v)))
@@ -101,6 +150,16 @@
         | (k, v) <- Map.toList m
         ]
 
+    fromValue l (VList xs) = do
+        kvs <- traverse fromPair xs
+        return (Map.fromList kvs)
+      where
+        fromPair (VRecord m)
+            | [("key", x), ("val", y)] <- Map.toList m
+            = (,) <$> fromValue l x <*> fromValue l y
+        fromPair v = throwRuntime $ CustomError l "Not pair" (valueType v)
+    fromValue l v = throwRuntime $ NotList l (valueType v)
+
 -------------------------------------------------------------------------------
 -- text
 -------------------------------------------------------------------------------
@@ -108,7 +167,9 @@
 instance Zinza T.Text where
     toType _ = TyString (Just "T.unpack")
     toValue  = VString . T.unpack
+    fromValue l = fmap T.pack . fromValue l
 
 instance Zinza LT.Text where
     toType _ = TyString (Just "LT.unpack")
     toValue  = VString . LT.unpack
+    fromValue l = fmap LT.pack . fromValue l
diff --git a/src/Zinza/Errors.hs b/src/Zinza/Errors.hs
--- a/src/Zinza/Errors.hs
+++ b/src/Zinza/Errors.hs
@@ -1,10 +1,12 @@
 module Zinza.Errors where
 
-import Control.Exception (Exception (..), throwIO)
+import Control.Exception         (Exception (..), throwIO)
+import Control.Monad.Trans.Class (lift)
+import Control.Monad.Trans.State (StateT (..))
 
+import Zinza.Pos
 import Zinza.Type
 import Zinza.Var
-import Zinza.Pos
 
 errorLoc :: Loc -> String -> String
 errorLoc l str = "Error at " ++ displayLoc l ++ ": " ++ str
@@ -25,12 +27,18 @@
 
 data CompileError
     = UnboundTopLevelVar Loc Var
+    | ShadowingBlock Loc Var
+    | UnboundUseBlock Loc Var
     | ARuntimeError RuntimeError
   deriving (Show)
 
 instance Exception CompileError where
     displayException (UnboundTopLevelVar loc var) = errorLoc loc $
         "unbound variable '" ++ var ++ "'"
+    displayException (ShadowingBlock loc var) = errorLoc loc $
+        "redefining block '" ++ var ++ "'"
+    displayException (UnboundUseBlock loc var) = errorLoc loc $
+        "unbound block '" ++ var ++ "' used"
     displayException (ARuntimeError err) =
         displayException err
 
@@ -58,7 +66,10 @@
     | NotRecord Loc Ty
     | NotList Loc Ty
     | FieldNotInRecord Loc Var Ty
-  deriving Show
+    | NotFunction Loc Ty
+    | FunArgDontMatch Loc Ty Ty
+    | CustomError Loc String Ty
+  deriving (Eq, Show)
 
 instance Exception RuntimeError where
     displayException (NotBool loc ty) = errorLoc loc $
@@ -71,6 +82,12 @@
         "Not a list " ++ displayTy ty
     displayException (FieldNotInRecord loc var ty) = errorLoc loc $
         "Field '" ++ var ++ "' isn't in a record of type " ++ displayTy ty
+    displayException (NotFunction loc ty) = errorLoc loc $
+        "Not a function " ++ displayTy ty
+    displayException (FunArgDontMatch loc tyA tyB) = errorLoc loc $
+        "Function argument type don't match " ++ displayTy tyA ++ "; expected " ++ displayTy tyB
+    displayException (CustomError loc msg ty) = errorLoc loc $
+        msg ++ " " ++ displayTy ty
 
 -- | Class representing errors containing 'RuntimeError's.
 --
@@ -89,3 +106,6 @@
 
 instance ThrowRuntime IO where
     throwRuntime = throwIO
+
+instance ThrowRuntime m => ThrowRuntime (StateT s m) where
+    throwRuntime = lift . throwRuntime
diff --git a/src/Zinza/Expr.hs b/src/Zinza/Expr.hs
--- a/src/Zinza/Expr.hs
+++ b/src/Zinza/Expr.hs
@@ -24,17 +24,21 @@
 data Expr a
     = EVar (Located a)                -- ^ variable
     | EField (LExpr a) (Located Var)  -- ^ field accessor
-    | ENot (LExpr a)                  -- ^ negation
+    | EApp (LExpr a) (LExpr a)        -- ^ function application
   deriving (Show, Functor, Foldable, Traversable)
 
 -- | Located expression.
 type LExpr a = Located (Expr a)
 
 instance TraversableWithLoc Expr where
-    traverseWithLoc f (EVar (L l x)) = EVar . L l <$> f l x
+    traverseWithLoc f (EVar (L l x)) = EVar . L l
+        <$> f l x
     traverseWithLoc f (EField (L l e) v) = (\e' -> EField (L l e') v)
         <$> traverseWithLoc f e
-    traverseWithLoc f (ENot (L l e)) = ENot . L l <$> traverseWithLoc f e
+    traverseWithLoc f (EApp (L lx x) (L ly y)) =
+        (\x' y' -> EApp (L lx x') (L ly y'))
+        <$> traverseWithLoc f x
+        <*> traverseWithLoc f y
 
 -- | 'Monad' instance gives substitution.
 instance Monad Expr where
@@ -42,7 +46,7 @@
 
     EVar (L _ x)           >>= k = k x
     EField (L l expr) var  >>= k = EField (L l (expr >>= k)) var
-    ENot (L l expr)        >>= k = ENot (L l (expr >>= k))
+    EApp (L lx x) (L ly y) >>= k = EApp (L lx (x >>= k)) (L ly (y >>= k))             
 
 instance Applicative Expr where
     pure = return
diff --git a/src/Zinza/Generic.hs b/src/Zinza/Generic.hs
--- a/src/Zinza/Generic.hs
+++ b/src/Zinza/Generic.hs
@@ -7,21 +7,24 @@
     GFieldNames, stripFieldPrefix,
     GZinzaType, genericToType, genericToTypeSFP,
     GZinzaValue, genericToValue, genericToValueSFP,
+    GZinzaFrom, genericFromValue, genericFromValueSFP,
     ) where
 
-import Data.Char    (isLower, toLower)
+import Data.Char      (isLower, toLower)
+import Data.Kind      (Type)
+import Data.List      (stripPrefix)
+import Data.Proxy     (Proxy (..))
 import Data.Semigroup (Semigroup (..))
-import Data.Kind    (Type)
-import Data.List    (stripPrefix)
-import Data.Proxy   (Proxy (..))
 import GHC.Generics
 
 import qualified Data.Map.Strict as M
 
 import Zinza.Class
+import Zinza.Errors
+import Zinza.Pos
 import Zinza.Type
 import Zinza.Value
-import Zinza.Var   (Var)
+import Zinza.Var    (Var)
 
 -- $setup
 -- >>> :set -XDeriveGeneric
@@ -186,3 +189,55 @@
 
 instance (i ~ R, Zinza a) => GZinzaValueLeaf (K1 i a) where
     gtoValueLeaf (K1 a) = toValue a
+
+-------------------------------------------------------------------------------
+-- Generic fromValue
+-------------------------------------------------------------------------------
+
+genericFromValue
+    :: forall a. (Generic a, GZinzaFrom (Rep a))
+    => (String -> String) -- ^ field renamer
+    -> Loc -> Value -> Either RuntimeError a
+genericFromValue namer l v@(VRecord m) = do
+    g <- gfromValue l (valueType v) $ \n -> M.lookup (namer n) m
+    return (to g)
+genericFromValue _ l v = throwRuntime $ NotRecord l (valueType v)
+
+-- | 'genericFromValue' with 'stripFieldPrefix'.
+genericFromValueSFP
+    :: forall a. (Generic a, GZinzaFrom (Rep a), GFieldNames (Rep a))
+    => Loc -> Value -> Either RuntimeError a
+genericFromValueSFP = genericFromValue (stripFieldPrefix (Proxy :: Proxy a))
+
+class GZinzaFrom (f :: Type -> Type) where
+    gfromValue :: Loc -> Ty -> (Var -> Maybe Value) -> Either RuntimeError (f ())
+
+instance (i ~ D, GZinzaFromSum f) => GZinzaFrom (M1 i c f) where
+    gfromValue l ty = fmap M1 . gfromValueSum l ty
+
+class GZinzaFromSum (f :: Type -> Type) where
+    gfromValueSum :: Loc -> Ty -> (Var -> Maybe Value) -> Either RuntimeError (f ())
+
+instance (i ~ C, GZinzaFromProd f) => GZinzaFromSum (M1 i c f) where
+    gfromValueSum l ty = fmap M1 . gfromValueProd l ty
+
+class GZinzaFromProd (f :: Type -> Type) where
+    gfromValueProd ::  Loc -> Ty -> (Var -> Maybe Value) -> Either RuntimeError (f ())
+
+instance (GZinzaFromProd f, GZinzaFromProd g) => GZinzaFromProd (f :*: g) where
+    gfromValueProd l ty v = (:*:)
+        <$> gfromValueProd l ty v
+        <*> gfromValueProd l ty v
+
+instance (i ~ S, Selector c, GZinzaFromLeaf f) => GZinzaFromProd (M1 i c f) where
+    gfromValueProd l ty f = case f n of
+        Nothing -> throwRuntime $ FieldNotInRecord l n ty
+        Just v  -> M1 <$> gfromValueLeaf l v
+      where
+        n = selName (undefined :: M1 i c f ())
+
+class GZinzaFromLeaf f where
+    gfromValueLeaf :: Loc -> Value -> Either RuntimeError (f ())
+
+instance (i ~ R, Zinza a) => GZinzaFromLeaf (K1 i a) where
+    gfromValueLeaf l = fmap K1 . fromValue l
diff --git a/src/Zinza/Module.hs b/src/Zinza/Module.hs
--- a/src/Zinza/Module.hs
+++ b/src/Zinza/Module.hs
@@ -31,7 +31,8 @@
 data S = S
     { sOutput :: [(Int, String)] -> [(Int, String)]
     , sIndent :: Int
-    , sVars  :: Int
+    , sVars   :: Int
+    , sBlocks :: M.Map Var HsExpr
     }
 
 tell :: String -> M ()
@@ -84,7 +85,7 @@
                 Nothing        -> Left (UnboundTopLevelVar loc var)
                 Just (sel, ty) -> Right (rootExpr `access` sel, ty)
 
-        ((), S out _ _) <- runStateT (header *> indented (checkNodes nodes')) (S id 0 0)
+        ((), S out _ _ _) <- runStateT (header *> indented (checkNodes nodes')) (S id 0 0 M.empty)
         return (flatten (out []))
     rootTy -> throwRuntime (NotRecord zeroLoc rootTy)
   where
@@ -108,15 +109,44 @@
     expr' <- lift $ checkBool expr
     tell $ "if " ++ displayHsExpr expr'
     tell "then do"
-    indented $ checkNodes xs
+    indented $ do
+        resettingBlocks $ checkNodes xs
+        tell $ "return ()"
     tell "else do"
-    indented $ checkNodes ys
+    indented $ do
+        resettingBlocks $ checkNodes ys
+        tell $ "return ()"
 checkNode (NFor v expr nodes) = do
     v' <- newVar v
     (expr', ty) <- lift (checkList expr)
     tell $ "forM_ " ++ displayHsExpr expr' ++ " $ \\" ++ v' ++ " -> do"
     indented $ checkNodes $ map (fmap (fromMaybe (hsVar v', ty))) nodes
+checkNode (NDefBlock l n nodes) = do
+    blocks <- fmap sBlocks get
+    if M.member n blocks
+    then lift (Left (UnboundUseBlock l n))
+    else do
+        v' <- fmap hsVar (newVar n)
+        tell $ "let"
+        indented $ do
+            tell $ displayHsExpr v' ++ " = do"
+            indented $ do
+                checkNodes nodes
+                tell $ "return ()"
+        modify' $ \s' -> s' { sBlocks = M.insert n v' blocks }
+checkNode (NUseBlock l n) = do
+    S _ _ _ blocks <- get
+    case M.lookup n blocks of
+        Nothing -> lift (Left (UnboundUseBlock l n))
+        Just block -> tell $ displayHsExpr block
 
+resettingBlocks :: M a -> M a
+resettingBlocks m = do
+    s <- get
+    x <- m
+    modify' $ \s' -> s' { sBlocks = sBlocks s }
+    return x
+
 -------------------------------------------------------------------------------
 -- Expression
 -------------------------------------------------------------------------------
@@ -144,9 +174,6 @@
 
 checkType :: LExpr (HsExpr, Ty) -> Either CompileError (HsExpr, Ty)
 checkType (L _ (EVar (L _ x))) = return x
-checkType (L _ (ENot b)) = do
-    b' <- checkBool b
-    return (b' `access` "not", TyBool)
 checkType (L eLoc (EField e (L nameLoc name))) =do
     (e', ty) <- checkType e
     case ty of
@@ -154,3 +181,11 @@
             Just (sel, tyf) -> return (e' `access` sel, tyf)
             Nothing         -> throwRuntime (FieldNotInRecord nameLoc name ty)
         _ -> throwRuntime (NotRecord eLoc ty)
+checkType (L eLoc (EApp f@(L fLoc _) x)) = do
+    (f', fTy) <- checkType f
+    (x', xTy) <- checkType x
+    case fTy of
+        TyFun xTy' yTy | xTy == xTy' -> do
+            return (HsApp f' x', yTy)
+        TyFun xTy' _ -> throwRuntime (FunArgDontMatch fLoc xTy xTy')
+        _            -> throwRuntime (NotFunction eLoc fTy)
diff --git a/src/Zinza/Node.hs b/src/Zinza/Node.hs
--- a/src/Zinza/Node.hs
+++ b/src/Zinza/Node.hs
@@ -22,8 +22,10 @@
 data Node a
     = NRaw  String                           -- ^ raw text block
     | NExpr (LExpr a)                        -- ^ expression @expr : String@
-    | NIf   (LExpr a) (Nodes a) (Nodes a)   -- ^ conditional block, @expr : Bool@
+    | NIf   (LExpr a) (Nodes a) (Nodes a)    -- ^ conditional block, @expr : Bool@
     | NFor  Var (LExpr a) (Nodes (Maybe a))  -- ^ for loop, @expr : List a@
+    | NDefBlock Loc Var (Nodes a)            -- ^ define block
+    | NUseBlock Loc Var                      -- ^ use block
     | NComment                               -- ^ comments
   deriving (Show, Functor, Foldable, Traversable)
 
@@ -42,6 +44,9 @@
       where
         f' _ Nothing  = pure Nothing
         f' l (Just x) = Just <$> f l x
+    traverseWithLoc f (NDefBlock l n xs) = NDefBlock l n
+        <$> traverse (traverseWithLoc f) xs
+    traverseWithLoc _ (NUseBlock l n) = pure (NUseBlock l n)
 
 -- | Substitution.
 (>>==) :: Node a -> (a -> Expr b) -> Node b
@@ -50,3 +55,5 @@
 NExpr (L l expr)       >>== k = NExpr (L l (expr >>= k))
 NIf (L l expr) xs ys   >>== k = NIf (L l (expr >>= k)) (map (>>== k) xs) (map (>>== k) ys)
 NFor var (L l expr) ns >>== k = NFor var (L l (expr >>= k)) (map (>>== traverse k) ns)
+NDefBlock l n xs       >>== k = NDefBlock l n (map (>>== k) xs)
+NUseBlock l n          >>== _ = NUseBlock l n
diff --git a/src/Zinza/Parser.hs b/src/Zinza/Parser.hs
--- a/src/Zinza/Parser.hs
+++ b/src/Zinza/Parser.hs
@@ -3,9 +3,10 @@
 import Control.Applicative (many, optional, some, (<|>))
 import Control.Monad       (void)
 import Data.Char           (isAlphaNum, isLower)
-import Data.Maybe          (isJust)
+import Data.List           (foldl')
 import Text.Parsec
-       (eof, getPosition, lookAhead, notFollowedBy, parse, anyChar, satisfy, try)
+       (anyChar, eof, getPosition, lookAhead, notFollowedBy, parse, satisfy,
+       try)
 import Text.Parsec.Char    (char, space, spaces, string)
 import Text.Parsec.Pos     (SourcePos, sourceColumn, sourceLine)
 import Text.Parsec.String  (Parser)
@@ -39,10 +40,13 @@
 varP :: Parser Var
 varP = (:) <$> satisfy isLower <*> many (satisfy isVarChar)
 
+locP :: Parser Loc
+locP = toLoc <$> getPosition
+
 located :: Parser a -> Parser (Located a)
 located p = do
-    pos <- getPosition
-    L (toLoc pos) <$> p
+    l <- locP
+    L l <$> p
 
 locVarP :: Parser (Located Var)
 locVarP = located varP
@@ -72,21 +76,36 @@
 exprNodeP = do
     _ <- try (string "{{")
     spaces
-    expr <- located exprP
+    expr <- exprP
     spaces
     _ <- string "}}"
     return (NExpr expr)
 
-exprP :: Parser (Expr Var)
+exprP :: Parser (LExpr Var)
 exprP = do
-    b <- optional (char '!')
+    expr <- primitiveExprP
+    exprs <- many primitiveExprP
+    return $ foldl' (\f@(L l _) x -> L l (EApp f x)) expr exprs
+
+primitiveExprP :: Parser (LExpr Var)
+primitiveExprP = parens exprP <|> located primitiveExprP'
+
+parens :: Parser a -> Parser a
+parens p = do
+    _ <- char '('
+    spaces
+    x <- p
+    _ <- char ')'
+    spaces
+    return x
+
+primitiveExprP' :: Parser (Expr Var)
+primitiveExprP' = do
     v@(L l _) <- locVarP
     vs <- many (char '.' *> locVarP)
+    spaces
     let expr = foldl (\e f -> EField (L l e) f) (EVar v) vs
-    return $
-        if isJust b
-        then ENot (L l expr)
-        else expr
+    return expr
 
 commentP :: Parser (Node var)
 commentP = do
@@ -103,13 +122,13 @@
                     '}' -> NComment <$ eatNewlineWhen (sourceColumn pos == 1)
                     _   -> go pos
             _   -> go pos
-    
+
 eatNewlineWhen :: Bool -> Parser ()
 eatNewlineWhen False = return ()
 eatNewlineWhen True  = void (optional (char '\n'))
 
 directiveP :: Parser (Node Var)
-directiveP = forP <|> ifP
+directiveP = forP <|> ifP <|> defBlockP <|> useBlockP
 
 spaces1 :: Parser ()
 spaces1 = space *> spaces
@@ -138,8 +157,7 @@
     _ <- string "in"
     notFollowedBy $ satisfy isAlphaNum
     spaces1
-    expr <- located exprP
-    spaces1
+    expr <- exprP
     close' on0
     ns <- nodesP
     close "for"
@@ -148,8 +166,7 @@
 ifP :: Parser (Node Var)
 ifP = do
     on0 <- open "if"
-    expr <- located exprP
-    spaces
+    expr <- exprP
     close' on0
     ns <- nodesP
     closing (NIf expr ns)
@@ -169,8 +186,27 @@
 
     elifP mk = do
         on0 <- open "elif"
-        expr <- located exprP
-        spaces
+        expr <- exprP
         close' on0
         ns <- nodesP
         closing (mk . pure . NIf expr ns)
+
+defBlockP :: Parser (Node Var)
+defBlockP = do
+    l <- locP
+    on0 <- open "defblock"
+    var <- varP
+    spaces
+    close' on0
+    ns <- nodesP
+    close "block"
+    return (NDefBlock l var ns)
+
+useBlockP :: Parser (Node Var)
+useBlockP = do
+    l <- locP
+    on0 <- open "useblock"
+    var <- varP
+    spaces
+    close' on0
+    return (NUseBlock l var)
diff --git a/src/Zinza/Type.hs b/src/Zinza/Type.hs
--- a/src/Zinza/Type.hs
+++ b/src/Zinza/Type.hs
@@ -27,6 +27,7 @@
     | TyString (Maybe Selector)              -- ^ string
     | TyList (Maybe Selector) Ty             -- ^ lists
     | TyRecord (M.Map Var (Selector, Ty))    -- ^ records
+    | TyFun Ty Ty                            -- ^ functions
   deriving (Eq, Ord, Show)
 
 -- | A record without fields is a unit type. Think of zero-field tuple: @()@.
@@ -35,17 +36,19 @@
 
 -- | Pretty print 'Ty'.
 displayTy :: Ty -> String
-displayTy ty = go ty "" where
-    go :: Ty -> ShowS
-    go TyBool       = showString "Bool"
-    go (TyString _) = showString "String"
-    go (TyList _ t) = showChar '[' . go t . showChar ']'
-    go (TyRecord m) = case M.toList m of
+displayTy ty = go 0 ty "" where
+    go :: Int -> Ty -> ShowS
+    go _ TyBool       = showString "Bool"
+    go _ (TyString _) = showString "String"
+    go _ (TyList _ t) = showChar '[' . go 0 t . showChar ']'
+    go _ (TyRecord m) = case M.toList m of
         []            -> showString "{}"
         ((n,(_,t)) : nts) -> foldl
             (\acc (n',(_,t')) -> acc . showString ", " . showPair n' t')
             (showChar '{' . showPair n t)
             nts
             . showChar '}'
+    go d (TyFun a b) = showParen (d > 10) $
+        go 11 a . showString " -> " . go 10 b
 
-    showPair n t = showString n . showString ": " . go t
+    showPair n t = showString n . showString ": " . go 0 t
diff --git a/src/Zinza/Value.hs b/src/Zinza/Value.hs
--- a/src/Zinza/Value.hs
+++ b/src/Zinza/Value.hs
@@ -2,16 +2,17 @@
 
 import qualified Data.Map.Strict as M
 
-import Zinza.Var
+import Zinza.Errors
 import Zinza.Type
+import Zinza.Var
 
 -- | Template values.
 data Value
-    = VBool Bool                 -- ^ booleans
-    | VString String             -- ^ strings
-    | VList [Value]              -- ^ lists
-    | VRecord (M.Map Var Value)  -- ^ records
-  deriving (Show)
+    = VBool Bool                                 -- ^ booleans
+    | VString String                             -- ^ strings
+    | VList [Value]                              -- ^ lists
+    | VRecord (M.Map Var Value)                  -- ^ records
+    | VFun (Value -> Either RuntimeError Value)  -- ^ function
 
 -- | Calculate 'Ty' of the 'Value'.
 -- This is only an approximation, for list we look at first
@@ -22,3 +23,4 @@
 valueType (VList [])    = TyList Nothing tyUnit
 valueType (VList (v:_)) = TyList Nothing (valueType v)
 valueType (VRecord m)   = TyRecord (fmap (\v -> ("", valueType v)) m)
+valueType (VFun _)      = TyFun tyUnit tyUnit
diff --git a/src/Zinza/Var.hs b/src/Zinza/Var.hs
--- a/src/Zinza/Var.hs
+++ b/src/Zinza/Var.hs
@@ -1,7 +1,5 @@
 module Zinza.Var where
 
-import Data.List.NonEmpty (NonEmpty (..), cons)
-
 -- | Variable name, possibly a fieldname in the record.
 type Var = String
 
@@ -9,16 +7,32 @@
 type Selector = String
 
 -- | A very simple haskell expression
-newtype HsExpr = HsExpr (NonEmpty Selector)
+data HsExpr
+    = HsVar Var
+    | HsSel HsExpr Var
+    | HsApp HsExpr HsExpr
 
 hsVar :: Selector -> HsExpr
-hsVar = HsExpr . pure
+hsVar = HsVar
 
 access :: HsExpr -> Selector -> HsExpr
-access (HsExpr xs) x = HsExpr (cons x xs)
+access = HsSel
 
 accessMaybe :: HsExpr -> Maybe Selector -> HsExpr
 accessMaybe e = maybe e (access e)
 
 displayHsExpr :: HsExpr -> String
-displayHsExpr (HsExpr xs) = "(" ++ foldr1 (\a b -> a ++ " $ " ++ b) xs ++ ")"
+displayHsExpr expr0 = go 11 expr0 "" where
+    go :: Int -> HsExpr -> ShowS
+    go _ (HsVar var)
+        = showString var
+    go d (HsSel e s)
+        = showParen (d > 10)
+        $ showString s
+        . showChar ' '
+        . go 11 e
+    go d (HsApp f x)
+        = showParen (d > 10)
+        $ go 10 f
+        . showChar ' '
+        . go 11 x
diff --git a/test/Bools.hs b/test/Bools.hs
new file mode 100644
--- /dev/null
+++ b/test/Bools.hs
@@ -0,0 +1,73 @@
+{-# LANGUAGE DeriveGeneric #-}
+module Bools where
+
+import GHC.Generics    (Generic (..))
+import Test.QuickCheck (Arbitrary (..), elements, genericShrink)
+
+import Zinza
+
+-- Worth trying is to 
+-- 
+-- make load-bools
+-- :m +Prelude
+-- putStr $ render (Bools [True, False] id (||))
+
+data Bools = Bools
+    { boolsBools :: [Bool]
+    , boolsNot   :: Bool -> Bool
+    , boolsAnd   :: Bool -> Bool -> Bool
+    }
+  deriving (Generic)
+
+instance Eq Bools where
+    Bools x0 x1 x2 == Bools y0 y1 y2 = and
+        [ x0 == y0
+        , eqFun1 x1 y1
+        , eqFun2 x2 y2
+        ]
+
+instance Zinza Bools where
+    toType    = genericToTypeSFP
+    toValue   = genericToValueSFP
+    fromValue = genericFromValueSFP
+
+instance Arbitrary Bools where
+    arbitrary = Bools
+        <$> arbitrary
+        <*> elements unaryBools
+        <*> elements binaryBools
+
+    shrink = genericShrink
+
+unaryBools :: [Bool -> Bool]
+unaryBools = 
+    [ id
+    , const True
+    , const False
+    , not
+    ] 
+
+binaryBools :: [Bool -> Bool -> Bool]
+binaryBools = 
+    [ \_ _ -> True
+    , \_ _ -> False
+    , (&&)
+    , (||)
+    , \x _ -> x
+    , \x _ -> not x
+    , \_ y -> y
+    , \_ y -> not y
+    ]
+
+eqFun1 :: Eq a => (Bool -> a) -> (Bool -> a) -> Bool
+eqFun1 f g = and
+    [ f x == g x
+    | x <- [True, False]
+    ]
+
+eqFun2 :: Eq a => (Bool -> Bool -> a) -> (Bool -> Bool -> a) -> Bool
+eqFun2 f g = and
+    [ f x y == g x y
+    | x <- [True, False]
+    , y <- [True, False]
+    ]
diff --git a/test/CabalInstall.hs b/test/CabalInstall.hs
new file mode 100644
--- /dev/null
+++ b/test/CabalInstall.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE DeriveGeneric #-}
+module CabalInstall where
+
+import GHC.Generics    (Generic (..))
+import Test.QuickCheck (Arbitrary (..), genericShrink)
+
+import Zinza
+
+data CabalInstall = CabalInstall
+    { ciLib        :: Bool
+    , ciMonolithic :: Bool
+    }
+  deriving (Eq, Show, Generic)
+
+instance Zinza CabalInstall where
+    toType    = genericToTypeSFP
+    toValue   = genericToValueSFP
+    fromValue = genericFromValueSFP
+
+instance Arbitrary CabalInstall where
+    arbitrary = CabalInstall
+        <$> arbitrary
+        <*> arbitrary
+
+    shrink = genericShrink
diff --git a/test/Fancy.hs b/test/Fancy.hs
--- a/test/Fancy.hs
+++ b/test/Fancy.hs
@@ -1,8 +1,9 @@
 {-# LANGUAGE DeriveGeneric #-}
 module Fancy where
 
-import Data.Map     (Map)
-import GHC.Generics (Generic (..))
+import Data.Map        (Map)
+import GHC.Generics    (Generic (..))
+import Test.QuickCheck (Arbitrary (..), elements, genericShrink)
 
 import Zinza
 
@@ -11,9 +12,36 @@
     , fancyBoolB  :: Bool
     , fancyString :: String
     , fancyMap    :: Map String String
+    , fancyNot    :: Bool -> Bool
     }
   deriving (Generic)
 
+instance Eq Fancy where
+    Fancy x0 x1 x2 x3 x4 == Fancy y0 y1 y2 y3 y4 = and
+        [ x0 == y0
+        , x1 == y1
+        , x2 == y2
+        , x3 == y3
+        , x4 True == y4 True
+        , x4 False == y4 False
+        ]
+
 instance Zinza Fancy where
-    toType  = genericToTypeSFP
-    toValue = genericToValueSFP
+    toType    = genericToTypeSFP
+    toValue   = genericToValueSFP
+    fromValue = genericFromValueSFP
+
+instance Arbitrary Fancy where
+    arbitrary = Fancy
+        <$> arbitrary
+        <*> arbitrary
+        <*> arbitrary
+        <*> arbitrary
+        <*> elements
+            [ id
+            , const True
+            , const False
+            , not
+            ] 
+
+    shrink = genericShrink
diff --git a/test/Licenses.hs b/test/Licenses.hs
--- a/test/Licenses.hs
+++ b/test/Licenses.hs
@@ -1,23 +1,36 @@
 {-# LANGUAGE DeriveGeneric #-}
 module Licenses where
 
-import GHC.Generics      (Generic (..))
+import GHC.Generics    (Generic (..))
+import Test.QuickCheck (Arbitrary (..), genericShrink)
 
 import Zinza
 
 newtype Licenses = Licenses { licenses :: [License] }
-  deriving (Generic)
+  deriving (Eq, Show, Generic)
 
 instance Zinza Licenses where
-    toType  = genericToType  id
-    toValue = genericToValue id
+    toType    = genericToType    id
+    toValue   = genericToValue   id
+    fromValue = genericFromValue id
 
 data License = License
     { licenseCon  :: String
     , licenseName :: String
     }
-  deriving (Show, Generic)
+  deriving (Eq, Show, Generic)
 
 instance Zinza License where
-    toType  = genericToTypeSFP
-    toValue = genericToValueSFP
+    toType    = genericToTypeSFP
+    toValue   = genericToValueSFP
+    fromValue = genericFromValueSFP
+
+instance Arbitrary Licenses where
+    arbitrary = Licenses <$> arbitrary
+    shrink    = genericShrink
+
+instance Arbitrary License where
+    arbitrary = License
+        <$> arbitrary
+        <*> arbitrary
+    shrink = genericShrink
diff --git a/test/Tests.hs b/test/Tests.hs
--- a/test/Tests.hs
+++ b/test/Tests.hs
@@ -1,13 +1,23 @@
+{-# LANGUAGE ScopedTypeVariables #-}
 module Main (main) where
 
 import Control.Exception (displayException, throwIO)
 import Test.Tasty        (TestName, TestTree, defaultMain, testGroup)
 import Test.Tasty.Golden (goldenVsStringDiff)
+import Data.List.NonEmpty (NonEmpty (..))
+import Data.Set (Set)
 import Test.Tasty.HUnit  (testCase, (@?=))
+import Data.Proxy (Proxy (..))
+import Data.Typeable (Typeable, typeRep)
+import Test.Tasty.QuickCheck (testProperty)
+import Test.QuickCheck (Arbitrary, (===), Property)
+import Test.QuickCheck.Instances ()
 
 import qualified Data.ByteString.Lazy.Char8 as LBS8
 import qualified Data.Map.Strict            as Map
 
+import Bools
+import CabalInstall
 import Fancy
 import Licenses
 
@@ -21,6 +31,19 @@
               [ "licenseName Foo = \"foo-1.0\""
               , "licenseName Bar = \"bar-1.2\""
               ]
+    , testGroup "Roundtrip"
+        [ roundtrip (Proxy :: Proxy ())
+        , roundtrip (Proxy :: Proxy Bool)
+        , roundtrip (Proxy :: Proxy Char)
+        , roundtrip (Proxy :: Proxy String)
+        , roundtrip (Proxy :: Proxy (NonEmpty Bool))
+        , roundtrip (Proxy :: Proxy (Set String))
+        , roundtrip (Proxy :: Proxy (Map.Map String Bool))
+        -- custom types
+        , roundtrip (Proxy :: Proxy CabalInstall)
+        -- , roundtrip (Proxy :: Proxy Fancy)
+        -- , roundtrip (Proxy :: Proxy Licenses)
+        ]
     , testGroup "Golden"
         [ testGolden lics licsMC "licenses"
         , testGolden lics licsMC "error-typo"
@@ -28,6 +51,8 @@
         , testGolden lics licsMC "error-string"
         , testGolden lics licsMC "regression-c"
         , testGolden fancy fancyMC "fancy"
+        , testGolden cabal cabalMC "cabal-install"
+        , testGolden bools boolsMC "bools"
         ]
     ]
   where
@@ -76,12 +101,40 @@
         [ ("foo", "Foo")
         , ("bar", "Bar")
         ]
+    , fancyNot = not
     }
 
 fancyMC :: ModuleConfig Fancy
 fancyMC = simpleConfig "DemoFancy" ["Fancy", "qualified Data.Map.Strict as Map"]
 
 -------------------------------------------------------------------------------
+-- cabal-install
+-------------------------------------------------------------------------------
+
+cabal :: CabalInstall
+cabal = CabalInstall
+    { ciLib = False
+    , ciMonolithic = False
+    }
+
+cabalMC :: ModuleConfig CabalInstall
+cabalMC = simpleConfig "DemoCabalInstall" ["CabalInstall"]
+
+-------------------------------------------------------------------------------
+-- Bools
+-------------------------------------------------------------------------------
+
+bools :: Bools
+bools = Bools
+    { boolsBools = [False, True]
+    , boolsNot   = not
+    , boolsAnd   = (&&)
+    }
+
+boolsMC :: ModuleConfig Bools
+boolsMC = simpleConfig "DemoBools" ["Bools"]
+
+-------------------------------------------------------------------------------
 -- Example
 -------------------------------------------------------------------------------
 
@@ -98,3 +151,14 @@
   where
     runEither (Left err) = throwIO err
     runEither (Right x)  = return x
+
+-------------------------------------------------------------------------------
+-- roundtrip
+-------------------------------------------------------------------------------
+
+roundtrip
+    :: forall a. (Typeable a, Zinza a, Eq a, Show a, Arbitrary a)
+    => Proxy a -> TestTree
+roundtrip p = testProperty (show (typeRep p)) prop where
+    prop :: a -> Property
+    prop x = fromValue zeroLoc (toValue x) === Right x
diff --git a/zinza.cabal b/zinza.cabal
--- a/zinza.cabal
+++ b/zinza.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.2
 name:               zinza
-version:            0.1
+version:            0.2
 synopsis:           Typed templates with jinja like syntax
 category:           Text, Template
 description:
@@ -15,6 +15,7 @@
 bug-reports:        https://github.com/phadej/zinza/issues
 tested-with:        GHC ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.1
 extra-source-files:
+  Changelog.md
   fixtures/*.zinza
   fixtures/*.hs
   fixtures/*.txt
@@ -56,14 +57,19 @@
   hs-source-dirs:   test
   main-is:          Tests.hs
   other-modules:
+    Bools
+    CabalInstall
     Fancy
     Licenses
 
   build-depends:
     , base
-    , bytestring    ^>=0.10.8.2
+    , bytestring            ^>=0.10.8.2
     , containers
-    , tasty         ^>=1.2.3
-    , tasty-golden  ^>=2.3.2
-    , tasty-hunit   ^>=0.10.0.2
+    , QuickCheck            ^>=2.13.2
+    , quickcheck-instances  ^>=0.3.22
+    , tasty                 ^>=1.2.3
+    , tasty-golden          ^>=2.3.2
+    , tasty-hunit           ^>=0.10.0.2
+    , tasty-quickcheck      ^>=0.10.1
     , zinza
