Full Code of travisbrown/dhallj for AI

main df19298a67dc cached
167 files
808.0 KB
213.8k tokens
1358 symbols
1 requests
Download .txt
Showing preview only (873K chars total). Download the full file or copy to clipboard to get everything.
Repository: travisbrown/dhallj
Branch: main
Commit: df19298a67dc
Files: 167
Total size: 808.0 KB

Directory structure:
gitextract_55f_2v4h/

├── .github/
│   └── workflows/
│       ├── ci.yml
│       └── clean.yml
├── .gitignore
├── .gitmodules
├── .scalafmt.conf
├── LICENSE
├── README.md
├── WORKSPACE
├── benchmarks/
│   └── src/
│       └── main/
│           └── scala/
│               └── org/
│                   └── dhallj/
│                       └── benchmarks/
│                           ├── EncodingBenchmark.scala
│                           └── ParsingBenchmark.scala
├── build.sbt
├── cli/
│   └── src/
│       └── main/
│           └── java/
│               └── org/
│                   └── dhallj/
│                       └── cli/
│                           └── Dhall.java
├── javascript/
│   ├── BUILD
│   ├── api/
│   │   ├── BUILD
│   │   ├── DhallJs.java
│   │   └── dhall.js
│   └── jre/
│       ├── BUILD
│       ├── BufferedReader.java
│       ├── InputStreamReader.java
│       ├── InvalidPathException.java
│       ├── Path.java
│       ├── Paths.java
│       ├── URI.java
│       └── URISyntaxException.java
├── modules/
│   ├── ast/
│   │   └── src/
│   │       └── main/
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── ast/
│   │                           └── package.scala
│   ├── cats/
│   │   └── src/
│   │       ├── main/
│   │       │   └── scala/
│   │       │       └── org/
│   │       │           └── dhallj/
│   │       │               └── cats/
│   │       │                   └── LiftVisitor.scala
│   │       └── test/
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── cats/
│   │                           └── LiftVisitorSuite.scala
│   ├── circe/
│   │   └── src/
│   │       ├── main/
│   │       │   └── scala/
│   │       │       └── org/
│   │       │           └── dhallj/
│   │       │               └── circe/
│   │       │                   ├── CirceHandler.scala
│   │       │                   └── Converter.scala
│   │       └── test/
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── circe/
│   │                           ├── CirceConverterSuite.scala
│   │                           └── JsonCleaner.scala
│   ├── core/
│   │   ├── BUILD
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── org/
│   │       │           └── dhallj/
│   │       │               ├── cbor/
│   │       │               │   ├── AdditionalInfo.java
│   │       │               │   ├── CborException.java
│   │       │               │   ├── HalfFloat.java
│   │       │               │   ├── MajorType.java
│   │       │               │   ├── NullVisitor.java
│   │       │               │   ├── Reader.java
│   │       │               │   ├── Visitor.java
│   │       │               │   └── Writer.java
│   │       │               └── core/
│   │       │                   ├── ArrayIterable.java
│   │       │                   ├── Constructors.java
│   │       │                   ├── DhallException.java
│   │       │                   ├── Expr.java
│   │       │                   ├── ExternalVisitor.java
│   │       │                   ├── IsResolved.java
│   │       │                   ├── Operator.java
│   │       │                   ├── Source.java
│   │       │                   ├── Tags.java
│   │       │                   ├── ToStringVisitor.java
│   │       │                   ├── VisitState.java
│   │       │                   ├── Visitor.java
│   │       │                   ├── binary/
│   │       │                   │   ├── CborDecodingVisitor.java
│   │       │                   │   ├── Decode.java
│   │       │                   │   ├── DecodingException.java
│   │       │                   │   ├── Encode.java
│   │       │                   │   └── Label.java
│   │       │                   ├── converters/
│   │       │                   │   ├── JsonConverter.java
│   │       │                   │   └── JsonHandler.java
│   │       │                   ├── normalization/
│   │       │                   │   ├── AlphaNormalize.java
│   │       │                   │   ├── BetaNormalize.java
│   │       │                   │   ├── BetaNormalizeApplication.java
│   │       │                   │   ├── BetaNormalizeFieldAccess.java
│   │       │                   │   ├── BetaNormalizeIf.java
│   │       │                   │   ├── BetaNormalizeMerge.java
│   │       │                   │   ├── BetaNormalizeOperatorApplication.java
│   │       │                   │   ├── BetaNormalizeProjection.java
│   │       │                   │   ├── BetaNormalizeTextLiteral.java
│   │       │                   │   ├── BetaNormalizeToMap.java
│   │       │                   │   ├── BetaNormalizeWith.java
│   │       │                   │   ├── NormalizationUtils.java
│   │       │                   │   ├── Shift.java
│   │       │                   │   └── Substitute.java
│   │       │                   └── typechecking/
│   │       │                       ├── BuiltInTypes.java
│   │       │                       ├── CheckEquivalence.java
│   │       │                       ├── Context.java
│   │       │                       ├── NonNegativeIndices.java
│   │       │                       ├── TypeCheck.java
│   │       │                       ├── TypeCheckApplication.java
│   │       │                       ├── TypeCheckFailure.java
│   │       │                       └── Universe.java
│   │       └── test/
│   │           └── java/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── cbor/
│   │                           ├── CborSuite.scala
│   │                           └── HalfFloatSuite.scala
│   ├── imports/
│   │   ├── README.md
│   │   └── src/
│   │       ├── main/
│   │       │   └── scala/
│   │       │       └── org/
│   │       │           └── dhallj/
│   │       │               └── imports/
│   │       │                   ├── Canonicalization.scala
│   │       │                   ├── CorsComplianceCheck.scala
│   │       │                   ├── ImportCache.scala
│   │       │                   ├── ImportContext.scala
│   │       │                   ├── ReferentialSanityCheck.scala
│   │       │                   ├── ResolveImports.scala
│   │       │                   ├── ResolveImportsVisitor.scala
│   │       │                   ├── ToHeaders.scala
│   │       │                   └── syntax/
│   │       │                       └── package.scala
│   │       └── test/
│   │           ├── resources/
│   │           │   ├── alternate/
│   │           │   │   ├── other.dhall
│   │           │   │   └── package.dhall
│   │           │   ├── cache-write/
│   │           │   │   └── package.dhall
│   │           │   ├── cyclic/
│   │           │   │   ├── other.dhall
│   │           │   │   └── package.dhall
│   │           │   ├── cyclic-relative-paths/
│   │           │   │   ├── other.dhall
│   │           │   │   └── package.dhall
│   │           │   ├── hashed/
│   │           │   │   └── package.dhall
│   │           │   ├── local/
│   │           │   │   └── package.dhall
│   │           │   ├── local-local-absolute/
│   │           │   │   └── package.dhall
│   │           │   ├── local-local-absolute-2/
│   │           │   │   └── package.dhall
│   │           │   ├── local-local-relative/
│   │           │   │   ├── other.dhall
│   │           │   │   └── package.dhall
│   │           │   ├── local-remote/
│   │           │   │   └── package.dhall
│   │           │   ├── multiple-imports/
│   │           │   │   ├── other.dhall
│   │           │   │   ├── other2.dhall
│   │           │   │   └── package.dhall
│   │           │   └── text-import/
│   │           │       └── package.dhall
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── imports/
│   │                           ├── CanonicalizationSuite.scala
│   │                           ├── CorsComplianceCheckSuite.scala
│   │                           ├── ImportCacheSuite.scala
│   │                           ├── ImportResolutionSuite.scala
│   │                           ├── ReferentialSanityCheckSuite.scala
│   │                           └── ToHeadersSuite.scala
│   ├── imports-mini/
│   │   └── src/
│   │       └── main/
│   │           └── java/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── imports/
│   │                           └── mini/
│   │                               ├── ResolutionVisitor.java
│   │                               └── Resolver.java
│   ├── javagen/
│   │   └── src/
│   │       └── main/
│   │           └── java/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── javagen/
│   │                           ├── Code.scala
│   │                           ├── ToCodeVisitor.scala
│   │                           └── package.scala
│   ├── jawn/
│   │   └── src/
│   │       ├── main/
│   │       │   └── scala/
│   │       │       └── org/
│   │       │           └── dhallj/
│   │       │               └── jawn/
│   │       │                   ├── FacadeHandler.scala
│   │       │                   └── JawnConverter.scala
│   │       └── test/
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── jawn/
│   │                           └── JawnConverterSuite.scala
│   ├── parser/
│   │   ├── BUILD
│   │   └── src/
│   │       ├── main/
│   │       │   ├── java/
│   │       │   │   └── org/
│   │       │   │       └── dhallj/
│   │       │   │           └── parser/
│   │       │   │               ├── DhallParser.java
│   │       │   │               └── support/
│   │       │   │                   ├── Comment.java
│   │       │   │                   ├── LetBinding.java
│   │       │   │                   ├── OperatorPrecedenceTable.java
│   │       │   │                   ├── Parser.java
│   │       │   │                   ├── ParsingHelpers.java
│   │       │   │                   ├── WhitespaceManager.java
│   │       │   │                   └── package-info.java
│   │       │   └── javacc/
│   │       │       └── JavaCCParser.jj
│   │       └── test/
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── parser/
│   │                           └── DhallParserSuite.scala
│   ├── prelude/
│   │   └── src/
│   │       └── main/
│   │           └── java/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── prelude/
│   │                           └── Prelude.java
│   ├── scala/
│   │   └── src/
│   │       └── main/
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── syntax/
│   │                           └── package.scala
│   ├── scala-codec/
│   │   └── src/
│   │       ├── main/
│   │       │   └── scala/
│   │       │       └── org/
│   │       │           └── dhallj/
│   │       │               └── codec/
│   │       │                   ├── Decoder.scala
│   │       │                   ├── DecodingFailure.scala
│   │       │                   ├── Encoder.scala
│   │       │                   └── syntax/
│   │       │                       └── package.scala
│   │       └── test/
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── codec/
│   │                           └── DecoderSuite.scala
│   ├── testing/
│   │   └── src/
│   │       └── main/
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── testing/
│   │                           ├── ArbitraryInstances.scala
│   │                           ├── WellTypedExpr.scala
│   │                           └── package.scala
│   └── yaml/
│       └── src/
│           ├── main/
│           │   └── java/
│           │       └── org/
│           │           └── dhallj/
│           │               └── yaml/
│           │                   ├── YamlContext.java
│           │                   ├── YamlConverter.java
│           │                   └── YamlHandler.java
│           └── test/
│               └── scala/
│                   └── org/
│                       └── dhallj/
│                           └── yaml/
│                               └── YamlConverterSuite.scala
├── project/
│   ├── build.properties
│   └── plugins.sbt
├── scalastyle-config.xml
├── tests/
│   └── src/
│       ├── main/
│       │   └── scala/
│       │       └── org/
│       │           └── dhallj/
│       │               └── tests/
│       │                   ├── HaskellDhall.scala
│       │                   └── acceptance/
│       │                       ├── AcceptanceFailureSuite.scala
│       │                       ├── AcceptanceSuccessSuite.scala
│       │                       ├── AcceptanceSuite.scala
│       │                       └── ImportResolutionSuite.scala
│       └── test/
│           ├── resources/
│           │   └── learndhall.dhall
│           └── scala/
│               └── org/
│                   └── dhallj/
│                       └── tests/
│                           ├── BinaryDecodingTests.scala
│                           ├── ImportResolutionSuite.scala
│                           ├── JsonConverterSuite.scala
│                           ├── MiscSuite.scala
│                           ├── PreludeSuite.scala
│                           ├── ToStringSuite.scala
│                           └── acceptance/
│                               └── AcceptanceSuites.scala
└── version.sbt

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/ci.yml
================================================
# This file was automatically generated by sbt-github-actions using the
# githubWorkflowGenerate task. You should add and commit this file to
# your git repository. It goes without saying that you shouldn't edit
# this file by hand! Instead, if you wish to make changes, you should
# change your sbt build configuration to revise the workflow description
# to meet your needs, then regenerate this file.

name: Continuous Integration

on:
  pull_request:
    branches: ['**']
  push:
    branches: ['**']

env:
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

jobs:
  build:
    name: Build and Test
    strategy:
      matrix:
        os: [ubuntu-latest]
        scala: [2.12.14, 2.13.6, 3.0.2]
        java: [adopt@1.8]
    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout current branch (full)
        uses: actions/checkout@v2
        with:
          fetch-depth: 0
          submodules: recursive

      - name: Setup Java and Scala
        uses: olafurpg/setup-scala@v13
        with:
          java-version: ${{ matrix.java }}

      - name: Cache sbt
        uses: actions/cache@v2
        with:
          path: |
            ~/.sbt
            ~/.ivy2/cache
            ~/.coursier/cache/v1
            ~/.cache/coursier/v1
            ~/AppData/Local/Coursier/Cache/v1
            ~/Library/Caches/Coursier/v1
          key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }}

      - name: Check that workflows are up to date
        run: sbt ++${{ matrix.scala }} githubWorkflowCheck

      - name: Test
        run: 'sbt ++${{ matrix.scala }} clean javacc coverage scalastyle scalafmtCheckAll scalafmtSbtCheck test slow:test coverageReport'

      - uses: codecov/codecov-action@v1


================================================
FILE: .github/workflows/clean.yml
================================================
# This file was automatically generated by sbt-github-actions using the
# githubWorkflowGenerate task. You should add and commit this file to
# your git repository. It goes without saying that you shouldn't edit
# this file by hand! Instead, if you wish to make changes, you should
# change your sbt build configuration to revise the workflow description
# to meet your needs, then regenerate this file.

name: Clean

on: push

jobs:
  delete-artifacts:
    name: Delete Artifacts
    runs-on: ubuntu-latest
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    steps:
      - name: Delete artifacts
        run: |
          # Customize those three lines with your repository and credentials:
          REPO=${GITHUB_API_URL}/repos/${{ github.repository }}

          # A shortcut to call GitHub API.
          ghapi() { curl --silent --location --user _:$GITHUB_TOKEN "$@"; }

          # A temporary file which receives HTTP response headers.
          TMPFILE=/tmp/tmp.$$

          # An associative array, key: artifact name, value: number of artifacts of that name.
          declare -A ARTCOUNT

          # Process all artifacts on this repository, loop on returned "pages".
          URL=$REPO/actions/artifacts
          while [[ -n "$URL" ]]; do

            # Get current page, get response headers in a temporary file.
            JSON=$(ghapi --dump-header $TMPFILE "$URL")

            # Get URL of next page. Will be empty if we are at the last page.
            URL=$(grep '^Link:' "$TMPFILE" | tr ',' '\n' | grep 'rel="next"' | head -1 | sed -e 's/.*<//' -e 's/>.*//')
            rm -f $TMPFILE

            # Number of artifacts on this page:
            COUNT=$(( $(jq <<<$JSON -r '.artifacts | length') ))

            # Loop on all artifacts on this page.
            for ((i=0; $i < $COUNT; i++)); do

              # Get name of artifact and count instances of this name.
              name=$(jq <<<$JSON -r ".artifacts[$i].name?")
              ARTCOUNT[$name]=$(( $(( ${ARTCOUNT[$name]} )) + 1))

              id=$(jq <<<$JSON -r ".artifacts[$i].id?")
              size=$(( $(jq <<<$JSON -r ".artifacts[$i].size_in_bytes?") ))
              printf "Deleting '%s' #%d, %'d bytes\n" $name ${ARTCOUNT[$name]} $size
              ghapi -X DELETE $REPO/actions/artifacts/$id
            done
          done


================================================
FILE: .gitignore
================================================
*.iml
target/
.idea/
.idea_modules/
.DS_STORE
.cache
.settings
.project
.classpath
tmp/
.bloop/
.metals/
.bsp/
project/metals.sbt
bazel-bin
bazel-dhallj
bazel-out
bazel-testlogs
.vscode/
project/project/


================================================
FILE: .gitmodules
================================================
[submodule "dhall-lang"]
	path = dhall-lang
	url = https://github.com/dhall-lang/dhall-lang.git


================================================
FILE: .scalafmt.conf
================================================
version=3.0.7
align.openParenCallSite = true
align.openParenDefnSite = true
maxColumn = 120
continuationIndent.defnSite = 2
assumeStandardLibraryStripMargin = true
danglingParentheses.preset = true
rewrite.rules = [AvoidInfix, SortImports, RedundantParens, SortModifiers]
docstrings.style = Asterisk
newlines.alwaysBeforeMultilineDef = false


================================================
FILE: LICENSE
================================================
BSD 3-Clause License

Copyright (c) 2020, Travis Brown
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: README.md
================================================
# Dhall for Java

[![Build status](https://img.shields.io/github/workflow/status/travisbrown/dhallj/Continuous%20Integration.svg)](https://github.com/travisbrown/dhallj/actions)
[![Gitter](https://img.shields.io/badge/gitter-join%20chat-green.svg)](https://gitter.im/dhallj/)
[![Maven Central](https://img.shields.io/maven-central/v/org.dhallj/dhall-core.svg)](https://maven-badges.herokuapp.com/maven-central/org.dhallj/dhall-core)

This project is an implementation of the [Dhall][dhall-lang] configuration language for the Java
Virtual Machine.

Our goal for this project is to make it as easy as possible to integrate Dhall
into JVM build systems (see the [dhall-kubernetes] demonstration
[below](#converting-to-other-formats) for a concrete example of why you might want to do this).

The core modules have no external dependencies, are Java 7-compatible, and are fairly minimal:

```bash
$ du -h modules/core/target/dhall-core-0.10.0-M1.jar
168K    modules/core/target/dhall-core-0.10.0-M1.jar

$ du -h modules/parser/target/dhall-parser-0.10.0-M1.jar
108K    modules/parser/target/dhall-parser-0.10.0-M1.jar
```

There are also several [Scala][scala] modules that are published for Scala 2.12,
2.13, and 3.0. While most of the examples in this README are focused on Scala, you
shouldn't need to know or care about Scala to use the core DhallJ modules.

The initial development of this project was supported in part by [Permutive][permutive].

## Table of contents

* [Status](#status)
* [Getting started](#getting-started)
* [Converting to other formats](#converting-to-other-formats)
* [Import resolution](#import-resolution)
* [Command-line interface](#command-line-interface)
* [Other stuff](#other-stuff)
* [Developing](#developing)
* [Community](#community)
* [Copyright and license](#copyright-and-license)

## Status

The current release of this project supports [Dhall 21.0.0][dhall-21-0-0].
We're running the [Dhall acceptance test suites][dhall-tests] for parsing, normalization,
[CBOR][cbor] encoding and decoding, hashing, and type inference, and
currently all tests are passing (with three exceptions; see the [0.10.0-M1 release notes for details](https://github.com/travisbrown/dhallj/releases/tag/v0.10.0-M1)).

There are several known issues:

* The parser [cannot parse deeply nested structures](https://github.com/travisbrown/dhallj/issues/2) (records, etc., although note that indefinitely long lists are fine).
* The type checker is [also not stack-safe](https://github.com/travisbrown/dhallj/issues/3) (this should be fixed soon).
* Import resolution is not provided in the core modules, and is a work in progress.

While we think the project is reasonably well-tested, it's very new, is sure to be full of bugs, and
nothing about the API should be considered stable at the moment. Please use responsibly.

## Getting started

The easiest way to try things out is to add the Scala wrapper module to your build.
If you're using [sbt][sbt] that would look like this:

```scala
libraryDependencies += "org.dhallj" %% "dhall-scala" % "0.10.0-M1"
```

This dependency includes two packages: `org.dhallj.syntax` and `org.dhallj.ast`.

The `syntax` package provides some extension methods, including a `parseExpr`
method for strings (note that this method returns an
`Either[ParsingFailure, Expr]`, which we unwrap here with `Right`):

```scala
scala> import org.dhallj.syntax._
import org.dhallj.syntax._

scala> val Right(expr) = "\\(n: Natural) -> [n + 0, n + 1, 1 + 1]".parseExpr
expr: org.dhallj.core.Expr = λ(n : Natural) → [n + 0, n + 1, 1 + 1]
```

Now that we have a Dhall expression, we can type-check it:

```scala
scala> val Right(exprType) = expr.typeCheck
exprType: org.dhallj.core.Expr = ∀(n : Natural) → List Natural
```

We can "reduce" (or _β-normalize_) it:

```scala
scala> val normalized = expr.normalize
normalized: org.dhallj.core.Expr = λ(n : Natural) → [n, n + 1, 2]
```

We can also _α-normalize_ it, which replaces all named variables with
indexed underscores:

```scala
scala> val alphaNormalized = normalized.alphaNormalize
alphaNormalized: org.dhallj.core.Expr = λ(_ : Natural) → [_, _ + 1, 2]
```

We can encode it as a CBOR byte array:

```scala
scala> alphaNormalized.getEncodedBytes
res0: Array[Byte] = Array(-125, 1, 103, 78, 97, 116, 117, 114, 97, 108, -123, 4, -10, 0, -124, 3, 4, 0, -126, 15, 1, -126, 15, 2)
```

And we can compute its semantic hash:

```scala
scala> alphaNormalized.hash
res1: String = c57cdcdae92638503f954e63c0b3ae8de00a59bc5e05b4dd24e49f42aca90054
```

If we have the official `dhall` CLI installed, we can confirm that this hash is
correct:

```bash
$ dhall hash <<< '\(n: Natural) -> [n + 0, n + 1, 1 + 1]'
sha256:c57cdcdae92638503f954e63c0b3ae8de00a59bc5e05b4dd24e49f42aca90054
```

We can also compare expressions:

```scala
scala> val Right(other) = "\\(n: Natural) -> [n, n + 1, 3]".parseExpr
other: org.dhallj.core.Expr = λ(n : Natural) → [n, n + 1, 3]

scala> normalized == other
res2: Boolean = false

scala> val Some(diff) = normalized.diff(other)
diff: (Option[org.dhallj.core.Expr], Option[org.dhallj.core.Expr]) = (Some(2),Some(3))
```

And apply them to other expressions:

```scala
scala> val Right(arg) = "10".parseExpr
arg: org.dhallj.core.Expr = 10

scala> expr(arg)
res3: org.dhallj.core.Expr = (λ(n : Natural) → [n + 0, n + 1, 1 + 1]) 10

scala> expr(arg).normalize
res4: org.dhallj.core.Expr = [10, 11, 2]
```

We can also resolve expressions containing imports (although at the moment
dhall-scala doesn't support remote imports or caching; please see the
[section on import resolution](#import-resolution) below for details about
how to set up remote import resolution if you need it):

```scala
val Right(enumerate) =
     |   "./dhall-lang/Prelude/Natural/enumerate".parseExpr.flatMap(_.resolve)
enumerate: org.dhallj.core.Expr = let enumerate : Natural → List Natural = ...

scala> enumerate(arg).normalize
res5: org.dhallj.core.Expr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
```

Note that we're working with values of type `Expr`, which comes from dhall-core,
which is a Java module. The `Expr` class includes static methods for creating
`Expr` values:

```scala
scala> import org.dhallj.core.Expr
import org.dhallj.core.Expr

scala> Expr.makeTextLiteral("foo")
res6: org.dhallj.core.Expr = "foo"

scala> Expr.makeEmptyListLiteral(Expr.Constants.BOOL)
res7: org.dhallj.core.Expr = [] : Bool
```

If you're working from Scala, though, you're generally better off using the
constructors included in the `org.dhallj.ast` package, which provide more
type-safety:

```scala
scala> TextLiteral("foo")
res8: org.dhallj.core.Expr = "foo"

scala> NonEmptyListLiteral(BoolLiteral(true), Vector())
res9: org.dhallj.core.Expr = [True]
```

The `ast` package also includes extractors that let you pattern match on
`Expr` values:

```scala
scala> expr match {
     |   case Lambda(name, _, NonEmptyListLiteral(first +: _)) => (name, first)
     | }
res10: (String, org.dhallj.core.Expr) = (n,n + 0)
```

Note that we don't have exhaustivity checking for these extractors, although we
might be able to add that in an eventual Dotty version.

In addition to dhall-scala, there's a (more experimental) dhall-scala-codec
module, which supports encoding and decoding Scala types to and from Dhall expressions.
If you add it to your build, you can write the following:

```scala
scala> import org.dhallj.codec.syntax._
import org.dhallj.codec.syntax._

scala> List(List(1, 2), Nil, List(3, -4)).asExpr
res0: org.dhallj.core.Expr = [[+1, +2], [] : List Integer, [+3, -4]]
```

You can even decode Dhall functions into Scala functions (assuming you have the
appropriate codecs for the input and output types):

```scala
val Right(f) = """

  let enumerate = ./dhall-lang/Prelude/Natural/enumerate

  let map = ./dhall-lang/Prelude/List/map

  in \(n: Natural) ->
    map Natural Integer Natural/toInteger (enumerate n)

""".parseExpr.flatMap(_.resolve)
```
And then:

```scala
scala> val Right(scalaEnumerate) = f.as[BigInt => List[BigInt]]
scalaEnumerate: BigInt => List[BigInt] = org.dhallj.codec.Decoder$$anon$11$$Lambda$15614/0000000050B06E20@94b036

scala> scalaEnumerate(BigInt(3))
res1: List[BigInt] = List(0, 1, 2)
```

Eventually we'll probably support generic derivation for encoding Dhall
expressions to and from algebraic data types in Scala, but we haven't
implemented this yet.

## Converting to other formats

DhallJ currently includes several ways to export Dhall expressions to other formats. The core module
includes very basic support for printing Dhall expressions as JSON:

```scala
scala> import org.dhallj.core.converters.JsonConverter
import org.dhallj.core.converters.JsonConverter

scala> import org.dhallj.parser.DhallParser.parse
import org.dhallj.parser.DhallParser.parse

scala> val expr = parse("(λ(n: Natural) → [n, n + 1, n + 2]) 100")
expr: org.dhallj.core.Expr.Parsed = (λ(n : Natural) → [n, n + 1, n + 2]) 100

scala> JsonConverter.toCompactString(expr.normalize)
res0: String = [100,101,102]
```

This conversion supports the same subset of Dhall expressions as [`dhall-to-json`][dhall-json] (e.g.
it can't produce JSON representation of functions, which means the normalization in the example
above is necessary—if we hadn't normalized the conversion would fail).

There's also a module that provides integration with [Circe][circe], allowing you to convert Dhall
expressions directly to (and from) `io.circe.Json` values without intermediate serialization to
strings:

```scala
scala> import org.dhallj.circe.Converter
import org.dhallj.circe.Converter

scala> import io.circe.syntax._
import io.circe.syntax._

scala> Converter(expr.normalize)
res0: Option[io.circe.Json] =
Some([
  100,
  101,
  102
])

scala> Converter(List(true, false).asJson)
res1: org.dhallj.core.Expr = [True, False]
```

Another module supports converting to any JSON representation for which you have a [Jawn][jawn]
facade. For example, the following build configuration would allow you to export [spray-json]
values:

```scala
libraryDependencies ++= Seq(
  "org.dhallj"    %% "dhall-jawn" % "0.4.0",
  "org.typelevel" %% "jawn-spray" % "1.0.0"
)
```

And then:

```scala
scala> import org.dhallj.jawn.JawnConverter
import org.dhallj.jawn.JawnConverter

scala> import org.typelevel.jawn.support.spray.Parser
import org.typelevel.jawn.support.spray.Parser

scala> val toSpray = new JawnConverter(Parser.facade)
toSpray: org.dhallj.jawn.JawnConverter[spray.json.JsValue] = org.dhallj.jawn.JawnConverter@be3ffe1d

scala> toSpray(expr.normalize)
res0: Option[spray.json.JsValue] = Some([100,101,102])
```

Note that unlike the dhall-circe module, the integration provided by dhall-jawn is only one way
(you can convert Dhall expressions to JSON values, but not the other way around).

We also support YAML export via [SnakeYAML][snake-yaml] (which doesn't require a Scala dependency):

```scala
scala> import org.dhallj.parser.DhallParser.parse
import org.dhallj.parser.DhallParser.parse

scala> import org.dhallj.yaml.YamlConverter
import org.dhallj.yaml.YamlConverter

scala> val expr = parse("{foo = [1, 2, 3], bar = [4, 5]}")
expr: org.dhallj.core.Expr.Parsed = {foo = [1, 2, 3], bar = [4, 5]}

scala> println(YamlConverter.toYamlString(expr))
foo:
- 1
- 2
- 3
bar:
- 4
- 5
```

You can use the YAML exporter with [dhall-kubernetes], for example. Instead of
maintaining a lot of verbose and repetitive and error-prone YAML files, you can
keep your configuration in well-typed Dhall files (like
[this example](https://github.com/dhall-lang/dhall-kubernetes/blob/506d633e382872346927b8cb9884d8b7382e6cab/1.17/examples/deploymentSimple.dhall))
and have your build system export them to YAML:

```scala
import org.dhallj.syntax._, org.dhallj.yaml.YamlConverter

val kubernetesExamplePath = "../dhall-kubernetes/1.17/examples/deploymentSimple.dhall"
val Right(kubernetesExample) = kubernetesExamplePath.parseExpr.flatMap(_.resolve)
```

And then:

```scala
scala> println(YamlConverter.toYamlString(kubernetesExample.normalize))
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      name: nginx
  template:
    metadata:
      name: nginx
    spec:
      containers:
      - image: nginx:1.15.3
        name: nginx
        ports:
        - containerPort: 80
```

It's not currently possible to convert to YAML without the SnakeYAML dependency, although we may support a simplified
version of this in the future (something similar to what we have for JSON in the core module).

## Import resolution

There are currently two modules that implement import resolution (to different degrees).

### dhall-imports

The first is dhall-imports, which is a Scala library built on [cats-effect] that uses [http4s] for
its HTTP client. This module is intended to be a complete implementation of the
[import resolution and caching specification][dhall-imports].

It requires a bit of ceremony to set up:

```scala
import cats.effect.{IO, Resource}
import org.dhallj.core.Expr
import org.dhallj.imports.syntax._
import org.dhallj.parser.DhallParser
import org.http4s.blaze.client.BlazeClientBuilder
import org.http4s.client.Client
import scala.concurrent.ExecutionContext

val client: Resource[IO, Client[IO]] = BlazeClientBuilder[IO](ExecutionContext.global).resource
```

And then if we have some definitions like this:

```scala
val concatSepImport = DhallParser.parse("https://prelude.dhall-lang.org/Text/concatSep")

val parts = DhallParser.parse("""["foo", "bar", "baz"]""")
val delimiter = Expr.makeTextLiteral("-")
```

We can use them with a function from the Dhall Prelude like this:

```scala
scala> val resolved = client.use { implicit c =>
     |   concatSepImport.resolveImports[IO]
     | }
resolved: cats.effect.IO[org.dhallj.core.Expr] = IO(...)

scala> import cats.effect.unsafe.implicits.global
import cats.effect.unsafe.implicits.global

scala> val result = resolved.map { concatSep =>
     |   Expr.makeApplication(concatSep, Array(delimiter, parts)).normalize
     | }
result: cats.effect.IO[org.dhallj.core.Expr] = IO(...)

scala> result.unsafeRunSync()
res0: org.dhallj.core.Expr = "foo-bar-baz"
```

(Note that we could use dhall-scala to avoid the use of `Array` above.)

#### Classpath imports

We support an extension of the spec which allows you to also import expressions
from the classpath using the syntax `let e = classpath:/absolute/path/to/file in e`.
The semantics are subject to change as we get more experience with it but
currently it should generally have the same behaviour as an absolute
path import of a local file (but files on the classpath can import each other
using relative paths). This includes it being protected by the referential
sanity check so that remote imports cannot exfiltrate information
from the classpath.

Also note that classpath imports as location are currently not supported as the spec
requires that an import as Location must return an expression of type
`<Local Text | Remote Text | Environment Text | Missing>`.

### dhall-imports-mini

The other implementation is dhall-imports-mini, which is a Java library that
depends only on the core and parser modules, but that doesn't support
remote imports or caching.

The previous example could be rewritten as follows using dhall-imports-mini
and a local copy of the Prelude:

```scala
import org.dhallj.core.Expr
import org.dhallj.imports.mini.Resolver
import org.dhallj.parser.DhallParser

val concatSep = Resolver.resolve(DhallParser.parse("./dhall-lang/Prelude/Text/concatSep"), false)

val parts = DhallParser.parse("""["foo", "bar", "baz"]""")
val delimiter = Expr.makeTextLiteral("-")
```

And then:

```scala
scala> Expr.makeApplication(concatSep, Array(delimiter, parts)).normalize
res0: org.dhallj.core.Expr = "foo-bar-baz"
```

It's likely that eventually we'll provide a complete pure-Java implementation of import resolution,
but this isn't currently a high priority for us.

## Command-line interface

We include a command-line interface that supports some common operations. It's currently similar to
the official `dhall` and `dhall-to-json` binaries, but with many fewer options.

If [GraalVM Native Image][graal-native-image] is available on your system, you can build the CLI as
a native binary (thanks to [sbt-native-packager]).

```bash
$ sbt cli/graalvm-native-image:packageBin

$ cd cli/target/graalvm-native-image/

$ du -h dhall-cli
8.2M    dhall-cli

$ time ./dhall-cli hash --normalize --alpha <<< "λ(n: Natural) → [n, n + 1]"
sha256:a8d9326812aaabeed29412e7b780dc733b1e633c5556c9ea588e8212d9dc48f3

real    0m0.009s
user    0m0.000s
sys     0m0.009s

$ time ./dhall-cli type <<< "{foo = [1, 2, 3]}"
{foo : List Natural}

real    0m0.003s
user    0m0.000s
sys     0m0.003s

$ time ./dhall-cli json <<< "{foo = [1, 2, 3]}"
{"foo":[1,2,3]}

real    0m0.005s
user    0m0.004s
sys     0m0.001s
```

Even on the JVM it's close to usable, although you can definitely feel the slow startup:

```bash
$ cd ..

$ time java -jar ./cli-assembly-0.4.0-SNAPSHOT.jar hash --normalize --alpha <<< "λ(n: Natural) → [n, n + 1]"
sha256:a8d9326812aaabeed29412e7b780dc733b1e633c5556c9ea588e8212d9dc48f3

real    0m0.104s
user    0m0.106s
sys     0m0.018s
```

There's probably not really any reason you'd want to use `dhall-cli` right now, but I think it's a
pretty neat demonstration of how Graal can make Java (or Scala) a viable language for building
native CLI applications.

## Other stuff

### dhall-testing

The dhall-testing module provides support for property-based testing with [ScalaCheck][scalacheck]
in the form of `Arbitrary` (and `Shrink`) instances:

```scala
scala> import org.dhallj.core.Expr
import org.dhallj.core.Expr

scala> import org.dhallj.testing.instances._
import org.dhallj.testing.instances._

scala> import org.scalacheck.Arbitrary
import org.scalacheck.Arbitrary

scala> Arbitrary.arbitrary[Expr].sample
res0: Option[org.dhallj.core.Expr] = Some(Optional (Optional (List Double)))

scala> Arbitrary.arbitrary[Expr].sample
res1: Option[org.dhallj.core.Expr] = Some(Optional (List <neftfEahtuSq : Double | kg...
```

It includes (fairly basic) support for producing both well-typed and probably-not-well-typed
expressions, and for generating arbitrary values of specified Dhall types:

```scala
scala> import org.dhallj.testing.WellTypedExpr
import org.dhallj.testing.WellTypedExpr

scala> Arbitrary.arbitrary[WellTypedExpr].sample
res2: Option[org.dhallj.testing.WellTypedExpr] = Some(WellTypedExpr(8436008296256993755))

scala> genForType(Expr.Constants.BOOL).flatMap(_.sample)
res3: Option[org.dhallj.core.Expr] = Some(True)

scala> genForType(Expr.Constants.BOOL).flatMap(_.sample)
res4: Option[org.dhallj.core.Expr] = Some(False)

scala> genForType(Expr.makeApplication(Expr.Constants.LIST, Expr.Constants.INTEGER)).flatMap(_.sample)
res5: Option[org.dhallj.core.Expr] = Some([+1522471910085416508, -9223372036854775809, ...
```

This module is currently fairly minimal, and is likely to change substantially in future releases.

### dhall-javagen and dhall-prelude

The dhall-javagen module lets you take a DhallJ representation of a Dhall expression and use it to
generate Java code that will build the DhallJ representation of that expression.

This is mostly a toy, but it allows us for example to distribute a "pre-compiled" jar containing the
Dhall Prelude:

```scala
scala> import java.math.BigInteger
import java.math.BigInteger

scala> import org.dhallj.core.Expr
import org.dhallj.core.Expr

scala> val ten = Expr.makeNaturalLiteral(new BigInteger("10"))
ten: org.dhallj.core.Expr = 10

scala> val Prelude = org.dhallj.prelude.Prelude.instance
Prelude: org.dhallj.core.Expr = ...

scala> val Natural = Expr.makeFieldAccess(Prelude, "Natural")
Natural: org.dhallj.core.Expr = ...

scala> val enumerate = Expr.makeFieldAccess(Natural, "enumerate")
enumerate: org.dhallj.core.Expr = ...

scala> Expr.makeApplication(enumerate, ten).normalize
res0: org.dhallj.core.Expr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
```

Note that the resulting jar (which is available from Maven Central as dhall-prelude) is many times
smaller than either the Prelude source or the Prelude serialized as CBOR.

## Developing

The project includes the currently-supported version of the Dhall language repository as a
submodule, so if you want to run the acceptance test suites, you'll need to clone recursively:

```bash
git clone --recurse-submodules git@github.com:travisbrown/dhallj.git
```

Or if you're like me and always forget to do this, you can initialize the submodule after cloning:

```bash
git submodule update --init
```

This project is built with [sbt][sbt], and you'll need to have sbt [installed][sbt-installation]
on your machine.

We're using the [JavaCC][javacc] parser generator for the parsing module, and we have
[our own sbt plugin][sbt-javacc] for integrating JavaCC into our build. This plugin is open source
and published to Maven Central, so you don't need to do anything to get it, but you will need to run
it manually the first time you build the project (or any time you update the JavaCC grammar):

```
sbt:root> javacc
Java Compiler Compiler Version 7.0.5 (Parser Generator)
File "Provider.java" does not exist.  Will create one.
File "StringProvider.java" does not exist.  Will create one.
File "StreamProvider.java" does not exist.  Will create one.
File "TokenMgrException.java" does not exist.  Will create one.
File "ParseException.java" does not exist.  Will create one.
File "Token.java" does not exist.  Will create one.
File "SimpleCharStream.java" does not exist.  Will create one.
Parser generated with 0 errors and 1 warnings.
[success] Total time: 0 s, completed 12-Apr-2020 08:48:53
```

After this is done, you can run the tests:

```
sbt:root> test
...
[info] Passed: Total 1319, Failed 0, Errors 0, Passed 1314, Skipped 5
[success] Total time: 36 s, completed 12-Apr-2020 08:51:07
```

Note that a few tests require the [dhall-haskell] `dhall` CLI. If you don't have it installed on
your machine, these tests will be skipped.

There are also a few additional slow tests that must be run manually:

```
sbt:root> slow:test
...
[info] Passed: Total 4, Failed 0, Errors 0, Passed 4
[success] Total time: 79 s (01:19), completed 12-Apr-2020 08:52:41
```

## Community

This project supports the [Scala code of conduct][code-of-conduct] and wants all of its channels
(Gitter, GitHub, etc.) to be inclusive environments.

## Copyright and license

All code in this repository is available under the [3-Clause BSD License][bsd-license].

Copyright [Travis Brown][travisbrown] and [Tim Spence][timspence], 2020.

[bsd-license]: https://opensource.org/licenses/BSD-3-Clause
[cats-effect]: https://github.com/typelevel/cats-effect
[cbor]: https://cbor.io/
[circe]: https://github.com/circe/circe
[code-of-conduct]: https://www.scala-lang.org/conduct/
[dhall-21-0-0]: https://github.com/dhall-lang/dhall-lang/pull/1194
[dhall-haskell]: https://github.com/dhall-lang/dhall-haskell
[dhall-imports]: https://github.com/dhall-lang/dhall-lang/blob/master/standard/imports.md
[dhall-json]: https://docs.dhall-lang.org/tutorials/Getting-started_Generate-JSON-or-YAML.html
[dhall-kubernetes]: https://github.com/dhall-lang/dhall-kubernetes
[dhall-tests]: https://github.com/dhall-lang/dhall-lang/tree/master/tests
[dhall-lang]: https://dhall-lang.org/
[discipline]: https://github.com/typelevel/discipline
[graal-native-image]: https://www.graalvm.org/docs/reference-manual/native-image/
[http4s]: https://http4s.org
[javacc]: https://javacc.github.io/javacc/
[jawn]: https://github.com/typelevel/jawn
[permutive]: https://permutive.com
[permutive-medium]: https://medium.com/permutive
[sbt]: https://www.scala-sbt.org
[sbt-installation]: https://www.scala-sbt.org/1.x/docs/Setup.html
[sbt-javacc]: https://github.com/travisbrown/sbt-javacc
[sbt-native-packager]: https://github.com/sbt/sbt-native-packager
[scala]: https://www.scala-lang.org
[scalacheck]: https://www.scalacheck.org/
[snake-yaml]: https://bitbucket.org/asomov/snakeyaml/
[spray-json]: https://github.com/spray/spray-json
[timspence]: https://github.com/TimWSpence
[travisbrown]: https://twitter.com/travisbrown


================================================
FILE: WORKSPACE
================================================
workspace(name = "org_dhallj")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

# Load j2cl repository
http_archive(
    name = "com_google_j2cl",
    strip_prefix = "j2cl-master",
    url = "https://github.com/google/j2cl/archive/master.zip",
)

load("@com_google_j2cl//build_defs:repository.bzl", "load_j2cl_repo_deps")

load_j2cl_repo_deps()

load("@com_google_j2cl//build_defs:rules.bzl", "setup_j2cl_workspace")

setup_j2cl_workspace()


================================================
FILE: benchmarks/src/main/scala/org/dhallj/benchmarks/EncodingBenchmark.scala
================================================
package org.dhallj.benchmarks

import java.util.concurrent.TimeUnit
import org.openjdk.jmh.annotations._
import org.dhallj.core.Expr
import org.dhallj.prelude.Prelude

/**
 * Compare the performance of various ways of folding JSON values.
 *
 * The following command will run the benchmarks with reasonable settings:
 *
 * > sbt "benchmarks/jmh:run -i 10 -wi 10 -f 2 -t 1 org.dhallj.benchmarks.EncodingBenchmark"
 */
@State(Scope.Thread)
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.SECONDS)
class EncodingBenchmark {
  val prelude: Expr = Prelude.instance
  val deep: Expr = (0 to 100000).foldLeft(Expr.makeDoubleLiteral(0)) { case (acc, i) =>
    Expr.makeRecordLiteral(s"a$i", acc)
  }

  @Benchmark
  def encodePreludeToBytes: Array[Byte] = prelude.getEncodedBytes

  @Benchmark
  def encodeDeepToBytes: Array[Byte] = deep.getEncodedBytes
}


================================================
FILE: benchmarks/src/main/scala/org/dhallj/benchmarks/ParsingBenchmark.scala
================================================
package org.dhallj.benchmarks

import java.util.concurrent.TimeUnit
import org.openjdk.jmh.annotations._
import org.dhallj.core.Expr
import org.dhallj.parser.DhallParser
import org.dhallj.prelude.Prelude

/**
 * Compare the performance of various ways of folding JSON values.
 *
 * The following command will run the benchmarks with reasonable settings:
 *
 * > sbt "benchmarks/jmh:run -i 10 -wi 10 -f 2 -t 1 org.dhallj.benchmarks.ParsingBenchmark"
 */
@State(Scope.Thread)
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.SECONDS)
class ParsingBenchmark {
  val preludeAsString = Prelude.instance.toString

  @Benchmark
  def parsePrelude: Expr = DhallParser.parse(preludeAsString)
}


================================================
FILE: build.sbt
================================================
import ReleaseTransformations._

ThisBuild / organization := "org.dhallj"
ThisBuild / crossScalaVersions := List("2.12.14", "2.13.6", "3.0.2")
ThisBuild / scalaVersion := crossScalaVersions.value.filter(_.startsWith("2")).last

ThisBuild / githubWorkflowJavaVersions := Seq("adopt@1.8")
ThisBuild / githubWorkflowPublishTargetBranches := Nil
ThisBuild / githubWorkflowJobSetup := {
  (ThisBuild / githubWorkflowJobSetup).value.toList.map {
    case step @ WorkflowStep.Use(UseRef.Public("actions", "checkout", "v2"), _, _, _, _, _) =>
      step.copy(params = step.params.updated("submodules", "recursive"))
    case other => other
  }
}
ThisBuild / githubWorkflowBuild := Seq(
  WorkflowStep.Sbt(
    List(
      "clean",
      "javacc",
      "coverage",
      "scalastyle",
      "scalafmtCheckAll",
      "scalafmtSbtCheck",
      "test",
      "slow:test",
      "coverageReport"
    ),
    id = None,
    name = Some("Test")
  ),
  WorkflowStep.Use(
    UseRef.Public(
      "codecov",
      "codecov-action",
      "v1"
    )
  )
)

val previousVersion = "0.9.0-M1"
val catsVersion = "2.6.1"
val circeVersion = "0.14.1"
val jawnVersion = "1.2.0"
val munitVersion = "0.7.29"
val scalaCheckVersion = "1.15.4"
val snakeYamlVersion = "1.29"
val http4sVersion = "0.23.6"

val testDependencies = Seq(
  "co.nstant.in" % "cbor" % "0.9",
  "org.scalacheck" %% "scalacheck" % scalaCheckVersion,
  "org.scalameta" %% "munit" % munitVersion,
  "org.scalameta" %% "munit-scalacheck" % munitVersion
)

val http4sDependencies = Seq(
  "org.typelevel" %% "cats-core" % catsVersion,
  "org.typelevel" %% "cats-effect" % "3.2.9",
  "org.http4s" %% "http4s-client" % http4sVersion
)

val http4sBlazeClient =
  "org.http4s" %% "http4s-blaze-client" % http4sVersion

val compilerOptions = Seq(
  "-deprecation",
  "-encoding",
  "UTF-8",
  "-feature",
  "-language:existentials",
  "-language:higherKinds",
  "-unchecked",
  "-Ywarn-dead-code",
  "-Ywarn-numeric-widen",
  "-Xfuture",
  "-Ywarn-unused-import"
)

def priorTo2_13(scalaVersion: String): Boolean =
  CrossVersion.partialVersion(scalaVersion) match {
    case Some((2, minor)) if minor < 13 => true
    case _                              => false
  }

val baseSettings = Seq(
  libraryDependencies ++= testDependencies.map(_ % Test),
  testFrameworks += new TestFramework("munit.Framework"),
  coverageEnabled := (if (scalaVersion.value.startsWith("3")) false else coverageEnabled.value)
)

val javaSettings = Seq(
  autoScalaLibrary := false,
  crossPaths := false,
  Compile / javacOptions ++= Seq("-source", "1.7"),
  Compile / compile / javacOptions ++= Seq("-target", "1.7"),
  mimaPreviousArtifacts := Set("org.dhallj" % moduleName.value % previousVersion)
)

val scalaSettings = Seq(
  mimaPreviousArtifacts := Set("org.dhallj" %% moduleName.value % previousVersion),
  scalacOptions ++= {
    if (priorTo2_13(scalaVersion.value)) compilerOptions
    else
      compilerOptions.flatMap {
        case "-Ywarn-unused-import" => Seq("-Ywarn-unused:imports")
        case "-Xfuture"             => Nil
        case other                  => Seq(other)
      }
  },
  Compile / compile / scalacOptions ~= {
    _.filterNot(Set("-Ywarn-unused-import", "-Ywarn-unused:imports"))
  }
)

val root = project
  .in(file("."))
  .enablePlugins(ScalaUnidocPlugin)
  .settings(baseSettings ++ publishSettings)
  .settings(
    publish / skip := true,
    mimaPreviousArtifacts := Set.empty,
    console / initialCommands := "import org.dhallj.parser.DhallParser.parse",
    releaseProcess := Seq[ReleaseStep](
      checkSnapshotDependencies,
      inquireVersions,
      runClean,
      releaseStepCommand("javacc"),
      runTest,
      setReleaseVersion,
      commitReleaseVersion,
      tagRelease,
      publishArtifacts,
      setNextVersion,
      commitNextVersion
    )
  )
  .aggregate(
    core,
    parser,
    javagen,
    prelude,
    cli,
    ast,
    scala,
    codec,
    circe,
    jawn,
    yaml,
    cats,
    imports,
    importsMini,
    testing,
    tests,
    benchmarks
  )
  .dependsOn(importsMini, scala, javagen, prelude, yaml)

lazy val core = project
  .in(file("modules/core"))
  .settings(baseSettings ++ javaSettings ++ publishSettings)
  .settings(
    moduleName := "dhall-core",
    name := "dhall-core",
    description := "DhallJ core"
  )

lazy val parser = project
  .in(file("modules/parser"))
  .settings(baseSettings ++ javaSettings ++ publishSettings)
  .settings(
    moduleName := "dhall-parser",
    name := "dhall-parser",
    description := "DhallJ parser",
    // Temporarily necessary because JavaCC produces invalid Javadocs.
    Compile / javacOptions ++= Seq("-Xdoclint:none")
  )
  .enablePlugins(JavaCCPlugin)
  .dependsOn(core)

lazy val prelude = project
  .in(file("modules/prelude"))
  .settings(baseSettings ++ javaSettings ++ publishSettings)
  .settings(
    moduleName := "dhall-prelude",
    name := "dhall-prelude",
    description := "DhallJ Prelude"
  )
  .dependsOn(core)

lazy val cli = project
  .in(file("cli"))
  .settings(baseSettings ++ javaSettings)
  .settings(
    publish / skip := true,
    mimaPreviousArtifacts := Set.empty,
    GraalVMNativeImage / name := "dhall-cli"
  )
  .enablePlugins(GraalVMNativeImagePlugin)
  .dependsOn(parser, importsMini)

lazy val circe = project
  .in(file("modules/circe"))
  .settings(baseSettings ++ scalaSettings ++ publishSettings)
  .settings(
    moduleName := "dhall-circe",
    name := "dhall-circe",
    description := "DhallJ Circe integration",
    libraryDependencies ++= Seq(
      "io.circe" %% "circe-core" % circeVersion,
      "io.circe" %% "circe-jawn" % circeVersion % Test,
      "io.circe" %% "circe-testing" % circeVersion % Test
    )
  )
  .dependsOn(core, scala % Test)

lazy val jawn = project
  .in(file("modules/jawn"))
  .settings(baseSettings ++ scalaSettings ++ publishSettings)
  .settings(
    moduleName := "dhall-jawn",
    name := "dhall-jawn",
    description := "DhallJ Jawn integration",
    libraryDependencies ++= Seq(
      "io.circe" %% "circe-jawn" % circeVersion % Test,
      "org.typelevel" %% "jawn-parser" % jawnVersion
    )
  )
  .dependsOn(core, scala % Test)

lazy val yaml = project
  .in(file("modules/yaml"))
  .settings(baseSettings ++ javaSettings ++ publishSettings)
  .settings(
    moduleName := "dhall-yaml",
    name := "dhall-yaml",
    description := "DhallJ YAML export",
    libraryDependencies += "org.yaml" % "snakeyaml" % snakeYamlVersion
  )
  .dependsOn(core, scala % Test)

lazy val ast = project
  .in(file("modules/ast"))
  .settings(baseSettings ++ scalaSettings ++ publishSettings)
  .settings(moduleName := "dhall-ast", name := "dhall-ast", description := "DhallJ Scala AST")
  .dependsOn(importsMini)

lazy val scala = project
  .in(file("modules/scala"))
  .settings(baseSettings ++ scalaSettings ++ publishSettings)
  .settings(moduleName := "dhall-scala", name := "dhall-scala", description := "DhallJ Scala wrapper")
  .dependsOn(ast, parser, importsMini)

lazy val codec = project
  .in(file("modules/scala-codec"))
  .settings(baseSettings ++ scalaSettings ++ publishSettings)
  .settings(
    moduleName := "dhall-scala-codec",
    name := "dhall-scala-codec",
    description := "DhallJ Scala encoding and decoding",
    libraryDependencies += "org.typelevel" %% "cats-core" % catsVersion
  )
  .dependsOn(scala)

lazy val testing = project
  .in(file("modules/testing"))
  .settings(baseSettings ++ scalaSettings ++ publishSettings)
  .settings(
    moduleName := "dhall-testing",
    name := "dhall-testing",
    description := "DhallJ ScalaCheck instances",
    libraryDependencies += "org.scalacheck" %% "scalacheck" % scalaCheckVersion
  )
  .dependsOn(ast)

lazy val javagen = project
  .in(file("modules/javagen"))
  .settings(baseSettings ++ scalaSettings ++ publishSettings)
  .settings(
    moduleName := "dhall-javagen",
    name := "dhall-javagen",
    description := "DhallJ Java code generation"
  )
  .dependsOn(core)

lazy val cats = project
  .in(file("modules/cats"))
  .settings(baseSettings ++ scalaSettings ++ publishSettings)
  .settings(moduleName := "dhall-cats", name := "dhall-cats", description := "DhallJ Cats integration")
  .settings(
    libraryDependencies += "org.typelevel" %% "cats-core" % catsVersion
  )
  .dependsOn(core, testing % Test)

lazy val imports = project
  .in(file("modules/imports"))
  .settings(baseSettings ++ scalaSettings ++ publishSettings)
  .settings(moduleName := "dhall-imports", name := "dhall-imports", description := "DhallJ import resolution")
  .settings(
    libraryDependencies ++= http4sDependencies :+ (http4sBlazeClient % Test)
  )
  .dependsOn(parser, cats)

lazy val importsMini = project
  .in(file("modules/imports-mini"))
  .settings(baseSettings ++ javaSettings ++ publishSettings)
  .settings(
    moduleName := "dhall-imports-mini",
    name := "dhall-imports-mini",
    description := "DhallJ import resolution for Java"
  )
  .dependsOn(parser, core)

lazy val Slow = config("slow").extend(Test)

lazy val tests = project
  .in(file("tests"))
  .configs(Slow)
  .settings(baseSettings ++ scalaSettings)
  .settings(
    libraryDependencies ++= testDependencies,
    libraryDependencies ++= http4sDependencies :+ http4sBlazeClient,
    publish / skip := true,
    mimaPreviousArtifacts := Set.empty,
    Test / fork := true,
    Test / baseDirectory := (ThisBuild / baseDirectory).value,
    Test / testOptions += Tests.Argument("--exclude-tags=Slow"),
    Test / unmanagedResourceDirectories += (ThisBuild / baseDirectory).value / "dhall-lang",
    inConfig(Slow)(Defaults.testTasks),
    Slow / testOptions -= Tests.Argument("--exclude-tags=Slow"),
    Slow / testOptions += Tests.Argument("--include-tags=Slow")
  )
  .dependsOn(scala, imports, importsMini, testing)

lazy val benchmarks = project
  .in(file("benchmarks"))
  .settings(baseSettings ++ scalaSettings)
  .settings(
    publish / skip := true,
    mimaPreviousArtifacts := Set.empty
  )
  .enablePlugins(JmhPlugin)
  .dependsOn(core, parser, prelude)

lazy val publishSettings = Seq(
  releaseCrossBuild := true,
  releasePublishArtifactsAction := PgpKeys.publishSigned.value,
  releaseVcsSign := true,
  homepage := Some(url("https://github.com/travisbrown/dhallj")),
  licenses := Seq("BSD 3-Clause" -> url("http://opensource.org/licenses/BSD-3-Clause")),
  publishMavenStyle := true,
  Test / publishArtifact := false,
  pomIncludeRepository := { _ => false },
  publishTo := {
    val nexus = "https://oss.sonatype.org/"
    if (isSnapshot.value)
      Some("snapshots".at(nexus + "content/repositories/snapshots"))
    else
      Some("releases".at(nexus + "service/local/staging/deploy/maven2"))
  },
  scmInfo := Some(
    ScmInfo(
      url("https://github.com/travisbrown/dhallj"),
      "scm:git:git@github.com:travisbrown/dhallj.git"
    )
  ),
  pomExtra := (
    <developers>
      <developer>
        <id>travisbrown</id>
        <name>Travis Brown</name>
        <email>travisrobertbrown@gmail.com</email>
        <url>https://twitter.com/travisbrown</url>
      </developer>
      <developer>
        <id>TimWSpence</id>
        <name>Tim Spence</name>
        <url>https://github.com/TimWSpence</url>
      </developer>
    </developers>
  )
)


================================================
FILE: cli/src/main/java/org/dhallj/cli/Dhall.java
================================================
package org.dhallj.cli;

import java.io.IOException;
import org.dhallj.core.Expr;
import org.dhallj.core.converters.JsonConverter;
import org.dhallj.imports.mini.Resolver;
import org.dhallj.parser.DhallParser;

public class Dhall {
  public static void main(String[] args) throws IOException {
    boolean resolveImports = false;
    boolean typeCheck = false;
    boolean normalize = false;
    boolean alphaNormalize = false;

    for (int i = 1; i < args.length; i++) {
      if (args[i].equals("--resolve")) {
        resolveImports = true;
      } else if (args[i].equals("--type-check")) {
        typeCheck = true;
      } else if (args[i].equals("--normalize")) {
        normalize = true;
      } else if (args[i].equals("--alpha")) {
        alphaNormalize = true;
      }
    }

    Expr expr = DhallParser.parse(System.in);
    Expr type = null;

    if (resolveImports) {
      expr = Resolver.resolve(expr, false);
    }

    if (normalize) {
      expr = expr.normalize();
    }

    if (alphaNormalize) {
      expr = expr.alphaNormalize();
    }

    if (typeCheck) {
      type = Expr.Util.typeCheck(expr);
    }

    if (args.length == 0 || args[0].startsWith("--")) {
      System.out.println(expr);
    } else if (args[0].equals("hash")) {
      System.out.printf("sha256:%s\n", expr.hash());
    } else if (args[0].equals("type")) {
      if (!typeCheck) {
        type = Expr.Util.typeCheck(expr);
      }
      System.out.println(type);
    } else if (args[0].equals("json")) {
      System.out.println(JsonConverter.toCompactString(expr));
    }
  }
}


================================================
FILE: javascript/BUILD
================================================
load("@com_google_j2cl//build_defs:rules.bzl", "j2cl_application")

j2cl_application(
    name = "dhall",
    closure_defines = {"jre.classMetadata": "'STRIPPED'"},
    entry_points = ["dhall.js"],
    jre_checks_check_level = "MINIMAL",
    deps = ["//javascript/api:dhall_js"],
)


================================================
FILE: javascript/api/BUILD
================================================
load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library")
load("@com_google_j2cl//build_defs:rules.bzl", "j2cl_library")

package(
    default_visibility = ["//visibility:public"],
)

j2cl_library(
    name = "dhall_js_java",
    srcs = ["DhallJs.java"],
    deps = [
        "//modules/core",
        "//modules/parser",
    ],
)

closure_js_library(
    name = "dhall_js",
    srcs = ["dhall.js"],
    deps = [":dhall_js_java"],
)


================================================
FILE: javascript/api/DhallJs.java
================================================
package org.dhallj.js;

import org.dhallj.core.Expr;
import org.dhallj.parser.DhallParser;
import jsinterop.annotations.JsType;

@JsType
public class DhallJs {
  public static String parse(String input) {
    return DhallParser.parse(input).toString();
  }

  public static String normalize(String input) {
    return DhallParser.parse(input).normalize().toString();
  }

  public static String typeCheck(String input) {
    return Expr.Util.typeCheck(DhallParser.parse(input)).toString();
  }
}

================================================
FILE: javascript/api/dhall.js
================================================
goog.module('dhall.js');

var DhallJs = goog.require('org.dhallj.js.DhallJs');

/**
 * @param {string} input
 * @return {null|string}
 */
function parse(input) {
  return DhallJs.parse(input);
}

/**
 * @param {string} input
 * @return {null|string}
 */
function typeCheck(input) {
  return DhallJs.typeCheck(input);
}

/**
 * @param {string} input
 * @return {null|string}
 */
function normalize(input) {
  return DhallJs.normalize(input);
}

// Otherwise we seem to lose stuff with some configurations?
parse("1");
typeCheck("1");
normalize("1");

goog.exportSymbol("parse", parse);
goog.exportSymbol("typeCheck", typeCheck);
goog.exportSymbol("normalize", normalize);

================================================
FILE: javascript/jre/BUILD
================================================
load("@com_google_j2cl//build_defs:rules.bzl", "j2cl_library")

package(
    default_visibility = ["//visibility:public"],
)

j2cl_library(
    name = "java_io",
    srcs = [
        "BufferedReader.java",
        "InputStreamReader.java",
    ],
)

j2cl_library(
    name = "java_net",
    srcs = [
        "URI.java",
        "URISyntaxException.java",
    ],
)

j2cl_library(
    name = "java_nio_file",
    srcs = [
        "InvalidPathException.java",
        "Path.java",
        "Paths.java",
    ],
)


================================================
FILE: javascript/jre/BufferedReader.java
================================================
package java.io;

public class BufferedReader extends Reader {
  public BufferedReader(InputStreamReader stream) {}

  public int read(char[] cbuf, int off, int len) {
    return -1;
  }

  public void close() {}
}


================================================
FILE: javascript/jre/InputStreamReader.java
================================================
package java.io;

public class InputStreamReader extends Reader {
  public InputStreamReader(InputStream in) {}
  public InputStreamReader(InputStream in, String charsetName) {}
  public int read(char[] cbuf, int off, int len) {
    return -1;
  }

  public void close() {}
}


================================================
FILE: javascript/jre/InvalidPathException.java
================================================
package java.nio.file;

public class InvalidPathException extends IllegalArgumentException {}


================================================
FILE: javascript/jre/Path.java
================================================
package java.nio.file;

import java.util.Iterator;

public class Path {
  private final String input;

  public Path(String input) {
    this.input = input;
  }

  public final boolean isAbsolute() {
    return this.input.charAt(0) == '/';
  }

  public final Iterator<Path> iterator() {
    return null;
  }

  public final int getNameCount() {
    return 0;
  }

  public Path resolve(String other) {
    return this;
  }
}


================================================
FILE: javascript/jre/Paths.java
================================================
package java.nio.file;

public class Paths {
  /**
   * @throws  InvalidPathException
   *          if the path string cannot be converted to a {@code Path}
   */
  public static final Path get(String input) {
    return new Path(input);
  }
}


================================================
FILE: javascript/jre/URI.java
================================================
package java.net;

public class URI {
  private final String input;

  public URI(String input) throws URISyntaxException {
    this.input = input;
  }

  public final String getScheme() {
    return "";
  }

  public final String getAuthority() {
    return "";
  }

  public final String getPath() {
    return "";
  }

  public final String getQuery() {
    return "";
  }
}


================================================
FILE: javascript/jre/URISyntaxException.java
================================================
package java.net;

public class URISyntaxException extends Throwable {}


================================================
FILE: modules/ast/src/main/scala/org/dhallj/ast/package.scala
================================================
package org.dhallj.ast

import java.net.URI
import java.nio.file.Path
import java.lang.{Iterable => JIterable}
import java.math.BigInteger
import java.util.AbstractMap.SimpleImmutableEntry
import java.util.{Map => JMap}
import org.dhallj.core.{Expr, ExternalVisitor, Operator}
import scala.collection.JavaConverters._

abstract private class OptionVisitor[A] extends ExternalVisitor.Constant[Option[A]](None)

abstract private[ast] class Constructor[A] {
  type Result = A

  protected[this] def extractor: ExternalVisitor[Option[A]]

  final def unapply(expr: Expr): Option[A] = expr.accept(extractor)

  protected[this] def tupleToEntry[K, V](tuple: (K, V)): JMap.Entry[K, V] = new SimpleImmutableEntry(tuple._1, tuple._2)
  protected[this] def optionTupleToEntry[K, V >: Null](tuple: (K, Option[V])): JMap.Entry[K, V] =
    new SimpleImmutableEntry(tuple._1, tuple._2.orNull)

  protected[this] def entryToTuple[K, V](entry: JMap.Entry[K, V]): (K, V) = (entry.getKey, entry.getValue)
  protected[this] def entryToOptionTuple[K, V](entry: JMap.Entry[K, V]): (K, Option[V]) =
    (entry.getKey, Option(entry.getValue))
}

object NaturalLiteral extends Constructor[BigInt] {
  def apply(value: BigInt): Option[Expr] =
    if (value >= 0) Some(Expr.makeNaturalLiteral(value.underlying)) else None
  def apply(value: Long): Option[Expr] =
    if (value >= 0) Some(Expr.makeNaturalLiteral(BigInteger.valueOf(value))) else None

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onNatural(value: BigInteger): Option[BigInt] = Some(new BigInt(value))
    }
}

object IntegerLiteral extends Constructor[BigInt] {
  def apply(value: BigInt): Expr = Expr.makeIntegerLiteral(value.underlying)
  def apply(value: Long): Expr = Expr.makeNaturalLiteral(BigInteger.valueOf(value))

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onInteger(value: BigInteger): Option[BigInt] = Some(new BigInt(value))
    }
}

object DoubleLiteral extends Constructor[Double] {
  def apply(value: Double): Expr = Expr.makeDoubleLiteral(value)

  protected[this] val extractor: ExternalVisitor[Option[Result]] = new OptionVisitor[Result] {
    override def onDouble(value: Double): Option[Double] = Some(value)
  }
}

object BoolLiteral extends Constructor[Boolean] {
  def apply(value: Boolean): Expr = if (value) Expr.Constants.TRUE else Expr.Constants.FALSE

  protected[this] val extractor: ExternalVisitor[Option[Result]] = new OptionVisitor[Result] {
    override def onBuiltIn(name: String): Option[Boolean] = name match {
      case "True"  => Some(true)
      case "False" => Some(false)
      case _       => None
    }
  }
}

object Identifier extends Constructor[(String, Option[Long])] {
  def apply(name: String, index: Long): Expr = Expr.makeIdentifier(name, index)
  def apply(name: String): Expr = apply(name, 0)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onIdentifier(name: String, index: Long): Option[Result] =
        Some((name, if (index == 0) None else Some(index)))
    }
}

object Lambda extends Constructor[(String, Expr, Expr)] {
  def apply(name: String, tpe: Expr, result: Expr): Expr = Expr.makeLambda(name, tpe, result)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onLambda(name: String, tpe: Expr, result: Expr): Option[Result] =
        Some((name, tpe, result))
    }
}

object Pi extends Constructor[(Option[String], Expr, Expr)] {
  def apply(name: String, tpe: Expr, result: Expr): Expr = Expr.makePi(name, tpe, result)
  def apply(tpe: Expr, result: Expr): Expr = Expr.makePi("_", tpe, result)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onPi(name: String, tpe: Expr, result: Expr): Option[Result] =
        Some((if (name == "_") None else Some(name), tpe, result))
    }
}

object Let extends Constructor[(String, Option[Expr], Expr, Expr)] {
  def apply(name: String, tpe: Expr, value: Expr, body: Expr): Expr = Expr.makeLet(name, tpe, value, body)
  def apply(name: String, value: Expr, body: Expr): Expr = Expr.makeLet(name, null, value, body)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onLet(name: String, tpe: Expr, value: Expr, body: Expr): Option[Result] =
        Some(name, Option(tpe), value, body)
    }
}

object TextLiteral extends Constructor[(String, Vector[(Expr, String)])] {
  def apply(value: String): Expr = Expr.makeTextLiteral(value)
  def apply(first: String, rest: Vector[(Expr, String)]): Expr = {
    val parts = first +: rest.map(_._2).toArray
    val interpolated = rest.map(_._1).toArray

    Expr.makeTextLiteral(parts, interpolated)
  }

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onText(parts: Array[String], interpolated: JIterable[Expr]): Option[Result] =
        Some((parts(0), interpolated.asScala.zip(parts.tail).toVector))
    }
}

object NonEmptyListLiteral extends Constructor[Vector[Expr]] {
  def apply(head: Expr, tail: Vector[Expr]): Expr = Expr.makeNonEmptyListLiteral((head +: tail).asJava)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onNonEmptyList(values: JIterable[Expr], size: Int): Option[Vector[Expr]] =
        Some(values.asScala.toVector)
    }
}

object EmptyListLiteral extends Constructor[Expr] {
  def apply(tpe: Expr): Expr = Expr.makeEmptyListLiteral(tpe)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onEmptyList(tpe: Expr): Option[Expr] = Some(tpe)
    }
}

object ListLiteral extends Constructor[Vector[Expr]] {
  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onNonEmptyList(values: JIterable[Expr], size: Int): Option[Vector[Expr]] =
        Some(values.asScala.toVector)
      override def onEmptyList(tpe: Expr): Option[Vector[Expr]] = Some(Vector.empty)
    }
}

object RecordLiteral extends Constructor[Map[String, Expr]] {
  def apply(fields: Map[String, Expr]): Expr = Expr.makeRecordLiteral(fields.toSeq.map(tupleToEntry).asJava)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onRecord(fields: JIterable[JMap.Entry[String, Expr]], size: Int): Option[Map[String, Expr]] =
        Some(fields.asScala.map(entryToTuple).toMap)
    }
}

object RecordType extends Constructor[Map[String, Expr]] {
  def apply(fields: Map[String, Expr]): Expr = Expr.makeRecordType(fields.toSeq.map(tupleToEntry).asJava)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onRecordType(fields: JIterable[JMap.Entry[String, Expr]], size: Int): Option[Map[String, Expr]] =
        Some(fields.asScala.map(entryToTuple).toMap)
    }
}

object UnionType extends Constructor[Map[String, Option[Expr]]] {
  def apply(fields: Map[String, Option[Expr]]): Expr = Expr.makeUnionType(fields.toSeq.map(optionTupleToEntry).asJava)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onUnionType(fields: JIterable[JMap.Entry[String, Expr]],
                               size: Int
      ): Option[Map[String, Option[Expr]]] =
        Some(fields.asScala.map(entryToOptionTuple).toMap)
    }
}

object FieldAccess extends Constructor[(Expr, String)] {
  def apply(base: Expr, fieldName: String): Expr = Expr.makeFieldAccess(base, fieldName)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onFieldAccess(base: Expr, fieldName: String): Option[Result] = Some((base, fieldName))
    }
}

object Projection extends Constructor[(Expr, Vector[String])] {
  def apply(base: Expr, fieldNames: Vector[String]): Expr = Expr.makeProjection(base, fieldNames.toArray)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onProjection(base: Expr, fieldNames: Array[String]): Option[Result] =
        Some(base, fieldNames.toVector)
    }
}

object ProjectionByType extends Constructor[(Expr, Expr)] {
  def apply(base: Expr, tpe: Expr): Expr = Expr.makeProjectionByType(base, tpe)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onProjectionByType(base: Expr, tpe: Expr): Option[Result] =
        Some(base, tpe)
    }
}

object Application extends Constructor[(Expr, Expr)] {
  def apply(base: Expr, arg: Expr): Expr = Expr.makeApplication(base, arg)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onApplication(base: Expr, arg: Expr): Option[Result] =
        Some((base, arg))
    }
}

object OperatorApplication extends Constructor[(Operator, Expr, Expr)] {
  def apply(operator: Operator, lhs: Expr, rhs: Expr): Expr = Expr.makeOperatorApplication(operator, lhs, rhs)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onOperatorApplication(operator: Operator, lhs: Expr, rhs: Expr): Option[Result] =
        Some((operator, lhs, rhs))
    }
}

object If extends Constructor[(Expr, Expr, Expr)] {
  def apply(cond: Expr, thenValue: Expr, elseValue: Expr): Expr = Expr.makeIf(cond, thenValue, elseValue)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onIf(cond: Expr, thenValue: Expr, elseValue: Expr): Option[Result] =
        Some((cond, thenValue, elseValue))
    }
}

object Annotated extends Constructor[(Expr, Expr)] {
  def apply(base: Expr, tpe: Expr): Expr = Expr.makeAnnotated(base, tpe)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onAnnotated(base: Expr, tpe: Expr): Option[Result] =
        Some((base, tpe))
    }
}

object Assert extends Constructor[Expr] {
  def apply(base: Expr): Expr = Expr.makeAssert(base)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onAssert(base: Expr): Option[Expr] =
        Some(base)
    }
}

object Merge extends Constructor[(Expr, Expr, Option[Expr])] {
  def apply(handlers: Expr, union: Expr, tpe: Expr): Expr = Expr.makeMerge(handlers, union, tpe)
  def apply(handlers: Expr, union: Expr): Expr = Expr.makeMerge(handlers, union, null)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onMerge(handlers: Expr, union: Expr, tpe: Expr): Option[Result] =
        Some(handlers, union, Option(tpe))
    }
}

object ToMap extends Constructor[(Expr, Option[Expr])] {
  def apply(base: Expr, tpe: Expr): Expr = Expr.makeToMap(base, tpe)
  def apply(base: Expr): Expr = Expr.makeToMap(base, null)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onToMap(base: Expr, tpe: Expr): Option[Result] = Some(base, Option(tpe))
    }
}

object MissingImport extends Constructor[(Expr.ImportMode, Option[String])] {

  /**
   * Note that this constructor does not verify that the input is a hex-encoded SHA-256 hash.
   */
  def apply(mode: Expr.ImportMode, hash: String): Expr = Expr.makeMissingImport(mode, Expr.Util.decodeHashBytes(hash))
  def apply(mode: Expr.ImportMode): Expr = Expr.makeMissingImport(mode, null)
  def apply(): Expr = Expr.makeMissingImport(Expr.ImportMode.CODE, null)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onMissingImport(mode: Expr.ImportMode, hash: Array[Byte]): Option[Result] =
        Some((mode, Option(hash).map(Expr.Util.encodeHashBytes)))
    }
}

object EnvImport extends Constructor[(String, Expr.ImportMode, Option[String])] {

  /**
   * Note that this constructor does not verify that the input is a hex-encoded SHA-256 hash.
   */
  def apply(name: String, mode: Expr.ImportMode, hash: String): Expr =
    Expr.makeEnvImport(name, mode, Expr.Util.decodeHashBytes(hash))
  def apply(name: String, mode: Expr.ImportMode): Expr = Expr.makeEnvImport(name, mode, null)
  def apply(name: String): Expr = Expr.makeEnvImport(name, Expr.ImportMode.CODE, null)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onEnvImport(name: String, mode: Expr.ImportMode, hash: Array[Byte]): Option[Result] =
        Some((name, mode, Option(hash).map(Expr.Util.encodeHashBytes)))
    }
}

object LocalImport extends Constructor[(Path, Expr.ImportMode, Option[String])] {

  /**
   * Note that this constructor does not verify that the input is a hex-encoded SHA-256 hash.
   */
  def apply(path: Path, mode: Expr.ImportMode, hash: String): Expr =
    Expr.makeLocalImport(path, mode, Expr.Util.decodeHashBytes(hash))
  def apply(path: Path, mode: Expr.ImportMode): Expr = Expr.makeLocalImport(path, mode, null)
  def apply(path: Path): Expr = Expr.makeLocalImport(path, Expr.ImportMode.CODE, null)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onLocalImport(path: Path, mode: Expr.ImportMode, hash: Array[Byte]): Option[Result] =
        Some((path, mode, Option(hash).map(Expr.Util.encodeHashBytes)))
    }
}

object RemoteImport extends Constructor[(URI, Option[Expr], Expr.ImportMode, Option[String])] {

  /**
   * Note that this constructor does not verify that the input is a hex-encoded SHA-256 hash.
   */
  def apply(url: URI, usingExpr: Expr, mode: Expr.ImportMode, hash: String): Expr =
    Expr.makeRemoteImport(url, usingExpr, mode, Expr.Util.decodeHashBytes(hash))
  def apply(url: URI, usingExpr: Expr, mode: Expr.ImportMode): Expr = Expr.makeRemoteImport(url, usingExpr, mode, null)
  def apply(url: URI, usingExpr: Expr): Expr = Expr.makeRemoteImport(url, usingExpr, Expr.ImportMode.CODE, null)
  def apply(url: URI): Expr = Expr.makeRemoteImport(url, null, Expr.ImportMode.CODE, null)

  protected[this] val extractor: ExternalVisitor[Option[Result]] =
    new OptionVisitor[Result] {
      override def onRemoteImport(url: URI, usingExpr: Expr, mode: Expr.ImportMode, hash: Array[Byte]): Option[Result] =
        Some((url, Option(usingExpr), mode, Option(hash).map(Expr.Util.encodeHashBytes)))
    }
}


================================================
FILE: modules/cats/src/main/scala/org/dhallj/cats/LiftVisitor.scala
================================================
package org.dhallj.cats

import cats.Applicative
import java.math.BigDecimal
import java.math.BigInteger
import java.net.URI
import java.nio.file.Path
import java.util.AbstractMap.SimpleImmutableEntry
import java.util.ArrayList
import java.util.{List => JList, Map => JMap}
import org.dhallj.core.{Expr, Operator, Source, Visitor}

/**
 * Represents a function lifting an `Expr` into an `F[Expr]`.
 *
 * This is a convenience class designed to help with implementations that don't need effects for most cases.
 */
class LiftVisitor[F[_] <: AnyRef](
  private[this] val F: Applicative[F],
  private[this] val sortFields: Boolean = false
) extends Visitor.NoPrepareEvents[F[Expr]] {
  def onNote(base: F[Expr], source: Source): F[Expr] = base
  def onNatural(self: Expr, value: BigInteger): F[Expr] = F.pure(self)
  def onInteger(self: Expr, value: BigInteger): F[Expr] = F.pure(self)
  def onDouble(self: Expr, value: Double): F[Expr] = F.pure(self)
  def onDate(self: Expr, year: Int, month: Int, day: Int): F[Expr] = F.pure(self)
  def onTime(self: Expr, hour: Int, minute: Int, second: Int, fractional: BigDecimal): F[Expr] = F.pure(self)
  def onTimeZone(self: Expr, seconds: Int): F[Expr] = F.pure(self)
  def onBuiltIn(self: Expr, name: String): F[Expr] = F.pure(self)
  def onIdentifier(self: Expr, value: String, index: Long): F[Expr] = F.pure(self)

  def onLambda(name: String, tpe: F[Expr], result: F[Expr]): F[Expr] =
    F.map2(tpe, result)(Expr.makeLambda(name, _, _))

  def onPi(name: String, tpe: F[Expr], result: F[Expr]): F[Expr] =
    F.map2(tpe, result)(Expr.makePi(name, _, _))

  def onLet(bindings: JList[Expr.LetBinding[F[Expr]]], body: F[Expr]): F[Expr] =
    F.map2(sequenceBindings(bindings), body)(Expr.makeLet(_, _))

  def onText(parts: Array[String], interpolated: JList[F[Expr]]): F[Expr] =
    F.map(sequenceValues(interpolated))(Expr.makeTextLiteral(parts, _))

  def onNonEmptyList(values: JList[F[Expr]]): F[Expr] =
    F.map(sequenceValues(values))(Expr.makeNonEmptyListLiteral(_))

  def onEmptyList(tpe: F[Expr]): F[Expr] = F.map(tpe)(Expr.makeEmptyListLiteral(_))

  def onRecord(fields: JList[JMap.Entry[String, F[Expr]]]): F[Expr] =
    F.map(sequenceFields(fields, false, sortFields))(Expr.makeRecordLiteral(_))

  def onRecordType(fields: JList[JMap.Entry[String, F[Expr]]]): F[Expr] =
    F.map(sequenceFields(fields, false, sortFields))(Expr.makeRecordType(_))

  def onUnionType(fields: JList[JMap.Entry[String, F[Expr]]]): F[Expr] =
    F.map(sequenceFields(fields, true, sortFields))(Expr.makeUnionType(_))

  def onFieldAccess(base: F[Expr], fieldName: String): F[Expr] =
    F.map(base)(Expr.makeFieldAccess(_, fieldName))

  def onProjection(base: F[Expr], fieldNames: Array[String]): F[Expr] =
    F.map(base)(Expr.makeProjection(_, fieldNames))

  def onProjectionByType(base: F[Expr], tpe: F[Expr]): F[Expr] =
    F.map2(base, tpe)(Expr.makeProjectionByType(_, _))

  def onApplication(base: F[Expr], args: JList[F[Expr]]): F[Expr] =
    F.map2(base, sequenceValues(args))(Expr.makeApplication(_, _))

  def onOperatorApplication(operator: Operator, lhs: F[Expr], rhs: F[Expr]): F[Expr] =
    F.map2(lhs, rhs)(Expr.makeOperatorApplication(operator, _, _))

  def onIf(predicate: F[Expr], thenValue: F[Expr], elseValue: F[Expr]): F[Expr] =
    F.map3(predicate, thenValue, elseValue)(Expr.makeIf(_, _, _))

  def onAnnotated(base: F[Expr], tpe: F[Expr]): F[Expr] =
    F.map2(base, tpe)(Expr.makeAnnotated(_, _))

  def onAssert(base: F[Expr]): F[Expr] = F.map(base)(Expr.makeAssert(_))

  def onMerge(handlers: F[Expr], union: F[Expr], tpe: F[Expr]): F[Expr] =
    if (tpe.eq(null)) {
      F.map2(handlers, union)(Expr.makeMerge(_, _, null))
    } else {
      F.map3(handlers, union, tpe)(Expr.makeMerge(_, _, _))
    }

  def onToMap(base: F[Expr], tpe: F[Expr]): F[Expr] =
    if (tpe.eq(null)) {
      F.map(base)(Expr.makeToMap(_, null))
    } else {
      F.map2(base, tpe)(Expr.makeToMap(_, _))
    }

  def onWith(base: F[Expr], path: Array[String], value: F[Expr]): F[Expr] =
    F.map2(base, value)(Expr.makeWith(_, path, _))

  def onMissingImport(mode: Expr.ImportMode, hash: Array[Byte]): F[Expr] =
    F.pure(Expr.makeMissingImport(mode, hash))

  def onEnvImport(name: String, mode: Expr.ImportMode, hash: Array[Byte]): F[Expr] =
    F.pure(Expr.makeEnvImport(name, mode, hash))

  def onLocalImport(path: Path, mode: Expr.ImportMode, hash: Array[Byte]): F[Expr] =
    F.pure(Expr.makeLocalImport(path, mode, hash))

  def onClasspathImport(path: Path, mode: Expr.ImportMode, hash: Array[Byte]): F[Expr] =
    F.pure(Expr.makeClasspathImport(path, mode, hash))

  def onRemoteImport(url: URI, headers: F[Expr], mode: Expr.ImportMode, hash: Array[Byte]): F[Expr] =
    if (headers.eq(null)) {
      F.pure(Expr.makeRemoteImport(url, null, mode, hash))
    } else {
      F.map(headers)(Expr.makeRemoteImport(url, _, mode, hash))
    }

  final private[this] def sequenceValues(values: JList[F[Expr]]): F[Array[Expr]] = {
    var result = F.pure(new Array[Expr](values.size))
    var i = 0

    while (i < values.size) {
      val index = i
      result = F.map2(result, values.get(index)) { case (acc, next) =>
        acc(index) = next
        acc
      }
      i += 1
    }

    result
  }

  final private[this] def sequenceFields(
    fields: JList[JMap.Entry[String, F[Expr]]],
    checkNull: Boolean,
    sortFields: Boolean
  ): F[Array[JMap.Entry[String, Expr]]] = {
    var result = F.pure(new Array[JMap.Entry[String, Expr]](fields.size))
    var i = 0

    while (i < fields.size) {
      val index = i
      val entry = fields.get(index)
      val fieldName = entry.getKey
      val value = entry.getValue

      if (checkNull && value.eq(null)) {
        result = F.map(result) { acc =>
          acc(index) = new SimpleImmutableEntry(fieldName, null)
          acc
        }
      } else {
        result = F.map2(result, value) { case (acc, nextValue) =>
          acc(index) = new SimpleImmutableEntry(fieldName, nextValue)
          acc
        }
      }

      i += 1
    }

    if (sortFields) {
      F.map(result)(_.sortBy(_.getKey))
    } else {
      result
    }
  }

  final private[this] def sequenceBindings(
    bindings: JList[Expr.LetBinding[F[Expr]]]
  ): F[JList[Expr.LetBinding[Expr]]] = {
    var result: F[JList[Expr.LetBinding[Expr]]] =
      F.pure(new ArrayList[Expr.LetBinding[Expr]](bindings.size))
    var i = 0

    while (i < bindings.size) {
      val index = i
      val binding = bindings.get(index)

      if (binding.hasType) {
        result = F.map3(result, binding.getType, binding.getValue) { case (acc, nextType, nextValue) =>
          acc.add(new Expr.LetBinding(binding.getName, nextType, nextValue))
          acc
        }
      } else {
        result = F.map2(result, binding.getValue) { case (acc, nextValue) =>
          acc.add(new Expr.LetBinding(binding.getName, null, nextValue))
          acc
        }
      }

      i += 1
    }

    result
  }
}


================================================
FILE: modules/cats/src/test/scala/org/dhallj/cats/LiftVisitorSuite.scala
================================================
package org.dhallj.cats

import cats.Applicative
import cats.instances.option._
import munit.ScalaCheckSuite
import org.dhallj.core.Expr
import org.dhallj.testing.instances._
import org.scalacheck.Prop

class LiftVisitorSuite extends ScalaCheckSuite {
  property("LiftVisitor with no overrides is pure") {
    Prop.forAll { (expr: Expr) =>
      val lift = new LiftVisitor[Option](Applicative[Option])

      expr.accept(lift) == Some(expr)
    }
  }
}


================================================
FILE: modules/circe/src/main/scala/org/dhallj/circe/CirceHandler.scala
================================================
package org.dhallj.circe

import io.circe.Json
import java.math.BigInteger
import java.util.{ArrayDeque, ArrayList, Deque, LinkedHashMap, List}
import org.dhallj.core.converters.JsonHandler
import scala.collection.JavaConverters._

sealed private trait Context {
  def add(key: String): Unit
  def add(value: Json): Unit
  def result: Json
}

final private class RootContext(value: Json) extends Context {
  def add(key: String): Unit = ()
  def add(value: Json): Unit = ()
  def result: Json = value
}

final private class ObjectContext() extends Context {
  private[this] var key: String = null
  private[this] val fields: LinkedHashMap[String, Json] = new LinkedHashMap()

  def add(key: String): Unit = this.key = key
  def add(value: Json): Unit = this.fields.put(this.key, value)
  def result: Json = Json.fromFields(this.fields.entrySet.asScala.map(entry => entry.getKey -> entry.getValue))
}

final private class ArrayContext() extends Context {
  private[this] val values: List[Json] = new ArrayList()

  def add(key: String): Unit = ()
  def add(value: Json): Unit = this.values.add(value)
  def result: Json = Json.fromValues(this.values.asScala)
}

class CirceHandler extends JsonHandler {
  private[this] val stack: Deque[Context] = new ArrayDeque()

  protected[this] def addValue(value: Json): Unit =
    if (stack.isEmpty) {
      stack.push(new RootContext(value))
    } else {
      stack.peek.add(value)
    }

  final def result: Json = stack.pop().result

  final def onNull(): Unit = addValue(Json.Null)
  final def onBoolean(value: Boolean): Unit = addValue(if (value) Json.True else Json.False)
  final def onNumber(value: BigInteger): Unit = addValue(Json.fromBigInt(new BigInt(value)))
  def onDouble(value: Double): Unit = addValue(Json.fromDoubleOrNull(value))
  final def onString(value: String): Unit = addValue(Json.fromString(value))

  final def onArrayStart(): Unit = stack.push(new ArrayContext())

  final def onArrayEnd(): Unit = {
    val current = stack.pop()

    if (stack.isEmpty) {
      stack.push(current)
    } else {
      stack.peek.add(current.result)
    }
  }

  final def onArrayElementGap(): Unit = ()

  final def onObjectStart(): Unit = stack.push(new ObjectContext())

  final def onObjectEnd(): Unit = {
    val current = stack.pop()

    if (stack.isEmpty) {
      stack.push(current)
    } else {
      stack.peek.add(current.result)
    }
  }

  final def onObjectField(name: String): Unit = stack.peek.add(name)

  final def onObjectFieldGap(): Unit = ()
}


================================================
FILE: modules/circe/src/main/scala/org/dhallj/circe/Converter.scala
================================================
package org.dhallj.circe

import io.circe.{Json, JsonNumber, JsonObject}
import java.math.BigInteger
import java.util.AbstractMap.SimpleImmutableEntry
import java.util.Map.Entry
import org.dhallj.core.Expr
import org.dhallj.core.converters.JsonConverter

object Converter {
  def apply(expr: Expr): Option[Json] = {
    val handler = new CirceHandler()
    val wasConverted = expr.accept(new JsonConverter(handler, false))

    if (wasConverted) Some(handler.result) else None
  }

  private[this] val folder: Json.Folder[Expr] = new Json.Folder[Expr] {
    def onBoolean(value: Boolean): Expr = if (value) Expr.Constants.TRUE else Expr.Constants.FALSE
    def onNull: Expr = Expr.makeApplication(Expr.Constants.NONE, Expr.Constants.EMPTY_RECORD_TYPE)
    def onNumber(value: JsonNumber): Expr = {
      val asDouble = value.toDouble

      if (java.lang.Double.compare(asDouble, -0.0) == 0) {
        Expr.makeDoubleLiteral(asDouble)
      } else
        value.toBigInt match {
          case Some(integer) =>
            if (integer.underlying.compareTo(BigInteger.ZERO) > 0) {
              Expr.makeNaturalLiteral(integer.underlying)
            } else {
              Expr.makeIntegerLiteral(integer.underlying)
            }
          case None => Expr.makeDoubleLiteral(asDouble)
        }
    }
    def onString(value: String): Expr = Expr.makeTextLiteral(value)
    def onObject(value: JsonObject): Expr = Expr.makeRecordLiteral(
      value.toMap.map { case (k, v) =>
        new SimpleImmutableEntry(k, v.foldWith(this)): Entry[String, Expr]
      }.toArray
    )
    def onArray(value: Vector[Json]): Expr =
      if (value.isEmpty) {
        Expr.makeEmptyListLiteral(Expr.makeApplication(Expr.Constants.LIST, Expr.Constants.EMPTY_RECORD_TYPE))
      } else {
        Expr.makeNonEmptyListLiteral(value.map(_.foldWith(this)).toArray)
      }
  }

  def apply(json: Json): Expr = json.foldWith(folder)
}


================================================
FILE: modules/circe/src/test/scala/org/dhallj/circe/CirceConverterSuite.scala
================================================
package org.dhallj.circe

import io.circe.Json
import io.circe.syntax._
import io.circe.testing.instances._
import munit.ScalaCheckSuite
import org.dhallj.ast._
import org.dhallj.core.Expr
import org.dhallj.parser.DhallParser
import org.scalacheck.Prop

class CirceConverterSuite extends ScalaCheckSuite {
  property("round-trip Json values through Dhall expressions") {
    Prop.forAll { (value: Json) =>
      val cleanedJson = value.foldWith(JsonCleaner)
      val asDhall = Converter(cleanedJson)
      Converter(asDhall) == Some(cleanedJson)
    }
  }

  property("convert integers") {
    Prop.forAll { (value: BigInt) =>
      val asDhall = IntegerLiteral(value.underlying)
      Converter(asDhall) == Some(value.asJson)
    }
  }

  property("convert lists of integers") {
    Prop.forAll { (values: Vector[BigInt]) =>
      val asDhall = if (values.isEmpty) {
        EmptyListLiteral(Expr.Constants.INTEGER)
      } else {
        val exprs = values.map(value => IntegerLiteral(value.underlying))
        NonEmptyListLiteral(exprs.head, exprs.tail)
      }
      Converter(asDhall) == Some(values.asJson)
    }
  }

  property("convert lists of doubles") {
    Prop.forAll { (values: Vector[Double]) =>
      val asDhall = if (values.isEmpty) {
        EmptyListLiteral(Expr.Constants.DOUBLE)
      } else {
        val exprs = values.map(DoubleLiteral(_))
        NonEmptyListLiteral(exprs.head, exprs.tail)
      }
      Converter(asDhall) == Some(values.asJson)
    }
  }

  property("convert lists of booleans") {
    Prop.forAll { (values: Vector[Boolean]) =>
      val asDhall = if (values.isEmpty) {
        EmptyListLiteral(Expr.Constants.BOOL)
      } else {
        val exprs = values.map(BoolLiteral(_))
        NonEmptyListLiteral(exprs.head, exprs.tail)
      }
      Converter(asDhall) == Some(values.asJson)
    }
  }

  test("convert nested lists") {
    val expr = DhallParser.parse("[[]: List Bool]")

    assert(Converter(expr) == Some(Json.arr(Json.arr())))
  }

  test("convert None") {
    val expr = DhallParser.parse("None Bool")

    assert(Converter(expr) == Some(Json.Null))
  }

  test("convert Some") {
    val expr = DhallParser.parse("""Some "foo"""")

    assert(Converter(expr) == Some(Json.fromString("foo")))
  }

  test("convert records") {
    val expr1 = DhallParser.parse("{foo = [{bar = [1]}, {bar = [1, 2, 3]}]}")
    val Right(json1) = io.circe.jawn.parse("""{"foo": [{"bar": [1]}, {"bar": [1, 2, 3]}]}""")

    assert(Converter(expr1) == Some(json1))
  }

  test("convert unions (nullary constructors)") {
    val expr1 = DhallParser.parse("[((\\(x: Natural) -> <foo: Bool|bar>) 1).bar]").normalize()
    val Right(json1) = io.circe.jawn.parse("""["bar"]""")

    assert(Converter(expr1) == Some(json1))
  }

  test("convert unions") {
    val expr1 = DhallParser.parse("[<foo: Bool|bar>.foo True]").normalize()
    val Right(json1) = io.circe.jawn.parse("""[true]""")

    assert(Converter(expr1) == Some(json1))
  }

  test("fail safely on unconvertible expressions") {
    val expr1 = Lambda("x", Expr.Constants.NATURAL, Identifier("x"))

    assert(Converter(expr1) == None)
  }
}


================================================
FILE: modules/circe/src/test/scala/org/dhallj/circe/JsonCleaner.scala
================================================
package org.dhallj.circe

import io.circe.{Json, JsonNumber, JsonObject}

object JsonCleaner extends Json.Folder[Json] {
  def onBoolean(value: Boolean): Json = Json.fromBoolean(value)
  def onNull: Json = Json.Null
  def onNumber(value: JsonNumber): Json = {
    val asDouble = value.toDouble
    val asJson = Json.fromDoubleOrNull(asDouble)
    if (asJson.asNumber == Some(value)) {
      asJson
    } else {
      Json.fromLong(0)
    }
  }
  def onString(value: String): Json = Json.fromString(value)
  def onObject(value: JsonObject): Json = Json.fromFields(
    value.toIterable.flatMap {
      case ("", v) => None
      case (k, v)  => Some((k, v.foldWith(this)))
    }
  )
  def onArray(values: Vector[Json]): Json = Json.fromValues(values.map(_.foldWith(this)))
}


================================================
FILE: modules/core/BUILD
================================================
load("@com_google_j2cl//build_defs:rules.bzl", "j2cl_library")

package(
    default_visibility = ["//visibility:public"],
)

j2cl_library(
    name = "cbor",
    srcs = glob([
        "src/main/java/org/dhallj/cbor/*.java",
    ]),
)

j2cl_library(
    name = "core",
    srcs = glob([
        "src/main/java/org/dhallj/core/*.java",
        "src/main/java/org/dhallj/core/binary/*.java",
        "src/main/java/org/dhallj/core/normalization/*.java",
        "src/main/java/org/dhallj/core/typechecking/*.java",
    ]),
    deps = [
        ":cbor",
        "//javascript/jre:java_net",
        "//javascript/jre:java_nio_file",
    ],
)


================================================
FILE: modules/core/src/main/java/org/dhallj/cbor/AdditionalInfo.java
================================================
package org.dhallj.cbor;

public enum AdditionalInfo {
  DIRECT(0), // 0-23
  ONE_BYTE(24), // 24
  TWO_BYTES(25), // 25
  FOUR_BYTES(26), // 26
  EIGHT_BYTES(27), // 27
  RESERVED(28), // 28-30
  INDEFINITE(31); // 31

  final int value;

  private AdditionalInfo(int value) {
    this.value = value;
  }

  public static AdditionalInfo fromByte(byte b) {
    switch (b & 31) {
      case 24:
        return ONE_BYTE;
      case 25:
        return TWO_BYTES;
      case 26:
        return FOUR_BYTES;
      case 27:
        return EIGHT_BYTES;
      case 28:
      case 29:
      case 30:
        return RESERVED;
      case 31:
        return INDEFINITE;
      default:
        return DIRECT;
    }
  }
}


================================================
FILE: modules/core/src/main/java/org/dhallj/cbor/CborException.java
================================================
package org.dhallj.cbor;

public class CborException extends RuntimeException {
  CborException(String message) {
    super(message);
  }
}


================================================
FILE: modules/core/src/main/java/org/dhallj/cbor/HalfFloat.java
================================================
package org.dhallj.cbor;

/**
 * Conversions between 16 and 32-bit floating point numbers.
 *
 * @see <a href="https://stackoverflow.com/a/6162687/334519">this Stack Overflow answer</a>
 */
public final class HalfFloat {
  public static final int fromFloat(float asFloat) {
    int bits = Float.floatToIntBits(asFloat);
    int sign = bits >>> 16 & 0x8000; // sign only
    int value = (bits & 0x7fffffff) + 0x1000; // rounded value

    if (value >= 0x47800000) // might be or become NaN/Inf
    { // avoid Inf due to rounding

      if (value < 0x7f800000) // was value but too large
      return sign | 0x7c00; // make it +/-Inf
      return sign
          | 0x7c00
          | // remains +/-Inf or NaN
          (bits & 0x007fffff) >>> 13; // keep NaN (and Inf) bits
    }
    if (value >= 0x38800000) { // remains normalized value
      return sign | value - 0x38000000 >>> 13; // exp - 127 + 15
    }
    if (value < 0x33000000) { // too small for subnormal
      return sign; // becomes +/-0
    }
    value = (bits & 0x7fffffff) >>> 23; // tmp exp for subnormal calc

    return sign
        | ((bits & 0x7fffff | 0x800000) // add subnormal bit
                + (0x800000 >>> value - 102) // round depending on cut off
            >>> 126 - value); // div by 2^(1-(exp-127+15)) and >> 13 | exp=0
  }

  public static final float toFloat(int bits) {
    int mant = bits & 0x03ff; // 10 bits mantissa
    int exp = bits & 0x7c00; // 5 bits exponent
    if (exp == 0x7c00) { // NaN/Inf
      exp = 0x3fc00; // -> NaN/Inf
    } else if (exp != 0) { // normalized value
      exp += 0x1c000; // exp - 15 + 127
    } else if (mant != 0) { // && exp==0 -> subnormal
      exp = 0x1c400; // make it normal
      do {
        mant <<= 1; // mantissa * 2
        exp -= 0x400; // decrease exp by 1
      } while ((mant & 0x400) == 0); // while not normal
      mant &= 0x3ff; // discard subnormal bit
    } // else +/-0 -> +/-0
    return Float.intBitsToFloat( // combine all parts
        (bits & 0x8000) << 16 // sign  << ( 31 - 15 )
            | (exp | mant) << 13); // value << ( 23 - 10 )
  }
}


================================================
FILE: modules/core/src/main/java/org/dhallj/cbor/MajorType.java
================================================
package org.dhallj.cbor;

public enum MajorType {
  UNSIGNED_INTEGER(0),
  NEGATIVE_INTEGER(1),
  BYTE_STRING(2),
  TEXT_STRING(3),
  ARRAY(4),
  MAP(5),
  SEMANTIC_TAG(6),
  PRIMITIVE(7);

  final int value;

  private MajorType(int value) {
    this.value = value;
  }

  public static MajorType fromByte(byte b) {
    switch ((b & 0xff) >> 5) {
      case 0:
        return UNSIGNED_INTEGER;
      case 1:
        return NEGATIVE_INTEGER;
      case 2:
        return BYTE_STRING;
      case 3:
        return TEXT_STRING;
      case 4:
        return ARRAY;
      case 5:
        return MAP;
      case 6:
        return SEMANTIC_TAG;
      case 7:
        return PRIMITIVE;
      default:
        throw new IllegalArgumentException("Invalid CBOR major type " + Byte.toString(b));
    }
  }
}


================================================
FILE: modules/core/src/main/java/org/dhallj/cbor/NullVisitor.java
================================================
package org.dhallj.cbor;

import java.math.BigInteger;

/** To read a CBOR primitive and ensure it is null. */
final class NullVisitor<R> implements Visitor<R> {
  static final Visitor<String> instanceForString = new NullVisitor<String>();
  static final Visitor<byte[]> instanceForByteArray = new NullVisitor<byte[]>();

  @Override
  public R onUnsignedInteger(BigInteger value) {
    return notExpected("Unsigned integer");
  }

  @Override
  public R onNegativeInteger(BigInteger value) {
    return notExpected("Negative integer");
  }

  @Override
  public R onByteString(byte[] value) {
    return notExpected("Byte string");
  }

  @Override
  public R onTextString(String value) {
    return notExpected("Text string");
  }

  @Override
  public R onVariableArray(BigInteger length, String name) {
    return notExpected("Variable array");
  }

  @Override
  public R onArray(BigInteger length, BigInteger tagI) {
    return notExpected("Array");
  }

  @Override
  public R onMap(BigInteger size) {
    return notExpected("Map");
  }

  @Override
  public R onFalse() {
    return notExpected("False");
  }

  @Override
  public R onTrue() {
    return notExpected("True");
  }

  @Override
  public R onNull() {
    return null;
  }

  @Override
  public R onHalfFloat(float value) {
    return notExpected("Half float");
  }

  @Override
  public R onSingleFloat(float value) {
    return notExpected("Single float");
  }

  @Override
  public R onDoubleFloat(double value) {
    return notExpected("Double float");
  }

  @Override
  public R onTag() {
    return null;
  }

  private R notExpected(String msg) {
    throw new CborException(msg + " not expected - expected null");
  }
}


================================================
FILE: modules/core/src/main/java/org/dhallj/cbor/Reader.java
================================================
package org.dhallj.cbor;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

/**
 * An implementation of enough of the CBOR spec to cope with decoding the CBOR values we need for
 * Dhall.
 */
public abstract class Reader {
  private static final BigInteger TWO = new BigInteger("2");

  /** Only allow symbols that correspond to entire encoded Dhall expressions. */
  public final <R> R nextSymbol(Visitor<R> visitor) {
    skip55799();
    byte b = this.read();
    switch (MajorType.fromByte(b)) {
      case UNSIGNED_INTEGER:
        return visitor.onUnsignedInteger(readUnsignedInteger(b));
      case NEGATIVE_INTEGER:
        return visitor.onNegativeInteger(readNegativeInteger(b));
      case BYTE_STRING:
        return visitor.onByteString(readByteString(b));
      case TEXT_STRING:
        return visitor.onTextString(readTextString(b));
      case ARRAY:
        return readArrayStart(b, visitor);
      case MAP:
        return visitor.onMap(readMapStart(b));
      case SEMANTIC_TAG:
        throw new CborException("We should have skipped semantic tags");
      case PRIMITIVE:
        return readPrimitive(b, visitor);
      default:
        throw new CborException("Invalid CBOR major type " + Byte.toString(b));
    }
  }

  protected abstract byte read();

  protected abstract byte peek();

  protected abstract byte[] read(int count);

  public final BigInteger readUnsignedInteger() {
    skip55799();
    return readUnsignedInteger(read());
  }

  public final BigInteger readPositiveBigNum() {
    skip55799();
    BigInteger result = readBigNum();
    if (result.compareTo(BigInteger.ZERO) < 0) {
      throw new CborException(result.toString() + " is not a positive big num");
    } else {
      return result;
    }
  }

  public final BigInteger readBigNum() {
    skip55799();
    byte next = read();
    switch (MajorType.fromByte(next)) {
      case UNSIGNED_INTEGER:
        return readUnsignedInteger(next);
      case NEGATIVE_INTEGER:
        return readNegativeInteger(next);
      case SEMANTIC_TAG:
        AdditionalInfo info = AdditionalInfo.fromByte(next);
        BigInteger t = readBigInteger(info, next);
        long tag = t.longValue();
        BigInteger length = readUnsignedInteger();
        long len = length.longValue(); // Don't handle Bignums larger than this
        BigInteger result = readBigInteger(len);
        if (tag == 2) {
          return result;
        } else if (tag == 3) {
          return BigInteger.valueOf(-1).subtract(result);
        } else {
          throw new CborException(Long.toString(tag) + " is not a valid tag for a bignum");
        }
      default:
        throw new CborException(
            "Not a valid major type for an Unsigned Integer: "
                + MajorType.fromByte(next).toString());
    }
  }

  public final BigDecimal readBigDecimal() {
    skip55799();
    byte next = read();
    switch (MajorType.fromByte(next)) {
      case SEMANTIC_TAG:
        AdditionalInfo info = AdditionalInfo.fromByte(next);
        BigInteger t = readBigInteger(info, next);
        long tag = t.longValue();
        if (tag == 4) {
          BigInteger length = readArrayStart();
          if (length.equals(TWO)) {
            BigInteger rawScale = readBigNum();
            int scale = -rawScale.intValue();
            BigInteger unscaledValue = readBigNum();

            return new BigDecimal(unscaledValue, scale);
          } else {
            throw new CborException("Invalid decimal fraction");
          }
        } else {
          throw new CborException(
              Long.toString(tag) + " is not a valid tag for a decimal fraction");
        }
      default:
        throw new CborException("Next symbol is not a decimal fraction");
    }
  }

  public final String readNullableTextString() {
    skip55799();
    byte next = read();
    switch (MajorType.fromByte(next)) {
      case TEXT_STRING:
        return readTextString(next);
      case PRIMITIVE:
        return readPrimitive(next, NullVisitor.instanceForString);
      default:
        throw new CborException("Next symbol is neither a text string or null");
    }
  }

  public final byte[] readNullableByteString() {
    skip55799();
    byte next = read();
    switch (MajorType.fromByte(next)) {
      case BYTE_STRING:
        return readByteString(next);
      case PRIMITIVE:
        return readPrimitive(next, NullVisitor.instanceForByteArray);
      default:
        throw new CborException("Next symbol is neither a byte string or null");
    }
  }

  /**
   * This is unfortunate and horrible.
   *
   * <p>A hack to support decoding record projections, which are the only expressions which have a
   * CBOR representation where we don't know simply from the length of the array and the first
   * element what type of expression we're decoding - could be projection or projection by type
   */
  public final String tryReadTextString() {
    skip55799();
    byte next = peek();
    switch (MajorType.fromByte(next)) {
      case TEXT_STRING:
        return readTextString(read());
      default:
        return null;
    }
  }

  public final BigInteger readArrayStart() {
    skip55799();
    byte next = read();
    switch (MajorType.fromByte(next)) {
      case ARRAY:
        AdditionalInfo info = AdditionalInfo.fromByte(next);
        BigInteger length = readBigInteger(info, next);
        if (length.compareTo(BigInteger.ZERO) < 0) {
          throw new CborException("Indefinite array not needed for Dhall");
        } else {
          return length;
        }
      default:
        throw new CborException("Next symbol is not an array");
    }
  }

  public final <R> Map<String, R> readMap(Visitor<R> visitor) {
    skip55799();
    byte b = this.read();
    switch (MajorType.fromByte(b)) {
      case MAP:
        int length = readMapStart(b).intValue();
        Map<String, R> entries = new HashMap<>(length);
        for (int i = 0; i < length; i++) {
          String key = readNullableTextString();
          R value = nextSymbol(visitor);
          entries.put(key, value);
        }
        return entries;
      default:
        throw new CborException(
            "Cannot read map - major type is " + MajorType.fromByte(b).toString());
    }
  }

  private final BigInteger readUnsignedInteger(byte b) {
    AdditionalInfo info = AdditionalInfo.fromByte(b);
    return readBigInteger(info, b);
  }

  private final BigInteger readNegativeInteger(byte b) {
    AdditionalInfo info = AdditionalInfo.fromByte(b);
    return BigInteger.valueOf(-1).subtract(readBigInteger(info, b));
  }

  private final byte[] readByteString(byte b) {
    AdditionalInfo info = AdditionalInfo.fromByte(b);
    BigInteger length = readBigInteger(info, b);
    if (length.compareTo(BigInteger.ZERO) < 0) {
      throw new CborException("Indefinite byte string not needed for Dhall");
    } else {
      // We don't handle the case where the length is > Integer.MaxValue
      return this.read(length.intValue());
    }
  }

  private final String readTextString(byte b) {
    AdditionalInfo info = AdditionalInfo.fromByte(b);
    BigInteger length = readBigInteger(info, b);
    if (length.compareTo(BigInteger.ZERO) < 0) {
      // Indefinite length - do we need this for Dhall?
      throw new CborException("Indefinite text string not needed for Dhall");
    } else {
      // We don't handle the case where the length is > Integer.MaxValue
      return new String(this.read(length.intValue()), Charset.forName("UTF-8"));
    }
  }

  private final <R> R readArrayStart(byte b, Visitor<R> visitor) {
    AdditionalInfo info = AdditionalInfo.fromByte(b);
    BigInteger length = readBigInteger(info, b);
    if (length.compareTo(BigInteger.ZERO) < 0) {
      throw new CborException("Indefinite array not needed for Dhall");
    } else {
      skip55799();
      byte next = read();
      switch (MajorType.fromByte(next)) {
        case UNSIGNED_INTEGER:
          return visitor.onArray(length, readUnsignedInteger(next));
        case TEXT_STRING:
          return visitor.onVariableArray(length, readTextString(next));
        default:
          throw new CborException(
              "Invalid start to CBOR-encoded Dhall expression " + MajorType.fromByte(b).toString());
      }
    }
  }

  private final BigInteger readMapStart(byte b) {
    AdditionalInfo info = AdditionalInfo.fromByte(b);
    BigInteger length = readBigInteger(info, b);
    if (length.compareTo(BigInteger.ZERO) < 0) {
      throw new CborException("Indefinite array not needed for Dhall");
    } else {
      return length;
    }
  }

  private static final String unassignedMessage(int v) {
    StringBuilder builder = new StringBuilder("Primitive ");
    builder.append(v);
    builder.append(" is unassigned");
    return builder.toString();
  }

  private static final String notValidMessage(int v) {
    StringBuilder builder = new StringBuilder("Primitive ");
    builder.append(v);
    builder.append(" is not valid");
    return builder.toString();
  }

  private final <R> R readPrimitive(byte b, Visitor<R> visitor) {
    int value = b & 31;
    if (0 <= value && value <= 19) {
      throw new CborException(unassignedMessage(value));
    } else if (value == 20) {
      return visitor.onFalse();
    } else if (value == 21) {
      return visitor.onTrue();
    } else if (value == 22) {
      return visitor.onNull();
    } else if (value == 23) {
      throw new CborException(unassignedMessage(value));
    } else if (value == 24) {
      throw new CborException("Simple value not needed for Dhall");
    } else if (value == 25) {
      // https://github.com/c-rack/cbor-java/blob/master/src/main/java/co/nstant/in/cbor/decoder/HalfPrecisionFloatDecoder.java
      int bits = 0;
      for (int i = 0; i < 2; i++) {
        int next = this.read() & 0xff;
        bits <<= 8;
        bits |= next;
      }

      return visitor.onHalfFloat(HalfFloat.toFloat(bits));
    } else if (value == 26) {
      int result = 0;
      for (int i = 0; i < 4; i++) {
        int next = this.read() & 0xff;
        result <<= 8;
        result |= next;
      }
      return visitor.onSingleFloat(Float.intBitsToFloat(result));
    } else if (value == 27) {
      long result = 0;
      for (int i = 0; i < 8; i++) {
        int next = this.read() & 0xff;
        result <<= 8;
        result |= next;
      }
      return visitor.onDoubleFloat(Double.longBitsToDouble(result));
    } else if (28 <= value && value <= 30) {
      throw new CborException(unassignedMessage(value));
    } else if (value == 31) {
      throw new CborException("Break stop code not needed for Dhall");
    } else {
      throw new CborException(notValidMessage(value));
    }
  }

  private final void skip55799() {
    byte next = peek();
    switch (MajorType.fromByte(next)) {
      case SEMANTIC_TAG:
        AdditionalInfo info = AdditionalInfo.fromByte(next);
        switch (info) {
          case DIRECT:
            return; // Don't advance pointer if it's a Bignum
          default:
            BigInteger tag = readBigInteger(info, read()); // Now advance pointer
            int t = tag.intValue();
            if (t != 55799) {
              throw new CborException("Unrecognized CBOR semantic tag " + Integer.toString(t));
            } else {
              skip55799(); // Please tell me no encoders do this
            }
        }
      default:
    }
  }

  private final BigInteger readBigInteger(AdditionalInfo info, byte first) {
    switch (info) {
      case DIRECT:
        return BigInteger.valueOf(first & 31);
      case ONE_BYTE:
        return readBigInteger(1);
      case TWO_BYTES:
        return readBigInteger(2);
      case FOUR_BYTES:
        return readBigInteger(4);
      case EIGHT_BYTES:
        return readBigInteger(8);
      case RESERVED:
        throw new CborException("Additional info RESERVED should not require reading a uintXX");
      case INDEFINITE:
        return BigInteger.valueOf(-1);
      default:
        throw new IllegalArgumentException("Invalid AdditionalInfo");
    }
  }

  private final BigInteger readBigInteger(long numBytes) {
    BigInteger result = BigInteger.ZERO;
    for (long i = 0; i < numBytes; i++) {
      int next = this.read() & 0xff;
      result = result.shiftLeft(8).or(BigInteger.valueOf(next));
    }
    return result;
  }

  public static final class ByteArrayReader extends Reader {
    private final byte[] bytes;
    private int cursor = 0;

    public ByteArrayReader(byte[] bytes) {
      this.bytes = bytes;
    }

    @Override
    protected byte read() {
      return this.bytes[this.cursor++];
    }

    @Override
    protected byte peek() {
      return this.bytes[this.cursor];
    }

    @Override
    protected byte[] read(int count) {
      byte[] bs = new byte[count];

      System.arraycopy(bytes, this.cursor, bs, 0, count);
      this.cursor += count;

      return bs;
    }
  }
}


================================================
FILE: modules/core/src/main/java/org/dhallj/cbor/Visitor.java
================================================
package org.dhallj.cbor;

import java.math.BigInteger;
import java.util.List;
import java.util.Map;

/**
 * Represents a function from a CBOR expression to a value.
 *
 * @param R The result type
 */
public interface Visitor<R> {

  public R onUnsignedInteger(BigInteger value);

  public R onNegativeInteger(BigInteger value);

  public R onByteString(byte[] value);

  public R onTextString(String value);

  public R onVariableArray(BigInteger length, String name);

  public R onArray(BigInteger length, BigInteger tagI);

  public R onMap(BigInteger size);

  public R onFalse();

  public R onTrue();

  public R onNull();

  public R onHalfFloat(float value);

  public R onSingleFloat(float value);

  public R onDoubleFloat(double value);

  public R onTag();
}


================================================
FILE: modules/core/src/main/java/org/dhallj/cbor/Writer.java
================================================
package org.dhallj.cbor;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;

public abstract class Writer {
  private static final int FALSE = 244;
  private static final int TRUE = 245;
  private static final int NULL = 246;
  private static final BigInteger LONG_MAX_VALUE = BigInteger.valueOf(Long.MAX_VALUE);
  private static final BigInteger EIGHT_BYTES_MAX_VALUE = new BigInteger("18446744073709551616");
  private static final Charset UTF_8 = Charset.forName("UTF-8");

  private static final class WrappedIOException extends RuntimeException {
    final IOException underlying;

    WrappedIOException(IOException underlying) {
      this.underlying = underlying;
    }
  }

  public static class OutputStreamWriter extends Writer {
    protected final OutputStream stream;

    public OutputStreamWriter(OutputStream stream) {
      this.stream = stream;
    }

    protected final void write(byte b) {
      try {
        this.stream.write(b);
      } catch (IOException e) {
        throw new WrappedIOException(e);
      }
    }

    protected final void write(byte... bs) {
      try {
        this.stream.write(bs);
      } catch (IOException e) {
        throw new WrappedIOException(e);
      }
    }
  }

  public static final class ByteArrayWriter extends OutputStreamWriter {
    public ByteArrayWriter() {
      super(new ByteArrayOutputStream());
    }

    public final byte[] getBytes() {
      return ((ByteArrayOutputStream) this.stream).toByteArray();
    }
  }

  public static final class SHA256Writer extends Writer {
    private final MessageDigest messageDigest;

    public SHA256Writer() {
      MessageDigest tmp = null;
      try {
        tmp = MessageDigest.getInstance("SHA-256");
      } catch (NoSuchAlgorithmException e) {
        // TODO: Something reasonable here.
      }

      this.messageDigest = tmp;
    }

    public final byte[] getHashBytes() {
      return this.messageDigest.digest();
    }

    protected final void write(byte b) {
      this.messageDigest.update(b);
    }

    protected final void write(byte... bs) {
      this.messageDigest.update(bs);
    }
  }

  protected abstract void write(byte b);

  protected abstract void write(byte... bs);

  public final void writeNull() {
    this.write((byte) NULL);
  }

  public final void writeBoolean(boolean value) {
    this.write((byte) (value ? TRUE : FALSE));
  }

  public final void writeLong(long value) {
    if (value >= 0) {
      this.writeTypeAndLength(MajorType.UNSIGNED_INTEGER.value, value);
    } else {
      this.writeTypeAndLength(MajorType.NEGATIVE_INTEGER.value, -(value + 1));
    }
  }

  public final void writeBigInteger(BigInteger value) {
    if (value.compareTo(BigInteger.ZERO) >= 0) {
      this.writeTypeAndLength(MajorType.UNSIGNED_INTEGER.value, value);
    } else {
      this.writeTypeAndLength(MajorType.NEGATIVE_INTEGER.value, value.add(BigInteger.ONE).negate());
    }
  }

  public final void writeBigDecimal(BigDecimal value) {
    this.writeTypeAndLength(MajorType.SEMANTIC_TAG.value, 4);
    this.writeTypeAndLength(MajorType.ARRAY.value, 2);
    this.writeLong(-((long) value.scale()));
    this.writeBigInteger(value.unscaledValue());
  }

  public final void writeString(String value) {
    byte[] bytes = value.getBytes(UTF_8);
    this.writeTypeAndLength(MajorType.TEXT_STRING.value, bytes.length);
    this.write(bytes);
  }

  public final void writeByteString(byte[] bytes) {
    this.writeTypeAndLength(MajorType.BYTE_STRING.value, bytes.length);
    this.write(bytes);
  }

  public final void writeDouble(double value) {
    int base = MajorType.PRIMITIVE.value << 5;

    if (Double.isNaN(value)) {
      this.write((byte) (base | AdditionalInfo.TWO_BYTES.value), (byte) 126, (byte) 0);
    } else {
      float asFloat = (float) value;
      if (value == (double) asFloat) {
        int asHalfFloatBits = HalfFloat.fromFloat(asFloat);

        if (HalfFloat.toFloat(asHalfFloatBits) == asFloat) {
          this.write(
              (byte) (base | AdditionalInfo.TWO_BYTES.value),
              (byte) ((asHalfFloatBits >> 8) & 0xff),
              (byte) ((asHalfFloatBits >> 0) & 0xff));
        } else {

          int bits = Float.floatToIntBits(asFloat);
          this.write(
              (byte) (base | AdditionalInfo.FOUR_BYTES.value),
              (byte) ((bits >> 24) & 0xff),
              (byte) ((bits >> 16) & 0xff),
              (byte) ((bits >> 8) & 0xff),
              (byte) ((bits >> 0) & 0xff));
        }
      } else {

        long bits = Double.doubleToLongBits(value);
        this.write(
            (byte) (base | AdditionalInfo.EIGHT_BYTES.value),
            (byte) ((bits >> 56) & 0xff),
            (byte) ((bits >> 48) & 0xff),
            (byte) ((bits >> 40) & 0xff),
            (byte) ((bits >> 32) & 0xff),
            (byte) ((bits >> 24) & 0xff),
            (byte) ((bits >> 16) & 0xff),
            (byte) ((bits >> 8) & 0xff),
            (byte) ((bits >> 0) & 0xff));
      }
    }
  }

  public final void writeArrayStart(int length) {
    this.writeTypeAndLength(MajorType.ARRAY.value, length);
  }

  public final void writeMapStart(int length) {
    this.writeTypeAndLength(MajorType.MAP.value, length);
  }

  private final void writeTypeAndLength(int majorType, long length) {
    int base = majorType << 5;

    if (length <= 23L) {
      this.write((byte) (base | length));
    } else if (length < (1L << 8)) {
      this.write((byte) (base | AdditionalInfo.ONE_BYTE.value), (byte) length);
    } else if (length < (1L << 16)) {
      this.write(
          (byte) (base | AdditionalInfo.TWO_BYTES.value),
          (byte) (length >> 8),
          (byte) (length & 0xff));
    } else if (length < (1L << 32)) {
      this.write(
          (byte) (base | AdditionalInfo.FOUR_BYTES.value),
          (byte) ((length >> 24) & 0xff),
          (byte) ((length >> 16) & 0xff),
          (byte) ((length >> 8) & 0xff),
          (byte) (length & 0xff));
    } else {
      this.write(
          (byte) (base | AdditionalInfo.EIGHT_BYTES.value),
          (byte) ((length >> 56) & 0xff),
          (byte) ((length >> 48) & 0xff),
          (byte) ((length >> 40) & 0xff),
          (byte) ((length >> 32) & 0xff),
          (byte) ((length >> 24) & 0xff),
          (byte) ((length >> 16) & 0xff),
          (byte) ((length >> 8) & 0xff),
          (byte) (length & 0xff));
    }
  }

  private final void writeTypeAndLength(int majorType, BigInteger length) {
    if (length.compareTo(LONG_MAX_VALUE) <= 0) {
      this.writeTypeAndLength(majorType, length.longValue());
    } else if (length.compareTo(EIGHT_BYTES_MAX_VALUE) < 0) {
      int base = majorType << 5;
      BigInteger mask = BigInteger.valueOf(0xff);
      this.write(
          (byte) (base | AdditionalInfo.EIGHT_BYTES.value),
          length.shiftRight(56).and(mask).byteValue(),
          length.shiftRight(48).and(mask).byteValue(),
          length.shiftRight(40).and(mask).byteValue(),
          length.shiftRight(32).and(mask).byteValue(),
          length.shiftRight(24).and(mask).byteValue(),
          length.shiftRight(16).and(mask).byteValue(),
          length.shiftRight(8).and(mask).byteValue(),
          length.and(mask).byteValue());
    } else {
      if (majorType == MajorType.NEGATIVE_INTEGER.value) {
        this.writeTypeAndLength(MajorType.SEMANTIC_TAG.value, 3);
      } else {
        this.writeTypeAndLength(MajorType.SEMANTIC_TAG.value, 2);
      }
      byte[] bs = length.toByteArray();
      writeTypeAndLength(MajorType.BYTE_STRING.value, bs.length);
      write(bs);
    }
  }
}


================================================
FILE: modules/core/src/main/java/org/dhallj/core/ArrayIterable.java
================================================
package org.dhallj.core;

import java.util.Iterator;
import java.util.NoSuchElementException;

final class ArrayIterable<A> implements Iterable<A> {
  private final A[] values;

  public ArrayIterable(A[] values) {
    this.values = values;
  }

  public final Iterator<A> iterator() {
    return new ArrayIterator<A>(this.values);
  }

  private static final class ArrayIterator<A> implements Iterator<A> {
    private final A[] values;
    private int i = 0;

    ArrayIterator(A[] values) {
      this.values = values;
    }

    public final boolean hasNext() {
      return this.i < this.values.length;
    }

    public final A next() {
      try {
        return this.values[this.i++];
      } catch (ArrayIndexOutOfBoundsException e) {
        throw new NoSuchElementException();
      }
    }

    public final void remove() {
      throw new UnsupportedOperationException("remove");
    }
  }
}


================================================
FILE: modules/core/src/main/java/org/dhallj/core/Constructors.java
================================================
package org.dhallj.core;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.nio.file.Path;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

/**
 * Constructors for the Dhall expression abstract syntax tree.
 *
 * <p>Note that nothing in this file is public, and that custom code shouldn't be added here, since
 * this is generated from the visitor definition.
 */
final class Constructors {
  static final class NaturalLiteral extends Expr {
    final BigInteger value;

    NaturalLiteral(BigInteger value) {
      super(Tags.NATURAL);
      this.value = value;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onNatural(this.value);
    }

    final <A> void advance(VisitState<A> state) {
      state.valueStack.push(state.visitor.onNatural(this, this.value));
    }
  }

  static final class IntegerLiteral extends Expr {
    final BigInteger value;

    IntegerLiteral(BigInteger value) {
      super(Tags.INTEGER);
      this.value = value;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onInteger(this.value);
    }

    final <A> void advance(VisitState<A> state) {
      state.valueStack.push(state.visitor.onInteger(this, this.value));
    }
  }

  static final class DoubleLiteral extends Expr {
    final double value;

    DoubleLiteral(double value) {
      super(Tags.DOUBLE);
      this.value = value;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onDouble(this.value);
    }

    final <A> void advance(VisitState<A> state) {
      state.valueStack.push(state.visitor.onDouble(this, this.value));
    }
  }

  static final class DateLiteral extends Expr {
    final int year;
    final int month;
    final int day;

    DateLiteral(int year, int month, int day) {
      super(Tags.DATE);
      this.year = year;
      this.month = month;
      this.day = day;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onDate(this.year, this.month, this.day);
    }

    final <A> void advance(VisitState<A> state) {
      state.valueStack.push(state.visitor.onDate(this, this.year, this.month, this.day));
    }
  }

  static final class TimeLiteral extends Expr {
    final int hour;
    final int minute;
    final int second;
    final BigDecimal fractional;

    TimeLiteral(int hour, int minute, int second, BigDecimal fractional) {
      super(Tags.TIME);
      this.hour = hour;
      this.minute = minute;
      this.second = second;
      this.fractional = fractional;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onTime(this.hour, this.minute, this.second, this.fractional);
    }

    final <A> void advance(VisitState<A> state) {
      state.valueStack.push(
          state.visitor.onTime(this, this.hour, this.minute, this.second, this.fractional));
    }
  }

  static final class TimeZoneLiteral extends Expr {
    final int minutes;

    TimeZoneLiteral(int minutes) {
      super(Tags.TIME_ZONE);
      this.minutes = minutes;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onTimeZone(this.minutes);
    }

    final <A> void advance(VisitState<A> state) {
      state.valueStack.push(state.visitor.onTimeZone(this, this.minutes));
    }
  }

  static final class TextLiteral extends Expr {
    final String[] parts;
    final Expr[] interpolated;

    TextLiteral(String[] parts, Expr[] interpolated) {
      super(Tags.TEXT);
      this.parts = parts;
      this.interpolated = interpolated;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onText(this.parts, new ArrayIterable<Expr>(interpolated));
    }

    final <A> void advance(VisitState<A> state) {
      if (state.current.state == 0) {
        state.visitor.prepareText(this.parts.length);
        state.visitor.prepareTextPart(this.parts[0]);

        if (this.interpolated.length == 0) {
          state.valueStack.push(state.visitor.onText(this.parts, new ArrayList<A>()));
        } else {
          state.current.state = 1;
          state.stack.push(state.current);
          state.stack.push(new ExprState(this.interpolated[state.current.state - 1], 0));
        }
      } else if (state.current.state == this.interpolated.length) {
        state.visitor.prepareTextPart(this.parts[this.parts.length - 1]);
        List<A> results = new ArrayList<A>();
        for (int i = 0; i < this.interpolated.length; i += 1) {
          results.add(state.valueStack.poll());
        }
        Collections.reverse(results);
        state.valueStack.push(state.visitor.onText(this.parts, results));
      } else {
        state.current.state += 1;
        state.visitor.prepareTextPart(this.parts[state.current.state - 1]);
        state.stack.push(state.current);
        state.stack.push(new ExprState(this.interpolated[state.current.state - 1], 0));
      }
    }
  }

  static final class Application extends Expr {
    final Expr base;
    final Expr arg;

    Application(Expr base, Expr arg) {
      super(Tags.APPLICATION);
      this.base = base;
      this.arg = arg;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onApplication(base, arg);
    }

    final <A> void advance(VisitState<A> state) {
      if (state.current.state == 0) {
        LinkedList<Expr> application = new LinkedList<>();
        application.push(this.arg);

        Expr base = Application.gatherApplicationArgs(this.base, application);

        state.current.state = 1;
        state.current.size = application.size();
        boolean processBase = state.visitor.prepareApplication(base, application.size());
        state.current.skippedRecursion = !processBase;

        state.stack.push(state.current);

        if (processBase) {
          state.stack.push(new ExprState(base, 0));
        }
        state.applicationStack.push(application);
      } else {
        LinkedList<Expr> application = state.applicationStack.poll();

        if (application.isEmpty()) {
          List<A> args = new ArrayList<>(state.current.size);
          for (int i = 0; i < state.current.size; i++) {
            args.add(state.valueStack.poll());
          }
          Collections.reverse(args);

          A base = null;
          if (!state.current.skippedRecursion) {
            base = state.valueStack.poll();
          }

          state.valueStack.push(state.visitor.onApplication(base, args));
        } else {
          state.stack.push(state.current);
          state.stack.push(new ExprState(application.poll(), 0));
          state.applicationStack.push(application);
        }
      }
    }

    private static final Expr gatherApplicationArgs(Expr candidate, Deque<Expr> args) {
      Expr current = candidate.getNonNote();

      while (current.tag == Tags.APPLICATION) {
        Constructors.Application currentApplication = (Constructors.Application) current;

        if (args != null) {
          args.push(currentApplication.arg);
        }
        current = currentApplication.base.getNonNote();
      }

      return current;
    }
  }

  static final class OperatorApplication extends Expr {
    final Operator operator;
    final Expr lhs;
    final Expr rhs;

    OperatorApplication(Operator operator, Expr lhs, Expr rhs) {
      super(Tags.OPERATOR_APPLICATION);
      this.operator = operator;
      this.lhs = lhs;
      this.rhs = rhs;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onOperatorApplication(this.operator, lhs, rhs);
    }

    final <A> void advance(VisitState<A> state) {
      if (state.current.state == 0) {
        state.visitor.prepareOperatorApplication(this.operator);
        state.current.state = 1;
        state.stack.push(state.current);
        state.stack.push(new ExprState(this.lhs, 0));
      } else if (state.current.state == 1) {
        state.current.state = 2;
        state.stack.push(state.current);
        state.stack.push(new ExprState(this.rhs, 0));
      } else {
        A v1 = state.valueStack.poll();
        A v0 = state.valueStack.poll();
        state.valueStack.push(state.visitor.onOperatorApplication(this.operator, v0, v1));
      }
    }
  }

  static final class If extends Expr {
    final Expr predicate;
    final Expr thenValue;
    final Expr elseValue;

    If(Expr predicate, Expr thenValue, Expr elseValue) {
      super(Tags.IF);
      this.predicate = predicate;
      this.thenValue = thenValue;
      this.elseValue = elseValue;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onIf(predicate, thenValue, elseValue);
    }

    final <A> void advance(VisitState<A> state) {
      if (state.current.state == 0) {
        state.visitor.prepareIf();
        state.current.state = 1;
        state.stack.push(state.current);
        state.stack.push(new ExprState(this.predicate, 0));
      } else if (state.current.state == 1) {
        state.current.state = 2;
        state.stack.push(state.current);
        state.stack.push(new ExprState(this.thenValue, 0));
      } else if (state.current.state == 2) {
        state.current.state = 3;
        state.stack.push(state.current);
        state.stack.push(new ExprState(this.elseValue, 0));
      } else {
        A v2 = state.valueStack.poll();
        A v1 = state.valueStack.poll();
        A v0 = state.valueStack.poll();
        state.valueStack.push(state.visitor.onIf(v0, v1, v2));
      }
    }
  }

  static final class Lambda extends Expr {
    final String name;
    final Expr type;
    final Expr result;

    Lambda(String name, Expr type, Expr result) {
      super(Tags.LAMBDA);
      this.name = name;
      this.type = type;
      this.result = result;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onLambda(this.name, type, result);
    }

    final <A> void advance(VisitState<A> state) {
      switch (state.current.state) {
        case 0:
          state.visitor.prepareLambda(this.name, this.type);
          state.current.state = 1;
          state.stack.push(state.current);
          state.stack.push(new ExprState(this.type, 0));
          break;
        case 1:
          state.visitor.bind(this.name, this.type);
          state.current.state = 2;
          state.stack.push(state.current);
          state.stack.push(new ExprState(this.result, 0));
          break;
        case 2:
          A v1 = state.valueStack.poll();
          A v0 = state.valueStack.poll();
          state.valueStack.push(state.visitor.onLambda(this.name, v0, v1));
          break;
      }
    }
  }

  static final class Pi extends Expr {
    final String name;
    final Expr type;
    final Expr result;

    Pi(String name, Expr type, Expr result) {
      super(Tags.PI);
      this.name = name;
      this.type = type;
      this.result = result;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onPi(this.name, type, result);
    }

    final <A> void advance(VisitState<A> state) {
      switch (state.current.state) {
        case 0:
          state.visitor.preparePi(this.name, this.type);
          state.current.state = 1;
          state.stack.push(state.current);
          state.stack.push(new ExprState(this.type, 0));
          break;
        case 1:
          state.visitor.bind(this.name, this.type);
          state.current.state = 2;
          state.stack.push(state.current);
          state.stack.push(new ExprState(this.result, 0));
          break;
        case 2:
          A v1 = state.valueStack.poll();
          A v0 = state.valueStack.poll();
          state.valueStack.push(state.visitor.onPi(this.name, v0, v1));
          break;
      }
    }
  }

  static final class Assert extends Expr {
    final Expr base;

    Assert(Expr base) {
      super(Tags.ASSERT);
      this.base = base;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onAssert(base);
    }

    final <A> void advance(VisitState<A> state) {
      if (state.current.state == 0) {
        state.visitor.prepareAssert();
        state.current.state = 1;
        state.stack.push(state.current);
        state.stack.push(new ExprState(this.base, 0));
      } else {
        state.valueStack.push(state.visitor.onAssert(state.valueStack.poll()));
      }
    }
  }

  static final class FieldAccess extends Expr {
    final Expr base;
    final String fieldName;

    FieldAccess(Expr base, String fieldName) {
      super(Tags.FIELD_ACCESS);
      this.base = base;
      this.fieldName = fieldName;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onFieldAccess(base, this.fieldName);
    }

    final <A> void advance(VisitState<A> state) {
      if (state.current.state == 0) {
        if (state.visitor.prepareFieldAccess(this.base, this.fieldName)) {
          state.current.state = 1;
          state.stack.push(state.current);
          state.stack.push(new ExprState(this.base, 0));
        } else {
          state.valueStack.push(state.visitor.onFieldAccess(null, this.fieldName));
        }
      } else {
        state.valueStack.push(state.visitor.onFieldAccess(state.valueStack.poll(), this.fieldName));
      }
    }
  }

  static final class Projection extends Expr {
    final Expr base;
    final String[] fieldNames;

    Projection(Expr base, String[] fieldNames) {
      super(Tags.PROJECTION);
      this.base = base;
      this.fieldNames = fieldNames;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onProjection(base, this.fieldNames);
    }

    final <A> void advance(VisitState<A> state) {
      if (state.current.state == 0) {
        state.visitor.prepareProjection(this.fieldNames.length);
        state.current.state = 1;
        state.stack.push(state.current);
        state.stack.push(new ExprState(this.base, 0));
      } else {
        state.valueStack.push(state.visitor.onProjection(state.valueStack.poll(), this.fieldNames));
      }
    }
  }

  static final class ProjectionByType extends Expr {
    final Expr base;
    final Expr type;

    ProjectionByType(Expr base, Expr type) {
      super(Tags.PROJECTION_BY_TYPE);
      this.base = base;
      this.type = type;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onProjectionByType(base, type);
    }

    final <A> void advance(VisitState<A> state) {
      if (state.current.state == 0) {
        state.visitor.prepareProjectionByType();
        state.current.state = 1;
        state.stack.push(state.current);
        state.stack.push(new ExprState(this.base, 0));
      } else if (state.current.state == 1) {
        state.visitor.prepareProjectionByType(this.type);
        state.current.state = 2;
        state.stack.push(state.current);
        state.stack.push(new ExprState(this.type, 0));
      } else {
        A v1 = state.valueStack.poll();
        A v0 = state.valueStack.poll();
        state.valueStack.push(state.visitor.onProjectionByType(v0, v1));
      }
    }
  }

  static final class BuiltIn extends Expr {
    final String name;

    BuiltIn(String name) {
      super(Tags.BUILT_IN);
      this.name = name;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onBuiltIn(this.name);
    }

    final <A> void advance(VisitState<A> state) {
      state.valueStack.push(state.visitor.onBuiltIn(this, this.name));
    }
  }

  static final class Identifier extends Expr {
    final String name;
    final long index;

    Identifier(String name, long index) {
      super(Tags.IDENTIFIER);
      this.name = name;
      this.index = index;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onIdentifier(this.name, this.index);
    }

    final <A> void advance(VisitState<A> state) {
      state.valueStack.push(state.visitor.onIdentifier(this, this.name, this.index));
    }
  }

  static final class RecordLiteral extends Expr {
    final Entry<String, Expr>[] fields;

    RecordLiteral(Entry<String, Expr>[] fields) {
      super(Tags.RECORD);
      this.fields = fields;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onRecord(new ArrayIterable<Entry<String, Expr>>(fields), fields.length);
    }

    final <A> void advance(VisitState<A> state) {
      if (state.current.state == 0) {
        state.visitor.prepareRecord(this.fields.length);
        if (this.fields.length == 0) {
          state.valueStack.push(state.visitor.onRecord(new ArrayList<Entry<String, A>>()));
        } else {
          state.current =
              new ExprState(state.current.expr, 1, this.fields, state.visitor.sortFields());
          state.stack.push(state.current);

          Entry<String, Expr> field = state.current.sortedFields[state.current.state - 1];
          state.visitor.prepareRecordField(
              field.getKey(), field.getValue(), state.current.state - 1);
          state.stack.push(new ExprState(field.getValue(), 0));
        }
      } else if (state.current.state == state.current.sortedFields.length) {
        List<Entry<String, A>> results = new ArrayList<Entry<String, A>>();
        for (int i = state.current.sortedFields.length - 1; i >= 0; i -= 1) {
          results.add(
              new SimpleImmutableEntry<>(
                  state.current.sortedFields[i].getKey(), state.valueStack.poll()));
        }
        Collections.reverse(results);
        state.valueStack.push(state.visitor.onRecord(results));
      } else {
        state.current.state += 1;
        Entry<String, Expr> field = state.current.sortedFields[state.current.state - 1];

        state.visitor.prepareRecordField(field.getKey(), field.getValue(), state.current.state - 1);

        state.stack.push(state.current);
        state.stack.push(new ExprState(field.getValue(), 0));
      }
    }
  }

  static final class RecordType extends Expr {
    final Entry<String, Expr>[] fields;

    RecordType(Entry<String, Expr>[] fields) {
      super(Tags.RECORD_TYPE);
      this.fields = fields;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onRecordType(new ArrayIterable<Entry<String, Expr>>(fields), fields.length);
    }

    final <A> void advance(VisitState<A> state) {
      if (state.current.state == 0) {
        state.visitor.prepareRecordType(this.fields.length);
        if (this.fields.length == 0) {
          state.valueStack.push(state.visitor.onRecordType(new ArrayList<Entry<String, A>>()));
        } else {
          state.current =
              new ExprState(state.current.expr, 1, this.fields, state.visitor.sortFields());
          state.stack.push(state.current);

          Entry<String, Expr> field = state.current.sortedFields[state.current.state - 1];
          state.visitor.prepareRecordTypeField(
              field.getKey(), field.getValue(), state.current.state - 1);
          state.stack.push(new ExprState(field.getValue(), 0));
        }
      } else if (state.current.state == state.current.sortedFields.length) {
        List<Entry<String, A>> results = new ArrayList<Entry<String, A>>();
        for (int i = state.current.sortedFields.length - 1; i >= 0; i -= 1) {
          results.add(
              new SimpleImmutableEntry<>(
                  state.current.sortedFields[i].getKey(), state.valueStack.poll()));
        }
        Collections.reverse(results);
        state.valueStack.push(state.visitor.onRecordType(results));
      } else {
        state.current.state += 1;
        Entry<String, Expr> field = state.current.sortedFields[state.current.state - 1];

        state.visitor.prepareRecordTypeField(
            field.getKey(), field.getValue(), state.current.state - 1);

        state.stack.push(state.current);
        state.stack.push(new ExprState(field.getValue(), 0));
      }
    }
  }

  static final class UnionType extends Expr {
    final Entry<String, Expr>[] fields;

    UnionType(Entry<String, Expr>[] fields) {
      super(Tags.UNION_TYPE);
      this.fields = fields;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onUnionType(new ArrayIterable<Entry<String, Expr>>(fields), fields.length);
    }

    final <A> void advance(VisitState<A> state) {
      if (state.current.state == 0) {
        state.visitor.prepareUnionType(this.fields.length);
        if (this.fields.length == 0) {
          state.valueStack.push(state.visitor.onUnionType(new ArrayList<Entry<String, A>>()));
        } else {
          state.current =
              new ExprState(state.current.expr, 1, this.fields, state.visitor.sortFields());
          state.stack.push(state.current);

          Entry<String, Expr> field = state.current.sortedFields[state.current.state - 1];
          state.visitor.prepareUnionTypeField(
              field.getKey(), field.getValue(), state.current.state - 1);

          Expr type = field.getValue();
          if (type == null) {
            state.valueStack.push(null);
          } else {
            state.stack.push(new ExprState(type, 0));
          }
        }
      } else if (state.current.state == this.fields.length) {
        List<Entry<String, A>> results = new ArrayList<Entry<String, A>>();
        for (int i = state.current.sortedFields.length - 1; i >= 0; i -= 1) {
          results.add(
              new SimpleImmutableEntry<>(
                  state.current.sortedFields[i].getKey(), state.valueStack.poll()));
        }
        Collections.reverse(results);
        state.valueStack.push(state.visitor.onUnionType(results));
      } else {
        state.current.state += 1;

        Entry<String, Expr> field = state.current.sortedFields[state.current.state - 1];
        Expr type = field.getValue();

        state.visitor.prepareUnionTypeField(field.getKey(), type, state.current.state - 1);

        state.stack.push(state.current);
        if (type == null) {
          state.valueStack.push(null);
        } else {
          state.stack.push(new ExprState(type, 0));
        }
      }
    }
  }

  static final class NonEmptyListLiteral extends Expr {
    final Expr[] values;

    NonEmptyListLiteral(Expr[] values) {
      super(Tags.NON_EMPTY_LIST);
      this.values = values;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onNonEmptyList(new ArrayIterable<Expr>(values), this.values.length);
    }

    final <A> void advance(VisitState<A> state) {
      if (state.current.state == 0) {
        boolean done = false;
        if (state.visitor.flattenToMapLists()) {
          Expr asRecord = NonEmptyListLiteral.flattenToMapList(this.values);

          if (asRecord != null) {
            state.stack.push(new ExprState(asRecord, 0));
            done = true;
          }
        }

        if (!done) {
          state.visitor.prepareNonEmptyList(this.values.length);
          state.visitor.prepareNonEmptyListElement(0);
          state.current.state = 1;
          state.stack.push(state.current);
          state.stack.push(new ExprState(this.values[state.current.state - 1], 0));
        }
      } else if (state.current.state == this.values.length) {
        List<A> results = new ArrayList<A>();
        for (int i = 0; i < this.values.length; i += 1) {
          results.add(state.valueStack.poll());
        }
        Collections.reverse(results);
        state.valueStack.push(state.visitor.onNonEmptyList(results));
      } else {
        state.visitor.prepareNonEmptyListElement(state.current.state);
        state.current.state += 1;
        state.stack.push(state.current);
        state.stack.push(new ExprState(this.values[state.current.state - 1], 0));
      }
    }

    private static final Expr flattenToMapList(Expr[] values) {
      LinkedHashMap<String, Expr> fieldMap = new LinkedHashMap<>(values.length);

      for (Expr value : values) {
        List<Entry<String, Expr>> asRecord = Expr.Util.asRecordLiteral(value);

        if (asRecord == null) {
          return null;
        }

        Entry<Expr, Expr> entry = Expr.Util.flattenToMapRecord(asRecord);

        if (entry == null) {
          return null;
        }

        String asText = Expr.Util.asSimpleTextLiteral(entry.getKey());

        if (asText == null) {
          return null;
        }

        fieldMap.put(asText, entry.getValue());
      }

      Set<Entry<String, Expr>> fields = fieldMap.entrySet();

      return Expr.makeRecordLiteral(fields.toArray(new Entry[fields.size()]));
    }
  }

  static final class EmptyListLiteral extends Expr {
    final Expr type;

    EmptyListLiteral(Expr type) {
      super(Tags.EMPTY_LIST);
      this.type = type;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onEmptyList(type);
    }

    final <A> void advance(VisitState<A> state) {
      if (state.current.state == 0) {
        if (state.visitor.flattenToMapLists() && EmptyListLiteral.isToMapListType(this.type)) {
          state.stack.push(new ExprState(Expr.Constants.EMPTY_RECORD_LITERAL, 0));
        } else {
          if (state.visitor.prepareEmptyList(this.type)) {
            state.current.state = 1;
            state.stack.push(state.current);
            state.stack.push(new ExprState(this.type, 0));
          } else {
            state.valueStack.push(null);
          }
        }
      } else {
        state.valueStack.push(state.visitor.onEmptyList(state.valueStack.poll()));
      }
    }

    private static final boolean isToMapListType(Expr type) {
      Expr elementType = Expr.Util.getListArg(type);

      if (elementType == null) {
        return false;
      } else {
        List<Entry<String, Expr>> asRecordType = Expr.Util.asRecordType(elementType);

        if (asRecordType == null) {
          return false;
        }

        Entry<Expr, Expr> entry = Expr.Util.flattenToMapRecord(asRecordType);

        if (entry == null) {
          return false;
        }

        String asBuiltIn = Expr.Util.asBuiltIn(entry.getKey());

        return asBuiltIn != null && asBuiltIn.equals("Text");
      }
    }
  }

  static final class Let extends Expr {
    final String name;
    final Expr type;
    final Expr value;
    final Expr body;

    Let(String name, Expr type, Expr value, Expr body) {
      super(Tags.LET);
      this.name = name;
      this.type = type;
      this.value = value;
      this.body = body;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onLet(name, type, value, body);
    }

    final <A> void advance(VisitState<A> state) {
      List<LetBinding<Expr>> letBindings;

      if (state.current.state == 0) {
        letBindings = new ArrayList<LetBinding<Expr>>();
        letBindings.add(new LetBinding(this.name, this.type, this.value));

        Let.gatherLetBindings(this.body, letBindings);

        state.current.state = 1;
        state.current.size = letBindings.size();

        List<String> letBindingNames = new ArrayList<>(state.current.size);

        for (LetBinding<Expr> letBinding : letBindings) {
          letBindingNames.add(letBinding.getName());
        }

        state.letBindingNamesStack.push(letBindingNames);

        state.visitor.prepareLet(letBindings.size());
      } else {
        letBindings = state.letBindingsStack.poll();
      }

      if (letBindings.isEmpty()) {
        if (state.current.state == 1) {
          state.current.state = 3;
          state.stack.push(state.current);
          state.stack.push(new ExprState(Let.gatherLetBindings(this.body, null), 0));
          state.letBindingsStack.push(letBindings);
        } else {
          List<String> letBindingNames = state.letBindingNamesStack.poll();
          LinkedList<LetBinding<A>> valueBindings = new LinkedList<LetBinding<A>>();

          A body = state.valueStack.poll();

          for (int i = 0; i < state.current.size; i++) {
            A v1 = state.valueStack.poll();
            A v0 = state.valueStack.poll();

            valueBindings.push(
                new LetBinding(letBindingNames.get(state.current.size - 1 - i), v0, v1));
          }

          state.valueStack.push(state.visitor.onLet(valueBindings, body));
        }
      } else {
        LetBinding<Expr> letBinding = letBindings.get(0);

        switch (state.current.state) {
          case 1:
            state.current.state = 2;
            state.visitor.prepareLetBinding(letBinding.getName(), letBinding.getType());
            if (letBinding.hasType()) {
              state.stack.push(state.current);
              state.stack.push(new ExprState(letBinding.getType(), 0));
              state.letBindingsStack.push(letBindings);
              break;
            } else {
              state.valueStack.push(null);
            }
          case 2:
            state.current.state = 1;
            state.visitor.bind(letBinding.getName(), letBinding.getType());
            state.stack.push(state.current);
            state.stack.push(new ExprState(letBinding.getValue(), 0));
            letBindings.remove(0);
            state.letBindingsStack.push(letBindings);
            break;
        }
      }
    }

    private static final Expr gatherLetBindings(Expr candidate, List<LetBinding<Expr>> args) {
      Expr current = candidate.getNonNote();

      while (current.tag == Tags.LET) {
        Constructors.Let currentLet = (Constructors.Let) current;

        if (args != null) {
          args.add(new LetBinding(currentLet.name, currentLet.type, currentLet.value));
        }
        current = currentLet.body.getNonNote();
      }

      return current;
    }
  }

  static final class Annotated extends Expr {
    final Expr base;
    final Expr type;

    Annotated(Expr base, Expr type) {
      super(Tags.ANNOTATED);
      this.base = base;
      this.type = type;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onAnnotated(base, type);
    }

    final <A> void advance(VisitState<A> state) {
      if (state.current.state == 0) {
        state.visitor.prepareAnnotated(this.type);
        state.current.state = 1;
        state.stack.push(state.current);
        state.stack.push(new ExprState(this.base, 0));
      } else if (state.current.state == 1) {
        state.current.state = 2;
        state.stack.push(state.current);
        state.stack.push(new ExprState(this.type, 0));
      } else {
        A v1 = state.valueStack.poll();
        A v0 = state.valueStack.poll();
        state.valueStack.push(state.visitor.onAnnotated(v0, v1));
      }
    }
  }

  static final class Merge extends Expr {
    final Expr handlers;
    final Expr union;
    final Expr type;

    Merge(Expr handlers, Expr union, Expr type) {
      super(Tags.MERGE);
      this.handlers = handlers;
      this.union = union;
      this.type = type;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onMerge(handlers, union, type);
    }

    final <A> void advance(VisitState<A> state) {
      switch (state.current.state) {
        case 0:
          state.visitor.prepareMerge(this.type);
          state.current.state = 1;
          state.stack.push(state.current);
          state.stack.push(new ExprState(this.handlers, 0));
          break;
        case 1:
          state.current.state = 2;
          state.stack.push(state.current);
          state.stack.push(new ExprState(this.union, 0));
          break;
        case 2:
          state.current.state = 3;

          if (this.type != null) {
            state.stack.push(state.current);
            state.stack.push(new ExprState(this.type, 0));
            break;
          } else {
            state.valueStack.push(null);
          }
        case 3:
          A v2 = state.valueStack.poll();
          A v1 = state.valueStack.poll();
          A v0 = state.valueStack.poll();
          state.valueStack.push(state.visitor.onMerge(v0, v1, v2));

          break;
      }
    }
  }

  static final class ToMap extends Expr {
    final Expr base;
    final Expr type;

    ToMap(Expr base, Expr type) {
      super(Tags.TO_MAP);
      this.base = base;
      this.type = type;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onToMap(base, type);
    }

    final <A> void advance(VisitState<A> state) {
      switch (state.current.state) {
        case 0:
          state.visitor.prepareToMap(this.type);
          state.current.state = 1;
          state.stack.push(state.current);
          state.stack.push(new ExprState(this.base, 0));
          break;
        case 1:
          state.current.state = 2;

          if (this.type != null) {
            state.stack.push(state.current);
            state.stack.push(new ExprState(this.type, 0));
            break;
          } else {
            state.valueStack.push(null);
          }
        case 2:
          A v1 = state.valueStack.poll();
          A v0 = state.valueStack.poll();
          state.valueStack.push(state.visitor.onToMap(v0, v1));
          break;
      }
    }
  }

  static final class With extends Expr {
    final Expr base;
    final String[] path;
    final Expr value;

    With(Expr base, String[] path, Expr value) {
      super(Tags.WITH);
      this.base = base;
      this.path = path;
      this.value = value;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onWith(this.base, this.path, this.value);
    }

    final <A> void advance(VisitState<A> state) {
      switch (state.current.state) {
        case 0:
          state.visitor.prepareWith(this.path);
          state.current.state = 1;
          state.stack.push(state.current);
          state.stack.push(new ExprState(this.base, 0));
          break;
        case 1:
          state.visitor.prepareWithValue(this.path);
          state.current.state = 2;
          state.stack.push(state.current);
          state.stack.push(new ExprState(this.value, 0));
          break;
        case 2:
          A v1 = state.valueStack.poll();
          A v0 = state.valueStack.poll();
          state.valueStack.push(state.visitor.onWith(v0, this.path, v1));
          break;
      }
    }
  }

  static final class MissingImport extends Expr {
    final Expr.ImportMode mode;
    final byte[] hash;

    MissingImport(Expr.ImportMode mode, byte[] hash) {
      super(Tags.MISSING_IMPORT);
      this.mode = mode;
      this.hash = hash;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onMissingImport(this.mode, this.hash);
    }

    final <A> void advance(VisitState<A> state) {
      state.valueStack.push(state.visitor.onMissingImport(this.mode, this.hash));
    }
  }

  static final class EnvImport extends Expr {
    final String name;
    final Expr.ImportMode mode;
    final byte[] hash;

    EnvImport(String name, Expr.ImportMode mode, byte[] hash) {
      super(Tags.ENV_IMPORT);
      this.name = name;
      this.mode = mode;
      this.hash = hash;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onEnvImport(this.name, this.mode, this.hash);
    }

    final <A> void advance(VisitState<A> state) {
      state.valueStack.push(state.visitor.onEnvImport(this.name, this.mode, this.hash));
    }
  }

  static final class LocalImport extends Expr {
    final Path path;
    final Expr.ImportMode mode;
    final byte[] hash;

    LocalImport(Path path, Expr.ImportMode mode, byte[] hash) {
      super(Tags.LOCAL_IMPORT);
      this.path = path;
      this.mode = mode;
      this.hash = hash;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onLocalImport(this.path, this.mode, this.hash);
    }

    final <A> void advance(VisitState<A> state) {
      state.valueStack.push(state.visitor.onLocalImport(this.path, this.mode, this.hash));
    }
  }

  static final class ClasspathImport extends Expr {
    final Path path;
    final Expr.ImportMode mode;
    final byte[] hash;

    ClasspathImport(Path path, Expr.ImportMode mode, byte[] hash) {
      super(Tags.CLASSPATH_IMPORT);
      this.path = path;
      this.mode = mode;
      this.hash = hash;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onClasspathImport(this.path, this.mode, this.hash);
    }

    final <A> void advance(VisitState<A> state) {
      state.valueStack.push(state.visitor.onClasspathImport(this.path, this.mode, this.hash));
    }
  }

  static final class RemoteImport extends Expr {
    final URI url;
    final Expr using;
    final Expr.ImportMode mode;
    final byte[] hash;

    RemoteImport(URI url, Expr using, Expr.ImportMode mode, byte[] hash) {
      super(Tags.REMOTE_IMPORT);
      this.url = url;
      this.using = using;
      this.mode = mode;
      this.hash = hash;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onRemoteImport(this.url, this.using, this.mode, this.hash);
    }

    final <A> void advance(VisitState<A> state) {
      switch (state.current.state) {
        case 0:
          state.visitor.prepareRemoteImport(this.url, this.using, this.mode, this.hash);
          state.current.state = 1;

          if (this.using != null) {
            state.stack.push(state.current);
            state.stack.push(new ExprState(this.using, 0));
            break;
          } else {
            state.valueStack.push(null);
          }
        case 1:
          state.valueStack.push(
              state.visitor.onRemoteImport(
                  this.url, state.valueStack.poll(), this.mode, this.hash));
      }
    }
  }
}


================================================
FILE: modules/core/src/main/java/org/dhallj/core/DhallException.java
================================================
package org.dhallj.core;

/** Base class of exceptions that may be thrown or returned by DhallJ. */
public class DhallException extends RuntimeException {
  public DhallException(String message) {
    super(message);
  }

  public DhallException(String message, Throwable cause) {
    super(message, cause);
  }

  /** Represents a parsing failure, generally wrapping an underlying exception. */
  public static final class ParsingFailure extends DhallException {
    @Override
    public Throwable fillInStackTrace() {
      // This is a failure type; stack traces aren't useful.
      return this;
    }

    public ParsingFailure(String message, Throwable cause) {
      super(message, cause);
    }

    public ParsingFailure(String message) {
      super(message);
    }
  }

  public static final class ResolutionFailure extends DhallException {
    private boolean isAbsentImport;

    @Override
    public Throwable fillInStackTrace() {
      // This is a failure type; stack traces aren't useful.
      return this;
    }

    public ResolutionFailure(String message, Throwable cause, boolean isAbsentImport) {
      super(message, cause);
      this.isAbsentImport = isAbsentImport;
    }

    public ResolutionFailure(String message, boolean isAbsentImport) {
      super(message);
      this.isAbsentImport = isAbsentImport;
    }

    public ResolutionFailure(String message, Throwable cause) {
      this(message, cause, false);
    }

    public ResolutionFailure(String message) {
      this(message, false);
    }

    public boolean isAbsentImport() {
      return this.isAbsentImport;
    }
  }
}


================================================
FILE: modules/core/src/main/java/org/dhallj/core/Expr.java
================================================
package org.dhallj.core;

import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.nio.file.Path;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import org.dhallj.cbor.Writer;
import org.dhallj.core.binary.Encode;
import org.dhallj.core.normalization.AlphaNormalize;
import org.dhallj.core.normalization.BetaNormalize;
import org.dhallj.core.normalization.Shift;
import org.dhallj.core.normalization.Substitute;
import org.dhallj.core.typechecking.TypeCheck;

/**
 * Represents a Dhall expression.
 *
 * <p>Note that there are two tools for manipulating expressions: internal visitors, which are a
 * fold over the structure, and external visitors, which correspond to pattern matching.
 */
public abstract class Expr {
  final int tag;
  private final AtomicReference<byte[]> cachedHashBytes = new AtomicReference<byte[]>();

  Expr(int tag) {
    this.tag = tag;
  }

  /** Run the given external visitor on this expression. */
  public abstract <A> A accept(ExternalVisitor<A> visitor);

  abstract <A> void advance(VisitState<A> state);

  /** Run the given internal visitor on this expression. */
  public final <A> A accept(Visitor<A> visitor) {
    VisitState<A> state = new VisitState<A>(visitor, this);
    while (state.current != null) {
      state.current.expr.advance(state);
      state.current = state.stack.poll();
    }
    return state.valueStack.poll();
  }

  /**
   * Beta-normalize this expression.
   *
   * <p>This operation "evaluates" the expression.
   */
  public final Expr normalize() {
    return this.accept(BetaNormalize.instance);
  }

  /**
   * Alpha-normalize this expression.
   *
   * <p>This operation replaces all variable names with De-Bruijn-indexed underscores.
   */
  public final Expr alphaNormalize() {
    return this.accept(new AlphaNormalize());
  }

  /** Increment a variable name in this expression. */
  public final Expr increment(String name) {
    return this.accept(new Shift(true, name));
  }

  /** Increment a variable name in this expression. */
  public final Expr decrement(String name) {
    return this.accept(new Shift(false, name));
  }

  /** Substitute the given expression for the given variable name in this expression. */
  public final Expr substitute(String name, Expr replacement) {
    return this.accept(new Substitute(name, replacement));
  }

  /**
   * Encode this expression as a CBOR byte array.
   *
   * <p>Note that this method does not normalize the expression.
   */
  public final byte[] getEncodedBytes() {
    Writer.ByteArrayWriter writer = new Writer.ByteArrayWriter();
    this.accept(new Encode(writer));
    return writer.getBytes();
  }

  /**
   * Encode this expression as a CBOR byte array and return the SHA-256 hash of the result.
   *
   * <p>Note that this method does not normalize the expression.
   */
  public final byte[] getHashBytes() {
    byte[] result = this.cachedHashBytes.get();

    if (result == null) {
      Writer.SHA256Writer writer = new Writer.SHA256Writer();
      this.accept(new Encode(writer));
      result = writer.getHashBytes();

      if (!this.cachedHashBytes.compareAndSet(null, result)) {
        return this.cachedHashBytes.get();
      }
    }
    return result;
  }

  /**
   * Encode this expression as a CBOR byte array and return the SHA-256 hash of the result as a
   * string.
   *
   * <p>Note that this method does not normalize the expression.
   */
  public final String hash() {
    return Util.encodeHashBytes(this.getHashBytes());
  }

  /** Check whether all imports in this expression have been resolved. */
  public final boolean isResolved() {
    return this.accept(IsResolved.instance);
  }

  /**
   * Check whether this expression has the same structure as another.
   *
   * <p>This method is a stricter than {@code equivalent} in that it doesn't normalize its
   * arguments.
   */
  public final boolean sameStructure(Expr other) {
    return this.getFirstDiff(other) == null;
  }

  /**
   * Check whether this expression is equivalent to another.
   *
   * <p>Note that this method normalizes both expressions before comparing.
   */
  public final boolean equivalent(Expr other) {
    return Arrays.equals(
        this.normalize().alphaNormalize().getEncodedBytes(),
        other.normalize().alphaNormalize().getEncodedBytes());
  }

  /**
   * Check whether this expression is equivalent to another value.
   *
   * <p>Note that this method normalizes both expressions before comparing. Also note that in future
   * releases this method may compare the hashes of the encoded bytes, but currently it is identical
   * to equivalent (but with an {@code Object} argument because Java).
   */
  public final boolean equals(Object obj) {
    if (obj instanceof Expr) {
      return this.equivalent((Expr) obj);
    } else {
      return false;
    }
  }

  /** Hashes the CBOR encoding of this expression. */
  public final int hashCode() {
    return Arrays.hashCode(this.normalize().alphaNormalize().getEncodedBytes());
  }

  public final String toString() {
    return this.accept(ToStringVisitor.instance).toString();
  }

  Expr getNonNote() {
    Expr current = this;

    while (current.tag == Tags.NOTE) {
      current = ((Parsed) current).base;
    }

    return current;
  }

  /**
   * Convenience methods for working with expressions.
   *
   * <p>Note that many of these operations represent "down-casts", and return {@code null} in cases
   * where the expression doesn't have the requested constructor.
   */
  public static final class Util {
    private Util() {}

    /** Type-check the given expression and return the inferred type. */
    public static final Expr typeCheck(Expr expr) {
      return expr.accept(new TypeCheck());
    }

    /** Return the first difference between the structure of two expressions as a pair. */
    public static final Entry<Expr, Expr> getFirstDiff(Expr first, Expr second) {
      return first.getFirstDiff(second);
    }

    /** Write an encoded expression to a stream. */
    public static final void encodeToStream(Expr expr, OutputStream stream) {
      Writer.OutputStreamWriter writer = new Writer.OutputStreamWriter(stream);
      expr.accept(new Encode(writer));
    }

    /** Encode an array of bytes as a hex string. */
    public static String encodeHashBytes(byte[] hash) {
      StringBuilder hexString = new StringBuilder();
      for (int i = 0; i < hash.length; i++) {
        String hex = Integer.toHexString(0xff & hash[i]);
        if (hex.length() == 1) hexString.append('0');
        hexString.append(hex);
      }
      return hexString.toString();
    }

    public static final byte[] decodeHashBytes(String input) {
      byte[] bytes = new byte[input.length() / 2];
      for (int i = 0; i < input.length(); i += 2) {

        int d1 = Character.digit(input.charAt(i), 16);
        int d2 = Character.digit(input.charAt(i + 1), 16);
        bytes[i / 2] = (byte) ((d1 << 4) + d2);
      }
      return bytes;
    }

    /** If the expression is an application of {@code List}, return the element type. */
    public static Expr getListArg(Expr expr) {
      return getElementType(expr, "List");
    }

    /** If the expression is an application of {@code Optional}, return the element type. */
    public static Expr getOptionalArg(Expr expr) {
      return getElementType(expr, "Optional");
    }

    /** If the expression is an application of {@code Some}, return the element type. */
    public static Expr getSomeArg(Expr expr) {
      return getElementType(expr, "Some");
    }

    /** If the expression is an application of {@code None}, return the element type. */
    public static Expr getNoneArg(Expr expr) {
      return getElementType(expr, "None");
    }

    /** If the expression is a {@code Bool} literal, return its value. */
    public static final Boolean asBoolLiteral(Expr expr) {
      String asBuiltIn = Util.asBuiltIn(expr);

      if (asBuiltIn != null) {
        if (asBuiltIn.equals("True")) {
          return true;
        } else if (asBuiltIn.equals("False")) {
          return false;
        }
      }
      return null;
    }

    /** If the expression is a {@code Natural} literal, return its value. */
    public static final BigInteger asNaturalLiteral(Expr expr) {
      Expr value = expr.getNonNote();

      if (value.tag == Tags.NATURAL) {
        return ((Constructors.NaturalLiteral) value).value;
      } else {
        return null;
      }
    }

    /** If the expression is an {@code Integer} literal, return its value. */
    public static final BigInteger asIntegerLiteral(Expr expr) {
      Expr value = expr.getNonNote();

      if (value.tag == Tags.INTEGER) {
        return ((Constructors.IntegerLiteral) value).value;
      } else {
        return null;
      }
    }

    /** If the expression is a {@code Double} literal, return its value. */
    public static final Double asDoubleLiteral(Expr expr) {
      Expr value = expr.getNonNote();

      if (value.tag == Tags.DOUBLE) {
        return ((Constructors.DoubleLiteral) value).value;
      } else {
        return null;
      }
    }

    /** If the expression is a {@code Text} literal with no interpolations, return its value. */
    public static final String asSimpleTextLiteral(Expr expr) {
      Expr value = expr.getNonNote();

      if (value.tag == Tags.TEXT) {
        Constructors.TextLiteral text = (Constructors.TextLiteral) value;

        if (text.parts.length == 1) {
          return text.parts[0];
        } else {
          return null;
        }
      } else {
        return null;
      }
    }

    /** If the expression is a Dhall built-in, return its name. */
    public static final String asBuiltIn(Expr expr) {
      Expr value = expr.getNonNote();

      if (value.tag == Tags.BUILT_IN) {
        return ((Constructors.BuiltIn) value).name;
      } else {
        return null;
      }
    }

    /** If the expression is a {@code List} literal, return its contents. */
    public static final List<Expr> asListLiteral(Expr expr) {
      Expr value = expr.getNonNote();

      if (value.tag == Tags.NON_EMPTY_LIST) {
        return Arrays.asList(((Constructors.NonEmptyListLiteral) value).values);
      } else if (value.tag == Tags.EMPTY_LIST) {
        return new ArrayList<Expr>(0);
      } else {
        return null;
      }
    }

    /** If the expression is a record literal, return its fields. */
    public static final List<Entry<String, Expr>> asRecordLiteral(Expr expr) {
      Expr value = expr.getNonNote();

      if (value.tag == Tags.RECORD) {
        return Arrays.asList(((Constructors.RecordLiteral) value).fields);
      } else {
        return null;
      }
    }

    /** If the expression is a record type, return its fields. */
    public static final List<Entry<String, Expr>> asRecordType(Expr expr) {
      Expr value = expr.getNonNote();

      if (value.tag == Tags.RECORD_TYPE) {
        return Arrays.asList(((Constructors.RecordType) value).fields);
      } else {
        return null;
      }
    }

    /** If the expression is a union type, return its fields. */
    public static final List<Entry<String, Expr>> asUnionType(Expr expr) {
      Expr value = expr.getNonNote();

      if (value.tag == Tags.UNION_TYPE) {
        return Arrays.asList(((Constructors.UnionType) value).fields);
      } else {
        return null;
      }
    }

    /** If the expression is a field access, return the base and field name. */
    public static final Entry<Expr, String> asFieldAccess(Expr expr) {
      Expr value = expr.getNonNote();

      if (value.tag == Tags.FIELD_ACCESS) {
        Constructors.FieldAccess fieldAccess = (Constructors.FieldAccess) value;
        return new SimpleImmutableEntry<>(fieldAccess.base, fieldAccess.fieldName);
      } else {
        return null;
      }
    }

    /**
     * If the expression is an application of the specified type constructor, return the element
     * type.
     */
    private static Expr getElementType(Expr expr, String typeConstructor) {
      Expr value = expr.getNonNote();

      if (value.tag == Tags.APPLICATION) {
        Constructors.Application application = (Constructors.Application) value;

        Expr applied = application.base.getNonNote();

        if (applied.tag == Tags.BUILT_IN
            && ((Constructors.BuiltIn) applied).name.equals(typeConstructor)) {
          return application.arg;
        }
      }

      return null;
    }

    public static final String escapeText(String input, boolean quoted) {
      StringBuilder builder = new StringBuilder();

      if (quoted) {
        builder.append("\\\"");
      }

      for (int i = 0; i < input.length(); i++) {
        char c = input.charAt(i);
        if (c == '"') {
          if (quoted) {
            builder.append("\\\\\"");
          } else {
            builder.append("\\\"");
          }
        } else if (c == '$') {
          if (quoted) {
            builder.append("\\\\u0024");
          } else {
            builder.append("$");
          }
        } else if (c == '\\') {
          if (quoted) {
            builder.append("\\\\");
          } else {
            builder.append("\\");
          }
        } else if (c >= '\u0000' && c <= '\u001f') {
          if (quoted) {
            builder.append('\\');
          }
          String asHex = Long.toHexString((long) c);

          builder.append("\\u");

          if (asHex.length() < 2) {
            builder.append('0');
          }
          if (asHex.length() < 3) {
            builder.append('0');
          }
          if (asHex.length() < 4) {
            builder.append('0');
          }
          builder.append(asHex);
        } else {
          builder.append(c);
        }
      }
      if (quoted) {
        builder.append("\\\"");
      }

      return builder.toString();
    }

    /** Desugar the complete operator ({@code ::}). */
    public static final Expr desugarComplete(Expr lhs, Expr rhs) {

      return Expr.makeAnnotated(
          Expr.makeOperatorApplication(Operator.PREFER, Expr.makeFieldAccess(lhs, "default"), rhs),
          Expr.makeFieldAccess(lhs, "Type"));
    }

    /**
     * If the expression is a lambda, apply it to the given argument.
     *
     * <p>Returns null if the expression is not a lambda.
     */
    public static final Expr applyAsLambda(Expr expr, Expr arg) {
      Expr value = expr.getNonNote();

      if (value.tag == Tags.LAMBDA) {
        Constructors.Lambda lambda = ((Constructors.Lambda) value);
        return lambda.result.substitute(lambda.name, arg);
      } else {
        return null;
      }
    }

    static final Entry<Expr, Expr> flattenToMapRecord(List<Entry<String, Expr>> fields) {
      if (fields == null || fields.size() != 2) {
        return null;
      }

      Expr key = null;
      Expr value = null;

      for (Entry<String, Expr> entry : fields) {
        if (entry.getKey().equals(Constants.MAP_KEY_FIELD_NAME)) {
          key = entry.getValue();
        } else if (entry.getKey().equals(Constants.MAP_VALUE_FIELD_NAME)) {
          value = entry.getValue();
        }
      }

      if (key == null || value == null) {
        return null;
      }

      return new SimpleImmutableEntry<>(key, value);
    }
  }

  /** Represents the first part of a {@code let}-expression. */
  public static final class LetBinding<A> {
    private final String name;
    private final A type;
    private final A value;

    public LetBinding(String name, A type, A value) {
      this.name = name;
      this.type = type;
      this.value = value;
    }

    public String getName() {
      return this.name;
    }

    public boolean hasType() {
      return this.type != null;
    }

    public A getType() {
      return this.type;
    }

    public A getValue() {
      return this.value;
    }
  }

  /** Modifier specifying how an import should be parsed into a Dhall expression. */
  public static enum ImportMode {
    CODE,
    RAW_TEXT,
    LOCATION;

    public String toString() {
      if (this == RAW_TEXT) {
        return "Text";
      } else if (this == LOCATION) {
        return "Location";
      } else {
        return "Code";
      }
    }
  }

  /** Definitions of Dhall built-ins and other frequently-used expressions. */
  public static final class Constants {
    private static final Entry[] emptyFields = {};

    public static final Expr UNDERSCORE = makeIdentifier("_");
    public static final Expr SORT = new Constructors.BuiltIn("Sort");
    public static final Expr KIND = new Constructors.BuiltIn("Kind");
    public static final Expr TYPE = new Constructors.BuiltIn("Type");
    public static final Expr BOOL = new Constructors.BuiltIn("Bool");
    public static final Expr TRUE = new Constructors.BuiltIn("True");
    public static final Expr FALSE = new Constructors.BuiltIn("False");
    public static final Expr LIST = new Constructors.BuiltIn("List");
    public static final Expr OPTIONAL = new Constructors.BuiltIn("Optional");
    public static final Expr DOUBLE = new Constructors.BuiltIn("Double");
    public static final Expr NATURAL = new Constructors.BuiltIn("Natural");
    public static final Expr INTEGER = new Constructors.BuiltIn("Integer");
    public static final Expr TEXT = new Constructors.BuiltIn("Text");
    public static final Expr NONE = new Constructors.BuiltIn("None");
    public static final Expr SOME = new Constructors.BuiltIn("Some");

    public static final Expr DATE = new Constructors.BuiltIn("Date");
    public static final Expr TIME = new Constructors.BuiltIn("Time");
    public static final Expr TIME_ZONE = new Constructors.BuiltIn("TimeZone");

    public static final Expr NATURAL_FOLD = new Constructors.BuiltIn("Natural/fold");
    public static final Expr LIST_FOLD = new Constructors.BuiltIn("List/fold");
    public static final Expr ZERO = makeNaturalLiteral(BigInteger.ZERO);
    public static final Expr EMPTY_RECORD_LITERAL = makeRecordLiteral(emptyFields);
    public static final Expr EMPTY_RECORD_TYPE = makeRecordType(emptyFields);
    public static final Expr EMPTY_UNION_TYPE = makeUnionType(emptyFields);
    public static final Expr LOCATION_TYPE =
        makeUnionType(
            new Entry[] {
              new SimpleImmutableEntry<>("Environment", TEXT),
              new SimpleImmutableEntry<>("Local", TEXT),
              new SimpleImmutableEntry<>("Missing", null),
              new SimpleImmutableEntry<>("Remote", TEXT)
            });
    public static final String MAP_KEY_FIELD_NAME = "mapKey";
    public static final String MAP_VALUE_FIELD_NAME = "mapValue";

    private static final Map<String, Expr> builtIns = new HashMap<String, Expr>(34);
    private static final Set<String> keywords = new HashSet<String>(16);

    static {
      builtIns.put("Bool", BOOL);
      builtIns.put("Date", DATE);
      builtIns.put("Double", DOUBLE);
      builtIns.put("Double/show", new Constructors.BuiltIn("Double/show"));
      builtIns.put("False", FALSE);
      builtIns.put("Integer", INTEGER);
      builtIns.put("Integer/clamp", new Constructors.BuiltIn("Integer/clamp"));
      builtIns.put("Integer/negate", new Constructors.BuiltIn("Integer/negate"));
      builtIns.put("Integer/show", new Constructors.BuiltIn("Integer/show"));
      builtIns.put("Integer/toDouble", new Constructors.BuiltIn("Integer/toDouble"));
      builtIns.put("Kind", KIND);
      builtIns.put("List", LIST);
      builtIns.put("List/build", new Constructors.BuiltIn("List/build"));
      builtIns.put("List/fold", new Constructors.BuiltIn("List/fold"));
      builtIns.put("List/head", new Constructors.BuiltIn("List/head"));
      builtIns.put("List/indexed", new Constructors.BuiltIn("List/indexed"));
      builtIns.put("List/last", new Constructors.BuiltIn("List/last"));
      builtIns.put("List/length", new Constructors.BuiltIn("List/length"));
      builtIns.put("List/reverse", new Constructors.BuiltIn("List/reverse"));
      builtIns.put("Natural", NATURAL);
      builtIns.put("Natural/build", new Constructors.BuiltIn("Natural/build"));
      builtIns.put("Natural/even", new Constructors.BuiltIn("Natural/even"));
      builtIns.put("Natural/fold", new Constructors.BuiltIn("Natural/fold"));
      builtIns.put("Natural/isZero", new Constructors.BuiltIn("Natural/isZero"));
      builtIns.put("Natural/odd", new Constructors.BuiltIn("Natural/odd"));
      builtIns.put("Natural/show", new Constructors.BuiltIn("Natural/show"));
      builtIns.put("Natural/subtract", new Constructors.BuiltIn("Natural/subtract"));
      builtIns.put("Natural/toInteger", new Constructors.BuiltIn("Natural/toInteger"));
      builtIns.put("None", NONE);
      builtIns.put("Optional", OPTIONAL);
      builtIns.put("Some", SOME);
      builtIns.put("Sort", SORT);
      builtIns.put("Text", TEXT);
      builtIns.put("Text/replace", new Constructors.BuiltIn("Text/replace"));
      builtIns.put("Text/show", new Constructors.BuiltIn("Text/show"));
      builtIns.put("Time", TIME);
      builtIns.put("TimeZone", TIME_ZONE);
      builtIns.put("True", TRUE);
      builtIns.put("Type", TYPE);

      keywords.add("if");
      keywords.add("then");
      keywords.add("else");
      keywords.add("let");
      keywords.add("in");
      keywords.add("using");
      keywords.add("missing");
      keywords.add("assert");
      keywords.add("as");
      keywords.add("Infinity");
      keywords.add("NaN");
      keywords.add("merge");
      keywords.add("Some");
      keywords.add("toMap");
      keywords.add("forall");
      keywords.add("with");
    }

    static Expr getBuiltIn(String name) {
      return builtIns.get(name);
    }

    public static boolean isBuiltIn(String name) {
      return builtIns.containsKey(name);
    }

    public static boolean isKeyword(String name) {
      return keywords.contains(name);
    }
  }

  /** Represents a Dhall expression that's been parsed and has associated source information. */
  public static final class Parsed extends Expr {
    final Expr base;
    final Source source;

    public Parsed(Expr base, Source source) {
      super(Tags.NOTE);
      this.base = base;
      this.source = source;
    }

    public final Source getSource() {
      return this.source;
    }

    public final <A> A accept(ExternalVisitor<A> visitor) {
      return visitor.onNote(base, this.source);
    }

    final <A> void advance(VisitState<A> state) {
      this.base.advance(state);
    }
  }

  public static final Expr makeDoubleLiteral(double value) {
    return new Constructors.DoubleLiteral(value);
  }

  public static final Expr makeNaturalLiteral(BigInteger value) {
    return new Constructors.NaturalLiteral(value);
  }

  public static final Expr makeIntegerLiteral(BigInteger value) {
    return new Constructors.IntegerLiteral(value);
  }

  public static final Expr makeDateLiteral(int year, int month, int day) {
    return new Constructors.DateLiteral(year, month, day);
  }

  public static final Expr makeTimeLiteral(
      int hour, int minute, int second, BigDecimal fractional) {
    return new Constructors.TimeLiteral(hour, minute, second, fractional);
  }

  public static final Expr makeTimeZoneLiteral(int seconds) {
    return new Constructors.TimeZoneLiteral(seconds);
  }

  public static final Expr makeTextLiteral(String[] parts, Expr[] interpolated) {
    return new Constructors.TextLiteral(parts, interpolated);
  }

  public static final Expr makeTextLiteral(String[] parts, Collection<Expr> interpolated) {
    return new Constructors.TextLiteral(parts, interpolated.toArray(new Expr[interpolated.size()]));
  }

  private static final Expr[] emptyExprArray = {};

  public static final Expr makeTextLiteral(String value) {
    String[] parts = {value};
    return new Constructors.TextLiteral(parts, emptyExprArray);
  }

  public static final Expr makeApplication(Expr base, Expr arg) {
    return new Constructors.Application(base, arg);
  }

  public static final Expr makeApplication(Expr base, Expr[] args) {
    Expr acc = base;
    for (int i = 0; i < args.length; i++) {
      acc = Expr.makeApplication(acc, args[i]);
    }
    return acc;
  }

  public static final Expr makeApplication(Expr base, List<Expr> args) {
    Expr acc = base;
    for (int i = 0; i < args.size(); i++) {
      acc = Expr.makeApplication(acc, args.get(i));
    }
    return acc;
  }

  public static final Expr makeOperatorApplication(Operator operator, Expr lhs, Expr rhs) {
    return new Constructors.OperatorApplication(operator, lhs, rhs);
  }

  public static final Expr makeIf(Expr cond, Expr thenValue, Expr elseValue) {
    return new Constructors.If(cond, thenValue, elseValue);
  }

  public static final Expr makeLambda(String param, Expr input, Expr result) {
    return new Constructors.Lambda(param, input, result);
  }

  public static final Expr makePi(String param, Expr input, Expr result) {
    return new Constructors.Pi(param, input, result);
  }

  public static final Expr makePi(Expr input, Expr result) {
    return makePi("_", input, result);
  }

  public static final Expr makeAssert(Expr base) {
    return new Constructors.Assert(base);
  }

  public static final Expr makeFieldAccess(Expr base, String fieldName) {
    return new Constructors.FieldAccess(base, fieldName);
  }

  public static final Expr makeProjection(Expr base, String[] fieldNames) {
    return new Constructors.Projection(base, fieldNames);
  }

  public static final Expr makeProjectionByType(Expr base, Expr tpe) {
    return new Constructors.ProjectionByType(base, tpe);
  }

  public static final Expr makeBuiltIn(String name) {
    if (Constants.getBuiltIn(name) == null) {
      throw new IllegalArgumentException(name + " is not a built-in");
    }
    return Constants.getBuiltIn(name);
  }

  public static final Expr makeIdentifier(String name, long index) {
    return new Constructors.Identifier(name, index);
  }

  public static final Expr makeIdentifier(String name) {
    return makeIdentifier(name, 0);
  }

  public static final Expr makeRecordLiteral(Entry<String, Expr>[] fields) {
    return new Constructors.RecordLiteral(fields);
  }

  public static final Expr makeRecordLiteral(Collection<Entry<String, Expr>> fields) {
    return new Constructors.RecordLiteral(fields.toArray(new Entry[fields.size()]));
  }

  public static final Expr makeRecordLiteral(String key, Expr value) {
    return new Constructors.RecordLiteral(new Entry[] {new SimpleImmutableEntry<>(key, value)});
  }

  public static final Expr makeRecordType(Entry<String, Expr>[] fields) {
    return new Constructors.RecordType(fields);
  }

  public static final Expr makeRecordType(Collection<Entry<String, Expr>> fields) {
    return new Constructors.RecordType(fields.toArray(new Entry[fields.size()]));
  }

  public static final Expr makeUnionType(Entry<String, Expr>[] fields) {
    return new Constructors.UnionType(fields);
  }

  public static final Expr makeUnionType(Collection<Entry<String, Expr>> fields) {
    return new Constructors.UnionType(fields.toArray(new Entry[fields.size()]));
  }

  public static final Expr makeNonEmptyListLiteral(Expr[] values) {
    return new Constructors.NonEmptyListLiteral(values);
  }

  public static final Expr makeNonEmptyListLiteral(Collection<Expr> values) {
    return new Constructors.NonEmptyListLiteral(values.toArray(new Expr[values.size()]));
  }

  public static final Expr makeEmptyListLiteral(Expr tpe) {
    return new Constructors.EmptyListLiteral(tpe);
  }

  public static final Expr makeNote(Expr base, Source source) {
    return new Parsed(base, source);
  }

  public static final Expr makeLet(String name, Expr type, Expr value, Expr body) {
    return new Constructors.Let(name, type, value, body);
  }

  public static final Expr makeLet(List<LetBinding<Expr>> bindings, Expr body) {
    Expr result = body;

    for (int i = bindings.size() - 1; i >= 0; i--) {
      LetBinding<Expr> binding = bindings.get(i);
      result =
          new Constructors.Let(binding.getName(), binding.getType(), binding.getValue(), result);
    }

    return result;
  }

  public static final Expr makeLet(String name, Expr value, Expr body) {
    return makeLet(name, null, value, body);
  }

  public static final Expr makeAnnotated(Expr base, Expr type) {
    return new Constructors.Annotated(base, type);
  }

  public static final Expr makeToMap(Expr base, Expr type) {
    return new Constructors.ToMap(base, type);
  }

  public static final Expr makeToMap(Expr base) {
    return makeToMap(base, null);
  }

  public static final Expr makeWith(Expr base, String[] path, Expr value) {
    return new Constructors.With(base, path, value);
  }

  public static final Expr makeMerge(Expr left, Expr right, Expr type) {
    return new Constructors.Merge(left, right, type);
  }

  public static final Expr makeMerge(Expr left, Expr right) {
    return makeMerge(left, right, null);
  }

  public static final Expr makeLocalImport(Path path, ImportMode mode, byte[] hash) {
    return new Constructors.LocalImport(path, mode, hash);
  }

  public static final Expr makeClasspathImport(Path path, ImportMode mode, byte[] hash) {
    return new Constructors.ClasspathImport(path, mode, hash);
  }

  public static final Expr makeRemoteImport(URI url, Expr using, ImportMode mode, byte[] hash) {
    return new Constructors.RemoteImport(url, using, mode, hash);
  }

  public static final Expr makeEnvImport(String value, ImportMode mode, byte[] hash) {
    return new Constructors.EnvImport(value, mode, hash);
  }

  public static final Expr makeMissingImport(ImportMode mode, byte[] hash) {
    return new Constructors.MissingImport(mode, hash);
  }

  private final Entry<Expr, Expr> getFirstDiff(Expr other) {
    Deque<Expr> stackA = new ArrayDeque<Expr>();
    Deque<Expr> stackB = new ArrayDeque<Expr>();

    Expr currentA = this;
    Expr currentB = other;

    stackA.add(currentA);
    stackB.add(currentB);

    while (true) {
      currentA = stackA.poll();
      currentB = stackB.poll();

      if (currentA == null || currentB == null) {
        break;
      }

      currentA = currentA.getNonNote();
      currentB = currentB.getNonNote();

      if (currentA.tag != currentB.tag) {
        break;
      }

      if (currentA.tag == Tags.NATURAL) {
        if (((Constructors.NaturalLiteral) currentA)
            .value.equals(((Constructors.NaturalLiteral) currentB).value)) {
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.INTEGER) {
        if (((Constructors.IntegerLiteral) currentA)
            .value.equals(((Constructors.IntegerLiteral) currentB).value)) {
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.DOUBLE) {
        // We must compare double literals using the binary encoding.
        if (Arrays.equals(currentA.getEncodedBytes(), currentB.getEncodedBytes())) {
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.BUILT_IN) {
        Constructors.BuiltIn builtInA = (Constructors.BuiltIn) currentA;
        Constructors.BuiltIn builtInB = (Constructors.BuiltIn) currentB;
        if (builtInA.name.equals(builtInB.name)) {
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.IDENTIFIER) {
        Constructors.Identifier identifierA = (Constructors.Identifier) currentA;
        Constructors.Identifier identifierB = (Constructors.Identifier) currentB;
        if (identifierA.name.equals(identifierB.name) && identifierA.index == identifierB.index) {
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.LAMBDA) {
        Constructors.Lambda lambdaA = (Constructors.Lambda) currentA;
        Constructors.Lambda lambdaB = (Constructors.Lambda) currentB;
        if (lambdaA.name.equals(lambdaB.name)) {
          stackA.add(lambdaA.type);
          stackB.add(lambdaB.type);
          stackA.add(lambdaA.result);
          stackB.add(lambdaB.result);
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.PI) {
        Constructors.Pi piA = (Constructors.Pi) currentA;
        Constructors.Pi piB = (Constructors.Pi) currentB;
        if (piA.name.equals(piB.name) && !(piA.type == null ^ piB.type == null)) {
          if (piA.type != null) {
            stackA.add(piA.type);
            stackB.add(piB.type);
          }
          stackA.add(piA.result);
          stackB.add(piB.result);
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.LET) {
        Constructors.Let letA = (Constructors.Let) currentA;
        Constructors.Let letB = (Constructors.Let) currentB;
        if (letA.name.equals(letB.name) && !(letA.type == null ^ letB.type == null)) {
          if (letA.type != null) {
            stackA.add(letA.type);
            stackB.add(letB.type);
          }
          stackA.add(letA.value);
          stackB.add(letB.value);
          stackA.add(letA.body);
          stackB.add(letB.body);
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.TEXT) {
        Constructors.TextLiteral textA = (Constructors.TextLiteral) currentA;
        Constructors.TextLiteral textB = (Constructors.TextLiteral) currentB;
        if (Arrays.equals(textA.interpolated, textB.interpolated)) {
          for (Expr interpolation : textA.interpolated) {
            stackA.add(interpolation);
          }

          for (Expr interpolation : textB.interpolated) {
            stackB.add(interpolation);
          }
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.NON_EMPTY_LIST) {
        Constructors.NonEmptyListLiteral nonEmptyListA =
            (Constructors.NonEmptyListLiteral) currentA;
        Constructors.NonEmptyListLiteral nonEmptyListB =
            (Constructors.NonEmptyListLiteral) currentB;
        for (Expr value : nonEmptyListA.values) {
          stackA.add(value);
        }

        for (Expr value : nonEmptyListB.values) {
          stackB.add(value);
        }
        continue;
      } else if (currentA.tag == Tags.EMPTY_LIST) {
        Constructors.EmptyListLiteral emptyListA = (Constructors.EmptyListLiteral) currentA;
        Constructors.EmptyListLiteral emptyListB = (Constructors.EmptyListLiteral) currentB;
        stackA.add(emptyListA.type);
        stackB.add(emptyListB.type);
        continue;
      } else if (currentA.tag == Tags.RECORD) {
        Constructors.RecordLiteral recordA = (Constructors.RecordLiteral) currentA;
        Constructors.RecordLiteral recordB = (Constructors.RecordLiteral) currentB;
        Entry<String, Expr>[] fieldsA = recordA.fields;
        Entry<String, Expr>[] fieldsB = recordB.fields;

        if (fieldsA.length == fieldsB.length) {
          for (int i = 0; i < fieldsA.length; i++) {
            if (!fieldsA[i].getKey().equals(fieldsB[i].getKey())) {
              return new SimpleImmutableEntry<>(fieldsA[i].getValue(), fieldsB[i].getValue());
            }

            stackA.add(fieldsA[i].getValue());
            stackB.add(fieldsB[i].getValue());
          }
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.RECORD_TYPE) {
        Constructors.RecordType recordTypeA = (Constructors.RecordType) currentA;
        Constructors.RecordType recordTypeB = (Constructors.RecordType) currentB;
        Entry<String, Expr>[] fieldsA = recordTypeA.fields;
        Entry<String, Expr>[] fieldsB = recordTypeB.fields;

        if (fieldsA.length == fieldsB.length) {
          for (int i = 0; i < fieldsA.length; i++) {
            if (!fieldsA[i].getKey().equals(fieldsB[i].getKey())) {
              return new SimpleImmutableEntry<>(fieldsA[i].getValue(), fieldsB[i].getValue());
            }

            stackA.add(fieldsA[i].getValue());
            stackB.add(fieldsB[i].getValue());
          }
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.UNION_TYPE) {
        Constructors.UnionType unionTypeA = (Constructors.UnionType) currentA;
        Constructors.UnionType unionTypeB = (Constructors.UnionType) currentB;
        Entry<String, Expr>[] fieldsA = unionTypeA.fields;
        Entry<String, Expr>[] fieldsB = unionTypeB.fields;

        if (fieldsA.length == fieldsB.length) {
          for (int i = 0; i < fieldsA.length; i++) {
            if (!fieldsA[i].getKey().equals(fieldsB[i].getKey())) {
              return new SimpleImmutableEntry<>(fieldsA[i].getValue(), fieldsB[i].getValue());
            }

            if (fieldsA[i].getValue() != null && fieldsB[i].getValue() != null) {
              stackA.add(fieldsA[i].getValue());
              stackB.add(fieldsB[i].getValue());
            } else if (fieldsA[i].getValue() == null ^ fieldsB[i].getValue() == null) {
              return new SimpleImmutableEntry<>(currentA, currentB);
            }
          }
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.FIELD_ACCESS) {
        Constructors.FieldAccess fieldAccessA = (Constructors.FieldAccess) currentA;
        Constructors.FieldAccess fieldAccessB = (Constructors.FieldAccess) currentB;

        if (fieldAccessA.fieldName.equals(fieldAccessB.fieldName)) {
          stackA.add(fieldAccessA.base);
          stackB.add(fieldAccessB.base);
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.PROJECTION) {
        Constructors.Projection projectionA = (Constructors.Projection) currentA;
        Constructors.Projection projectionB = (Constructors.Projection) currentB;

        if (Arrays.equals(projectionA.fieldNames, projectionB.fieldNames)) {
          stackA.add(projectionA.base);
          stackB.add(projectionB.base);
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.PROJECTION_BY_TYPE) {
        Constructors.ProjectionByType projectionByTypeA = (Constructors.ProjectionByType) currentA;
        Constructors.ProjectionByType projectionByTypeB = (Constructors.ProjectionByType) currentB;

        stackA.add(projectionByTypeA.base);
        stackB.add(projectionByTypeB.base);
        stackA.add(projectionByTypeA.type);
        stackB.add(projectionByTypeB.type);
        continue;
      } else if (currentA.tag == Tags.APPLICATION) {
        Constructors.Application applicationA = (Constructors.Application) currentA;
        Constructors.Application applicationB = (Constructors.Application) currentB;

        stackA.add(applicationA.base);
        stackB.add(applicationB.base);
        stackA.add(applicationA.arg);
        stackB.add(applicationB.arg);
        continue;
      } else if (currentA.tag == Tags.OPERATOR_APPLICATION) {
        Constructors.OperatorApplication operatorApplicationA =
            (Constructors.OperatorApplication) currentA;
        Constructors.OperatorApplication operatorApplicationB =
            (Constructors.OperatorApplication) currentB;

        if (operatorApplicationA.operator.equals(operatorApplicationB.operator)) {
          stackA.add(operatorApplicationA.lhs);
          stackB.add(operatorApplicationB.lhs);
          stackA.add(operatorApplicationA.rhs);
          stackB.add(operatorApplicationB.rhs);
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.IF) {
        Constructors.If ifA = (Constructors.If) currentA;
        Constructors.If ifB = (Constructors.If) currentB;

        stackA.add(ifA.predicate);
        stackB.add(ifB.predicate);
        stackA.add(ifA.thenValue);
        stackB.add(ifB.thenValue);
        stackA.add(ifA.elseValue);
        stackB.add(ifB.elseValue);
        continue;
      } else if (currentA.tag == Tags.ANNOTATED) {
        Constructors.Annotated annotatedA = (Constructors.Annotated) currentA;
        Constructors.Annotated annotatedB = (Constructors.Annotated) currentB;

        stackA.add(annotatedA.base);
        stackB.add(annotatedB.base);
        stackA.add(annotatedA.type);
        stackB.add(annotatedB.type);
        continue;
      } else if (currentA.tag == Tags.ASSERT) {
        Constructors.Assert assertA = (Constructors.Assert) currentA;
        Constructors.Assert assertB = (Constructors.Assert) currentB;

        stackA.add(assertA.base);
        stackB.add(assertB.base);
        continue;
      } else if (currentA.tag == Tags.MERGE) {
        Constructors.Merge mergeA = (Constructors.Merge) currentA;
        Constructors.Merge mergeB = (Constructors.Merge) currentB;
        if (!(mergeA.type == null ^ mergeB.type == null)) {
          stackA.add(mergeA.handlers);
          stackB.add(mergeB.handlers);
          stackA.add(mergeA.union);
          stackB.add(mergeB.union);
          if (mergeA.type != null) {
            stackA.add(mergeA.type);
            stackB.add(mergeB.type);
          }
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.TO_MAP) {
        Constructors.ToMap toMapA = (Constructors.ToMap) currentA;
        Constructors.ToMap toMapB = (Constructors.ToMap) currentB;
        if (!(toMapA.type == null ^ toMapB.type == null)) {
          stackA.add(toMapA.base);
          stackB.add(toMapB.base);
          if (toMapA.type != null) {
            stackA.add(toMapA.type);
            stackB.add(toMapB.type);
          }
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.MISSING_IMPORT) {
        Constructors.MissingImport missingImportA = (Constructors.MissingImport) currentA;
        Constructors.MissingImport missingImportB = (Constructors.MissingImport) currentB;
        if (missingImportA.mode.equals(missingImportB.mode)
            && Arrays.equals(missingImportA.hash, missingImportB.hash)) {
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.ENV_IMPORT) {
        Constructors.EnvImport envImportA = (Constructors.EnvImport) currentA;
        Constructors.EnvImport envImportB = (Constructors.EnvImport) currentB;
        if (envImportA.name.equals(envImportB.name)
            && envImportA.mode.equals(envImportB.mode)
            && Arrays.equals(envImportA.hash, envImportB.hash)) {
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.LOCAL_IMPORT) {
        Constructors.LocalImport localImportA = (Constructors.LocalImport) currentA;
        Constructors.LocalImport localImportB = (Constructors.LocalImport) currentB;
        if (localImportA.path.equals(localImportB.path)
            && localImportA.mode.equals(localImportB.mode)
            && Arrays.equals(localImportA.hash, localImportB.hash)) {
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.CLASSPATH_IMPORT) {
        Constructors.ClasspathImport classpathImportA = (Constructors.ClasspathImport) currentA;
        Constructors.ClasspathImport classpathImportB = (Constructors.ClasspathImport) currentB;
        if (classpathImportA.path.equals(classpathImportB.path)
            && classpathImportA.mode.equals(classpathImportB.mode)
            && Arrays.equals(classpathImportA.hash, classpathImportB.hash)) {
          continue;
        } else {
          break;
        }
      } else if (currentA.tag == Tags.REMOTE_IMPORT) {
        Constructors.RemoteImport remoteImportA = (Constructors.RemoteImport) currentA;
        Constructors.RemoteImport remoteImportB = (Constructors.RemoteImport) currentB;
        if (remoteImportA.url.equals(remoteImportB.url)
            && remoteImportA.mode.equals(remoteImportB.mode)
            && Arrays.equals(remoteImportA.hash, remoteImportB.hash)) {
          continue;
        } else {
          break;
        }
      }
    }

    if (currentA == null && currentB == null) {
      return null;
    } else {
      return new SimpleImmutableEntry<>(currentA, currentB);
    }
  }
}


================================================
FILE: modules/core/src/main/java/org/dhallj/core/ExternalVisitor.java
================================================
package org.dhallj.core;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.nio.file.Path;
import java.util.Map.Entry;

/**
 * Represents a function from a Dhall expression to a value.
 *
 * @param A The result type
 */
public interface Exte
Download .txt
gitextract_55f_2v4h/

├── .github/
│   └── workflows/
│       ├── ci.yml
│       └── clean.yml
├── .gitignore
├── .gitmodules
├── .scalafmt.conf
├── LICENSE
├── README.md
├── WORKSPACE
├── benchmarks/
│   └── src/
│       └── main/
│           └── scala/
│               └── org/
│                   └── dhallj/
│                       └── benchmarks/
│                           ├── EncodingBenchmark.scala
│                           └── ParsingBenchmark.scala
├── build.sbt
├── cli/
│   └── src/
│       └── main/
│           └── java/
│               └── org/
│                   └── dhallj/
│                       └── cli/
│                           └── Dhall.java
├── javascript/
│   ├── BUILD
│   ├── api/
│   │   ├── BUILD
│   │   ├── DhallJs.java
│   │   └── dhall.js
│   └── jre/
│       ├── BUILD
│       ├── BufferedReader.java
│       ├── InputStreamReader.java
│       ├── InvalidPathException.java
│       ├── Path.java
│       ├── Paths.java
│       ├── URI.java
│       └── URISyntaxException.java
├── modules/
│   ├── ast/
│   │   └── src/
│   │       └── main/
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── ast/
│   │                           └── package.scala
│   ├── cats/
│   │   └── src/
│   │       ├── main/
│   │       │   └── scala/
│   │       │       └── org/
│   │       │           └── dhallj/
│   │       │               └── cats/
│   │       │                   └── LiftVisitor.scala
│   │       └── test/
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── cats/
│   │                           └── LiftVisitorSuite.scala
│   ├── circe/
│   │   └── src/
│   │       ├── main/
│   │       │   └── scala/
│   │       │       └── org/
│   │       │           └── dhallj/
│   │       │               └── circe/
│   │       │                   ├── CirceHandler.scala
│   │       │                   └── Converter.scala
│   │       └── test/
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── circe/
│   │                           ├── CirceConverterSuite.scala
│   │                           └── JsonCleaner.scala
│   ├── core/
│   │   ├── BUILD
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── org/
│   │       │           └── dhallj/
│   │       │               ├── cbor/
│   │       │               │   ├── AdditionalInfo.java
│   │       │               │   ├── CborException.java
│   │       │               │   ├── HalfFloat.java
│   │       │               │   ├── MajorType.java
│   │       │               │   ├── NullVisitor.java
│   │       │               │   ├── Reader.java
│   │       │               │   ├── Visitor.java
│   │       │               │   └── Writer.java
│   │       │               └── core/
│   │       │                   ├── ArrayIterable.java
│   │       │                   ├── Constructors.java
│   │       │                   ├── DhallException.java
│   │       │                   ├── Expr.java
│   │       │                   ├── ExternalVisitor.java
│   │       │                   ├── IsResolved.java
│   │       │                   ├── Operator.java
│   │       │                   ├── Source.java
│   │       │                   ├── Tags.java
│   │       │                   ├── ToStringVisitor.java
│   │       │                   ├── VisitState.java
│   │       │                   ├── Visitor.java
│   │       │                   ├── binary/
│   │       │                   │   ├── CborDecodingVisitor.java
│   │       │                   │   ├── Decode.java
│   │       │                   │   ├── DecodingException.java
│   │       │                   │   ├── Encode.java
│   │       │                   │   └── Label.java
│   │       │                   ├── converters/
│   │       │                   │   ├── JsonConverter.java
│   │       │                   │   └── JsonHandler.java
│   │       │                   ├── normalization/
│   │       │                   │   ├── AlphaNormalize.java
│   │       │                   │   ├── BetaNormalize.java
│   │       │                   │   ├── BetaNormalizeApplication.java
│   │       │                   │   ├── BetaNormalizeFieldAccess.java
│   │       │                   │   ├── BetaNormalizeIf.java
│   │       │                   │   ├── BetaNormalizeMerge.java
│   │       │                   │   ├── BetaNormalizeOperatorApplication.java
│   │       │                   │   ├── BetaNormalizeProjection.java
│   │       │                   │   ├── BetaNormalizeTextLiteral.java
│   │       │                   │   ├── BetaNormalizeToMap.java
│   │       │                   │   ├── BetaNormalizeWith.java
│   │       │                   │   ├── NormalizationUtils.java
│   │       │                   │   ├── Shift.java
│   │       │                   │   └── Substitute.java
│   │       │                   └── typechecking/
│   │       │                       ├── BuiltInTypes.java
│   │       │                       ├── CheckEquivalence.java
│   │       │                       ├── Context.java
│   │       │                       ├── NonNegativeIndices.java
│   │       │                       ├── TypeCheck.java
│   │       │                       ├── TypeCheckApplication.java
│   │       │                       ├── TypeCheckFailure.java
│   │       │                       └── Universe.java
│   │       └── test/
│   │           └── java/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── cbor/
│   │                           ├── CborSuite.scala
│   │                           └── HalfFloatSuite.scala
│   ├── imports/
│   │   ├── README.md
│   │   └── src/
│   │       ├── main/
│   │       │   └── scala/
│   │       │       └── org/
│   │       │           └── dhallj/
│   │       │               └── imports/
│   │       │                   ├── Canonicalization.scala
│   │       │                   ├── CorsComplianceCheck.scala
│   │       │                   ├── ImportCache.scala
│   │       │                   ├── ImportContext.scala
│   │       │                   ├── ReferentialSanityCheck.scala
│   │       │                   ├── ResolveImports.scala
│   │       │                   ├── ResolveImportsVisitor.scala
│   │       │                   ├── ToHeaders.scala
│   │       │                   └── syntax/
│   │       │                       └── package.scala
│   │       └── test/
│   │           ├── resources/
│   │           │   ├── alternate/
│   │           │   │   ├── other.dhall
│   │           │   │   └── package.dhall
│   │           │   ├── cache-write/
│   │           │   │   └── package.dhall
│   │           │   ├── cyclic/
│   │           │   │   ├── other.dhall
│   │           │   │   └── package.dhall
│   │           │   ├── cyclic-relative-paths/
│   │           │   │   ├── other.dhall
│   │           │   │   └── package.dhall
│   │           │   ├── hashed/
│   │           │   │   └── package.dhall
│   │           │   ├── local/
│   │           │   │   └── package.dhall
│   │           │   ├── local-local-absolute/
│   │           │   │   └── package.dhall
│   │           │   ├── local-local-absolute-2/
│   │           │   │   └── package.dhall
│   │           │   ├── local-local-relative/
│   │           │   │   ├── other.dhall
│   │           │   │   └── package.dhall
│   │           │   ├── local-remote/
│   │           │   │   └── package.dhall
│   │           │   ├── multiple-imports/
│   │           │   │   ├── other.dhall
│   │           │   │   ├── other2.dhall
│   │           │   │   └── package.dhall
│   │           │   └── text-import/
│   │           │       └── package.dhall
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── imports/
│   │                           ├── CanonicalizationSuite.scala
│   │                           ├── CorsComplianceCheckSuite.scala
│   │                           ├── ImportCacheSuite.scala
│   │                           ├── ImportResolutionSuite.scala
│   │                           ├── ReferentialSanityCheckSuite.scala
│   │                           └── ToHeadersSuite.scala
│   ├── imports-mini/
│   │   └── src/
│   │       └── main/
│   │           └── java/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── imports/
│   │                           └── mini/
│   │                               ├── ResolutionVisitor.java
│   │                               └── Resolver.java
│   ├── javagen/
│   │   └── src/
│   │       └── main/
│   │           └── java/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── javagen/
│   │                           ├── Code.scala
│   │                           ├── ToCodeVisitor.scala
│   │                           └── package.scala
│   ├── jawn/
│   │   └── src/
│   │       ├── main/
│   │       │   └── scala/
│   │       │       └── org/
│   │       │           └── dhallj/
│   │       │               └── jawn/
│   │       │                   ├── FacadeHandler.scala
│   │       │                   └── JawnConverter.scala
│   │       └── test/
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── jawn/
│   │                           └── JawnConverterSuite.scala
│   ├── parser/
│   │   ├── BUILD
│   │   └── src/
│   │       ├── main/
│   │       │   ├── java/
│   │       │   │   └── org/
│   │       │   │       └── dhallj/
│   │       │   │           └── parser/
│   │       │   │               ├── DhallParser.java
│   │       │   │               └── support/
│   │       │   │                   ├── Comment.java
│   │       │   │                   ├── LetBinding.java
│   │       │   │                   ├── OperatorPrecedenceTable.java
│   │       │   │                   ├── Parser.java
│   │       │   │                   ├── ParsingHelpers.java
│   │       │   │                   ├── WhitespaceManager.java
│   │       │   │                   └── package-info.java
│   │       │   └── javacc/
│   │       │       └── JavaCCParser.jj
│   │       └── test/
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── parser/
│   │                           └── DhallParserSuite.scala
│   ├── prelude/
│   │   └── src/
│   │       └── main/
│   │           └── java/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── prelude/
│   │                           └── Prelude.java
│   ├── scala/
│   │   └── src/
│   │       └── main/
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── syntax/
│   │                           └── package.scala
│   ├── scala-codec/
│   │   └── src/
│   │       ├── main/
│   │       │   └── scala/
│   │       │       └── org/
│   │       │           └── dhallj/
│   │       │               └── codec/
│   │       │                   ├── Decoder.scala
│   │       │                   ├── DecodingFailure.scala
│   │       │                   ├── Encoder.scala
│   │       │                   └── syntax/
│   │       │                       └── package.scala
│   │       └── test/
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── codec/
│   │                           └── DecoderSuite.scala
│   ├── testing/
│   │   └── src/
│   │       └── main/
│   │           └── scala/
│   │               └── org/
│   │                   └── dhallj/
│   │                       └── testing/
│   │                           ├── ArbitraryInstances.scala
│   │                           ├── WellTypedExpr.scala
│   │                           └── package.scala
│   └── yaml/
│       └── src/
│           ├── main/
│           │   └── java/
│           │       └── org/
│           │           └── dhallj/
│           │               └── yaml/
│           │                   ├── YamlContext.java
│           │                   ├── YamlConverter.java
│           │                   └── YamlHandler.java
│           └── test/
│               └── scala/
│                   └── org/
│                       └── dhallj/
│                           └── yaml/
│                               └── YamlConverterSuite.scala
├── project/
│   ├── build.properties
│   └── plugins.sbt
├── scalastyle-config.xml
├── tests/
│   └── src/
│       ├── main/
│       │   └── scala/
│       │       └── org/
│       │           └── dhallj/
│       │               └── tests/
│       │                   ├── HaskellDhall.scala
│       │                   └── acceptance/
│       │                       ├── AcceptanceFailureSuite.scala
│       │                       ├── AcceptanceSuccessSuite.scala
│       │                       ├── AcceptanceSuite.scala
│       │                       └── ImportResolutionSuite.scala
│       └── test/
│           ├── resources/
│           │   └── learndhall.dhall
│           └── scala/
│               └── org/
│                   └── dhallj/
│                       └── tests/
│                           ├── BinaryDecodingTests.scala
│                           ├── ImportResolutionSuite.scala
│                           ├── JsonConverterSuite.scala
│                           ├── MiscSuite.scala
│                           ├── PreludeSuite.scala
│                           ├── ToStringSuite.scala
│                           └── acceptance/
│                               └── AcceptanceSuites.scala
└── version.sbt
Download .txt
SYMBOL INDEX (1358 symbols across 72 files)

FILE: cli/src/main/java/org/dhallj/cli/Dhall.java
  class Dhall (line 9) | public class Dhall {
    method main (line 10) | public static void main(String[] args) throws IOException {

FILE: javascript/api/DhallJs.java
  class DhallJs (line 7) | @JsType
    method parse (line 9) | public static String parse(String input) {
    method normalize (line 13) | public static String normalize(String input) {
    method typeCheck (line 17) | public static String typeCheck(String input) {

FILE: javascript/api/dhall.js
  function parse (line 9) | function parse(input) {
  function typeCheck (line 17) | function typeCheck(input) {
  function normalize (line 25) | function normalize(input) {

FILE: javascript/jre/BufferedReader.java
  class BufferedReader (line 3) | public class BufferedReader extends Reader {
    method BufferedReader (line 4) | public BufferedReader(InputStreamReader stream) {}
    method read (line 6) | public int read(char[] cbuf, int off, int len) {
    method close (line 10) | public void close() {}

FILE: javascript/jre/InputStreamReader.java
  class InputStreamReader (line 3) | public class InputStreamReader extends Reader {
    method InputStreamReader (line 4) | public InputStreamReader(InputStream in) {}
    method InputStreamReader (line 5) | public InputStreamReader(InputStream in, String charsetName) {}
    method read (line 6) | public int read(char[] cbuf, int off, int len) {
    method close (line 10) | public void close() {}

FILE: javascript/jre/InvalidPathException.java
  class InvalidPathException (line 3) | public class InvalidPathException extends IllegalArgumentException {}

FILE: javascript/jre/Path.java
  class Path (line 5) | public class Path {
    method Path (line 8) | public Path(String input) {
    method isAbsolute (line 12) | public final boolean isAbsolute() {
    method iterator (line 16) | public final Iterator<Path> iterator() {
    method getNameCount (line 20) | public final int getNameCount() {
    method resolve (line 24) | public Path resolve(String other) {

FILE: javascript/jre/Paths.java
  class Paths (line 3) | public class Paths {
    method get (line 8) | public static final Path get(String input) {

FILE: javascript/jre/URI.java
  class URI (line 3) | public class URI {
    method URI (line 6) | public URI(String input) throws URISyntaxException {
    method getScheme (line 10) | public final String getScheme() {
    method getAuthority (line 14) | public final String getAuthority() {
    method getPath (line 18) | public final String getPath() {
    method getQuery (line 22) | public final String getQuery() {

FILE: javascript/jre/URISyntaxException.java
  class URISyntaxException (line 3) | public class URISyntaxException extends Throwable {}

FILE: modules/core/src/main/java/org/dhallj/cbor/AdditionalInfo.java
  type AdditionalInfo (line 3) | public enum AdditionalInfo {
    method AdditionalInfo (line 14) | private AdditionalInfo(int value) {
    method fromByte (line 18) | public static AdditionalInfo fromByte(byte b) {

FILE: modules/core/src/main/java/org/dhallj/cbor/CborException.java
  class CborException (line 3) | public class CborException extends RuntimeException {
    method CborException (line 4) | CborException(String message) {

FILE: modules/core/src/main/java/org/dhallj/cbor/HalfFloat.java
  class HalfFloat (line 8) | public final class HalfFloat {
    method fromFloat (line 9) | public static final int fromFloat(float asFloat) {
    method toFloat (line 38) | public static final float toFloat(int bits) {

FILE: modules/core/src/main/java/org/dhallj/cbor/MajorType.java
  type MajorType (line 3) | public enum MajorType {
    method MajorType (line 15) | private MajorType(int value) {
    method fromByte (line 19) | public static MajorType fromByte(byte b) {

FILE: modules/core/src/main/java/org/dhallj/cbor/NullVisitor.java
  class NullVisitor (line 6) | final class NullVisitor<R> implements Visitor<R> {
    method onUnsignedInteger (line 10) | @Override
    method onNegativeInteger (line 15) | @Override
    method onByteString (line 20) | @Override
    method onTextString (line 25) | @Override
    method onVariableArray (line 30) | @Override
    method onArray (line 35) | @Override
    method onMap (line 40) | @Override
    method onFalse (line 45) | @Override
    method onTrue (line 50) | @Override
    method onNull (line 55) | @Override
    method onHalfFloat (line 60) | @Override
    method onSingleFloat (line 65) | @Override
    method onDoubleFloat (line 70) | @Override
    method onTag (line 75) | @Override
    method notExpected (line 80) | private R notExpected(String msg) {

FILE: modules/core/src/main/java/org/dhallj/cbor/Reader.java
  class Reader (line 13) | public abstract class Reader {
    method nextSymbol (line 17) | public final <R> R nextSymbol(Visitor<R> visitor) {
    method read (line 42) | protected abstract byte read();
    method peek (line 44) | protected abstract byte peek();
    method read (line 46) | protected abstract byte[] read(int count);
    method readUnsignedInteger (line 48) | public final BigInteger readUnsignedInteger() {
    method readPositiveBigNum (line 53) | public final BigInteger readPositiveBigNum() {
    method readBigNum (line 63) | public final BigInteger readBigNum() {
    method readBigDecimal (line 92) | public final BigDecimal readBigDecimal() {
    method readNullableTextString (line 120) | public final String readNullableTextString() {
    method readNullableByteString (line 133) | public final byte[] readNullableByteString() {
    method tryReadTextString (line 153) | public final String tryReadTextString() {
    method readArrayStart (line 164) | public final BigInteger readArrayStart() {
    method readMap (line 181) | public final <R> Map<String, R> readMap(Visitor<R> visitor) {
    method readUnsignedInteger (line 200) | private final BigInteger readUnsignedInteger(byte b) {
    method readNegativeInteger (line 205) | private final BigInteger readNegativeInteger(byte b) {
    method readByteString (line 210) | private final byte[] readByteString(byte b) {
    method readTextString (line 221) | private final String readTextString(byte b) {
    method readArrayStart (line 233) | private final <R> R readArrayStart(byte b, Visitor<R> visitor) {
    method readMapStart (line 253) | private final BigInteger readMapStart(byte b) {
    method unassignedMessage (line 263) | private static final String unassignedMessage(int v) {
    method notValidMessage (line 270) | private static final String notValidMessage(int v) {
    method readPrimitive (line 277) | private final <R> R readPrimitive(byte b, Visitor<R> visitor) {
    method skip55799 (line 326) | private final void skip55799() {
    method readBigInteger (line 347) | private final BigInteger readBigInteger(AdditionalInfo info, byte firs...
    method readBigInteger (line 368) | private final BigInteger readBigInteger(long numBytes) {
    class ByteArrayReader (line 377) | public static final class ByteArrayReader extends Reader {
      method ByteArrayReader (line 381) | public ByteArrayReader(byte[] bytes) {
      method read (line 385) | @Override
      method peek (line 390) | @Override
      method read (line 395) | @Override

FILE: modules/core/src/main/java/org/dhallj/cbor/Visitor.java
  type Visitor (line 12) | public interface Visitor<R> {
    method onUnsignedInteger (line 14) | public R onUnsignedInteger(BigInteger value);
    method onNegativeInteger (line 16) | public R onNegativeInteger(BigInteger value);
    method onByteString (line 18) | public R onByteString(byte[] value);
    method onTextString (line 20) | public R onTextString(String value);
    method onVariableArray (line 22) | public R onVariableArray(BigInteger length, String name);
    method onArray (line 24) | public R onArray(BigInteger length, BigInteger tagI);
    method onMap (line 26) | public R onMap(BigInteger size);
    method onFalse (line 28) | public R onFalse();
    method onTrue (line 30) | public R onTrue();
    method onNull (line 32) | public R onNull();
    method onHalfFloat (line 34) | public R onHalfFloat(float value);
    method onSingleFloat (line 36) | public R onSingleFloat(float value);
    method onDoubleFloat (line 38) | public R onDoubleFloat(double value);
    method onTag (line 40) | public R onTag();

FILE: modules/core/src/main/java/org/dhallj/cbor/Writer.java
  class Writer (line 16) | public abstract class Writer {
    class WrappedIOException (line 24) | private static final class WrappedIOException extends RuntimeException {
      method WrappedIOException (line 27) | WrappedIOException(IOException underlying) {
    class OutputStreamWriter (line 32) | public static class OutputStreamWriter extends Writer {
      method OutputStreamWriter (line 35) | public OutputStreamWriter(OutputStream stream) {
      method write (line 39) | protected final void write(byte b) {
      method write (line 47) | protected final void write(byte... bs) {
    class ByteArrayWriter (line 56) | public static final class ByteArrayWriter extends OutputStreamWriter {
      method ByteArrayWriter (line 57) | public ByteArrayWriter() {
      method getBytes (line 61) | public final byte[] getBytes() {
    class SHA256Writer (line 66) | public static final class SHA256Writer extends Writer {
      method SHA256Writer (line 69) | public SHA256Writer() {
      method getHashBytes (line 80) | public final byte[] getHashBytes() {
      method write (line 84) | protected final void write(byte b) {
      method write (line 88) | protected final void write(byte... bs) {
    method write (line 93) | protected abstract void write(byte b);
    method write (line 95) | protected abstract void write(byte... bs);
    method writeNull (line 97) | public final void writeNull() {
    method writeBoolean (line 101) | public final void writeBoolean(boolean value) {
    method writeLong (line 105) | public final void writeLong(long value) {
    method writeBigInteger (line 113) | public final void writeBigInteger(BigInteger value) {
    method writeBigDecimal (line 121) | public final void writeBigDecimal(BigDecimal value) {
    method writeString (line 128) | public final void writeString(String value) {
    method writeByteString (line 134) | public final void writeByteString(byte[] bytes) {
    method writeDouble (line 139) | public final void writeDouble(double value) {
    method writeArrayStart (line 181) | public final void writeArrayStart(int length) {
    method writeMapStart (line 185) | public final void writeMapStart(int length) {
    method writeTypeAndLength (line 189) | private final void writeTypeAndLength(int majorType, long length) {
    method writeTypeAndLength (line 222) | private final void writeTypeAndLength(int majorType, BigInteger length) {

FILE: modules/core/src/main/java/org/dhallj/core/ArrayIterable.java
  class ArrayIterable (line 6) | final class ArrayIterable<A> implements Iterable<A> {
    method ArrayIterable (line 9) | public ArrayIterable(A[] values) {
    method iterator (line 13) | public final Iterator<A> iterator() {
    class ArrayIterator (line 17) | private static final class ArrayIterator<A> implements Iterator<A> {
      method ArrayIterator (line 21) | ArrayIterator(A[] values) {
      method hasNext (line 25) | public final boolean hasNext() {
      method next (line 29) | public final A next() {
      method remove (line 37) | public final void remove() {

FILE: modules/core/src/main/java/org/dhallj/core/Constructors.java
  class Constructors (line 23) | final class Constructors {
    class NaturalLiteral (line 24) | static final class NaturalLiteral extends Expr {
      method NaturalLiteral (line 27) | NaturalLiteral(BigInteger value) {
      method accept (line 32) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 36) | final <A> void advance(VisitState<A> state) {
    class IntegerLiteral (line 41) | static final class IntegerLiteral extends Expr {
      method IntegerLiteral (line 44) | IntegerLiteral(BigInteger value) {
      method accept (line 49) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 53) | final <A> void advance(VisitState<A> state) {
    class DoubleLiteral (line 58) | static final class DoubleLiteral extends Expr {
      method DoubleLiteral (line 61) | DoubleLiteral(double value) {
      method accept (line 66) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 70) | final <A> void advance(VisitState<A> state) {
    class DateLiteral (line 75) | static final class DateLiteral extends Expr {
      method DateLiteral (line 80) | DateLiteral(int year, int month, int day) {
      method accept (line 87) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 91) | final <A> void advance(VisitState<A> state) {
    class TimeLiteral (line 96) | static final class TimeLiteral extends Expr {
      method TimeLiteral (line 102) | TimeLiteral(int hour, int minute, int second, BigDecimal fractional) {
      method accept (line 110) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 114) | final <A> void advance(VisitState<A> state) {
    class TimeZoneLiteral (line 120) | static final class TimeZoneLiteral extends Expr {
      method TimeZoneLiteral (line 123) | TimeZoneLiteral(int minutes) {
      method accept (line 128) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 132) | final <A> void advance(VisitState<A> state) {
    class TextLiteral (line 137) | static final class TextLiteral extends Expr {
      method TextLiteral (line 141) | TextLiteral(String[] parts, Expr[] interpolated) {
      method accept (line 147) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 151) | final <A> void advance(VisitState<A> state) {
    class Application (line 180) | static final class Application extends Expr {
      method Application (line 184) | Application(Expr base, Expr arg) {
      method accept (line 190) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 194) | final <A> void advance(VisitState<A> state) {
      method gatherApplicationArgs (line 236) | private static final Expr gatherApplicationArgs(Expr candidate, Dequ...
    class OperatorApplication (line 252) | static final class OperatorApplication extends Expr {
      method OperatorApplication (line 257) | OperatorApplication(Operator operator, Expr lhs, Expr rhs) {
      method accept (line 264) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 268) | final <A> void advance(VisitState<A> state) {
    class If (line 286) | static final class If extends Expr {
      method If (line 291) | If(Expr predicate, Expr thenValue, Expr elseValue) {
      method accept (line 298) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 302) | final <A> void advance(VisitState<A> state) {
    class Lambda (line 325) | static final class Lambda extends Expr {
      method Lambda (line 330) | Lambda(String name, Expr type, Expr result) {
      method accept (line 337) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 341) | final <A> void advance(VisitState<A> state) {
    class Pi (line 364) | static final class Pi extends Expr {
      method Pi (line 369) | Pi(String name, Expr type, Expr result) {
      method accept (line 376) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 380) | final <A> void advance(VisitState<A> state) {
    class Assert (line 403) | static final class Assert extends Expr {
      method Assert (line 406) | Assert(Expr base) {
      method accept (line 411) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 415) | final <A> void advance(VisitState<A> state) {
    class FieldAccess (line 427) | static final class FieldAccess extends Expr {
      method FieldAccess (line 431) | FieldAccess(Expr base, String fieldName) {
      method accept (line 437) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 441) | final <A> void advance(VisitState<A> state) {
    class Projection (line 456) | static final class Projection extends Expr {
      method Projection (line 460) | Projection(Expr base, String[] fieldNames) {
      method accept (line 466) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 470) | final <A> void advance(VisitState<A> state) {
    class ProjectionByType (line 482) | static final class ProjectionByType extends Expr {
      method ProjectionByType (line 486) | ProjectionByType(Expr base, Expr type) {
      method accept (line 492) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 496) | final <A> void advance(VisitState<A> state) {
    class BuiltIn (line 515) | static final class BuiltIn extends Expr {
      method BuiltIn (line 518) | BuiltIn(String name) {
      method accept (line 523) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 527) | final <A> void advance(VisitState<A> state) {
    class Identifier (line 532) | static final class Identifier extends Expr {
      method Identifier (line 536) | Identifier(String name, long index) {
      method accept (line 542) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 546) | final <A> void advance(VisitState<A> state) {
    class RecordLiteral (line 551) | static final class RecordLiteral extends Expr {
      method RecordLiteral (line 554) | RecordLiteral(Entry<String, Expr>[] fields) {
      method accept (line 559) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 563) | final <A> void advance(VisitState<A> state) {
    class RecordType (line 599) | static final class RecordType extends Expr {
      method RecordType (line 602) | RecordType(Entry<String, Expr>[] fields) {
      method accept (line 607) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 611) | final <A> void advance(VisitState<A> state) {
    class UnionType (line 648) | static final class UnionType extends Expr {
      method UnionType (line 651) | UnionType(Entry<String, Expr>[] fields) {
      method accept (line 656) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 660) | final <A> void advance(VisitState<A> state) {
    class NonEmptyListLiteral (line 708) | static final class NonEmptyListLiteral extends Expr {
      method NonEmptyListLiteral (line 711) | NonEmptyListLiteral(Expr[] values) {
      method accept (line 716) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 720) | final <A> void advance(VisitState<A> state) {
      method flattenToMapList (line 754) | private static final Expr flattenToMapList(Expr[] values) {
    class EmptyListLiteral (line 785) | static final class EmptyListLiteral extends Expr {
      method EmptyListLiteral (line 788) | EmptyListLiteral(Expr type) {
      method accept (line 793) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 797) | final <A> void advance(VisitState<A> state) {
      method isToMapListType (line 815) | private static final boolean isToMapListType(Expr type) {
    class Let (line 840) | static final class Let extends Expr {
      method Let (line 846) | Let(String name, Expr type, Expr value, Expr body) {
      method accept (line 854) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 858) | final <A> void advance(VisitState<A> state) {
      method gatherLetBindings (line 932) | private static final Expr gatherLetBindings(Expr candidate, List<Let...
    class Annotated (line 948) | static final class Annotated extends Expr {
      method Annotated (line 952) | Annotated(Expr base, Expr type) {
      method accept (line 958) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 962) | final <A> void advance(VisitState<A> state) {
    class Merge (line 980) | static final class Merge extends Expr {
      method Merge (line 985) | Merge(Expr handlers, Expr union, Expr type) {
      method accept (line 992) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 996) | final <A> void advance(VisitState<A> state) {
    class ToMap (line 1030) | static final class ToMap extends Expr {
      method ToMap (line 1034) | ToMap(Expr base, Expr type) {
      method accept (line 1040) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 1044) | final <A> void advance(VisitState<A> state) {
    class With (line 1071) | static final class With extends Expr {
      method With (line 1076) | With(Expr base, String[] path, Expr value) {
      method accept (line 1083) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 1087) | final <A> void advance(VisitState<A> state) {
    class MissingImport (line 1110) | static final class MissingImport extends Expr {
      method MissingImport (line 1114) | MissingImport(Expr.ImportMode mode, byte[] hash) {
      method accept (line 1120) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 1124) | final <A> void advance(VisitState<A> state) {
    class EnvImport (line 1129) | static final class EnvImport extends Expr {
      method EnvImport (line 1134) | EnvImport(String name, Expr.ImportMode mode, byte[] hash) {
      method accept (line 1141) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 1145) | final <A> void advance(VisitState<A> state) {
    class LocalImport (line 1150) | static final class LocalImport extends Expr {
      method LocalImport (line 1155) | LocalImport(Path path, Expr.ImportMode mode, byte[] hash) {
      method accept (line 1162) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 1166) | final <A> void advance(VisitState<A> state) {
    class ClasspathImport (line 1171) | static final class ClasspathImport extends Expr {
      method ClasspathImport (line 1176) | ClasspathImport(Path path, Expr.ImportMode mode, byte[] hash) {
      method accept (line 1183) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 1187) | final <A> void advance(VisitState<A> state) {
    class RemoteImport (line 1192) | static final class RemoteImport extends Expr {
      method RemoteImport (line 1198) | RemoteImport(URI url, Expr using, Expr.ImportMode mode, byte[] hash) {
      method accept (line 1206) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 1210) | final <A> void advance(VisitState<A> state) {

FILE: modules/core/src/main/java/org/dhallj/core/DhallException.java
  class DhallException (line 4) | public class DhallException extends RuntimeException {
    method DhallException (line 5) | public DhallException(String message) {
    method DhallException (line 9) | public DhallException(String message, Throwable cause) {
    class ParsingFailure (line 14) | public static final class ParsingFailure extends DhallException {
      method fillInStackTrace (line 15) | @Override
      method ParsingFailure (line 21) | public ParsingFailure(String message, Throwable cause) {
      method ParsingFailure (line 25) | public ParsingFailure(String message) {
    class ResolutionFailure (line 30) | public static final class ResolutionFailure extends DhallException {
      method fillInStackTrace (line 33) | @Override
      method ResolutionFailure (line 39) | public ResolutionFailure(String message, Throwable cause, boolean is...
      method ResolutionFailure (line 44) | public ResolutionFailure(String message, boolean isAbsentImport) {
      method ResolutionFailure (line 49) | public ResolutionFailure(String message, Throwable cause) {
      method ResolutionFailure (line 53) | public ResolutionFailure(String message) {
      method isAbsentImport (line 57) | public boolean isAbsentImport() {

FILE: modules/core/src/main/java/org/dhallj/core/Expr.java
  class Expr (line 40) | public abstract class Expr {
    method Expr (line 44) | Expr(int tag) {
    method accept (line 49) | public abstract <A> A accept(ExternalVisitor<A> visitor);
    method advance (line 51) | abstract <A> void advance(VisitState<A> state);
    method accept (line 54) | public final <A> A accept(Visitor<A> visitor) {
    method normalize (line 68) | public final Expr normalize() {
    method alphaNormalize (line 77) | public final Expr alphaNormalize() {
    method increment (line 82) | public final Expr increment(String name) {
    method decrement (line 87) | public final Expr decrement(String name) {
    method substitute (line 92) | public final Expr substitute(String name, Expr replacement) {
    method getEncodedBytes (line 101) | public final byte[] getEncodedBytes() {
    method getHashBytes (line 112) | public final byte[] getHashBytes() {
    method hash (line 133) | public final String hash() {
    method isResolved (line 138) | public final boolean isResolved() {
    method sameStructure (line 148) | public final boolean sameStructure(Expr other) {
    method equivalent (line 157) | public final boolean equivalent(Expr other) {
    method equals (line 170) | public final boolean equals(Object obj) {
    method hashCode (line 179) | public final int hashCode() {
    method toString (line 183) | public final String toString() {
    method getNonNote (line 187) | Expr getNonNote() {
    class Util (line 203) | public static final class Util {
      method Util (line 204) | private Util() {}
      method typeCheck (line 207) | public static final Expr typeCheck(Expr expr) {
      method getFirstDiff (line 212) | public static final Entry<Expr, Expr> getFirstDiff(Expr first, Expr ...
      method encodeToStream (line 217) | public static final void encodeToStream(Expr expr, OutputStream stre...
      method encodeHashBytes (line 223) | public static String encodeHashBytes(byte[] hash) {
      method decodeHashBytes (line 233) | public static final byte[] decodeHashBytes(String input) {
      method getListArg (line 245) | public static Expr getListArg(Expr expr) {
      method getOptionalArg (line 250) | public static Expr getOptionalArg(Expr expr) {
      method getSomeArg (line 255) | public static Expr getSomeArg(Expr expr) {
      method getNoneArg (line 260) | public static Expr getNoneArg(Expr expr) {
      method asBoolLiteral (line 265) | public static final Boolean asBoolLiteral(Expr expr) {
      method asNaturalLiteral (line 279) | public static final BigInteger asNaturalLiteral(Expr expr) {
      method asIntegerLiteral (line 290) | public static final BigInteger asIntegerLiteral(Expr expr) {
      method asDoubleLiteral (line 301) | public static final Double asDoubleLiteral(Expr expr) {
      method asSimpleTextLiteral (line 312) | public static final String asSimpleTextLiteral(Expr expr) {
      method asBuiltIn (line 329) | public static final String asBuiltIn(Expr expr) {
      method asListLiteral (line 340) | public static final List<Expr> asListLiteral(Expr expr) {
      method asRecordLiteral (line 353) | public static final List<Entry<String, Expr>> asRecordLiteral(Expr e...
      method asRecordType (line 364) | public static final List<Entry<String, Expr>> asRecordType(Expr expr) {
      method asUnionType (line 375) | public static final List<Entry<String, Expr>> asUnionType(Expr expr) {
      method asFieldAccess (line 386) | public static final Entry<Expr, String> asFieldAccess(Expr expr) {
      method getElementType (line 401) | private static Expr getElementType(Expr expr, String typeConstructor) {
      method escapeText (line 418) | public static final String escapeText(String input, boolean quoted) {
      method desugarComplete (line 475) | public static final Expr desugarComplete(Expr lhs, Expr rhs) {
      method applyAsLambda (line 487) | public static final Expr applyAsLambda(Expr expr, Expr arg) {
      method flattenToMapRecord (line 498) | static final Entry<Expr, Expr> flattenToMapRecord(List<Entry<String,...
    class LetBinding (line 523) | public static final class LetBinding<A> {
      method LetBinding (line 528) | public LetBinding(String name, A type, A value) {
      method getName (line 534) | public String getName() {
      method hasType (line 538) | public boolean hasType() {
      method getType (line 542) | public A getType() {
      method getValue (line 546) | public A getValue() {
    type ImportMode (line 552) | public static enum ImportMode {
      method toString (line 557) | public String toString() {
    class Constants (line 569) | public static final class Constants {
      method getBuiltIn (line 671) | static Expr getBuiltIn(String name) {
      method isBuiltIn (line 675) | public static boolean isBuiltIn(String name) {
      method isKeyword (line 679) | public static boolean isKeyword(String name) {
    class Parsed (line 685) | public static final class Parsed extends Expr {
      method Parsed (line 689) | public Parsed(Expr base, Source source) {
      method getSource (line 695) | public final Source getSource() {
      method accept (line 699) | public final <A> A accept(ExternalVisitor<A> visitor) {
      method advance (line 703) | final <A> void advance(VisitState<A> state) {
    method makeDoubleLiteral (line 708) | public static final Expr makeDoubleLiteral(double value) {
    method makeNaturalLiteral (line 712) | public static final Expr makeNaturalLiteral(BigInteger value) {
    method makeIntegerLiteral (line 716) | public static final Expr makeIntegerLiteral(BigInteger value) {
    method makeDateLiteral (line 720) | public static final Expr makeDateLiteral(int year, int month, int day) {
    method makeTimeLiteral (line 724) | public static final Expr makeTimeLiteral(
    method makeTimeZoneLiteral (line 729) | public static final Expr makeTimeZoneLiteral(int seconds) {
    method makeTextLiteral (line 733) | public static final Expr makeTextLiteral(String[] parts, Expr[] interp...
    method makeTextLiteral (line 737) | public static final Expr makeTextLiteral(String[] parts, Collection<Ex...
    method makeTextLiteral (line 743) | public static final Expr makeTextLiteral(String value) {
    method makeApplication (line 748) | public static final Expr makeApplication(Expr base, Expr arg) {
    method makeApplication (line 752) | public static final Expr makeApplication(Expr base, Expr[] args) {
    method makeApplication (line 760) | public static final Expr makeApplication(Expr base, List<Expr> args) {
    method makeOperatorApplication (line 768) | public static final Expr makeOperatorApplication(Operator operator, Ex...
    method makeIf (line 772) | public static final Expr makeIf(Expr cond, Expr thenValue, Expr elseVa...
    method makeLambda (line 776) | public static final Expr makeLambda(String param, Expr input, Expr res...
    method makePi (line 780) | public static final Expr makePi(String param, Expr input, Expr result) {
    method makePi (line 784) | public static final Expr makePi(Expr input, Expr result) {
    method makeAssert (line 788) | public static final Expr makeAssert(Expr base) {
    method makeFieldAccess (line 792) | public static final Expr makeFieldAccess(Expr base, String fieldName) {
    method makeProjection (line 796) | public static final Expr makeProjection(Expr base, String[] fieldNames) {
    method makeProjectionByType (line 800) | public static final Expr makeProjectionByType(Expr base, Expr tpe) {
    method makeBuiltIn (line 804) | public static final Expr makeBuiltIn(String name) {
    method makeIdentifier (line 811) | public static final Expr makeIdentifier(String name, long index) {
    method makeIdentifier (line 815) | public static final Expr makeIdentifier(String name) {
    method makeRecordLiteral (line 819) | public static final Expr makeRecordLiteral(Entry<String, Expr>[] field...
    method makeRecordLiteral (line 823) | public static final Expr makeRecordLiteral(Collection<Entry<String, Ex...
    method makeRecordLiteral (line 827) | public static final Expr makeRecordLiteral(String key, Expr value) {
    method makeRecordType (line 831) | public static final Expr makeRecordType(Entry<String, Expr>[] fields) {
    method makeRecordType (line 835) | public static final Expr makeRecordType(Collection<Entry<String, Expr>...
    method makeUnionType (line 839) | public static final Expr makeUnionType(Entry<String, Expr>[] fields) {
    method makeUnionType (line 843) | public static final Expr makeUnionType(Collection<Entry<String, Expr>>...
    method makeNonEmptyListLiteral (line 847) | public static final Expr makeNonEmptyListLiteral(Expr[] values) {
    method makeNonEmptyListLiteral (line 851) | public static final Expr makeNonEmptyListLiteral(Collection<Expr> valu...
    method makeEmptyListLiteral (line 855) | public static final Expr makeEmptyListLiteral(Expr tpe) {
    method makeNote (line 859) | public static final Expr makeNote(Expr base, Source source) {
    method makeLet (line 863) | public static final Expr makeLet(String name, Expr type, Expr value, E...
    method makeLet (line 867) | public static final Expr makeLet(List<LetBinding<Expr>> bindings, Expr...
    method makeLet (line 879) | public static final Expr makeLet(String name, Expr value, Expr body) {
    method makeAnnotated (line 883) | public static final Expr makeAnnotated(Expr base, Expr type) {
    method makeToMap (line 887) | public static final Expr makeToMap(Expr base, Expr type) {
    method makeToMap (line 891) | public static final Expr makeToMap(Expr base) {
    method makeWith (line 895) | public static final Expr makeWith(Expr base, String[] path, Expr value) {
    method makeMerge (line 899) | public static final Expr makeMerge(Expr left, Expr right, Expr type) {
    method makeMerge (line 903) | public static final Expr makeMerge(Expr left, Expr right) {
    method makeLocalImport (line 907) | public static final Expr makeLocalImport(Path path, ImportMode mode, b...
    method makeClasspathImport (line 911) | public static final Expr makeClasspathImport(Path path, ImportMode mod...
    method makeRemoteImport (line 915) | public static final Expr makeRemoteImport(URI url, Expr using, ImportM...
    method makeEnvImport (line 919) | public static final Expr makeEnvImport(String value, ImportMode mode, ...
    method makeMissingImport (line 923) | public static final Expr makeMissingImport(ImportMode mode, byte[] has...
    method getFirstDiff (line 927) | private final Entry<Expr, Expr> getFirstDiff(Expr other) {

FILE: modules/core/src/main/java/org/dhallj/core/ExternalVisitor.java
  type ExternalVisitor (line 14) | public interface ExternalVisitor<A> {
    method onNote (line 15) | A onNote(Expr base, Source source);
    method onNatural (line 17) | A onNatural(BigInteger value);
    method onInteger (line 19) | A onInteger(BigInteger value);
    method onDouble (line 21) | A onDouble(double value);
    method onDate (line 23) | A onDate(int year, int month, int day);
    method onTime (line 25) | A onTime(int hour, int minute, int second, BigDecimal fractional);
    method onTimeZone (line 27) | A onTimeZone(int minutes);
    method onBuiltIn (line 29) | A onBuiltIn(String name);
    method onIdentifier (line 31) | A onIdentifier(String name, long index);
    method onLambda (line 33) | A onLambda(String name, Expr type, Expr result);
    method onPi (line 35) | A onPi(String name, Expr type, Expr result);
    method onLet (line 37) | A onLet(String name, Expr type, Expr value, Expr body);
    method onText (line 39) | A onText(String[] parts, Iterable<Expr> interpolated);
    method onNonEmptyList (line 41) | A onNonEmptyList(Iterable<Expr> values, int size);
    method onEmptyList (line 43) | A onEmptyList(Expr type);
    method onRecord (line 45) | A onRecord(Iterable<Entry<String, Expr>> fields, int size);
    method onRecordType (line 47) | A onRecordType(Iterable<Entry<String, Expr>> fields, int size);
    method onUnionType (line 49) | A onUnionType(Iterable<Entry<String, Expr>> fields, int size);
    method onFieldAccess (line 51) | A onFieldAccess(Expr base, String fieldName);
    method onProjection (line 53) | A onProjection(Expr base, String[] fieldNames);
    method onProjectionByType (line 55) | A onProjectionByType(Expr base, Expr type);
    method onApplication (line 57) | A onApplication(Expr base, Expr arg);
    method onOperatorApplication (line 59) | A onOperatorApplication(Operator operator, Expr lhs, Expr rhs);
    method onIf (line 61) | A onIf(Expr predicate, Expr thenValue, Expr elseValue);
    method onAnnotated (line 63) | A onAnnotated(Expr base, Expr type);
    method onAssert (line 65) | A onAssert(Expr base);
    method onMerge (line 67) | A onMerge(Expr handlers, Expr union, Expr type);
    method onToMap (line 69) | A onToMap(Expr base, Expr type);
    method onWith (line 71) | A onWith(Expr base, String[] path, Expr value);
    method onMissingImport (line 73) | A onMissingImport(Expr.ImportMode mode, byte[] hash);
    method onEnvImport (line 75) | A onEnvImport(String value, Expr.ImportMode mode, byte[] hash);
    method onLocalImport (line 77) | A onLocalImport(Path path, Expr.ImportMode mode, byte[] hash);
    method onClasspathImport (line 79) | A onClasspathImport(Path path, Expr.ImportMode mode, byte[] hash);
    method onRemoteImport (line 81) | A onRemoteImport(URI url, Expr using, Expr.ImportMode mode, byte[] hash);
    class Constant (line 93) | public static class Constant<A> implements ExternalVisitor<A> {
      method getReturnValue (line 96) | protected A getReturnValue() {
      method Constant (line 100) | public Constant(A value) {
      method onNote (line 104) | @Override
      method onNatural (line 109) | @Override
      method onInteger (line 114) | @Override
      method onDouble (line 119) | @Override
      method onDate (line 124) | @Override
      method onTime (line 129) | @Override
      method onTimeZone (line 134) | @Override
      method onBuiltIn (line 139) | @Override
      method onIdentifier (line 144) | @Override
      method onLambda (line 149) | @Override
      method onPi (line 154) | @Override
      method onLet (line 159) | @Override
      method onText (line 164) | @Override
      method onNonEmptyList (line 169) | @Override
      method onEmptyList (line 174) | @Override
      method onRecord (line 179) | @Override
      method onRecordType (line 184) | @Override
      method onUnionType (line 189) | @Override
      method onFieldAccess (line 194) | @Override
      method onProjection (line 199) | @Override
      method onProjectionByType (line 204) | @Override
      method onApplication (line 209) | @Override
      method onOperatorApplication (line 214) | @Override
      method onIf (line 219) | @Override
      method onAnnotated (line 224) | @Override
      method onAssert (line 229) | @Override
      method onMerge (line 234) | @Override
      method onToMap (line 239) | @Override
      method onWith (line 244) | @Override
      method onMissingImport (line 249) | @Override
      method onEnvImport (line 254) | @Override
      method onLocalImport (line 259) | @Override
      method onClasspathImport (line 264) | @Override
      method onRemoteImport (line 269) | @Override

FILE: modules/core/src/main/java/org/dhallj/core/IsResolved.java
  class IsResolved (line 6) | final class IsResolved extends Visitor.Property {
    method onOperatorApplication (line 9) | @Override
    method onLocalImport (line 18) | @Override
    method onClasspathImport (line 23) | @Override
    method onRemoteImport (line 28) | @Override
    method onEnvImport (line 33) | @Override
    method onMissingImport (line 38) | @Override

FILE: modules/core/src/main/java/org/dhallj/core/Operator.java
  type Operator (line 4) | public enum Operator {
    method Operator (line 26) | Operator(String value, int precedence, boolean isBoolOperator) {
    method isBoolOperator (line 32) | public final boolean isBoolOperator() {
    method getLabel (line 36) | public final int getLabel() {
    method getPrecedence (line 40) | public final int getPrecedence() {
    method toString (line 44) | public final String toString() {
    method fromLabel (line 48) | public static final Operator fromLabel(int ordinal) {
    method parse (line 56) | public static final Operator parse(String input) {

FILE: modules/core/src/main/java/org/dhallj/core/Source.java
  class Source (line 4) | public abstract class Source {
    method Source (line 10) | public Source(int beginLine, int beginColumn, int endLine, int endColu...
    method printText (line 17) | public abstract void printText(StringBuilder builder);
    method getText (line 19) | public final String getText() {
    method getBeginLine (line 25) | public final int getBeginLine() {
    method getBeginColumn (line 29) | public final int getBeginColumn() {
    method getEndLine (line 33) | public final int getEndLine() {
    method getEndColumn (line 37) | public final int getEndColumn() {
    method toString (line 41) | public final String toString() {
    class FromString (line 55) | private static final class FromString extends Source {
      method FromString (line 58) | FromString(String text, int beginLine, int beginColumn, int endLine,...
      method printText (line 63) | public final void printText(StringBuilder builder) {
    method fromString (line 68) | public static final Source fromString(

FILE: modules/core/src/main/java/org/dhallj/core/Tags.java
  class Tags (line 4) | final class Tags {

FILE: modules/core/src/main/java/org/dhallj/core/ToStringVisitor.java
  class ToStringState (line 12) | final class ToStringState {
    method ToStringState (line 24) | ToStringState(String text, int level) {
    method ToStringState (line 29) | ToStringState(String text) {
    method withText (line 33) | ToStringState withText(String text) {
    method toString (line 37) | String toString(int contextLevel) {
    method toString (line 45) | public String toString() {
    method getOperatorLevel (line 49) | static final int getOperatorLevel(Operator operator) {
  class ToStringVisitor (line 58) | final class ToStringVisitor extends Visitor.NoPrepareEvents<ToStringStat...
    method bind (line 61) | public void bind(String name, Expr type) {}
    method onNote (line 63) | public ToStringState onNote(ToStringState base, Source source) {
    method onNatural (line 67) | public ToStringState onNatural(Expr self, BigInteger value) {
    method onInteger (line 71) | public ToStringState onInteger(Expr self, BigInteger value) {
    method onDouble (line 77) | public ToStringState onDouble(Expr self, double value) {
    method pad2 (line 81) | private static String pad2(int input) {
    method pad4 (line 90) | private static String pad4(int input) {
    method onDate (line 98) | public ToStringState onDate(Expr self, int year, int month, int day) {
    method onTime (line 102) | public ToStringState onTime(Expr self, int hour, int minute, int secon...
    method onTimeZone (line 112) | public ToStringState onTimeZone(Expr self, int minutes) {
    method onBuiltIn (line 120) | public ToStringState onBuiltIn(Expr self, String name) {
    method onIdentifier (line 124) | public ToStringState onIdentifier(Expr self, String name, long index) {
    method onRecord (line 130) | public ToStringState onRecord(List<Entry<String, ToStringState>> field...
    method onRecordType (line 151) | public ToStringState onRecordType(List<Entry<String, ToStringState>> f...
    method onUnionType (line 168) | public ToStringState onUnionType(List<Entry<String, ToStringState>> fi...
    method onNonEmptyList (line 188) | public ToStringState onNonEmptyList(List<ToStringState> values) {
    method onFieldAccess (line 202) | public ToStringState onFieldAccess(ToStringState base, String fieldNam...
    method onProjection (line 207) | public ToStringState onProjection(ToStringState base, String[] fieldNa...
    method onProjectionByType (line 221) | public ToStringState onProjectionByType(ToStringState base, ToStringSt...
    method onOperatorApplication (line 227) | public ToStringState onOperatorApplication(
    method onMissingImport (line 251) | public ToStringState onMissingImport(Expr.ImportMode mode, byte[] hash) {
    method onEnvImport (line 267) | public ToStringState onEnvImport(String value, Expr.ImportMode mode, b...
    method onLocalImport (line 284) | public ToStringState onLocalImport(Path path, Expr.ImportMode mode, by...
    method onClasspathImport (line 300) | @Override
    method onRemoteImport (line 319) | public ToStringState onRemoteImport(
    method onMerge (line 341) | public ToStringState onMerge(ToStringState handlers, ToStringState uni...
    method onLambda (line 356) | public ToStringState onLambda(String name, ToStringState type, ToStrin...
    method onPi (line 362) | public ToStringState onPi(String name, ToStringState type, ToStringSta...
    method onLet (line 372) | public ToStringState onLet(List<Expr.LetBinding<ToStringState>> bindin...
    method onText (line 393) | public ToStringState onText(String[] parts, List<ToStringState> interp...
    method onEmptyList (line 413) | public ToStringState onEmptyList(ToStringState type) {
    method onApplication (line 418) | public ToStringState onApplication(ToStringState base, List<ToStringSt...
    method onIf (line 432) | public ToStringState onIf(
    method onAnnotated (line 444) | public ToStringState onAnnotated(ToStringState base, ToStringState typ...
    method onAssert (line 449) | public ToStringState onAssert(ToStringState base) {
    method onToMap (line 453) | public ToStringState onToMap(ToStringState base, ToStringState type) {
    method onWith (line 466) | public ToStringState onWith(ToStringState base, String[] path, ToStrin...
    method isAlpha (line 482) | private static boolean isAlpha(char c) {
    method isDigit (line 486) | private static boolean isDigit(char c) {
    method isSimpleLabel (line 490) | private static boolean isSimpleLabel(String name) {
    method escapeName (line 510) | private static String escapeName(String name) {

FILE: modules/core/src/main/java/org/dhallj/core/VisitState.java
  class ExprState (line 11) | final class ExprState {
    method ExprState (line 18) | ExprState(Expr expr, int state, int size) {
    method ExprState (line 25) | ExprState(Expr expr, int state, Entry<String, Expr>[] fields, boolean ...
    method ExprState (line 38) | ExprState(Expr expr, int state) {
    method compare (line 45) | public int compare(Entry<String, Expr> a, Entry<String, Expr> b) {
  class VisitState (line 51) | final class VisitState<A> {
    method VisitState (line 62) | public VisitState(Visitor<A> visitor, Expr expr) {

FILE: modules/core/src/main/java/org/dhallj/core/Visitor.java
  type Visitor (line 16) | public interface Visitor<A> {
    method bind (line 17) | void bind(String name, Expr type);
    method onNote (line 19) | A onNote(A base, Source source);
    method onNatural (line 21) | A onNatural(Expr self, BigInteger value);
    method onInteger (line 23) | A onInteger(Expr self, BigInteger value);
    method onDouble (line 25) | A onDouble(Expr self, double value);
    method onDate (line 27) | A onDate(Expr self, int year, int month, int day);
    method onTime (line 29) | A onTime(Expr self, int hour, int minute, int second, BigDecimal fract...
    method onTimeZone (line 31) | A onTimeZone(Expr self, int minutes);
    method onBuiltIn (line 33) | A onBuiltIn(Expr self, String value);
    method onIdentifier (line 35) | A onIdentifier(Expr self, String value, long index);
    method onLambda (line 37) | A onLambda(String name, A type, A result);
    method onPi (line 39) | A onPi(String name, A type, A result);
    method onLet (line 41) | A onLet(List<Expr.LetBinding<A>> bindings, A body);
    method onText (line 43) | A onText(String[] parts, List<A> interpolated);
    method onNonEmptyList (line 45) | A onNonEmptyList(List<A> values);
    method onEmptyList (line 47) | A onEmptyList(A type);
    method onRecord (line 49) | A onRecord(List<Entry<String, A>> fields);
    method onRecordType (line 51) | A onRecordType(List<Entry<String, A>> fields);
    method onUnionType (line 53) | A onUnionType(List<Entry<String, A>> fields);
    method onFieldAccess (line 55) | A onFieldAccess(A base, String fieldName);
    method onProjection (line 57) | A onProjection(A base, String[] fieldNames);
    method onProjectionByType (line 59) | A onProjectionByType(A base, A type);
    method onApplication (line 61) | A onApplication(A base, List<A> args);
    method onOperatorApplication (line 63) | A onOperatorApplication(Operator operator, A lhs, A rhs);
    method onIf (line 65) | A onIf(A predicate, A thenValue, A elseValue);
    method onAnnotated (line 67) | A onAnnotated(A base, A type);
    method onAssert (line 69) | A onAssert(A base);
    method onMerge (line 71) | A onMerge(A handlers, A union, A type);
    method onToMap (line 73) | A onToMap(A base, A type);
    method onWith (line 75) | A onWith(A base, String[] path, A value);
    method onMissingImport (line 77) | A onMissingImport(Expr.ImportMode mode, byte[] hash);
    method onEnvImport (line 79) | A onEnvImport(String value, Expr.ImportMode mode, byte[] hash);
    method onLocalImport (line 81) | A onLocalImport(Path path, Expr.ImportMode mode, byte[] hash);
    method onClasspathImport (line 83) | A onClasspathImport(Path path, Expr.ImportMode mode, byte[] hash);
    method onRemoteImport (line 85) | A onRemoteImport(URI url, A using, Expr.ImportMode mode, byte[] hash);
    method sortFields (line 88) | boolean sortFields();
    method flattenToMapLists (line 94) | boolean flattenToMapLists();
    method prepareLambda (line 96) | boolean prepareLambda(String name, Expr type);
    method preparePi (line 98) | boolean preparePi(String name, Expr type);
    method prepareLet (line 100) | boolean prepareLet(int size);
    method prepareLetBinding (line 102) | boolean prepareLetBinding(String name, Expr type);
    method prepareText (line 104) | boolean prepareText(int size);
    method prepareTextPart (line 106) | boolean prepareTextPart(String part);
    method prepareNonEmptyList (line 108) | boolean prepareNonEmptyList(int size);
    method prepareNonEmptyListElement (line 110) | boolean prepareNonEmptyListElement(int index);
    method prepareEmptyList (line 112) | boolean prepareEmptyList(Expr type);
    method prepareRecord (line 114) | boolean prepareRecord(int size);
    method prepareRecordField (line 116) | boolean prepareRecordField(String name, Expr type, int index);
    method prepareRecordType (line 118) | boolean prepareRecordType(int size);
    method prepareRecordTypeField (line 120) | boolean prepareRecordTypeField(String name, Expr type, int index);
    method prepareUnionType (line 122) | boolean prepareUnionType(int size);
    method prepareUnionTypeField (line 124) | boolean prepareUnionTypeField(String name, Expr type, int index);
    method prepareFieldAccess (line 126) | boolean prepareFieldAccess(Expr base, String fieldName);
    method prepareProjection (line 128) | boolean prepareProjection(int size);
    method prepareProjectionByType (line 130) | boolean prepareProjectionByType();
    method prepareProjectionByType (line 132) | boolean prepareProjectionByType(Expr type);
    method prepareApplication (line 134) | boolean prepareApplication(Expr base, int size);
    method prepareOperatorApplication (line 136) | boolean prepareOperatorApplication(Operator operator);
    method prepareIf (line 138) | boolean prepareIf();
    method prepareAnnotated (line 140) | boolean prepareAnnotated(Expr type);
    method prepareAssert (line 142) | boolean prepareAssert();
    method prepareMerge (line 144) | boolean prepareMerge(Expr type);
    method prepareToMap (line 146) | boolean prepareToMap(Expr type);
    method prepareWith (line 148) | boolean prepareWith(String[] path);
    method prepareWithValue (line 150) | boolean prepareWithValue(String[] path);
    method prepareRemoteImport (line 152) | boolean prepareRemoteImport(URI url, Expr using, Expr.ImportMode mode,...
    class NoPrepareEvents (line 161) | public abstract static class NoPrepareEvents<A> implements Visitor<A> {
      method bind (line 163) | public void bind(String name, Expr type) {}
      method sortFields (line 165) | public boolean sortFields() {
      method flattenToMapLists (line 169) | public boolean flattenToMapLists() {
      method prepareLambda (line 173) | public boolean prepareLambda(String name, Expr type) {
      method preparePi (line 177) | public boolean preparePi(String name, Expr type) {
      method prepareLet (line 181) | public boolean prepareLet(int size) {
      method prepareLetBinding (line 185) | public boolean prepareLetBinding(String name, Expr type) {
      method prepareText (line 189) | public boolean prepareText(int size) {
      method prepareTextPart (line 193) | public boolean prepareTextPart(String part) {
      method prepareNonEmptyList (line 197) | public boolean prepareNonEmptyList(int size) {
      method prepareNonEmptyListElement (line 201) | public boolean prepareNonEmptyListElement(int index) {
      method prepareEmptyList (line 205) | public boolean prepareEmptyList(Expr type) {
      method prepareRecord (line 209) | public boolean prepareRecord(int size) {
      method prepareRecordField (line 213) | public boolean prepareRecordField(String name, Expr type, int index) {
      method prepareRecordType (line 217) | public boolean prepareRecordType(int size) {
      method prepareRecordTypeField (line 221) | public boolean prepareRecordTypeField(String name, Expr type, int in...
      method prepareUnionType (line 225) | public boolean prepareUnionType(int size) {
      method prepareUnionTypeField (line 229) | public boolean prepareUnionTypeField(String name, Expr type, int ind...
      method prepareFieldAccess (line 233) | public boolean prepareFieldAccess(Expr base, String fieldName) {
      method prepareProjection (line 237) | public boolean prepareProjection(int size) {
      method prepareProjectionByType (line 241) | public boolean prepareProjectionByType() {
      method prepareProjectionByType (line 245) | public boolean prepareProjectionByType(Expr type) {
      method prepareApplication (line 249) | public boolean prepareApplication(Expr base, int size) {
      method prepareOperatorApplication (line 253) | public boolean prepareOperatorApplication(Operator operator) {
      method prepareIf (line 257) | public boolean prepareIf() {
      method prepareAnnotated (line 261) | public boolean prepareAnnotated(Expr type) {
      method prepareAssert (line 265) | public boolean prepareAssert() {
      method prepareMerge (line 269) | public boolean prepareMerge(Expr type) {
      method prepareToMap (line 273) | public boolean prepareToMap(Expr type) {
      method prepareWith (line 277) | public boolean prepareWith(String[] path) {
      method prepareWithValue (line 281) | public boolean prepareWithValue(String[] path) {
      method prepareRemoteImport (line 285) | public boolean prepareRemoteImport(URI url, Expr using, Expr.ImportM...
    class Constant (line 317) | public static class Constant<A> extends NoPrepareEvents<A> {
      method getReturnValue (line 320) | protected A getReturnValue() {
      method Constant (line 324) | public Constant(A value) {
      method bind (line 328) | public void bind(String name, Expr type) {}
      method onNote (line 330) | @Override
      method onNatural (line 335) | @Override
      method onInteger (line 340) | @Override
      method onDouble (line 345) | @Override
      method onDate (line 350) | @Override
      method onTime (line 355) | @Override
      method onTimeZone (line 360) | @Override
      method onBuiltIn (line 365) | @Override
      method onIdentifier (line 370) | @Override
      method onLambda (line 375) | @Override
      method onPi (line 380) | @Override
      method onLet (line 385) | @Override
      method onText (line 390) | @Override
      method onNonEmptyList (line 395) | @Override
      method onEmptyList (line 400) | @Override
      method onRecord (line 405) | @Override
      method onRecordType (line 410) | @Override
      method onUnionType (line 415) | @Override
      method onFieldAccess (line 420) | @Override
      method onProjection (line 425) | @Override
      method onProjectionByType (line 430) | @Override
      method onApplication (line 435) | @Override
      method onOperatorApplication (line 440) | @Override
      method onIf (line 445) | @Override
      method onAnnotated (line 450) | @Override
      method onAssert (line 455) | @Override
      method onMerge (line 460) | @Override
      method onToMap (line 465) | @Override
      method onWith (line 470) | @Override
      method onMissingImport (line 475) | @Override
      method onEnvImport (line 480) | @Override
      method onLocalImport (line 485) | @Override
      method onClasspathImport (line 490) | @Override
      method onRemoteImport (line 495) | @Override
    class Property (line 507) | public class Property extends Constant<Boolean> {
      method Property (line 508) | public Property() {
      method onLambda (line 512) | public Boolean onLambda(String name, Boolean type, Boolean result) {
      method onPi (line 516) | public Boolean onPi(String name, Boolean type, Boolean result) {
      method onLet (line 520) | public Boolean onLet(List<Expr.LetBinding<Boolean>> bindings, Boolea...
      method onText (line 533) | public Boolean onText(String[] parts, List<Boolean> interpolated) {
      method onNonEmptyList (line 542) | public Boolean onNonEmptyList(List<Boolean> values) {
      method onEmptyList (line 551) | public Boolean onEmptyList(Boolean type) {
      method onRecord (line 555) | public Boolean onRecord(List<Entry<String, Boolean>> fields) {
      method onRecordType (line 564) | public Boolean onRecordType(List<Entry<String, Boolean>> fields) {
      method onUnionType (line 573) | public Boolean onUnionType(List<Entry<String, Boolean>> fields) {
      method onFieldAccess (line 582) | public Boolean onFieldAccess(Boolean base, String fieldName) {
      method onProjection (line 586) | public Boolean onProjection(Boolean base, String[] fieldNames) {
      method onProjectionByType (line 590) | public Boolean onProjectionByType(Boolean base, Boolean type) {
      method onApplication (line 594) | public Boolean onApplication(Boolean base, List<Boolean> args) {
      method onOperatorApplication (line 606) | public Boolean onOperatorApplication(Operator operator, Boolean lhs,...
      method onIf (line 610) | public Boolean onIf(Boolean predicate, Boolean thenValue, Boolean el...
      method onAnnotated (line 614) | public Boolean onAnnotated(Boolean base, Boolean type) {
      method onAssert (line 618) | public Boolean onAssert(Boolean base) {
      method onMerge (line 622) | public Boolean onMerge(Boolean handlers, Boolean union, Boolean type) {
      method onToMap (line 626) | public Boolean onToMap(Boolean base, Boolean type) {
      method onToMap (line 630) | public Boolean onToMap(Boolean base, String[] path, Boolean value) {
      method onLocalImport (line 634) | public Boolean onLocalImport(Path path, Expr.ImportMode mode, byte[]...
      method onRemoteImport (line 638) | public Boolean onRemoteImport(URI url, Boolean using, Expr.ImportMod...
      method onEnvImport (line 642) | public Boolean onEnvImport(String value, Expr.ImportMode mode, byte[...
      method onMissingImport (line 646) | public Boolean onMissingImport(Expr.ImportMode mode, byte[] hash) {
    class Identity (line 657) | public abstract class Identity extends NoPrepareEvents<Expr> {
      method onNote (line 658) | public Expr onNote(Expr base, Source source) {
      method onNatural (line 662) | public Expr onNatural(Expr self, BigInteger value) {
      method onInteger (line 666) | public Expr onInteger(Expr self, BigInteger value) {
      method onDouble (line 670) | public Expr onDouble(Expr self, double value) {
      method onDate (line 674) | public Expr onDate(Expr self, int year, int month, int day) {
      method onTime (line 678) | public Expr onTime(Expr self, int hour, int minute, int second, BigD...
      method onTimeZone (line 682) | public Expr onTimeZone(Expr self, int minutes) {
      method onBuiltIn (line 686) | public Expr onBuiltIn(Expr self, String name) {
      method onIdentifier (line 690) | public Expr onIdentifier(Expr self, String name, long index) {
      method onLambda (line 694) | public Expr onLambda(String name, Expr type, Expr result) {
      method onPi (line 698) | public Expr onPi(String name, Expr type, Expr result) {
      method onLet (line 702) | public Expr onLet(List<Expr.LetBinding<Expr>> bindings, Expr body) {
      method onText (line 706) | public Expr onText(String[] parts, List<Expr> interpolated) {
      method onNonEmptyList (line 710) | public Expr onNonEmptyList(List<Expr> values) {
      method onEmptyList (line 714) | public Expr onEmptyList(Expr type) {
      method onRecord (line 718) | public Expr onRecord(List<Entry<String, Expr>> fields) {
      method onRecordType (line 722) | public Expr onRecordType(List<Entry<String, Expr>> fields) {
      method onUnionType (line 726) | public Expr onUnionType(List<Entry<String, Expr>> fields) {
      method onFieldAccess (line 730) | public Expr onFieldAccess(Expr base, String fieldName) {
      method onProjection (line 734) | public Expr onProjection(Expr base, String[] fieldNames) {
      method onProjectionByType (line 738) | public Expr onProjectionByType(Expr base, Expr type) {
      method onApplication (line 742) | public Expr onApplication(Expr base, List<Expr> args) {
      method onOperatorApplication (line 746) | public Expr onOperatorApplication(Operator operator, Expr lhs, Expr ...
      method onIf (line 750) | public Expr onIf(Expr predicate, Expr thenValue, Expr elseValue) {
      method onAnnotated (line 754) | public Expr onAnnotated(Expr base, Expr type) {
      method onAssert (line 758) | public Expr onAssert(Expr base) {
      method onMerge (line 762) | public Expr onMerge(Expr handlers, Expr union, Expr type) {
      method onToMap (line 766) | public Expr onToMap(Expr base, Expr type) {
      method onWith (line 770) | public Expr onWith(Expr base, String[] path, Expr value) {
      method onMissingImport (line 774) | public Expr onMissingImport(Expr.ImportMode mode, byte[] hash) {
      method onEnvImport (line 778) | public Expr onEnvImport(String value, Expr.ImportMode mode, byte[] h...
      method onLocalImport (line 782) | public Expr onLocalImport(Path path, Expr.ImportMode mode, byte[] ha...
      method onClasspathImport (line 786) | public Expr onClasspathImport(Path path, Expr.ImportMode mode, byte[...
      method onRemoteImport (line 790) | public Expr onRemoteImport(URI url, Expr using, Expr.ImportMode mode...

FILE: modules/core/src/main/java/org/dhallj/core/binary/CborDecodingVisitor.java
  class CborDecodingVisitor (line 24) | final class CborDecodingVisitor implements Visitor<Expr> {
    method CborDecodingVisitor (line 28) | CborDecodingVisitor(Reader reader) {
    method onUnsignedInteger (line 32) | @Override
    method onNegativeInteger (line 37) | @Override
    method onByteString (line 42) | @Override
    method onTextString (line 47) | @Override
    method onVariableArray (line 52) | @Override
    method onArray (line 64) | @Override
    method onMap (line 124) | @Override
    method onFalse (line 129) | @Override
    method onTrue (line 134) | @Override
    method onNull (line 139) | @Override
    method onHalfFloat (line 144) | @Override
    method onSingleFloat (line 149) | @Override
    method onDoubleFloat (line 154) | @Override
    method onTag (line 159) | @Override
    method readFnApplication (line 165) | private Expr readFnApplication(BigInteger length) {
    method readFunction (line 178) | private Expr readFunction(BigInteger length) {
    method readPi (line 197) | private Expr readPi(BigInteger length) {
    method readOperator (line 216) | private Expr readOperator(BigInteger length) {
    method readList (line 234) | private Expr readList(BigInteger length) {
    method readEmptyListAbstractType (line 255) | private Expr readEmptyListAbstractType(BigInteger length) {
    method readSome (line 268) | private Expr readSome(BigInteger length) {
    method readMerge (line 281) | private Expr readMerge(BigInteger length) {
    method readMap (line 297) | private Expr readMap(BigInteger length) {
    method readWith (line 311) | private Expr readWith(BigInteger length) {
    method readRecordType (line 329) | private Expr readRecordType(BigInteger length) {
    method readRecordLiteral (line 339) | private Expr readRecordLiteral(BigInteger length) {
    method readFieldAccess (line 349) | private Expr readFieldAccess(BigInteger length) {
    method readProjection (line 360) | private Expr readProjection(BigInteger length) {
    method readUnion (line 390) | private Expr readUnion(BigInteger length) {
    method readIf (line 400) | private Expr readIf(BigInteger length) {
    method readTypeAnnotation (line 412) | private Expr readTypeAnnotation(BigInteger length) {
    method readLet (line 423) | private Expr readLet(BigInteger length) {
    method readLet (line 427) | private Expr readLet(long len) {
    method readImport (line 442) | private Expr readImport(BigInteger length) {
    method readMode (line 473) | private Expr.ImportMode readMode() {
    method readLocalImport (line 486) | private Expr readLocalImport(
    method readClasspathImport (line 496) | private Expr readClasspathImport(
    method readRemoteImport (line 506) | private Expr readRemoteImport(
    method readEnvImport (line 526) | private Expr readEnvImport(BigInteger length, Expr.ImportMode mode, by...
    method readAssert (line 531) | private Expr readAssert(BigInteger length) {
    method readTextLiteral (line 541) | private Expr readTextLiteral(BigInteger length) {
    method readInteger (line 555) | private Expr readInteger(BigInteger length) {
    method readNatural (line 559) | private Expr readNatural(BigInteger length) {
    method readDate (line 563) | private Expr readDate(BigInteger length) {
    method readTime (line 575) | private Expr readTime(BigInteger length) {
    method readTimeZone (line 590) | private Expr readTimeZone(BigInteger length) {
    method readExpr (line 609) | private Expr readExpr() {
    method notExpected (line 613) | private Expr notExpected(String msg) {

FILE: modules/core/src/main/java/org/dhallj/core/binary/Decode.java
  class Decode (line 6) | public class Decode {
    method decode (line 7) | public static final Expr decode(byte[] bytes) {

FILE: modules/core/src/main/java/org/dhallj/core/binary/DecodingException.java
  class DecodingException (line 5) | public class DecodingException extends DhallException {
    method DecodingException (line 6) | public DecodingException(String message) {
    method DecodingException (line 10) | public DecodingException(String message, Throwable cause) {

FILE: modules/core/src/main/java/org/dhallj/core/binary/Encode.java
  class Encode (line 17) | public final class Encode implements Visitor<Void> {
    method Encode (line 20) | public Encode(Writer writer) {
    method sortFields (line 24) | public boolean sortFields() {
    method flattenToMapLists (line 28) | public boolean flattenToMapLists() {
    method onNote (line 32) | public Void onNote(Void base, Source source) {
    method onNatural (line 36) | public Void onNatural(Expr self, BigInteger value) {
    method onInteger (line 43) | public Void onInteger(Expr self, BigInteger value) {
    method onDouble (line 50) | public Void onDouble(Expr self, double value) {
    method onDate (line 55) | public Void onDate(Expr self, int year, int month, int day) {
    method onTime (line 64) | public Void onTime(Expr self, int hour, int minute, int second, BigDec...
    method onTimeZone (line 73) | public Void onTimeZone(Expr self, int minutes) {
    method onBuiltIn (line 84) | public Void onBuiltIn(Expr self, String name) {
    method onIdentifier (line 95) | public Void onIdentifier(Expr self, String name, long index) {
    method bind (line 106) | public void bind(String param, Expr type) {}
    method prepareLambda (line 108) | public boolean prepareLambda(String name, Expr type) {
    method onLambda (line 120) | public Void onLambda(String name, Void type, Void result) {
    method preparePi (line 124) | public boolean preparePi(String name, Expr type) {
    method onPi (line 136) | public Void onPi(String name, Void type, Void result) {
    method prepareLet (line 140) | public boolean prepareLet(int size) {
    method prepareLetBinding (line 146) | public boolean prepareLetBinding(String name, Expr type) {
    method onLet (line 154) | public Void onLet(List<Expr.LetBinding<Void>> bindings, Void body) {
    method unescapeText (line 158) | private static final String unescapeText(String input) {
    method prepareText (line 192) | public boolean prepareText(int size) {
    method prepareTextPart (line 198) | public boolean prepareTextPart(String part) {
    method onText (line 203) | public Void onText(String[] parts, List<Void> interpolated) {
    method prepareNonEmptyList (line 207) | public boolean prepareNonEmptyList(int size) {
    method prepareNonEmptyListElement (line 214) | public boolean prepareNonEmptyListElement(int index) {
    method onNonEmptyList (line 218) | public Void onNonEmptyList(final List<Void> values) {
    method prepareEmptyList (line 222) | public boolean prepareEmptyList(Expr type) {
    method onEmptyList (line 238) | public Void onEmptyList(Void type) {
    method prepareRecord (line 242) | public boolean prepareRecord(int size) {
    method prepareRecordField (line 249) | public boolean prepareRecordField(String name, Expr type, int index) {
    method onRecord (line 254) | public Void onRecord(final List<Entry<String, Void>> fields) {
    method prepareRecordType (line 258) | public boolean prepareRecordType(int size) {
    method prepareRecordTypeField (line 265) | public boolean prepareRecordTypeField(String name, Expr type, int inde...
    method onRecordType (line 270) | public Void onRecordType(final List<Entry<String, Void>> fields) {
    method prepareUnionType (line 274) | public boolean prepareUnionType(int size) {
    method prepareUnionTypeField (line 281) | public boolean prepareUnionTypeField(String name, Expr type, int index) {
    method onUnionType (line 289) | public Void onUnionType(final List<Entry<String, Void>> fields) {
    method prepareFieldAccess (line 293) | public boolean prepareFieldAccess(Expr base, String fieldName) {
    method onFieldAccess (line 299) | public Void onFieldAccess(Void base, final String fieldName) {
    method prepareProjection (line 304) | public boolean prepareProjection(int size) {
    method onProjection (line 310) | public Void onProjection(Void base, final String[] fieldNames) {
    method prepareProjectionByType (line 317) | public boolean prepareProjectionByType() {
    method prepareProjectionByType (line 323) | public boolean prepareProjectionByType(Expr type) {
    method onProjectionByType (line 328) | public Void onProjectionByType(Void base, Void type) {
    method prepareApplication (line 332) | public boolean prepareApplication(Expr base, int size) {
    method onApplication (line 355) | public Void onApplication(Void base, final List<Void> args) {
    method prepareOperatorApplication (line 359) | public boolean prepareOperatorApplication(final Operator operator) {
    method onOperatorApplication (line 366) | public Void onOperatorApplication(Operator operator, Void lhs, Void rh...
    method prepareIf (line 370) | public boolean prepareIf() {
    method onIf (line 376) | public Void onIf(Void predicate, Void thenValue, Void elseValue) {
    method prepareAnnotated (line 380) | public boolean prepareAnnotated(Expr type) {
    method onAnnotated (line 386) | public Void onAnnotated(Void base, Void type) {
    method prepareAssert (line 390) | public boolean prepareAssert() {
    method onAssert (line 396) | public Void onAssert(Void base) {
    method prepareMerge (line 400) | public boolean prepareMerge(Expr type) {
    method onMerge (line 406) | public Void onMerge(Void handlers, Void union, Void type) {
    method prepareToMap (line 410) | public boolean prepareToMap(Expr type) {
    method onToMap (line 416) | public Void onToMap(Void base, Void type) {
    method prepareWith (line 420) | public boolean prepareWith(String[] path) {
    method prepareWithValue (line 426) | public boolean prepareWithValue(String[] path) {
    method onWith (line 434) | public Void onWith(Void base, String[] path, Void value) {
    method modeLabel (line 438) | private final int modeLabel(Expr.ImportMode mode) {
    method multihash (line 448) | private static final byte[] multihash(byte[] hash) {
    method onMissingImport (line 458) | public Void onMissingImport(final Expr.ImportMode mode, final byte[] h...
    method onEnvImport (line 471) | public Void onEnvImport(final String value, final Expr.ImportMode mode...
    method pathLabel (line 485) | private static final int pathLabel(Path path) {
    method onLocalImport (line 501) | public Void onLocalImport(final Path path, final Expr.ImportMode mode,...
    method onClasspathImport (line 523) | @Override
    method urlLabel (line 543) | private static final int urlLabel(URI url) {
    method getUrlPathParts (line 553) | private static final List<String> getUrlPathParts(URI url) {
    method prepareRemoteImport (line 571) | public boolean prepareRemoteImport(URI url, Expr using, Expr.ImportMod...
    method onRemoteImport (line 590) | public Void onRemoteImport(URI url, Void using, Expr.ImportMode mode, ...

FILE: modules/core/src/main/java/org/dhallj/core/binary/Label.java
  class Label (line 3) | final class Label {

FILE: modules/core/src/main/java/org/dhallj/core/converters/JsonConverter.java
  class JsonConverter (line 9) | public final class JsonConverter extends Visitor.Constant<Boolean> {
    method JsonConverter (line 13) | public JsonConverter(JsonHandler handler, boolean escapeStrings) {
    method JsonConverter (line 19) | public JsonConverter(JsonHandler handler) {
    method toCompactString (line 23) | public static final String toCompactString(Expr expr) {
    method escape (line 33) | private static final String escape(String input) {
    method sortFields (line 60) | @Override
    method flattenToMapLists (line 65) | @Override
    method onNatural (line 70) | @Override
    method onInteger (line 76) | @Override
    method onDouble (line 82) | @Override
    method onBuiltIn (line 88) | @Override
    method onText (line 101) | @Override
    method prepareNonEmptyList (line 115) | @Override
    method prepareNonEmptyListElement (line 121) | @Override
    method onNonEmptyList (line 129) | @Override
    method onEmptyList (line 140) | @Override
    method prepareRecord (line 147) | @Override
    method prepareRecordField (line 153) | @Override
    method onRecord (line 166) | @Override
    method prepareFieldAccess (line 177) | @Override
    method onFieldAccess (line 192) | @Override
    method prepareApplication (line 197) | @Override
    method onApplication (line 227) | @Override

FILE: modules/core/src/main/java/org/dhallj/core/converters/JsonHandler.java
  type JsonHandler (line 6) | public interface JsonHandler {
    method onNull (line 7) | void onNull();
    method onBoolean (line 9) | void onBoolean(boolean value);
    method onNumber (line 11) | void onNumber(BigInteger value);
    method onDouble (line 13) | void onDouble(double value);
    method onString (line 15) | void onString(String value);
    method onArrayStart (line 17) | void onArrayStart();
    method onArrayEnd (line 19) | void onArrayEnd();
    method onArrayElementGap (line 21) | void onArrayElementGap();
    method onObjectStart (line 23) | void onObjectStart();
    method onObjectEnd (line 25) | void onObjectEnd();
    method onObjectField (line 27) | void onObjectField(String name);
    method onObjectFieldGap (line 29) | void onObjectFieldGap();
    class CompactPrinter (line 31) | public static final class CompactPrinter implements JsonHandler {
      method CompactPrinter (line 34) | public CompactPrinter(PrintWriter writer) {
      method onNull (line 38) | public void onNull() {
      method onBoolean (line 42) | public void onBoolean(boolean value) {
      method onNumber (line 46) | public void onNumber(BigInteger value) {
      method onDouble (line 50) | public void onDouble(double value) {
      method onString (line 54) | public void onString(String value) {
      method onArrayStart (line 58) | public void onArrayStart() {
      method onArrayEnd (line 62) | public void onArrayEnd() {
      method onArrayElementGap (line 66) | public void onArrayElementGap() {
      method onObjectStart (line 70) | public void onObjectStart() {
      method onObjectEnd (line 74) | public void onObjectEnd() {
      method onObjectField (line 78) | public void onObjectField(String name) {
      method onObjectFieldGap (line 82) | public void onObjectFieldGap() {
    class CompactStringPrinter (line 87) | public static final class CompactStringPrinter implements JsonHandler {
      method CompactStringPrinter (line 90) | public CompactStringPrinter() {
      method toString (line 94) | public String toString() {
      method onNull (line 98) | public void onNull() {
      method onBoolean (line 102) | public void onBoolean(boolean value) {
      method onNumber (line 106) | public void onNumber(BigInteger value) {
      method onDouble (line 110) | public void onDouble(double value) {
      method onString (line 114) | public void onString(String value) {
      method onArrayStart (line 118) | public void onArrayStart() {
      method onArrayEnd (line 122) | public void onArrayEnd() {
      method onArrayElementGap (line 126) | public void onArrayElementGap() {
      method onObjectStart (line 130) | public void onObjectStart() {
      method onObjectEnd (line 134) | public void onObjectEnd() {
      method onObjectField (line 138) | public void onObjectField(String name) {
      method onObjectFieldGap (line 142) | public void onObjectFieldGap() {

FILE: modules/core/src/main/java/org/dhallj/core/normalization/AlphaNormalize.java
  class AlphaNormalize (line 24) | public final class AlphaNormalize extends Visitor.Identity {
    method onIdentifier (line 35) | @Override
    method bind (line 52) | @Override
    method onLambda (line 67) | @Override
    method onPi (line 77) | @Override
    method onLet (line 87) | @Override

FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalize.java
  class BetaNormalize (line 22) | public final class BetaNormalize extends Visitor.NoPrepareEvents<Expr> {
    method bind (line 25) | public void bind(String name, Expr type) {}
    method onNote (line 27) | public Expr onNote(Expr base, Source source) {
    method onNatural (line 31) | public Expr onNatural(Expr self, BigInteger value) {
    method onInteger (line 35) | public Expr onInteger(Expr self, BigInteger value) {
    method onDouble (line 39) | public Expr onDouble(Expr self, double value) {
    method onDate (line 43) | public Expr onDate(Expr self, int year, int month, int day) {
    method onTime (line 47) | public Expr onTime(Expr self, int hour, int minute, int second, BigDec...
    method onTimeZone (line 51) | public Expr onTimeZone(Expr self, int minutes) {
    method onBuiltIn (line 55) | public Expr onBuiltIn(Expr self, String name) {
    method onIdentifier (line 59) | public Expr onIdentifier(Expr self, String name, long index) {
    method onLambda (line 63) | public Expr onLambda(String name, Expr type, Expr result) {
    method onPi (line 67) | public Expr onPi(String name, Expr type, Expr result) {
    method onLet (line 71) | public Expr onLet(List<Expr.LetBinding<Expr>> bindings, Expr body) {
    method onText (line 84) | public Expr onText(String[] parts, List<Expr> interpolated) {
    method onNonEmptyList (line 88) | public Expr onNonEmptyList(List<Expr> values) {
    method onEmptyList (line 92) | public Expr onEmptyList(Expr type) {
    method onRecord (line 96) | public Expr onRecord(List<Entry<String, Expr>> fields) {
    method onRecordType (line 101) | public Expr onRecordType(List<Entry<String, Expr>> fields) {
    method onUnionType (line 106) | public Expr onUnionType(List<Entry<String, Expr>> fields) {
    method onFieldAccess (line 111) | public Expr onFieldAccess(Expr base, String fieldName) {
    method onProjection (line 115) | public Expr onProjection(Expr base, String[] fieldNames) {
    method onProjectionByType (line 119) | public Expr onProjectionByType(Expr base, Expr arg) {
    method onApplication (line 129) | public Expr onApplication(Expr base, List<Expr> args) {
    method onOperatorApplication (line 133) | public Expr onOperatorApplication(Operator operator, Expr lhs, Expr rh...
    method onIf (line 137) | public Expr onIf(Expr predicate, Expr thenValue, Expr elseValue) {
    method onAnnotated (line 141) | public Expr onAnnotated(Expr base, Expr type) {
    method onAssert (line 145) | public Expr onAssert(Expr type) {
    method onMerge (line 149) | public Expr onMerge(Expr handlers, Expr union, Expr type) {
    method onToMap (line 153) | public Expr onToMap(Expr base, Expr type) {
    method onWith (line 157) | public Expr onWith(Expr base, String[] path, Expr value) {
    method onMissingImport (line 161) | public Expr onMissingImport(Expr.ImportMode mode, byte[] hash) {
    method onEnvImport (line 165) | public Expr onEnvImport(String value, Expr.ImportMode mode, byte[] has...
    method onLocalImport (line 169) | public Expr onLocalImport(Path path, Expr.ImportMode mode, byte[] hash) {
    method onClasspathImport (line 173) | @Override
    method onRemoteImport (line 178) | public Expr onRemoteImport(URI url, Expr using, Expr.ImportMode mode, ...

FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeApplication.java
  class BetaNormalizeApplication (line 12) | final class BetaNormalizeApplication {
    method applyLambdas (line 13) | private static Expr applyLambdas(Expr base, final List<Expr> args) {
    method booleanToExpr (line 38) | private static final Expr booleanToExpr(boolean value) {
    method apply (line 42) | static final Expr apply(Expr base, List<Expr> args) {
    method indexedRecordType (line 91) | private static final Expr indexedRecordType(Expr type) {
    method isBigIntegerEven (line 98) | private static final boolean isBigIntegerEven(BigInteger value) {
    method isBigIntegerNatural (line 102) | private static final boolean isBigIntegerNatural(BigInteger value) {
    method arity1 (line 106) | private static final Expr arity1(String identifier, Expr arg) {
    method arity2 (line 203) | private static final Expr arity2(String identifier, Expr arg1, Expr ar...
    method drop (line 312) | private static List<Expr> drop(List<Expr> args, int count) {
    method naturalFold (line 320) | private static final Expr naturalFold(List<Expr> args) {
    method listFold (line 357) | private static final Expr listFold(List<Expr> args) {
    method textReplace (line 400) | private static final Expr textReplace(List<Expr> args) {

FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeFieldAccess.java
  class BetaNormalizeFieldAccess (line 10) | final class BetaNormalizeFieldAccess extends ExternalVisitor.Constant<Ex...
    method BetaNormalizeFieldAccess (line 13) | public BetaNormalizeFieldAccess(String fieldName) {
    method apply (line 18) | static final Expr apply(Expr base, String fieldName) {
    method onRecord (line 28) | @Override
    method onProjection (line 33) | @Override
    method onOperatorApplication (line 38) | @Override

FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeIf.java
  class BetaNormalizeIf (line 5) | final class BetaNormalizeIf {
    method apply (line 6) | static final Expr apply(Expr predicate, Expr thenValue, Expr elseValue) {

FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeMerge.java
  class BetaNormalizeMerge (line 9) | final class BetaNormalizeMerge extends ExternalVisitor.Constant<Expr> {
    method BetaNormalizeMerge (line 12) | private BetaNormalizeMerge(List<Entry<String, Expr>> handlers) {
    method apply (line 17) | static final Expr apply(Expr handlers, Expr union, Expr type) {
    method onFieldAccess (line 30) | @Override
    method onApplication (line 41) | @Override
    method merge (line 67) | private static Expr merge(List<Entry<String, Expr>> handlers, String f...
    method merge (line 71) | private static Expr merge(List<Entry<String, Expr>> handlers, String f...

FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeOperatorApplication.java
  class BetaNormalizeOperatorApplication (line 12) | final class BetaNormalizeOperatorApplication {
    method apply (line 13) | static final Expr apply(Operator operator, Expr lhs, Expr rhs) {
    method mergeRecursive (line 176) | private static final Expr mergeRecursive(
    method mergeTypesRecursive (line 212) | private static final Expr mergeTypesRecursive(

FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeProjection.java
  class BetaNormalizeProjection (line 15) | final class BetaNormalizeProjection extends ExternalVisitor.Constant<Exp...
    method BetaNormalizeProjection (line 18) | private BetaNormalizeProjection(String[] fieldNames) {
    method apply (line 23) | static final Expr apply(Expr base, final String[] fieldNames) {
    method onRecord (line 41) | @Override
    method onProjection (line 59) | @Override
    method onOperatorApplication (line 64) | @Override

FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeTextLiteral.java
  class BetaNormalizeTextLiteral (line 9) | final class BetaNormalizeTextLiteral {
    method apply (line 10) | static final Expr apply(String[] parts, List<Expr> interpolated) {
    class InlineInterpolatedTextLiteral (line 66) | private static final class InlineInterpolatedTextLiteral
      method InlineInterpolatedTextLiteral (line 71) | InlineInterpolatedTextLiteral(List<String> newParts, List<Expr> newI...
      method onText (line 77) | @Override

FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeToMap.java
  class BetaNormalizeToMap (line 9) | final class BetaNormalizeToMap {
    method apply (line 10) | static final Expr apply(Expr base, Expr type) {
    method escape (line 32) | private static final String escape(String input) {
    method makeRecord (line 59) | private static final Expr makeRecord(String key, Expr value) {

FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeWith.java
  class BetaNormalizeWith (line 11) | final class BetaNormalizeWith {
    method apply (line 12) | static final Expr apply(Expr base, String[] path, Expr value) {
    method compare (line 74) | public int compare(Entry<String, Expr> a, Entry<String, Expr> b) {

FILE: modules/core/src/main/java/org/dhallj/core/normalization/NormalizationUtils.java
  class NormalizationUtilities (line 8) | final class NormalizationUtilities {
    method lookup (line 9) | static final <A> A lookup(Iterable<Entry<String, A>> entries, String k...
    method lookupEntry (line 18) | static final <A> Entry<String, A> lookupEntry(Iterable<Entry<String, A...
    method compare (line 29) | public int compare(Entry<String, Expr> a, Entry<String, Expr> b) {

FILE: modules/core/src/main/java/org/dhallj/core/normalization/Shift.java
  class Shift (line 21) | public final class Shift extends Visitor.Identity {
    method Shift (line 26) | public Shift(boolean isIncrement, String name) {
    method onIdentifier (line 31) | @Override
    method bind (line 40) | @Override
    method onLambda (line 47) | @Override
    method onPi (line 56) | @Override
    method onLet (line 65) | @Override

FILE: modules/core/src/main/java/org/dhallj/core/normalization/Substitute.java
  class Substitute (line 14) | public final class Substitute extends Visitor.Identity {
    method Substitute (line 19) | public Substitute(String name, Expr replacement) {
    method bind (line 24) | @Override
    method onIdentifier (line 33) | @Override
    method onLambda (line 48) | @Override
    method onPi (line 59) | @Override
    method onLet (line 70) | @Override

FILE: modules/core/src/main/java/org/dhallj/core/typechecking/BuiltInTypes.java
  class BuiltInTypes (line 10) | final class BuiltInTypes {
    method getType (line 11) | static final Expr getType(String name) {

FILE: modules/core/src/main/java/org/dhallj/core/typechecking/CheckEquivalence.java
  class CheckEquivalence (line 7) | final class CheckEquivalence extends ExternalVisitor.Constant<Boolean> {
    method CheckEquivalence (line 10) | CheckEquivalence() {
    method onOperatorApplication (line 14) | @Override

FILE: modules/core/src/main/java/org/dhallj/core/typechecking/Context.java
  class Context (line 5) | final class Context {
    method Context (line 10) | Context(String key, Expr value, Context tail) {
    method lookup (line 16) | public Expr lookup(String targetKey, long index) {
    method insert (line 36) | public Context insert(String key, Expr value) {
    method increment (line 40) | public Context increment(String name) {
    method decrement (line 48) | public Context decrement(String name) {

FILE: modules/core/src/main/java/org/dhallj/core/typechecking/NonNegativeIndices.java
  class NonNegativeIndices (line 6) | final class NonNegativeIndices extends Visitor.Property {
    method onIdentifier (line 9) | @Override

FILE: modules/core/src/main/java/org/dhallj/core/typechecking/TypeCheck.java
  class TypeCheck (line 27) | public final class TypeCheck implements ExternalVisitor<Expr> {
    method TypeCheck (line 30) | public TypeCheck(Context context) {
    method TypeCheck (line 34) | public TypeCheck() {
    method onNote (line 38) | @Override
    method onNatural (line 43) | @Override
    method onInteger (line 48) | @Override
    method onDouble (line 53) | @Override
    method onDate (line 58) | @Override
    method onTime (line 63) | @Override
    method onTimeZone (line 68) | @Override
    method onBuiltIn (line 73) | @Override
    method onIdentifier (line 87) | @Override
    method onLambda (line 98) | @Override
    method onPi (line 113) | @Override
    method onLet (line 136) | @Override
    method onText (line 152) | @Override
    method onNonEmptyList (line 164) | @Override
    method onEmptyList (line 183) | @Override
    method onRecord (line 198) | @Override
    method onRecordType (line 218) | @Override
    method onUnionType (line 245) | @Override
    method onFieldAccess (line 275) | @Override
    method onProjection (line 307) | @Override
    method onProjectionByType (line 344) | @Override
    method onApplication (line 378) | @Override
    method onOperatorApplication (line 392) | @Override
    method onIf (line 505) | @Override
    method onAnnotated (line 526) | @Override
    method onAssert (line 536) | @Override
    method onMerge (line 550) | @Override
    method onToMap (line 603) | @Override
    method onWith (line 698) | @Override
    method onMissingImport (line 755) | @Override
    method onEnvImport (line 760) | @Override
    method onLocalImport (line 765) | @Override
    method onClasspathImport (line 770) | @Override
    method onRemoteImport (line 775) | @Override
    method isBool (line 780) | static final boolean isBool(Expr expr) {
    method isText (line 785) | static final boolean isText(Expr expr) {
    method isList (line 790) | static final boolean isList(Expr expr) {
    method isNatural (line 795) | static final boolean isNatural(Expr expr) {
    method isOptional (line 800) | static final boolean isOptional(Expr expr) {
    method isType (line 805) | static final boolean isType(Expr expr) {
    method checkRecursiveTypeMerge (line 810) | private final void checkRecursiveTypeMerge(
    method makeOptionalConstructors (line 829) | private static final List<Entry<String, Expr>> makeOptionalConstructor...
    method getMergeInferredType (line 836) | private static final Expr getMergeInferredType(
    method prefer (line 911) | private static final Entry<String, Expr>[] prefer(
    method compare (line 947) | public int compare(Entry<String, Expr> a, Entry<String, Expr> b) {

FILE: modules/core/src/main/java/org/dhallj/core/typechecking/TypeCheckApplication.java
  class TypeCheckApplication (line 6) | final class TypeCheckApplication extends ExternalVisitor.Constant<Expr> {
    method TypeCheckApplication (line 11) | TypeCheckApplication(Expr arg, Expr argType, TypeCheck typeCheck) {
    method onBuiltIn (line 18) | @Override
    method onPi (line 30) | @Override

FILE: modules/core/src/main/java/org/dhallj/core/typechecking/TypeCheckFailure.java
  class TypeCheckFailure (line 7) | public final class TypeCheckFailure extends DhallException {
    method fillInStackTrace (line 8) | @Override
    method TypeCheckFailure (line 14) | TypeCheckFailure(String message) {
    method makeSortError (line 18) | static final TypeCheckFailure makeSortError() {
    method makeUnboundVariableError (line 22) | static final TypeCheckFailure makeUnboundVariableError(String name) {
    method makeOperatorError (line 26) | static final TypeCheckFailure makeOperatorError(Operator operator) {
    method makeListAppendError (line 53) | static final TypeCheckFailure makeListAppendError(Expr lhs, Expr rhs) {
    method makeEquivalenceError (line 57) | static final TypeCheckFailure makeEquivalenceError(Expr lhs, Expr rhs) {
    method makeInterpolationError (line 61) | static final TypeCheckFailure makeInterpolationError(Expr interpolated...
    method makeSomeApplicationError (line 65) | static final TypeCheckFailure makeSomeApplicationError(Expr arg, Expr ...
    method makeBuiltInApplicationError (line 69) | static final TypeCheckFailure makeBuiltInApplicationError(String name,...
    method makeApplicationTypeError (line 73) | static final TypeCheckFailure makeApplicationTypeError(Expr expected, ...
    method makeApplicationError (line 77) | static final TypeCheckFailure makeApplicationError(Expr base, Expr arg) {
    method makeUnresolvedImportError (line 81) | static final TypeCheckFailure makeUnresolvedImportError() {
    method makeIfPredicateError (line 85) | static final TypeCheckFailure makeIfPredicateError(Expr type) {
    method makeIfBranchTypeMismatchError (line 89) | static final TypeCheckFailure makeIfBranchTypeMismatchError(Expr thenT...
    method makeLambdaInputError (line 93) | static final TypeCheckFailure makeLambdaInputError(Expr type) {
    method makePiInputError (line 97) | static final TypeCheckFailure makePiInputError(Expr type) {
    method makePiOutputError (line 101) | static final TypeCheckFailure makePiOutputError(Expr type) {
    method makeAssertError (line 105) | static final TypeCheckFailure makeAssertError(Expr type) {
    method makeFieldAccessError (line 109) | static final TypeCheckFailure makeFieldAccessError() {
    method makeFieldAccessRecordMissingError (line 113) | static final TypeCheckFailure makeFieldAccessRecordMissingError(String...
    method makeFieldAccessUnionMissingError (line 117) | static final TypeCheckFailure makeFieldAccessUnionMissingError(String ...
    method makeProjectionError (line 121) | static final TypeCheckFailure makeProjectionError() {
    method makeFieldTypeError (line 125) | static final TypeCheckFailure makeFieldTypeError(String fieldName, Exp...
    method makeFieldDuplicateError (line 129) | static final TypeCheckFailure makeFieldDuplicateError(String fieldName) {
    method makeListTypeMismatchError (line 133) | static final TypeCheckFailure makeListTypeMismatchError(Expr type1, Ex...
    method makeListTypeError (line 137) | static final TypeCheckFailure makeListTypeError(Expr type) {
    method makeAnnotationError (line 141) | static final TypeCheckFailure makeAnnotationError(Expr expected, Expr ...
    method makeAlternativeTypeError (line 145) | static final TypeCheckFailure makeAlternativeTypeError(String fieldNam...
    method makeAlternativeDuplicateError (line 150) | static final TypeCheckFailure makeAlternativeDuplicateError(String fie...
    method makeMergeHandlersTypeError (line 154) | static final TypeCheckFailure makeMergeHandlersTypeError(Expr type) {
    method makeMergeUnionTypeError (line 158) | static final TypeCheckFailure makeMergeUnionTypeError(Expr type) {
    method makeMergeHandlerMissingError (line 162) | static final TypeCheckFailure makeMergeHandlerMissingError(String fiel...
    method makeMergeHandlerUnusedError (line 166) | static final TypeCheckFailure makeMergeHandlerUnusedError(String field...
    method makeMergeHandlerTypeInvalidError (line 170) | static final TypeCheckFailure makeMergeHandlerTypeInvalidError(Expr ex...
    method makeMergeHandlerTypeNotFunctionError (line 174) | static final TypeCheckFailure makeMergeHandlerTypeNotFunctionError(
    method makeMergeHandlerTypeMismatchError (line 179) | static final TypeCheckFailure makeMergeHandlerTypeMismatchError(Expr t...
    method makeMergeHandlerTypeDisallowedError (line 183) | static final TypeCheckFailure makeMergeHandlerTypeDisallowedError(Expr...
    method makeMergeInvalidAnnotationError (line 187) | static final TypeCheckFailure makeMergeInvalidAnnotationError(Expr exp...
    method makeToMapTypeError (line 191) | static final TypeCheckFailure makeToMapTypeError(Expr type) {
    method makeToMapRecordKindError (line 195) | static final TypeCheckFailure makeToMapRecordKindError(Expr type) {
    method makeToMapRecordTypeMismatchError (line 199) | static final TypeCheckFailure makeToMapRecordTypeMismatchError(Expr ty...
    method makeToMapResultTypeMismatchError (line 203) | static final TypeCheckFailure makeToMapResultTypeMismatchError(Expr ex...
    method makeToMapMissingAnnotationError (line 207) | static final TypeCheckFailure makeToMapMissingAnnotationError() {
    method makeToMapInvalidAnnotationError (line 211) | static final TypeCheckFailure makeToMapInvalidAnnotationError(Expr typ...
    method makeWithTypeError (line 215) | static final TypeCheckFailure makeWithTypeError(Expr type) {

FILE: modules/core/src/main/java/org/dhallj/core/typechecking/Universe.java
  type Universe (line 5) | public enum Universe {
    method max (line 10) | public final Universe max(Universe other) {
    method toExpr (line 20) | public final Expr toExpr() {
    method isUniverse (line 30) | public static final boolean isUniverse(Expr expr) {
    method fromExpr (line 34) | public static final Universe fromExpr(Expr expr) {
    method functionCheck (line 49) | public static Universe functionCheck(Universe input, Universe output) {

FILE: modules/imports-mini/src/main/java/org/dhallj/imports/mini/ResolutionVisitor.java
  class ResolutionVisitor (line 16) | abstract class ResolutionVisitor extends Visitor.Identity {
    method ResolutionVisitor (line 20) | ResolutionVisitor(Path currentPath, boolean integrityChecks) {
    method readContents (line 25) | protected abstract String readContents(Path path) throws IOException, ...
    method withCurrentPath (line 27) | protected abstract ResolutionVisitor withCurrentPath(Path newCurrentPa...
    method bind (line 29) | public void bind(String name, Expr type) {}
    method onOperatorApplication (line 31) | @Override
    method onMissingImport (line 40) | @Override
    method onEnvImport (line 53) | @Override
    method onLocalImport (line 83) | @Override
    method onRemoteImport (line 118) | @Override
    method checkHash (line 134) | private final Expr checkHash(Expr result, byte[] expected) {
    class Filesystem (line 144) | static final class Filesystem extends ResolutionVisitor {
      method Filesystem (line 145) | Filesystem(Path currentPath, boolean integrityChecks) {
      method withCurrentPath (line 149) | protected ResolutionVisitor withCurrentPath(Path newCurrentPath) {
      method readContents (line 153) | protected String readContents(Path path) throws IOException, URISynt...
    class Resources (line 158) | static final class Resources extends ResolutionVisitor {
      method Resources (line 161) | Resources(Path currentPath, boolean integrityChecks, ClassLoader cla...
      method withCurrentPath (line 166) | protected ResolutionVisitor withCurrentPath(Path newCurrentPath) {
      method readContents (line 170) | protected String readContents(Path path) throws IOException, URISynt...
    class WrappedParsingFailure (line 176) | static final class WrappedParsingFailure extends RuntimeException {
      method WrappedParsingFailure (line 180) | WrappedParsingFailure(String location, ParsingFailure underlying) {
    class WrappedIOException (line 187) | static final class WrappedIOException extends RuntimeException {
      method WrappedIOException (line 191) | WrappedIOException(Path path, Exception underlying) {
    class Missing (line 198) | static final class Missing extends RuntimeException {
      method Missing (line 199) | Missing() {
    class MissingEnv (line 204) | static final class MissingEnv extends RuntimeException {
      method MissingEnv (line 207) | MissingEnv(String name) {
    class IntegrityCheckException (line 213) | static final class IntegrityCheckException extends RuntimeException {
      method IntegrityCheckException (line 217) | IntegrityCheckException(byte[] expected, byte[] received) {

FILE: modules/imports-mini/src/main/java/org/dhallj/imports/mini/Resolver.java
  class Resolver (line 7) | public final class Resolver {
    method resolve (line 8) | public static final Expr resolve(Expr expr, boolean integrityChecks, P...
    method resolve (line 13) | public static final Expr resolve(Expr expr, boolean integrityChecks) t...
    method resolve (line 17) | public static final Expr resolve(Expr expr) throws ResolutionFailure {
    method resolveFromResources (line 21) | public static final Expr resolveFromResources(
    method resolveFromResources (line 28) | public static final Expr resolveFromResources(
    method resolveFromResources (line 34) | public static final Expr resolveFromResources(Expr expr, boolean integ...
    method resolveFromResources (line 39) | public static final Expr resolveFromResources(Expr expr) throws Resolu...
    method resolveWithVisitor (line 43) | private static final Expr resolveWithVisitor(Expr expr, ResolutionVisi...

FILE: modules/parser/src/main/java/org/dhallj/parser/DhallParser.java
  class DhallParser (line 10) | public final class DhallParser {
    method parse (line 13) | public static Expr.Parsed parse(String input) {
    method parse (line 17) | public static Expr.Parsed parse(InputStream input) throws IOException {
    method parse (line 21) | public static Expr.Parsed parse(InputStream input, Charset charset) th...

FILE: modules/parser/src/main/java/org/dhallj/parser/support/Comment.java
  class Comment (line 3) | final class Comment {
    method Comment (line 10) | Comment(String content, int beginLine, int beginColumn, int endLine, i...
    method getContent (line 18) | public String getContent() {
    method getBeginLine (line 22) | public final int getBeginLine() {
    method getBeginColumn (line 26) | public final int getBeginColumn() {
    method getEndLine (line 30) | public final int getEndLine() {
    method getEndColumn (line 34) | public final int getEndColumn() {

FILE: modules/parser/src/main/java/org/dhallj/parser/support/LetBinding.java
  class LetBinding (line 5) | final class LetBinding {
    method LetBinding (line 15) | LetBinding(

FILE: modules/parser/src/main/java/org/dhallj/parser/support/OperatorPrecedenceTable.java
  class OperatorPrecedenceTable (line 5) | final class OperatorPrecedenceTable implements JavaCCParserConstants {
    method get (line 29) | static final int get(int tokenKind) {

FILE: modules/parser/src/main/java/org/dhallj/parser/support/Parser.java
  class Parser (line 10) | public final class Parser {
    method parse (line 11) | public static Expr.Parsed parse(String input) {
    method parse (line 21) | public static Expr.Parsed parse(InputStream input, Charset charset) th...

FILE: modules/parser/src/main/java/org/dhallj/parser/support/ParsingHelpers.java
  class ParsingHelpers (line 22) | final class ParsingHelpers {
    method sourceFromToken (line 24) | private static Source sourceFromToken(Token token) {
    method sourceFromTokens (line 29) | private static Source sourceFromTokens(Token token1, Token token2) {
    method makeDoubleLiteral (line 38) | static final Expr.Parsed makeDoubleLiteral(Token token) {
    method makeNaturalLiteral (line 50) | static final Expr.Parsed makeNaturalLiteral(Token token) {
    method makeIntegerLiteral (line 59) | static final Expr.Parsed makeIntegerLiteral(Token token) {
    method isValidDate (line 73) | static final boolean isValidDate(int year, int month, int day) {
    method makeDateLiteral (line 110) | static final Expr.Parsed makeDateLiteral(Token token) {
    method makeTimeZoneLiteral (line 122) | static final Expr.Parsed makeTimeZoneLiteral(Token token) {
    method makeTimeLiteral (line 137) | static final Expr.Parsed makeTimeLiteral(Token token, Token timeZone) {
    method makeDateTimeLiteral (line 186) | static final Expr.Parsed makeDateTimeLiteral(Token token, Token timeZo...
    method unescapeText (line 240) | private static String unescapeText(String in) {
    method makeTextLiteral (line 276) | static final Expr.Parsed makeTextLiteral(
    method dedent (line 307) | static final void dedent(String[] input) {
    method reEscape (line 374) | static final String reEscape(String input) {
    method makeSingleQuotedTextLiteral (line 378) | static final Expr.Parsed makeSingleQuotedTextLiteral(
    method makeApplication (line 413) | static final Expr.Parsed makeApplication(Expr.Parsed base, Expr.Parsed...
    method makeOperatorApplication (line 417) | static final Expr.Parsed makeOperatorApplication(
    method makeAnnotated (line 438) | static final Expr.Parsed makeAnnotated(
    method makeToMap (line 452) | static final Expr.Parsed makeToMap(
    method makeToMap (line 480) | static final Expr.Parsed makeToMap(Expr.Parsed base, Token first, Toke...
    method makeMerge (line 487) | static final Expr.Parsed makeMerge(
    method makeLambda (line 529) | static final Expr.Parsed makeLambda(
    method makePi (line 543) | static final Expr.Parsed makePi(
    method makePi (line 557) | static final Expr.Parsed makePi(Expr.Parsed input, Expr.Parsed result) {
    method makeIf (line 570) | static final Expr.Parsed makeIf(
    method makeLet (line 584) | static final Expr.Parsed makeLet(List<LetBinding> bindings, Expr.Parse...
    method makeAssert (line 622) | static Expr.Parsed makeAssert(Expr.Parsed base, Token first, Token whs...
    method makeFieldAccess (line 634) | static Expr.Parsed makeFieldAccess(
    method makeProjection (line 650) | static Expr.Parsed makeProjection(
    method makeProjectionByType (line 665) | static Expr.Parsed makeProjectionByType(
    method isBuiltIn (line 679) | private static final boolean isBuiltIn(String input) {
    method unescapeLabel (line 683) | private static final String unescapeLabel(String input) {
    method makeBuiltInOrIdentifier (line 687) | static final Expr.Parsed makeBuiltInOrIdentifier(Token value) {
    method makeIdentifier (line 696) | static final Expr.Parsed makeIdentifier(Token value, Token whsp0, Toke...
    method makeRecordLiteral (line 719) | static final Expr.Parsed makeRecordLiteral(
    method makeRecordType (line 783) | static final Expr.Parsed makeRecordType(
    method makeUnionType (line 795) | static final Expr.Parsed makeUnionType(
    method makeWith (line 807) | static final Expr.Parsed makeWith(Expr base, List<String> path, Expr.P...
    method makeNonEmptyListLiteral (line 814) | static final Expr.Parsed makeNonEmptyListLiteral(
    method makeEmptyListLiteral (line 823) | static final Expr.Parsed makeEmptyListLiteral(Expr.Parsed tpe, String ...
    method makeParenthesized (line 828) | static final Expr.Parsed makeParenthesized(Expr.Parsed value, Token fi...
    method makeImport (line 835) | static final Expr.Parsed makeImport(
    class ESESource (line 872) | private static final class ESESource extends Source {
      method ESESource (line 877) | ESESource(Expr.Parsed i0, String i1, Expr.Parsed i2) {
      method printText (line 889) | public final void printText(StringBuilder builder) {
    class ESSource (line 896) | private static final class ESSource extends Source {
      method ESSource (line 900) | ESSource(Expr.Parsed i0, String i1, int endLine, int endColumn) {
      method printText (line 906) | public final void printText(StringBuilder builder) {
    class SESource (line 912) | private static final class SESource extends Source {
      method SESource (line 916) | SESource(String i0, Expr.Parsed i1, int beginLine, int beginColumn) {
      method printText (line 922) | public final void printText(StringBuilder builder) {
    class SESSource (line 928) | private static final class SESSource extends Source {
      method SESSource (line 933) | SESSource(
      method printText (line 947) | public final void printText(StringBuilder builder) {
    class SESESource (line 954) | private static final class SESESource extends Source {
      method SESESource (line 960) | SESESource(
      method printText (line 969) | public final void printText(StringBuilder builder) {
    class SESESESource (line 977) | private static final class SESESESource extends Source {
      method SESESESource (line 985) | SESESESource(
      method printText (line 1003) | public final void printText(StringBuilder builder) {
    class InterspersedSource (line 1013) | private static final class InterspersedSource extends Source {
      method InterspersedSource (line 1017) | InterspersedSource(
      method printText (line 1029) | public final void printText(StringBuilder builder) {

FILE: modules/parser/src/main/java/org/dhallj/parser/support/WhitespaceManager.java
  class WhitespaceManager (line 15) | final class WhitespaceManager {
    method advance (line 19) | private boolean advance(SimpleCharStream stream) {
    method advanceNotEof (line 28) | private void advanceNotEof(SimpleCharStream stream) {
    method isCommentChar (line 34) | private static boolean isCommentChar(char c) {
    method fail (line 38) | private static void fail(int current, SimpleCharStream stream) {
    method consume (line 49) | List<Comment> consume(SimpleCharStream stream, char first) {
    method consumeLineComment (line 75) | private Comment consumeLineComment(SimpleCharStream stream) {
    method consumeBlockComment (line 110) | private Comment consumeBlockComment(SimpleCharStream stream) {
    method consumeWithComments (line 163) | private List<Comment> consumeWithComments(SimpleCharStream stream, boo...

FILE: modules/prelude/src/main/java/org/dhallj/prelude/Prelude.java
  class Prelude (line 11) | public final class Prelude {

FILE: modules/yaml/src/main/java/org/dhallj/yaml/YamlContext.java
  type YamlContext (line 8) | interface YamlContext {
    method add (line 9) | void add(String key);
    method add (line 11) | void add(Object value);
    method getResult (line 13) | Object getResult();
  class RootContext (line 16) | final class RootContext implements YamlContext {
    method RootContext (line 19) | RootContext(Object result) {
    method add (line 23) | public void add(String key) {}
    method add (line 25) | public void add(Object value) {}
    method getResult (line 27) | public Object getResult() {
  class ObjectContext (line 32) | final class ObjectContext implements YamlContext {
    method ObjectContext (line 37) | public ObjectContext(boolean skipNulls) {
    method add (line 41) | public void add(String key) {
    method add (line 45) | public void add(Object value) {
    method getResult (line 53) | public Object getResult() {
  class ArrayContext (line 58) | final class ArrayContext implements YamlContext {
    method add (line 61) | public void add(String key) {}
    method add (line 63) | public void add(Object value) {
    method getResult (line 67) | public Object getResult() {

FILE: modules/yaml/src/main/java/org/dhallj/yaml/YamlConverter.java
  class YamlConverter (line 8) | public class YamlConverter {
    method toYamlString (line 15) | public static final String toYamlString(Expr expr) {
    method toYamlString (line 19) | public static final String toYamlString(Expr expr, DumperOptions optio...
    method toYamlString (line 23) | public static final String toYamlString(Expr expr, boolean skipNulls) {
    method toYamlString (line 27) | public static final String toYamlString(Expr expr, DumperOptions optio...

FILE: modules/yaml/src/main/java/org/dhallj/yaml/YamlHandler.java
  class YamlHandler (line 8) | public class YamlHandler implements JsonHandler {
    method YamlHandler (line 12) | public YamlHandler(boolean skipNulls) {
    method YamlHandler (line 16) | public YamlHandler() {
    method addValue (line 20) | private final void addValue(Object value) {
    method getResult (line 28) | public Object getResult() {
    method onNull (line 32) | public void onNull() {
    method onBoolean (line 36) | public void onBoolean(boolean value) {
    method onNumber (line 40) | public void onNumber(BigInteger value) {
    method onDouble (line 44) | public void onDouble(double value) {
    method onString (line 48) | public void onString(String value) {
    method onArrayStart (line 52) | public void onArrayStart() {
    method onArrayEnd (line 56) | public void onArrayEnd() {
    method onArrayElementGap (line 66) | public void onArrayElementGap() {}
    method onObjectStart (line 68) | public void onObjectStart() {
    method onObjectEnd (line 72) | public void onObjectEnd() {
    method onObjectField (line 82) | public void onObjectField(String name) {
    method onObjectFieldGap (line 86) | public void onObjectFieldGap() {}
Condensed preview — 167 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (874K chars).
[
  {
    "path": ".github/workflows/ci.yml",
    "chars": 1755,
    "preview": "# This file was automatically generated by sbt-github-actions using the\n# githubWorkflowGenerate task. You should add an"
  },
  {
    "path": ".github/workflows/clean.yml",
    "chars": 2337,
    "preview": "# This file was automatically generated by sbt-github-actions using the\n# githubWorkflowGenerate task. You should add an"
  },
  {
    "path": ".gitignore",
    "chars": 204,
    "preview": "*.iml\ntarget/\n.idea/\n.idea_modules/\n.DS_STORE\n.cache\n.settings\n.project\n.classpath\ntmp/\n.bloop/\n.metals/\n.bsp/\nproject/m"
  },
  {
    "path": ".gitmodules",
    "chars": 96,
    "preview": "[submodule \"dhall-lang\"]\n\tpath = dhall-lang\n\turl = https://github.com/dhall-lang/dhall-lang.git\n"
  },
  {
    "path": ".scalafmt.conf",
    "chars": 342,
    "preview": "version=3.0.7\nalign.openParenCallSite = true\nalign.openParenDefnSite = true\nmaxColumn = 120\ncontinuationIndent.defnSite "
  },
  {
    "path": "LICENSE",
    "chars": 1520,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2020, Travis Brown\nAll rights reserved.\n\nRedistribution and use in source and binary"
  },
  {
    "path": "README.md",
    "chars": 24184,
    "preview": "# Dhall for Java\n\n[![Build status](https://img.shields.io/github/workflow/status/travisbrown/dhallj/Continuous%20Integra"
  },
  {
    "path": "WORKSPACE",
    "chars": 463,
    "preview": "workspace(name = \"org_dhallj\")\n\nload(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n\n# Load j2cl reposi"
  },
  {
    "path": "benchmarks/src/main/scala/org/dhallj/benchmarks/EncodingBenchmark.scala",
    "chars": 864,
    "preview": "package org.dhallj.benchmarks\n\nimport java.util.concurrent.TimeUnit\nimport org.openjdk.jmh.annotations._\nimport org.dhal"
  },
  {
    "path": "benchmarks/src/main/scala/org/dhallj/benchmarks/ParsingBenchmark.scala",
    "chars": 700,
    "preview": "package org.dhallj.benchmarks\n\nimport java.util.concurrent.TimeUnit\nimport org.openjdk.jmh.annotations._\nimport org.dhal"
  },
  {
    "path": "build.sbt",
    "chars": 11254,
    "preview": "import ReleaseTransformations._\n\nThisBuild / organization := \"org.dhallj\"\nThisBuild / crossScalaVersions := List(\"2.12.1"
  },
  {
    "path": "cli/src/main/java/org/dhallj/cli/Dhall.java",
    "chars": 1577,
    "preview": "package org.dhallj.cli;\n\nimport java.io.IOException;\nimport org.dhallj.core.Expr;\nimport org.dhallj.core.converters.Json"
  },
  {
    "path": "javascript/BUILD",
    "chars": 282,
    "preview": "load(\"@com_google_j2cl//build_defs:rules.bzl\", \"j2cl_application\")\n\nj2cl_application(\n    name = \"dhall\",\n    closure_de"
  },
  {
    "path": "javascript/api/BUILD",
    "chars": 447,
    "preview": "load(\"@io_bazel_rules_closure//closure:defs.bzl\", \"closure_js_library\")\nload(\"@com_google_j2cl//build_defs:rules.bzl\", \""
  },
  {
    "path": "javascript/api/DhallJs.java",
    "chars": 495,
    "preview": "package org.dhallj.js;\n\nimport org.dhallj.core.Expr;\nimport org.dhallj.parser.DhallParser;\nimport jsinterop.annotations."
  },
  {
    "path": "javascript/api/dhall.js",
    "chars": 670,
    "preview": "goog.module('dhall.js');\n\nvar DhallJs = goog.require('org.dhallj.js.DhallJs');\n\n/**\n * @param {string} input\n * @return "
  },
  {
    "path": "javascript/jre/BUILD",
    "chars": 509,
    "preview": "load(\"@com_google_j2cl//build_defs:rules.bzl\", \"j2cl_library\")\n\npackage(\n    default_visibility = [\"//visibility:public\""
  },
  {
    "path": "javascript/jre/BufferedReader.java",
    "chars": 215,
    "preview": "package java.io;\n\npublic class BufferedReader extends Reader {\n  public BufferedReader(InputStreamReader stream) {}\n\n  p"
  },
  {
    "path": "javascript/jre/InputStreamReader.java",
    "chars": 276,
    "preview": "package java.io;\n\npublic class InputStreamReader extends Reader {\n  public InputStreamReader(InputStream in) {}\n  public"
  },
  {
    "path": "javascript/jre/InvalidPathException.java",
    "chars": 94,
    "preview": "package java.nio.file;\n\npublic class InvalidPathException extends IllegalArgumentException {}\n"
  },
  {
    "path": "javascript/jre/Path.java",
    "chars": 426,
    "preview": "package java.nio.file;\n\nimport java.util.Iterator;\n\npublic class Path {\n  private final String input;\n\n  public Path(Str"
  },
  {
    "path": "javascript/jre/Paths.java",
    "chars": 244,
    "preview": "package java.nio.file;\n\npublic class Paths {\n  /**\n   * @throws  InvalidPathException\n   *          if the path string c"
  },
  {
    "path": "javascript/jre/URI.java",
    "chars": 378,
    "preview": "package java.net;\n\npublic class URI {\n  private final String input;\n\n  public URI(String input) throws URISyntaxExceptio"
  },
  {
    "path": "javascript/jre/URISyntaxException.java",
    "chars": 72,
    "preview": "package java.net;\n\npublic class URISyntaxException extends Throwable {}\n"
  },
  {
    "path": "modules/ast/src/main/scala/org/dhallj/ast/package.scala",
    "chars": 14775,
    "preview": "package org.dhallj.ast\n\nimport java.net.URI\nimport java.nio.file.Path\nimport java.lang.{Iterable => JIterable}\nimport ja"
  },
  {
    "path": "modules/cats/src/main/scala/org/dhallj/cats/LiftVisitor.scala",
    "chars": 6986,
    "preview": "package org.dhallj.cats\n\nimport cats.Applicative\nimport java.math.BigDecimal\nimport java.math.BigInteger\nimport java.net"
  },
  {
    "path": "modules/cats/src/test/scala/org/dhallj/cats/LiftVisitorSuite.scala",
    "chars": 453,
    "preview": "package org.dhallj.cats\n\nimport cats.Applicative\nimport cats.instances.option._\nimport munit.ScalaCheckSuite\nimport org."
  },
  {
    "path": "modules/circe/src/main/scala/org/dhallj/circe/CirceHandler.scala",
    "chars": 2518,
    "preview": "package org.dhallj.circe\n\nimport io.circe.Json\nimport java.math.BigInteger\nimport java.util.{ArrayDeque, ArrayList, Dequ"
  },
  {
    "path": "modules/circe/src/main/scala/org/dhallj/circe/Converter.scala",
    "chars": 1916,
    "preview": "package org.dhallj.circe\n\nimport io.circe.{Json, JsonNumber, JsonObject}\nimport java.math.BigInteger\nimport java.util.Ab"
  },
  {
    "path": "modules/circe/src/test/scala/org/dhallj/circe/CirceConverterSuite.scala",
    "chars": 3138,
    "preview": "package org.dhallj.circe\n\nimport io.circe.Json\nimport io.circe.syntax._\nimport io.circe.testing.instances._\nimport munit"
  },
  {
    "path": "modules/circe/src/test/scala/org/dhallj/circe/JsonCleaner.scala",
    "chars": 774,
    "preview": "package org.dhallj.circe\n\nimport io.circe.{Json, JsonNumber, JsonObject}\n\nobject JsonCleaner extends Json.Folder[Json] {"
  },
  {
    "path": "modules/core/BUILD",
    "chars": 639,
    "preview": "load(\"@com_google_j2cl//build_defs:rules.bzl\", \"j2cl_library\")\n\npackage(\n    default_visibility = [\"//visibility:public\""
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/cbor/AdditionalInfo.java",
    "chars": 707,
    "preview": "package org.dhallj.cbor;\n\npublic enum AdditionalInfo {\n  DIRECT(0), // 0-23\n  ONE_BYTE(24), // 24\n  TWO_BYTES(25), // 25"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/cbor/CborException.java",
    "chars": 140,
    "preview": "package org.dhallj.cbor;\n\npublic class CborException extends RuntimeException {\n  CborException(String message) {\n    su"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/cbor/HalfFloat.java",
    "chars": 2100,
    "preview": "package org.dhallj.cbor;\n\n/**\n * Conversions between 16 and 32-bit floating point numbers.\n *\n * @see <a href=\"https://s"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/cbor/MajorType.java",
    "chars": 797,
    "preview": "package org.dhallj.cbor;\n\npublic enum MajorType {\n  UNSIGNED_INTEGER(0),\n  NEGATIVE_INTEGER(1),\n  BYTE_STRING(2),\n  TEXT"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/cbor/NullVisitor.java",
    "chars": 1700,
    "preview": "package org.dhallj.cbor;\n\nimport java.math.BigInteger;\n\n/** To read a CBOR primitive and ensure it is null. */\nfinal cla"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/cbor/Reader.java",
    "chars": 13020,
    "preview": "package org.dhallj.cbor;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.nio.charset.Charset;\nimp"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/cbor/Visitor.java",
    "chars": 771,
    "preview": "package org.dhallj.cbor;\n\nimport java.math.BigInteger;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Represents a"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/cbor/Writer.java",
    "chars": 7940,
    "preview": "package org.dhallj.cbor;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.UnsupportedEn"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/ArrayIterable.java",
    "chars": 905,
    "preview": "package org.dhallj.core;\n\nimport java.util.Iterator;\nimport java.util.NoSuchElementException;\n\nfinal class ArrayIterable"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/Constructors.java",
    "chars": 38017,
    "preview": "package org.dhallj.core;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.net.URI;\nimport java.nio"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/DhallException.java",
    "chars": 1616,
    "preview": "package org.dhallj.core;\n\n/** Base class of exceptions that may be thrown or returned by DhallJ. */\npublic class DhallEx"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/Expr.java",
    "chars": 44742,
    "preview": "package org.dhallj.core;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.math.BigDecimal;\nimport j"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/ExternalVisitor.java",
    "chars": 6532,
    "preview": "package org.dhallj.core;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.net.URI;\nimport java.nio"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/IsResolved.java",
    "chars": 1027,
    "preview": "package org.dhallj.core;\n\nimport java.net.URI;\nimport java.nio.file.Path;\n\nfinal class IsResolved extends Visitor.Proper"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/Operator.java",
    "chars": 2499,
    "preview": "package org.dhallj.core;\n\n/** Represents a Dhall operator. */\npublic enum Operator {\n  OR(\"||\", 3, true),\n  AND(\"&&\", 7,"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/Source.java",
    "chars": 1903,
    "preview": "package org.dhallj.core;\n\n/** Represents a section of a source document corresponding to a parsed expression. */\npublic "
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/Tags.java",
    "chars": 1381,
    "preview": "package org.dhallj.core;\n\n// Note that these are internal identifiers only.\nfinal class Tags {\n  static final int NOTE ="
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/ToStringVisitor.java",
    "chars": 15560,
    "preview": "package org.dhallj.core;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.net.URI;\nimport java.nio"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/VisitState.java",
    "chars": 2094,
    "preview": "package org.dhallj.core;\n\nimport java.util.ArrayDeque;\nimport java.util.Arrays;\nimport java.util.Comparator;\nimport java"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/Visitor.java",
    "chars": 19352,
    "preview": "package org.dhallj.core;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.net.URI;\nimport java.nio"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/binary/CborDecodingVisitor.java",
    "chars": 19225,
    "preview": "package org.dhallj.core.binary;\n\nimport org.dhallj.cbor.Reader;\nimport org.dhallj.cbor.Visitor;\nimport org.dhallj.core.E"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/binary/Decode.java",
    "chars": 399,
    "preview": "package org.dhallj.core.binary;\n\nimport org.dhallj.cbor.Reader;\nimport org.dhallj.core.Expr;\n\npublic class Decode {\n  pu"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/binary/DecodingException.java",
    "chars": 294,
    "preview": "package org.dhallj.core.binary;\n\nimport org.dhallj.core.DhallException;\n\npublic class DecodingException extends DhallExc"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/binary/Encode.java",
    "chars": 15790,
    "preview": "package org.dhallj.core.binary;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.net.URI;\nimport j"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/binary/Label.java",
    "chars": 1690,
    "preview": "package org.dhallj.core.binary;\n\nfinal class Label {\n  public static final int APPLICATION = 0;\n  public static final in"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/converters/JsonConverter.java",
    "chars": 5394,
    "preview": "package org.dhallj.core.converters;\n\nimport java.math.BigInteger;\nimport java.util.List;\nimport java.util.Map.Entry;\nimp"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/converters/JsonHandler.java",
    "chars": 2867,
    "preview": "package org.dhallj.core.converters;\n\nimport java.io.PrintWriter;\nimport java.math.BigInteger;\n\npublic interface JsonHand"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/normalization/AlphaNormalize.java",
    "chars": 3108,
    "preview": "package org.dhallj.core.normalization;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.LinkedLis"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalize.java",
    "chars": 5060,
    "preview": "package org.dhallj.core.normalization;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.net.URI;\ni"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeApplication.java",
    "chars": 14639,
    "preview": "package org.dhallj.core.normalization;\n\nimport java.math.BigInteger;\nimport java.util.AbstractMap.SimpleImmutableEntry;\n"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeFieldAccess.java",
    "chars": 3519,
    "preview": "package org.dhallj.core.normalization;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map.Entry;\ni"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeIf.java",
    "chars": 757,
    "preview": "package org.dhallj.core.normalization;\n\nimport org.dhallj.core.Expr;\n\nfinal class BetaNormalizeIf {\n  static final Expr "
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeMerge.java",
    "chars": 2295,
    "preview": "package org.dhallj.core.normalization;\n\nimport java.util.List;\nimport java.util.Map.Entry;\nimport org.dhallj.core.Expr;\n"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeOperatorApplication.java",
    "chars": 8985,
    "preview": "package org.dhallj.core.normalization;\n\nimport java.math.BigInteger;\nimport java.util.ArrayList;\nimport java.util.List;\n"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeProjection.java",
    "chars": 2919,
    "preview": "package org.dhallj.core.normalization;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeTextLiteral.java",
    "chars": 2779,
    "preview": "package org.dhallj.core.normalization;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nim"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeToMap.java",
    "chars": 1885,
    "preview": "package org.dhallj.core.normalization;\n\nimport java.util.AbstractMap.SimpleImmutableEntry;\nimport java.util.ArrayList;\ni"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeWith.java",
    "chars": 2381,
    "preview": "package org.dhallj.core.normalization;\n\nimport java.util.AbstractMap.SimpleImmutableEntry;\nimport java.util.ArrayList;\ni"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/normalization/NormalizationUtils.java",
    "chars": 955,
    "preview": "package org.dhallj.core.normalization;\n\nimport java.util.Comparator;\nimport java.util.Map.Entry;\nimport org.dhallj.core."
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/normalization/Shift.java",
    "chars": 1781,
    "preview": "package org.dhallj.core.normalization;\n\nimport java.math.BigInteger;\nimport java.net.URI;\nimport java.nio.file.Path;\nimp"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/normalization/Substitute.java",
    "chars": 2025,
    "preview": "package org.dhallj.core.normalization;\n\nimport java.util.ArrayDeque;\nimport java.util.Deque;\nimport java.util.List;\nimpo"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/typechecking/BuiltInTypes.java",
    "chars": 4483,
    "preview": "package org.dhallj.core.typechecking;\n\nimport java.util.AbstractMap.SimpleImmutableEntry;\nimport java.util.HashMap;\nimpo"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/typechecking/CheckEquivalence.java",
    "chars": 513,
    "preview": "package org.dhallj.core.typechecking;\n\nimport org.dhallj.core.Expr;\nimport org.dhallj.core.Operator;\nimport org.dhallj.c"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/typechecking/Context.java",
    "chars": 1350,
    "preview": "package org.dhallj.core.typechecking;\n\nimport org.dhallj.core.Expr;\n\nfinal class Context {\n  private final String key;\n "
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/typechecking/NonNegativeIndices.java",
    "chars": 345,
    "preview": "package org.dhallj.core.typechecking;\n\nimport org.dhallj.core.Expr;\nimport org.dhallj.core.Visitor;\n\nfinal class NonNega"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/typechecking/TypeCheck.java",
    "chars": 30795,
    "preview": "package org.dhallj.core.typechecking;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.net.URI;\nim"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/typechecking/TypeCheckApplication.java",
    "chars": 1144,
    "preview": "package org.dhallj.core.typechecking;\n\nimport org.dhallj.core.Expr;\nimport org.dhallj.core.ExternalVisitor;\n\nfinal class"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/typechecking/TypeCheckFailure.java",
    "chars": 8102,
    "preview": "package org.dhallj.core.typechecking;\n\nimport org.dhallj.core.DhallException;\nimport org.dhallj.core.Expr;\nimport org.dh"
  },
  {
    "path": "modules/core/src/main/java/org/dhallj/core/typechecking/Universe.java",
    "chars": 1237,
    "preview": "package org.dhallj.core.typechecking;\n\nimport org.dhallj.core.Expr;\n\npublic enum Universe {\n  TYPE,\n  KIND,\n  SORT;\n\n  p"
  },
  {
    "path": "modules/core/src/test/java/org/dhallj/cbor/CborSuite.scala",
    "chars": 2428,
    "preview": "package org.dhallj.cbor\n\nimport co.nstant.in.cbor.{CborBuilder, CborEncoder}\nimport java.io.ByteArrayOutputStream\nimport"
  },
  {
    "path": "modules/core/src/test/java/org/dhallj/cbor/HalfFloatSuite.scala",
    "chars": 1053,
    "preview": "package org.dhallj.cbor\n\nimport munit.FunSuite\nimport org.scalacheck.Prop\n\nclass HalfFloatSuite extends FunSuite {\n  def"
  },
  {
    "path": "modules/imports/README.md",
    "chars": 115,
    "preview": "# Imports\n\nA reference implementation of import resolution using [Cats Effect](https://typelevel.org/cats-effect/)."
  },
  {
    "path": "modules/imports/src/main/scala/org/dhallj/imports/Canonicalization.scala",
    "chars": 4521,
    "preview": "package org.dhallj.imports\n\nimport java.nio.file.{Path, Paths}\n\nimport cats.{ApplicativeError, MonadError}\nimport cats.i"
  },
  {
    "path": "modules/imports/src/main/scala/org/dhallj/imports/CorsComplianceCheck.scala",
    "chars": 1542,
    "preview": "package org.dhallj.imports\n\nimport java.net.URI\n\nimport cats.effect.Sync\nimport org.dhallj.core.DhallException.Resolutio"
  },
  {
    "path": "modules/imports/src/main/scala/org/dhallj/imports/ImportCache.scala",
    "chars": 2862,
    "preview": "package org.dhallj.imports\n\nimport cats.Applicative\nimport cats.effect.Async\nimport cats.implicits._\nimport java.nio.fil"
  },
  {
    "path": "modules/imports/src/main/scala/org/dhallj/imports/ImportContext.scala",
    "chars": 488,
    "preview": "package org.dhallj.imports\n\nimport java.net.URI\nimport java.nio.file.Path\nimport org.dhallj.core.Expr\n\nsealed abstract c"
  },
  {
    "path": "modules/imports/src/main/scala/org/dhallj/imports/ReferentialSanityCheck.scala",
    "chars": 1249,
    "preview": "package org.dhallj.imports\n\nimport cats.effect.Sync\nimport org.dhallj.core.DhallException.ResolutionFailure\n\nobject Refe"
  },
  {
    "path": "modules/imports/src/main/scala/org/dhallj/imports/ResolveImports.scala",
    "chars": 1860,
    "preview": "package org.dhallj.imports\n\nimport java.nio.file.{Path, Paths}\n\nimport cats.effect.Async\nimport org.dhallj.core.Expr\nimp"
  },
  {
    "path": "modules/imports/src/main/scala/org/dhallj/imports/ResolveImportsVisitor.scala",
    "chars": 10898,
    "preview": "package org.dhallj.imports\n\nimport java.net.URI\nimport java.nio.file.{Files, Path, Paths}\nimport java.security.MessageDi"
  },
  {
    "path": "modules/imports/src/main/scala/org/dhallj/imports/ToHeaders.scala",
    "chars": 2946,
    "preview": "package org.dhallj.imports\n\nimport java.util.AbstractMap.SimpleImmutableEntry\nimport java.util.Map.Entry\nimport org.dhal"
  },
  {
    "path": "modules/imports/src/main/scala/org/dhallj/imports/syntax/package.scala",
    "chars": 210,
    "preview": "package org.dhallj.imports\n\nimport org.dhallj.core.Expr\nimport scala.language.implicitConversions\n\npackage object syntax"
  },
  {
    "path": "modules/imports/src/test/resources/alternate/other.dhall",
    "chars": 14,
    "preview": "let x = 2 in x"
  },
  {
    "path": "modules/imports/src/test/resources/alternate/package.dhall",
    "chars": 14,
    "preview": "let x = 1 in x"
  },
  {
    "path": "modules/imports/src/test/resources/cache-write/package.dhall",
    "chars": 14,
    "preview": "let x = 2 in x"
  },
  {
    "path": "modules/imports/src/test/resources/cyclic/other.dhall",
    "chars": 34,
    "preview": "let x = /cyclic/package.dhall in x"
  },
  {
    "path": "modules/imports/src/test/resources/cyclic/package.dhall",
    "chars": 26,
    "preview": "let x = ./other.dhall in x"
  },
  {
    "path": "modules/imports/src/test/resources/cyclic-relative-paths/other.dhall",
    "chars": 28,
    "preview": "let x = ./package.dhall in x"
  },
  {
    "path": "modules/imports/src/test/resources/cyclic-relative-paths/package.dhall",
    "chars": 26,
    "preview": "let x = ./other.dhall in x"
  },
  {
    "path": "modules/imports/src/test/resources/hashed/package.dhall",
    "chars": 14,
    "preview": "let x = 1 in x"
  },
  {
    "path": "modules/imports/src/test/resources/local/package.dhall",
    "chars": 14,
    "preview": "let x = 1 in x"
  },
  {
    "path": "modules/imports/src/test/resources/local-local-absolute/package.dhall",
    "chars": 50,
    "preview": "let x = /local-local-absolute-2/package.dhall in x"
  },
  {
    "path": "modules/imports/src/test/resources/local-local-absolute-2/package.dhall",
    "chars": 14,
    "preview": "let x = 1 in x"
  },
  {
    "path": "modules/imports/src/test/resources/local-local-relative/other.dhall",
    "chars": 14,
    "preview": "let x = 1 in x"
  },
  {
    "path": "modules/imports/src/test/resources/local-local-relative/package.dhall",
    "chars": 26,
    "preview": "let x = ./other.dhall in x"
  },
  {
    "path": "modules/imports/src/test/resources/local-remote/package.dhall",
    "chars": 96,
    "preview": "let any = https://raw.githubusercontent.com/dhall-lang/dhall-lang/master/Prelude/List/any in any"
  },
  {
    "path": "modules/imports/src/test/resources/multiple-imports/other.dhall",
    "chars": 14,
    "preview": "let x = 1 in x"
  },
  {
    "path": "modules/imports/src/test/resources/multiple-imports/other2.dhall",
    "chars": 14,
    "preview": "let x = 2 in x"
  },
  {
    "path": "modules/imports/src/test/resources/multiple-imports/package.dhall",
    "chars": 55,
    "preview": "let x = ./other.dhall\n\nlet y = ./other2.dhall\n\nin [x,y]"
  },
  {
    "path": "modules/imports/src/test/resources/text-import/package.dhall",
    "chars": 14,
    "preview": "let x = 1 in x"
  },
  {
    "path": "modules/imports/src/test/scala/org/dhallj/imports/CanonicalizationSuite.scala",
    "chars": 6203,
    "preview": "package org.dhallj.imports\n\nimport java.net.URI\nimport java.nio.file.Paths\n\nimport cats.effect.IO\nimport cats.effect.uns"
  },
  {
    "path": "modules/imports/src/test/scala/org/dhallj/imports/CorsComplianceCheckSuite.scala",
    "chars": 2934,
    "preview": "package org.dhallj.imports\n\nimport java.net.URI\nimport java.nio.file.Paths\n\nimport cats.effect.IO\nimport cats.effect.uns"
  },
  {
    "path": "modules/imports/src/test/scala/org/dhallj/imports/ImportCacheSuite.scala",
    "chars": 846,
    "preview": "package org.dhallj.imports\n\nimport java.nio.file.{Files, Path}\n\nimport cats.effect.IO\nimport cats.effect.unsafe.implicit"
  },
  {
    "path": "modules/imports/src/test/scala/org/dhallj/imports/ImportResolutionSuite.scala",
    "chars": 7645,
    "preview": "package org.dhallj.imports\n\nimport java.security.MessageDigest\n\nimport cats.effect.{IO, Ref, Resource}\nimport cats.effec"
  },
  {
    "path": "modules/imports/src/test/scala/org/dhallj/imports/ReferentialSanityCheckSuite.scala",
    "chars": 1921,
    "preview": "package org.dhallj.imports\n\nimport java.net.URI\nimport java.nio.file.Paths\n\nimport cats.effect.IO\nimport cats.effect.uns"
  },
  {
    "path": "modules/imports/src/test/scala/org/dhallj/imports/ToHeadersSuite.scala",
    "chars": 1979,
    "preview": "package org.dhallj.imports\n\nimport munit.FunSuite\nimport org.dhallj.core.Expr\nimport org.http4s.{Header, Headers}\nimport"
  },
  {
    "path": "modules/imports-mini/src/main/java/org/dhallj/imports/mini/ResolutionVisitor.java",
    "chars": 6817,
    "preview": "package org.dhallj.imports.mini;\n\nimport java.io.IOException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\ni"
  },
  {
    "path": "modules/imports-mini/src/main/java/org/dhallj/imports/mini/Resolver.java",
    "chars": 2393,
    "preview": "package org.dhallj.imports.mini;\n\nimport org.dhallj.core.DhallException.ResolutionFailure;\nimport org.dhallj.core.Expr;\n"
  },
  {
    "path": "modules/javagen/src/main/java/org/dhallj/javagen/Code.scala",
    "chars": 3769,
    "preview": "package org.dhallj.javagen\n\ncase class Code(content: String, defs: Vector[Code] = Vector.empty) {\n  final private def re"
  },
  {
    "path": "modules/javagen/src/main/java/org/dhallj/javagen/ToCodeVisitor.scala",
    "chars": 7497,
    "preview": "package org.dhallj.javagen\n\nimport java.math.BigDecimal\nimport java.math.BigInteger\nimport java.net.URI\nimport java.nio."
  },
  {
    "path": "modules/javagen/src/main/java/org/dhallj/javagen/package.scala",
    "chars": 230,
    "preview": "package org.dhallj\n\nimport org.dhallj.core.Expr\n\npackage object javagen {\n  def toJavaCode(expr: Expr, packageName: Stri"
  },
  {
    "path": "modules/jawn/src/main/scala/org/dhallj/jawn/FacadeHandler.scala",
    "chars": 1967,
    "preview": "package org.dhallj.jawn\n\nimport java.math.BigInteger\nimport java.util.{ArrayDeque, Deque}\nimport org.dhallj.core.convert"
  },
  {
    "path": "modules/jawn/src/main/scala/org/dhallj/jawn/JawnConverter.scala",
    "chars": 385,
    "preview": "package org.dhallj.jawn\n\nimport org.dhallj.core.Expr\nimport org.dhallj.core.converters.JsonConverter\nimport org.typeleve"
  },
  {
    "path": "modules/jawn/src/test/scala/org/dhallj/jawn/JawnConverterSuite.scala",
    "chars": 2952,
    "preview": "package org.dhallj.jawn\n\nimport io.circe.Json\nimport io.circe.jawn.CirceSupportParser\nimport io.circe.syntax._\nimport mu"
  },
  {
    "path": "modules/parser/BUILD",
    "chars": 490,
    "preview": "load(\"@com_google_j2cl//build_defs:rules.bzl\", \"j2cl_library\")\n\npackage(\n    default_visibility = [\"//visibility:public\""
  },
  {
    "path": "modules/parser/src/main/java/org/dhallj/parser/DhallParser.java",
    "chars": 672,
    "preview": "package org.dhallj.parser;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.Charset;\nimp"
  },
  {
    "path": "modules/parser/src/main/java/org/dhallj/parser/support/Comment.java",
    "chars": 798,
    "preview": "package org.dhallj.parser.support;\n\nfinal class Comment {\n  private final String content;\n  private final int beginLine;"
  },
  {
    "path": "modules/parser/src/main/java/org/dhallj/parser/support/LetBinding.java",
    "chars": 682,
    "preview": "package org.dhallj.parser.support;\n\nimport org.dhallj.core.Expr;\n\nfinal class LetBinding {\n  final String name;\n  final "
  },
  {
    "path": "modules/parser/src/main/java/org/dhallj/parser/support/OperatorPrecedenceTable.java",
    "chars": 1261,
    "preview": "package org.dhallj.parser.support;\n\nimport org.dhallj.core.Operator;\n\nfinal class OperatorPrecedenceTable implements Jav"
  },
  {
    "path": "modules/parser/src/main/java/org/dhallj/parser/support/Parser.java",
    "chars": 1075,
    "preview": "package org.dhallj.parser.support;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.Char"
  },
  {
    "path": "modules/parser/src/main/java/org/dhallj/parser/support/ParsingHelpers.java",
    "chars": 32866,
    "preview": "package org.dhallj.parser.support;\n\nimport java.io.UnsupportedEncodingException;\nimport java.math.BigDecimal;\nimport jav"
  },
  {
    "path": "modules/parser/src/main/java/org/dhallj/parser/support/WhitespaceManager.java",
    "chars": 5676,
    "preview": "package org.dhallj.parser.support;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n"
  },
  {
    "path": "modules/parser/src/main/java/org/dhallj/parser/support/package-info.java",
    "chars": 149,
    "preview": "/**\n * Support classes generated by JavaCC.\n *\n * <p>Please do not use the contents of this package directly!\n */\npackag"
  },
  {
    "path": "modules/parser/src/main/javacc/JavaCCParser.jj",
    "chars": 30939,
    "preview": "options {\n  JDK_VERSION=\"1.8\";\n  JAVA_TEMPLATE_TYPE = \"modern\";\n  SUPPORT_CLASS_VISIBILITY_PUBLIC = false;\n  UNICODE_INP"
  },
  {
    "path": "modules/parser/src/test/scala/org/dhallj/parser/DhallParserSuite.scala",
    "chars": 4197,
    "preview": "package org.dhallj.parser\n\nimport java.net.URI\nimport java.io.FileInputStream\nimport java.nio.charset.StandardCharsets\ni"
  },
  {
    "path": "modules/prelude/src/main/java/org/dhallj/prelude/Prelude.java",
    "chars": 198649,
    "preview": "package org.dhallj.prelude;\n\nimport java.math.BigInteger;\nimport java.util.AbstractMap.SimpleImmutableEntry;\nimport java"
  },
  {
    "path": "modules/scala/src/main/scala/org/dhallj/syntax/package.scala",
    "chars": 1199,
    "preview": "package org.dhallj\n\nimport org.dhallj.core.DhallException.{ParsingFailure, ResolutionFailure}\nimport org.dhallj.core.Exp"
  },
  {
    "path": "modules/scala-codec/src/main/scala/org/dhallj/codec/Decoder.scala",
    "chars": 8472,
    "preview": "package org.dhallj.codec\n\nimport cats.Traverse\nimport cats.instances.either._\nimport cats.instances.vector._\nimport org."
  },
  {
    "path": "modules/scala-codec/src/main/scala/org/dhallj/codec/DecodingFailure.scala",
    "chars": 244,
    "preview": "package org.dhallj.codec\n\nimport org.dhallj.core.{DhallException, Expr}\n\nclass DecodingFailure(val target: String, val v"
  },
  {
    "path": "modules/scala-codec/src/main/scala/org/dhallj/codec/Encoder.scala",
    "chars": 5377,
    "preview": "package org.dhallj.codec\n\nimport org.dhallj.core.Expr\nimport org.dhallj.ast._\n\ntrait Encoder[A] { self =>\n  def encode(v"
  },
  {
    "path": "modules/scala-codec/src/main/scala/org/dhallj/codec/syntax/package.scala",
    "chars": 369,
    "preview": "package org.dhallj.codec\n\nimport org.dhallj.core.Expr\n\npackage object syntax {\n  implicit final class DhallCodecAnyOps[A"
  },
  {
    "path": "modules/scala-codec/src/test/scala/org/dhallj/codec/DecoderSuite.scala",
    "chars": 1348,
    "preview": "package org.dhallj.codec\n\nimport munit.FunSuite\nimport org.dhallj.codec.syntax._\nimport org.dhallj.syntax._\n\nclass Decod"
  },
  {
    "path": "modules/testing/src/main/scala/org/dhallj/testing/ArbitraryInstances.scala",
    "chars": 7034,
    "preview": "package org.dhallj.testing\n\nimport org.dhallj.ast._\nimport org.dhallj.core.{Expr, Operator}\nimport org.scalacheck.{Arbit"
  },
  {
    "path": "modules/testing/src/main/scala/org/dhallj/testing/WellTypedExpr.scala",
    "chars": 95,
    "preview": "package org.dhallj.testing\n\nimport org.dhallj.core.Expr\n\ncase class WellTypedExpr(value: Expr)\n"
  },
  {
    "path": "modules/testing/src/main/scala/org/dhallj/testing/package.scala",
    "chars": 207,
    "preview": "package org.dhallj.testing\n\nimport org.scalacheck.Gen\n\npackage object instances extends ArbitraryInstances {\n  def genNa"
  },
  {
    "path": "modules/yaml/src/main/java/org/dhallj/yaml/YamlContext.java",
    "chars": 1319,
    "preview": "package org.dhallj.yaml;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java"
  },
  {
    "path": "modules/yaml/src/main/java/org/dhallj/yaml/YamlConverter.java",
    "chars": 1120,
    "preview": "package org.dhallj.yaml;\n\nimport org.dhallj.core.Expr;\nimport org.dhallj.core.converters.JsonConverter;\nimport org.yaml."
  },
  {
    "path": "modules/yaml/src/main/java/org/dhallj/yaml/YamlHandler.java",
    "chars": 1822,
    "preview": "package org.dhallj.yaml;\n\nimport java.math.BigInteger;\nimport java.util.ArrayDeque;\nimport java.util.Deque;\nimport org.d"
  },
  {
    "path": "modules/yaml/src/test/scala/org/dhallj/yaml/YamlConverterSuite.scala",
    "chars": 2897,
    "preview": "package org.dhallj.yaml\n\nimport munit.ScalaCheckSuite\nimport org.dhallj.ast._\nimport org.dhallj.core.Expr\nimport org.dha"
  },
  {
    "path": "project/build.properties",
    "chars": 18,
    "preview": "sbt.version=1.5.5\n"
  },
  {
    "path": "project/plugins.sbt",
    "chars": 943,
    "preview": "addSbtPlugin(\"com.codecommit\" % \"sbt-github-actions\" % \"0.13.0\")\naddSbtPlugin(\"com.eed3si9n\" % \"sbt-assembly\" % \"1.1.0\")"
  },
  {
    "path": "scalastyle-config.xml",
    "chars": 4647,
    "preview": "<scalastyle>\n    <name>Circe Configuration</name>\n    <check level=\"error\" class=\"org.scalastyle.scalariform.EnsureSingl"
  },
  {
    "path": "tests/src/main/scala/org/dhallj/tests/HaskellDhall.scala",
    "chars": 625,
    "preview": "package org.dhallj.tests\n\nimport java.io.ByteArrayInputStream\nimport java.nio.charset.StandardCharsets.UTF_8\nimport scal"
  },
  {
    "path": "tests/src/main/scala/org/dhallj/tests/acceptance/AcceptanceFailureSuite.scala",
    "chars": 1323,
    "preview": "package org.dhallj.tests.acceptance\n\nimport org.dhallj.core.Expr\nimport org.dhallj.core.binary.Decode.decode\nimport org."
  },
  {
    "path": "tests/src/main/scala/org/dhallj/tests/acceptance/AcceptanceSuccessSuite.scala",
    "chars": 5319,
    "preview": "package org.dhallj.tests.acceptance\n\nimport cats.effect.IO\nimport cats.effect.unsafe.IORuntime\nimport java.nio.file.{Fil"
  },
  {
    "path": "tests/src/main/scala/org/dhallj/tests/acceptance/AcceptanceSuite.scala",
    "chars": 1460,
    "preview": "package org.dhallj.tests.acceptance\n\nimport java.nio.file.{Files, Path, Paths}\n\nimport munit.{FunSuite, Ignore, Slow}\n\ni"
  },
  {
    "path": "tests/src/main/scala/org/dhallj/tests/acceptance/ImportResolutionSuite.scala",
    "chars": 3275,
    "preview": "package org.dhallj.tests.acceptance\n\nimport java.nio.file.{Files, Paths}\n\nimport cats.effect.IO\nimport cats.effect.unsaf"
  },
  {
    "path": "tests/src/test/resources/learndhall.dhall",
    "chars": 11216,
    "preview": "\n-- Single-line comment\n\n{- Multi-line comment\n\n   Unicode is fine 🙂\n\n   This file is a valid Dhall expression that eval"
  },
  {
    "path": "tests/src/test/scala/org/dhallj/tests/BinaryDecodingTests.scala",
    "chars": 7346,
    "preview": "package org.dhallj.tests\n\nimport java.nio.file.{Files, Paths}\n\nimport munit.FunSuite\nimport org.dhallj.core.binary.Decod"
  },
  {
    "path": "tests/src/test/scala/org/dhallj/tests/ImportResolutionSuite.scala",
    "chars": 1053,
    "preview": "package org.dhallj.tests\n\nimport java.nio.file.{Path, Paths}\n\nimport cats.effect.{IO, Resource}\nimport cats.effect.unsaf"
  },
  {
    "path": "tests/src/test/scala/org/dhallj/tests/JsonConverterSuite.scala",
    "chars": 2054,
    "preview": "package org.dhallj.tests\n\nimport munit.FunSuite\nimport org.dhallj.core.converters.JsonConverter\nimport org.dhallj.parser"
  },
  {
    "path": "tests/src/test/scala/org/dhallj/tests/MiscSuite.scala",
    "chars": 2535,
    "preview": "package org.dhallj.tests\n\nimport java.time.Instant\nimport munit.ScalaCheckSuite\nimport org.dhallj.ast._\nimport org.dhall"
  },
  {
    "path": "tests/src/test/scala/org/dhallj/tests/PreludeSuite.scala",
    "chars": 1598,
    "preview": "package org.dhallj.tests\n\nimport java.nio.file.Paths\nimport munit.{FunSuite, Ignore, Slow, TestOptions}\nimport org.dhall"
  },
  {
    "path": "tests/src/test/scala/org/dhallj/tests/ToStringSuite.scala",
    "chars": 2606,
    "preview": "package org.dhallj.tests\n\nimport munit.{Ignore, ScalaCheckSuite, Slow}\nimport org.dhallj.core.{Expr, Operator}\nimport or"
  },
  {
    "path": "tests/src/test/scala/org/dhallj/tests/acceptance/AcceptanceSuites.scala",
    "chars": 4047,
    "preview": "package org.dhallj.tests.acceptance\n\nclass NormalizationSimpleSuite extends NormalizationUSuite(\"normalization/success/s"
  },
  {
    "path": "version.sbt",
    "chars": 35,
    "preview": "ThisBuild / version := \"0.10.0-M2\"\n"
  }
]

About this extraction

This page contains the full source code of the travisbrown/dhallj GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 167 files (808.0 KB), approximately 213.8k tokens, and a symbol index with 1358 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!