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/.*.*//') 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 ``. ### 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 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 := ( travisbrown Travis Brown travisrobertbrown@gmail.com https://twitter.com/travisbrown TimWSpence Tim Spence https://github.com/TimWSpence ) ) ================================================ 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 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) -> ) 1).bar]").normalize() val Right(json1) = io.circe.jawn.parse("""["bar"]""") assert(Converter(expr1) == Some(json1)) } test("convert unions") { val expr1 = DhallParser.parse("[.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 this Stack Overflow answer */ 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 implements Visitor { static final Visitor instanceForString = new NullVisitor(); static final Visitor instanceForByteArray = new NullVisitor(); @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 nextSymbol(Visitor 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. * *

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 Map readMap(Visitor visitor) { skip55799(); byte b = this.read(); switch (MajorType.fromByte(b)) { case MAP: int length = readMapStart(b).intValue(); Map 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 readArrayStart(byte b, Visitor 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 readPrimitive(byte b, Visitor 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 { 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 implements Iterable { private final A[] values; public ArrayIterable(A[] values) { this.values = values; } public final Iterator iterator() { return new ArrayIterator(this.values); } private static final class ArrayIterator implements Iterator { 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. * *

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 accept(ExternalVisitor visitor) { return visitor.onNatural(this.value); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onInteger(this.value); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onDouble(this.value); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onDate(this.year, this.month, this.day); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onTime(this.hour, this.minute, this.second, this.fractional); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onTimeZone(this.minutes); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onText(this.parts, new ArrayIterable(interpolated)); } final void advance(VisitState 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())); } 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 results = new ArrayList(); 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 accept(ExternalVisitor visitor) { return visitor.onApplication(base, arg); } final void advance(VisitState state) { if (state.current.state == 0) { LinkedList 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 application = state.applicationStack.poll(); if (application.isEmpty()) { List 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 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 accept(ExternalVisitor visitor) { return visitor.onOperatorApplication(this.operator, lhs, rhs); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onIf(predicate, thenValue, elseValue); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onLambda(this.name, type, result); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onPi(this.name, type, result); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onAssert(base); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onFieldAccess(base, this.fieldName); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onProjection(base, this.fieldNames); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onProjectionByType(base, type); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onBuiltIn(this.name); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onIdentifier(this.name, this.index); } final void advance(VisitState state) { state.valueStack.push(state.visitor.onIdentifier(this, this.name, this.index)); } } static final class RecordLiteral extends Expr { final Entry[] fields; RecordLiteral(Entry[] fields) { super(Tags.RECORD); this.fields = fields; } public final A accept(ExternalVisitor visitor) { return visitor.onRecord(new ArrayIterable>(fields), fields.length); } final void advance(VisitState 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>())); } else { state.current = new ExprState(state.current.expr, 1, this.fields, state.visitor.sortFields()); state.stack.push(state.current); Entry 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> results = new ArrayList>(); 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 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[] fields; RecordType(Entry[] fields) { super(Tags.RECORD_TYPE); this.fields = fields; } public final A accept(ExternalVisitor visitor) { return visitor.onRecordType(new ArrayIterable>(fields), fields.length); } final void advance(VisitState 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>())); } else { state.current = new ExprState(state.current.expr, 1, this.fields, state.visitor.sortFields()); state.stack.push(state.current); Entry 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> results = new ArrayList>(); 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 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[] fields; UnionType(Entry[] fields) { super(Tags.UNION_TYPE); this.fields = fields; } public final A accept(ExternalVisitor visitor) { return visitor.onUnionType(new ArrayIterable>(fields), fields.length); } final void advance(VisitState 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>())); } else { state.current = new ExprState(state.current.expr, 1, this.fields, state.visitor.sortFields()); state.stack.push(state.current); Entry 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> results = new ArrayList>(); 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 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 accept(ExternalVisitor visitor) { return visitor.onNonEmptyList(new ArrayIterable(values), this.values.length); } final void advance(VisitState 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 results = new ArrayList(); 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 fieldMap = new LinkedHashMap<>(values.length); for (Expr value : values) { List> asRecord = Expr.Util.asRecordLiteral(value); if (asRecord == null) { return null; } Entry 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> 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 accept(ExternalVisitor visitor) { return visitor.onEmptyList(type); } final void advance(VisitState 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> asRecordType = Expr.Util.asRecordType(elementType); if (asRecordType == null) { return false; } Entry 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 accept(ExternalVisitor visitor) { return visitor.onLet(name, type, value, body); } final void advance(VisitState state) { List> letBindings; if (state.current.state == 0) { letBindings = new ArrayList>(); 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 letBindingNames = new ArrayList<>(state.current.size); for (LetBinding 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 letBindingNames = state.letBindingNamesStack.poll(); LinkedList> valueBindings = new LinkedList>(); 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 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> 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 accept(ExternalVisitor visitor) { return visitor.onAnnotated(base, type); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onMerge(handlers, union, type); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onToMap(base, type); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onWith(this.base, this.path, this.value); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onMissingImport(this.mode, this.hash); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onEnvImport(this.name, this.mode, this.hash); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onLocalImport(this.path, this.mode, this.hash); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onClasspathImport(this.path, this.mode, this.hash); } final void advance(VisitState 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 accept(ExternalVisitor visitor) { return visitor.onRemoteImport(this.url, this.using, this.mode, this.hash); } final void advance(VisitState 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. * *

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 cachedHashBytes = new AtomicReference(); Expr(int tag) { this.tag = tag; } /** Run the given external visitor on this expression. */ public abstract A accept(ExternalVisitor visitor); abstract void advance(VisitState state); /** Run the given internal visitor on this expression. */ public final A accept(Visitor visitor) { VisitState state = new VisitState(visitor, this); while (state.current != null) { state.current.expr.advance(state); state.current = state.stack.poll(); } return state.valueStack.poll(); } /** * Beta-normalize this expression. * *

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

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. * *

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. * *

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. * *

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. * *

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. * *

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. * *

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. * *

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 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 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(0); } else { return null; } } /** If the expression is a record literal, return its fields. */ public static final List> 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> 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> 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 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. * *

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 flattenToMapRecord(List> fields) { if (fields == null || fields.size() != 2) { return null; } Expr key = null; Expr value = null; for (Entry 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 { 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 builtIns = new HashMap(34); private static final Set keywords = new HashSet(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 accept(ExternalVisitor visitor) { return visitor.onNote(base, this.source); } final void advance(VisitState 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 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 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[] fields) { return new Constructors.RecordLiteral(fields); } public static final Expr makeRecordLiteral(Collection> 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[] fields) { return new Constructors.RecordType(fields); } public static final Expr makeRecordType(Collection> fields) { return new Constructors.RecordType(fields.toArray(new Entry[fields.size()])); } public static final Expr makeUnionType(Entry[] fields) { return new Constructors.UnionType(fields); } public static final Expr makeUnionType(Collection> 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 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> bindings, Expr body) { Expr result = body; for (int i = bindings.size() - 1; i >= 0; i--) { LetBinding 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 getFirstDiff(Expr other) { Deque stackA = new ArrayDeque(); Deque stackB = new ArrayDeque(); 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[] fieldsA = recordA.fields; Entry[] 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[] fieldsA = recordTypeA.fields; Entry[] 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[] fieldsA = unionTypeA.fields; Entry[] 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 ExternalVisitor { A onNote(Expr base, Source source); A onNatural(BigInteger value); A onInteger(BigInteger value); A onDouble(double value); A onDate(int year, int month, int day); A onTime(int hour, int minute, int second, BigDecimal fractional); A onTimeZone(int minutes); A onBuiltIn(String name); A onIdentifier(String name, long index); A onLambda(String name, Expr type, Expr result); A onPi(String name, Expr type, Expr result); A onLet(String name, Expr type, Expr value, Expr body); A onText(String[] parts, Iterable interpolated); A onNonEmptyList(Iterable values, int size); A onEmptyList(Expr type); A onRecord(Iterable> fields, int size); A onRecordType(Iterable> fields, int size); A onUnionType(Iterable> fields, int size); A onFieldAccess(Expr base, String fieldName); A onProjection(Expr base, String[] fieldNames); A onProjectionByType(Expr base, Expr type); A onApplication(Expr base, Expr arg); A onOperatorApplication(Operator operator, Expr lhs, Expr rhs); A onIf(Expr predicate, Expr thenValue, Expr elseValue); A onAnnotated(Expr base, Expr type); A onAssert(Expr base); A onMerge(Expr handlers, Expr union, Expr type); A onToMap(Expr base, Expr type); A onWith(Expr base, String[] path, Expr value); A onMissingImport(Expr.ImportMode mode, byte[] hash); A onEnvImport(String value, Expr.ImportMode mode, byte[] hash); A onLocalImport(Path path, Expr.ImportMode mode, byte[] hash); A onClasspathImport(Path path, Expr.ImportMode mode, byte[] hash); A onRemoteImport(URI url, Expr using, Expr.ImportMode mode, byte[] hash); /** * Represents a function from a Dhall expression that always returns the same value. * *

This is a convenience class designed to help with implementations that have a default value * for most cases. * *

Note that by default the implementation sees through note layers. * * @param A The result type */ public static class Constant implements ExternalVisitor { private final A returnValue; protected A getReturnValue() { return this.returnValue; } public Constant(A value) { this.returnValue = value; } @Override public A onNote(Expr base, Source source) { return base.accept(this); } @Override public A onNatural(BigInteger value) { return this.getReturnValue(); } @Override public A onInteger(BigInteger value) { return this.getReturnValue(); } @Override public A onDouble(double value) { return this.getReturnValue(); } @Override public A onDate(int year, int month, int day) { return this.getReturnValue(); } @Override public A onTime(int hour, int minute, int second, BigDecimal fractional) { return this.getReturnValue(); } @Override public A onTimeZone(int minutes) { return this.getReturnValue(); } @Override public A onBuiltIn(String name) { return this.getReturnValue(); } @Override public A onIdentifier(String name, long index) { return this.getReturnValue(); } @Override public A onLambda(String name, Expr input, Expr result) { return this.getReturnValue(); } @Override public A onPi(String name, Expr input, Expr result) { return this.getReturnValue(); } @Override public A onLet(String name, Expr type, Expr value, Expr body) { return this.getReturnValue(); } @Override public A onText(String[] parts, Iterable interpolated) { return this.getReturnValue(); } @Override public A onNonEmptyList(Iterable values, int size) { return this.getReturnValue(); } @Override public A onEmptyList(Expr tpe) { return this.getReturnValue(); } @Override public A onRecord(Iterable> fields, int size) { return this.getReturnValue(); } @Override public A onRecordType(Iterable> fields, int size) { return this.getReturnValue(); } @Override public A onUnionType(Iterable> fields, int size) { return this.getReturnValue(); } @Override public A onFieldAccess(Expr base, String fieldName) { return this.getReturnValue(); } @Override public A onProjection(Expr base, String[] fieldNames) { return this.getReturnValue(); } @Override public A onProjectionByType(Expr base, Expr tpe) { return this.getReturnValue(); } @Override public A onApplication(Expr base, Expr arg) { return this.getReturnValue(); } @Override public A onOperatorApplication(Operator operator, Expr lhs, Expr rhs) { return this.getReturnValue(); } @Override public A onIf(Expr predicate, Expr thenValue, Expr elseValue) { return this.getReturnValue(); } @Override public A onAnnotated(Expr base, Expr tpe) { return this.getReturnValue(); } @Override public A onAssert(Expr base) { return this.getReturnValue(); } @Override public A onMerge(Expr handlers, Expr union, Expr tpe) { return this.getReturnValue(); } @Override public A onToMap(Expr base, Expr type) { return this.getReturnValue(); } @Override public A onWith(Expr base, String[] path, Expr value) { return this.getReturnValue(); } @Override public A onMissingImport(Expr.ImportMode mode, byte[] hash) { return this.getReturnValue(); } @Override public A onEnvImport(String value, Expr.ImportMode mode, byte[] hash) { return this.getReturnValue(); } @Override public A onLocalImport(Path path, Expr.ImportMode mode, byte[] hash) { return this.getReturnValue(); } @Override public A onClasspathImport(Path path, Expr.ImportMode mode, byte[] hash) { return this.getReturnValue(); } @Override public A onRemoteImport(URI url, Expr using, Expr.ImportMode mode, byte[] hash) { return this.getReturnValue(); } } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/IsResolved.java ================================================ package org.dhallj.core; import java.net.URI; import java.nio.file.Path; final class IsResolved extends Visitor.Property { public static final Visitor instance = new IsResolved(); @Override public Boolean onOperatorApplication(Operator operator, Boolean lhs, Boolean rhs) { if (operator.equals(Operator.IMPORT_ALT)) { return false; } else { return super.onOperatorApplication(operator, lhs, rhs); } } @Override public Boolean onLocalImport(Path path, Expr.ImportMode mode, byte[] hash) { return false; } @Override public Boolean onClasspathImport(Path path, Expr.ImportMode mode, byte[] hash) { return false; } @Override public Boolean onRemoteImport(URI url, Boolean using, Expr.ImportMode mode, byte[] hash) { return false; } @Override public Boolean onEnvImport(String value, Expr.ImportMode mode, byte[] hash) { return false; } @Override public Boolean onMissingImport(Expr.ImportMode mode, byte[] hash) { return false; } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/Operator.java ================================================ package org.dhallj.core; /** Represents a Dhall operator. */ public enum Operator { OR("||", 3, true), AND("&&", 7, true), EQUALS("==", 12, true), NOT_EQUALS("!=", 13, true), PLUS("+", 4, false), TIMES("*", 11, false), TEXT_APPEND("++", 5, false), LIST_APPEND("#", 6, false), COMBINE("\u2227", 8, false), PREFER("\u2afd", 9, false), COMBINE_TYPES("\u2a53", 10, false), IMPORT_ALT("?", 2, false), EQUIVALENT("\u2261", 1, false), COMPLETE("::", 0, false); private static final Operator[] values = values(); private final String value; private final int precedence; private final boolean isBoolOperator; Operator(String value, int precedence, boolean isBoolOperator) { this.value = value; this.precedence = precedence; this.isBoolOperator = isBoolOperator; } public final boolean isBoolOperator() { return this.isBoolOperator; } public final int getLabel() { return this.ordinal(); } public final int getPrecedence() { return this.precedence; } public final String toString() { return this.value; } public static final Operator fromLabel(int ordinal) { if (ordinal >= 0 && ordinal < values.length) { return values[ordinal]; } else { return null; } } public static final Operator parse(String input) { if (input.equals(OR.value)) { return OR; } else if (input.equals(AND.value)) { return AND; } else if (input.equals(EQUALS.value)) { return EQUALS; } else if (input.equals(NOT_EQUALS.value)) { return NOT_EQUALS; } else if (input.equals(PLUS.value)) { return PLUS; } else if (input.equals(TIMES.value)) { return TIMES; } else if (input.equals(TEXT_APPEND.value)) { return TEXT_APPEND; } else if (input.equals(LIST_APPEND.value)) { return LIST_APPEND; } else if (input.equals(COMBINE.value) || input.equals("/\\")) { return COMBINE; } else if (input.equals(PREFER.value) || input.equals("//")) { return PREFER; } else if (input.equals(COMBINE_TYPES.value) || input.equals("//\\\\")) { return COMBINE_TYPES; } else if (input.equals(IMPORT_ALT.value)) { return IMPORT_ALT; } else if (input.equals(EQUIVALENT.value) || input.equals("===")) { return EQUIVALENT; } else if (input.equals(COMPLETE.value)) { return COMPLETE; } else { throw new IllegalArgumentException("No org.dhallj.core.Operator represented by " + input); } } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/Source.java ================================================ package org.dhallj.core; /** Represents a section of a source document corresponding to a parsed expression. */ public abstract class Source { final int beginLine; final int beginColumn; final int endLine; final int endColumn; public Source(int beginLine, int beginColumn, int endLine, int endColumn) { this.beginLine = beginLine; this.beginColumn = beginColumn; this.endLine = endLine; this.endColumn = endColumn; } public abstract void printText(StringBuilder builder); public final String getText() { StringBuilder builder = new StringBuilder(); this.printText(builder); return builder.toString(); } public final int getBeginLine() { return this.beginLine; } public final int getBeginColumn() { return this.beginColumn; } public final int getEndLine() { return this.endLine; } public final int getEndColumn() { return this.endColumn; } public final String toString() { StringBuilder builder = new StringBuilder("[("); builder.append(this.beginLine); builder.append(", "); builder.append(this.beginColumn); builder.append(") ("); builder.append(this.endLine); builder.append(", "); builder.append(this.endColumn); builder.append(")]\n"); this.printText(builder); return builder.toString(); } private static final class FromString extends Source { private final String text; FromString(String text, int beginLine, int beginColumn, int endLine, int endColumn) { super(beginLine, beginColumn, endLine, endColumn); this.text = text; } public final void printText(StringBuilder builder) { builder.append(this.text); } } public static final Source fromString( String text, int beginLine, int beginColumn, int endLine, int endColumn) { return new FromString(text, beginLine, beginColumn, endLine, endColumn); } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/Tags.java ================================================ package org.dhallj.core; // Note that these are internal identifiers only. final class Tags { static final int NOTE = 0; // Non-recursive constructors. static final int NATURAL = 1; static final int INTEGER = 2; static final int DOUBLE = 3; static final int BUILT_IN = 4; static final int IDENTIFIER = 5; static final int DATE = 31; static final int TIME = 32; static final int TIME_ZONE = 33; // Binding constructors. static final int LAMBDA = 6; static final int PI = 7; static final int LET = 8; // Other. static final int TEXT = 9; static final int NON_EMPTY_LIST = 10; static final int EMPTY_LIST = 11; static final int RECORD = 12; static final int RECORD_TYPE = 13; static final int UNION_TYPE = 14; static final int FIELD_ACCESS = 15; static final int PROJECTION = 16; static final int PROJECTION_BY_TYPE = 17; static final int APPLICATION = 18; static final int OPERATOR_APPLICATION = 19; static final int IF = 20; static final int ANNOTATED = 21; static final int ASSERT = 22; // Syntactic sugar. static final int MERGE = 23; static final int TO_MAP = 24; // Imports. static final int MISSING_IMPORT = 25; static final int ENV_IMPORT = 26; static final int LOCAL_IMPORT = 27; static final int REMOTE_IMPORT = 28; static final int CLASSPATH_IMPORT = 29; static final int WITH = 30; } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/ToStringVisitor.java ================================================ package org.dhallj.core; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URI; import java.nio.charset.Charset; import java.nio.file.Path; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; final class ToStringState { static final int NONE = 0; static final int OPERATOR = 1; static final int APPLICATION = Operator.NOT_EQUALS.getPrecedence() + 1; static final int IMPORT = APPLICATION + 1; static final int COMPLETE = IMPORT + 1; static final int SELECTOR = COMPLETE + 1; static final int PRIMITIVE = SELECTOR + 1; private final String text; private final int level; ToStringState(String text, int level) { this.text = text; this.level = level; } ToStringState(String text) { this(text, PRIMITIVE); } ToStringState withText(String text) { return new ToStringState(text, this.level); } String toString(int contextLevel) { if (this.level < contextLevel) { return "(" + this.text + ")"; } else { return this.text; } } public String toString() { return this.toString(NONE); } static final int getOperatorLevel(Operator operator) { if (operator == Operator.COMPLETE) { return COMPLETE; } else { return operator.getPrecedence(); } } } final class ToStringVisitor extends Visitor.NoPrepareEvents { public static Visitor instance = new ToStringVisitor(); public void bind(String name, Expr type) {} public ToStringState onNote(ToStringState base, Source source) { return base; } public ToStringState onNatural(Expr self, BigInteger value) { return new ToStringState(value.toString()); } public ToStringState onInteger(Expr self, BigInteger value) { String withSign = (value.compareTo(BigInteger.ZERO) >= 0) ? ("+" + value.toString()) : value.toString(); return new ToStringState(withSign); } public ToStringState onDouble(Expr self, double value) { return new ToStringState(Double.toString(value)); } private static String pad2(int input) { String asString = Integer.toString(input); if (asString.length() == 1) { return "0" + asString; } else { return asString; } } private static String pad4(int input) { String asString = Integer.toString(input); for (int i = 0; i < asString.length() - 4; i += 1) { asString = "0" + asString; } return asString; } public ToStringState onDate(Expr self, int year, int month, int day) { return new ToStringState(pad4(year) + "-" + pad2(month) + "-" + pad2(day)); } public ToStringState onTime(Expr self, int hour, int minute, int second, BigDecimal fractional) { String result = pad2(hour) + ":" + pad2(minute) + ":" + pad2(second); if (!fractional.equals(BigDecimal.ZERO)) { result = result + fractional.toString().substring(1); } return new ToStringState(result); } public ToStringState onTimeZone(Expr self, int minutes) { if (Long.signum(minutes) < 0) { return new ToStringState("-" + pad2(-minutes / 60) + ":" + pad2(-minutes % 60)); } else { return new ToStringState("+" + pad2(minutes / 60) + ":" + pad2(minutes % 60)); } } public ToStringState onBuiltIn(Expr self, String name) { return new ToStringState(name); } public ToStringState onIdentifier(Expr self, String name, long index) { String maybeEscaped = escapeName(name); return new ToStringState( (index == 0) ? maybeEscaped : (maybeEscaped + "@" + Long.toString(index))); } public ToStringState onRecord(List> fields) { if (fields.isEmpty()) { return new ToStringState("{=}"); } else { StringBuilder builder = new StringBuilder("{"); Iterator> it = fields.iterator(); while (it.hasNext()) { Entry entry = it.next(); builder.append(escapeName(entry.getKey())); builder.append(" = "); builder.append(entry.getValue().toString()); if (it.hasNext()) { builder.append(", "); } } builder.append("}"); return new ToStringState(builder.toString()); } } public ToStringState onRecordType(List> fields) { StringBuilder builder = new StringBuilder("{"); Iterator> it = fields.iterator(); while (it.hasNext()) { Entry entry = it.next(); builder.append(escapeName(entry.getKey())); builder.append(" : "); builder.append(entry.getValue().toString()); if (it.hasNext()) { builder.append(", "); } } builder.append("}"); return new ToStringState(builder.toString()); } public ToStringState onUnionType(List> fields) { StringBuilder builder = new StringBuilder("<"); Iterator> it = fields.iterator(); while (it.hasNext()) { Entry entry = it.next(); builder.append(escapeName(entry.getKey())); ToStringState type = entry.getValue(); if (type != null) { builder.append(" : "); builder.append(type.toString()); } if (it.hasNext()) { builder.append(" | "); } } builder.append(">"); return new ToStringState(builder.toString()); } public ToStringState onNonEmptyList(List values) { StringBuilder builder = new StringBuilder("["); Iterator it = values.iterator(); while (it.hasNext()) { builder.append(it.next().toString()); if (it.hasNext()) { builder.append(", "); } } builder.append("]"); return new ToStringState(builder.toString()); } public ToStringState onFieldAccess(ToStringState base, String fieldName) { return new ToStringState( base.toString(ToStringState.PRIMITIVE) + "." + fieldName, ToStringState.SELECTOR); } public ToStringState onProjection(ToStringState base, String[] fieldNames) { StringBuilder builder = new StringBuilder(base.toString(ToStringState.PRIMITIVE)); builder.append(".{"); for (int i = 0; i < fieldNames.length; i += 1) { builder.append(fieldNames[i]); if (i < fieldNames.length - 1) { builder.append(", "); } } builder.append("}"); return new ToStringState(builder.toString(), ToStringState.SELECTOR); } public ToStringState onProjectionByType(ToStringState base, ToStringState type) { return new ToStringState( base.toString(ToStringState.PRIMITIVE) + ".(" + type.toString() + ")", ToStringState.SELECTOR); } public ToStringState onOperatorApplication( Operator operator, ToStringState lhs, ToStringState rhs) { int operatorLevel = ToStringState.getOperatorLevel(operator); if (operatorLevel == ToStringState.COMPLETE) { return new ToStringState( lhs.toString(ToStringState.SELECTOR) + " " + operator.toString() + " " + rhs.toString(ToStringState.SELECTOR), operatorLevel); } else { return new ToStringState( lhs.toString(operatorLevel) + " " + operator.toString() + " " + rhs.toString(operatorLevel + 1), operatorLevel); } } public ToStringState onMissingImport(Expr.ImportMode mode, byte[] hash) { StringBuilder builder = new StringBuilder("missing"); if (hash != null) { builder.append(" "); builder.append(Expr.Util.encodeHashBytes(hash)); } if (mode != Expr.ImportMode.CODE) { builder.append(" as "); builder.append(mode); } return new ToStringState(builder.toString(), ToStringState.IMPORT); } public ToStringState onEnvImport(String value, Expr.ImportMode mode, byte[] hash) { StringBuilder builder = new StringBuilder("env:"); builder.append(value); if (hash != null) { builder.append(" "); builder.append(Expr.Util.encodeHashBytes(hash)); } if (mode != Expr.ImportMode.CODE) { builder.append(" as "); builder.append(mode); } return new ToStringState(builder.toString(), ToStringState.IMPORT); } public ToStringState onLocalImport(Path path, Expr.ImportMode mode, byte[] hash) { StringBuilder builder = new StringBuilder(path.toString()); if (hash != null) { builder.append(" "); builder.append(Expr.Util.encodeHashBytes(hash)); } if (mode != Expr.ImportMode.CODE) { builder.append(" as "); builder.append(mode); } return new ToStringState(builder.toString(), ToStringState.IMPORT); } @Override public ToStringState onClasspathImport(Path path, Expr.ImportMode mode, byte[] hash) { StringBuilder builder = new StringBuilder("classpath:"); builder.append(path.toString()); if (hash != null) { builder.append(" "); builder.append(Expr.Util.encodeHashBytes(hash)); } if (mode != Expr.ImportMode.CODE) { builder.append(" as "); builder.append(mode); } return new ToStringState(builder.toString(), ToStringState.IMPORT); } public ToStringState onRemoteImport( URI url, ToStringState using, Expr.ImportMode mode, byte[] hash) { StringBuilder builder = new StringBuilder(url.toString()); if (using != null) { builder.append(" using "); builder.append(using.toString(ToStringState.IMPORT)); } if (hash != null) { builder.append(" "); builder.append(Expr.Util.encodeHashBytes(hash)); } if (mode != Expr.ImportMode.CODE) { builder.append(" as "); builder.append(mode); } return new ToStringState(builder.toString(), ToStringState.IMPORT); } public ToStringState onMerge(ToStringState handlers, ToStringState union, ToStringState type) { StringBuilder builder = new StringBuilder("merge "); builder.append(handlers.toString(ToStringState.IMPORT)); builder.append(" "); builder.append(union.toString(ToStringState.IMPORT)); if (type != null) { builder.append(" : "); builder.append(type.toString(ToStringState.APPLICATION)); return new ToStringState(builder.toString(), ToStringState.NONE); } else { return new ToStringState(builder.toString(), ToStringState.APPLICATION); } } public ToStringState onLambda(String name, ToStringState type, ToStringState result) { return new ToStringState( "λ(" + escapeName(name) + " : " + type.toString() + ") → " + result.toString(), ToStringState.NONE); } public ToStringState onPi(String name, ToStringState type, ToStringState result) { String resultString = result.toString(); return new ToStringState( name.equals("_") ? (type.toString(ToStringState.OPERATOR) + " → " + resultString) : ("∀(" + escapeName(name) + " : " + type.toString() + ") → " + resultString), ToStringState.NONE); } public ToStringState onLet(List> bindings, ToStringState body) { String result = body.toString(); for (int i = bindings.size() - 1; i >= 0; i--) { Expr.LetBinding binding = bindings.get(i); String typeString = binding.hasType() ? (" : " + binding.getType().toString()) : ""; result = "let " + escapeName(binding.getName()) + typeString + " = " + binding.getValue().toString() + " in " + result; } return new ToStringState(result, ToStringState.NONE); } public ToStringState onText(String[] parts, List interpolated) { StringBuilder builder = new StringBuilder("\""); builder.append(Expr.Util.escapeText(parts[0], false)); int i = 1; Iterator it = interpolated.iterator(); while (it.hasNext()) { builder.append("${"); builder.append(it.next().toString()); builder.append("}"); builder.append(Expr.Util.escapeText(parts[i++], false)); } if (i < parts.length) { builder.append(Expr.Util.escapeText(parts[i], false)); } builder.append("\""); return new ToStringState(builder.toString()); } public ToStringState onEmptyList(ToStringState type) { return new ToStringState( "[] : " + type.toString(ToStringState.APPLICATION), ToStringState.NONE); } public ToStringState onApplication(ToStringState base, List args) { StringBuilder builder = new StringBuilder(base.toString(ToStringState.IMPORT)); builder.append(" "); for (int i = 0; i < args.size(); i += 1) { builder.append(args.get(i).toString(ToStringState.IMPORT)); if (i < args.size() - 1) { builder.append(" "); } } return new ToStringState(builder.toString(), ToStringState.APPLICATION); } public ToStringState onIf( ToStringState predicate, ToStringState thenValue, ToStringState elseValue) { return new ToStringState( "if " + predicate.toString() + " then " + thenValue.toString() + " else " + elseValue.toString(), ToStringState.NONE); } public ToStringState onAnnotated(ToStringState base, ToStringState type) { return new ToStringState( base.toString(ToStringState.OPERATOR) + " : " + type.toString(), ToStringState.NONE); } public ToStringState onAssert(ToStringState base) { return new ToStringState("assert : " + base.toString(), ToStringState.NONE); } public ToStringState onToMap(ToStringState base, ToStringState type) { StringBuilder builder = new StringBuilder("toMap "); builder.append(base.toString(ToStringState.IMPORT)); if (type != null) { builder.append(" : "); builder.append(type.toString(ToStringState.APPLICATION)); return new ToStringState(builder.toString(), ToStringState.NONE); } else { return new ToStringState(builder.toString(), ToStringState.APPLICATION); } } public ToStringState onWith(ToStringState base, String[] path, ToStringState value) { StringBuilder builder = new StringBuilder(); builder.append(base.toString(ToStringState.IMPORT)); builder.append(" with "); for (int i = 0; i < path.length - 1; i += 1) { builder.append(path[i]); builder.append("."); } builder.append(path[path.length - 1]); builder.append(" = "); builder.append(value.toString(ToStringState.OPERATOR)); return new ToStringState(builder.toString(), ToStringState.NONE); } private static boolean isAlpha(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } private static boolean isDigit(char c) { return (c >= '0' && c <= '9'); } private static boolean isSimpleLabel(String name) { if (name.length() == 0) { return false; } char c = name.charAt(0); if (!isAlpha(c) && c != '_') { return false; } for (int i = 1; i < name.length(); i++) { c = name.charAt(i); if (!isAlpha(c) && !isDigit(c) && c != '-' && c != '/' && c != '_') { return false; } } return true; } private static String escapeName(String name) { if (!isSimpleLabel(name) || Expr.Constants.isBuiltIn(name) || Expr.Constants.isKeyword(name)) { return "`" + name + "`"; } else { return name; } } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/VisitState.java ================================================ package org.dhallj.core; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Comparator; import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; final class ExprState { final Expr expr; int state; int size; Entry[] sortedFields; boolean skippedRecursion = false; ExprState(Expr expr, int state, int size) { this.expr = expr; this.state = state; this.size = size; this.sortedFields = null; } ExprState(Expr expr, int state, Entry[] fields, boolean sortFields) { this.expr = expr; this.state = state; this.size = 0; if (sortFields) { this.sortedFields = new Entry[fields.length]; System.arraycopy(fields, 0, sortedFields, 0, fields.length); Arrays.sort(sortedFields, ExprState.entryComparator); } else { this.sortedFields = fields; } } ExprState(Expr expr, int state) { this(expr, state, 0); } /** Java 8 introduces {@code comparingByKey}, but we can roll our own pretty easily. */ private static final Comparator> entryComparator = new Comparator>() { public int compare(Entry a, Entry b) { return a.getKey().compareTo(b.getKey()); } }; } final class VisitState { final Visitor visitor; ExprState current; final Deque stack; final Deque valueStack; final Deque> applicationStack; final Deque>> letBindingsStack; final Deque> letBindingNamesStack; public VisitState(Visitor visitor, Expr expr) { this.visitor = visitor; this.current = new ExprState(expr, 0); this.stack = new ArrayDeque<>(); // Note that we have to use a linked list here because we store null values on the stack. this.valueStack = new LinkedList<>(); this.applicationStack = new ArrayDeque<>(); this.letBindingsStack = new ArrayDeque<>(); this.letBindingNamesStack = new ArrayDeque<>(); } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/Visitor.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.List; import java.util.Map.Entry; /** * Represents a function from a Dhall expression to a value that recurses through the structure of * the expression. * * @param A The final result type */ public interface Visitor { void bind(String name, Expr type); A onNote(A base, Source source); A onNatural(Expr self, BigInteger value); A onInteger(Expr self, BigInteger value); A onDouble(Expr self, double value); A onDate(Expr self, int year, int month, int day); A onTime(Expr self, int hour, int minute, int second, BigDecimal fractional); A onTimeZone(Expr self, int minutes); A onBuiltIn(Expr self, String value); A onIdentifier(Expr self, String value, long index); A onLambda(String name, A type, A result); A onPi(String name, A type, A result); A onLet(List> bindings, A body); A onText(String[] parts, List interpolated); A onNonEmptyList(List values); A onEmptyList(A type); A onRecord(List> fields); A onRecordType(List> fields); A onUnionType(List> fields); A onFieldAccess(A base, String fieldName); A onProjection(A base, String[] fieldNames); A onProjectionByType(A base, A type); A onApplication(A base, List args); A onOperatorApplication(Operator operator, A lhs, A rhs); A onIf(A predicate, A thenValue, A elseValue); A onAnnotated(A base, A type); A onAssert(A base); A onMerge(A handlers, A union, A type); A onToMap(A base, A type); A onWith(A base, String[] path, A value); A onMissingImport(Expr.ImportMode mode, byte[] hash); A onEnvImport(String value, Expr.ImportMode mode, byte[] hash); A onLocalImport(Path path, Expr.ImportMode mode, byte[] hash); A onClasspathImport(Path path, Expr.ImportMode mode, byte[] hash); A onRemoteImport(URI url, A using, Expr.ImportMode mode, byte[] hash); /** Determines whether the driver sorts fields by name before feeding them to the visitor. */ boolean sortFields(); /** * Determines whether the driver flattens lists matching the {@code toMap} format into records * before feeding them to the visitor. */ boolean flattenToMapLists(); boolean prepareLambda(String name, Expr type); boolean preparePi(String name, Expr type); boolean prepareLet(int size); boolean prepareLetBinding(String name, Expr type); boolean prepareText(int size); boolean prepareTextPart(String part); boolean prepareNonEmptyList(int size); boolean prepareNonEmptyListElement(int index); boolean prepareEmptyList(Expr type); boolean prepareRecord(int size); boolean prepareRecordField(String name, Expr type, int index); boolean prepareRecordType(int size); boolean prepareRecordTypeField(String name, Expr type, int index); boolean prepareUnionType(int size); boolean prepareUnionTypeField(String name, Expr type, int index); boolean prepareFieldAccess(Expr base, String fieldName); boolean prepareProjection(int size); boolean prepareProjectionByType(); boolean prepareProjectionByType(Expr type); boolean prepareApplication(Expr base, int size); boolean prepareOperatorApplication(Operator operator); boolean prepareIf(); boolean prepareAnnotated(Expr type); boolean prepareAssert(); boolean prepareMerge(Expr type); boolean prepareToMap(Expr type); boolean prepareWith(String[] path); boolean prepareWithValue(String[] path); boolean prepareRemoteImport(URI url, Expr using, Expr.ImportMode mode, byte[] hash); /** * Represents a function from a Dhall expression that doesn't need preparation events. * *

Note that by default the implementation sees through note layers. * * @param A The result type */ public abstract static class NoPrepareEvents implements Visitor { public void bind(String name, Expr type) {} public boolean sortFields() { return false; } public boolean flattenToMapLists() { return false; } public boolean prepareLambda(String name, Expr type) { return true; } public boolean preparePi(String name, Expr type) { return true; } public boolean prepareLet(int size) { return true; } public boolean prepareLetBinding(String name, Expr type) { return true; } public boolean prepareText(int size) { return true; } public boolean prepareTextPart(String part) { return true; } public boolean prepareNonEmptyList(int size) { return true; } public boolean prepareNonEmptyListElement(int index) { return true; } public boolean prepareEmptyList(Expr type) { return true; } public boolean prepareRecord(int size) { return true; } public boolean prepareRecordField(String name, Expr type, int index) { return true; } public boolean prepareRecordType(int size) { return true; } public boolean prepareRecordTypeField(String name, Expr type, int index) { return true; } public boolean prepareUnionType(int size) { return true; } public boolean prepareUnionTypeField(String name, Expr type, int index) { return true; } public boolean prepareFieldAccess(Expr base, String fieldName) { return true; } public boolean prepareProjection(int size) { return true; } public boolean prepareProjectionByType() { return true; } public boolean prepareProjectionByType(Expr type) { return true; } public boolean prepareApplication(Expr base, int size) { return true; } public boolean prepareOperatorApplication(Operator operator) { return true; } public boolean prepareIf() { return true; } public boolean prepareAnnotated(Expr type) { return true; } public boolean prepareAssert() { return true; } public boolean prepareMerge(Expr type) { return true; } public boolean prepareToMap(Expr type) { return true; } public boolean prepareWith(String[] path) { return true; } public boolean prepareWithValue(String[] path) { return true; } public boolean prepareRemoteImport(URI url, Expr using, Expr.ImportMode mode, byte[] hash) { return true; } /* public A onMissingImport(Expr.ImportMode mode, byte[] hash) { return this.getReturnValue(); } public A onEnvImport(String value, Expr.ImportMode mode, byte[] hash) { return this.getReturnValue(); } public A onLocalImport(Path path, Expr.ImportMode mode, byte[] hash) { return this.getReturnValue(); } public A onRemoteImport(URI url, A using, Expr.ImportMode mode, byte[] hash) { return this.getReturnValue(); }*/ } /** * Represents a function from a Dhall expression that always returns the same value. * *

This is a convenience class designed to help with implementations that have a default value * for most cases. * *

Note that by default the implementation sees through note layers. * * @param A The result type */ public static class Constant extends NoPrepareEvents { private final A returnValue; protected A getReturnValue() { return this.returnValue; } public Constant(A value) { this.returnValue = value; } public void bind(String name, Expr type) {} @Override public A onNote(A base, Source source) { return base; } @Override public A onNatural(Expr self, BigInteger value) { return this.getReturnValue(); } @Override public A onInteger(Expr self, BigInteger value) { return this.getReturnValue(); } @Override public A onDouble(Expr self, double value) { return this.getReturnValue(); } @Override public A onDate(Expr self, int year, int month, int day) { return this.getReturnValue(); } @Override public A onTime(Expr self, int hour, int minute, int second, BigDecimal fractional) { return this.getReturnValue(); } @Override public A onTimeZone(Expr self, int minutes) { return this.getReturnValue(); } @Override public A onBuiltIn(Expr self, String value) { return this.getReturnValue(); } @Override public A onIdentifier(Expr self, String value, long index) { return this.getReturnValue(); } @Override public A onLambda(String name, A type, A result) { return this.getReturnValue(); } @Override public A onPi(String name, A type, A result) { return this.getReturnValue(); } @Override public A onLet(List> bindings, A body) { return this.getReturnValue(); } @Override public A onText(String[] parts, List interpolated) { return this.getReturnValue(); } @Override public A onNonEmptyList(List values) { return this.getReturnValue(); } @Override public A onEmptyList(A type) { return this.getReturnValue(); } @Override public A onRecord(List> fields) { return this.getReturnValue(); } @Override public A onRecordType(List> fields) { return this.getReturnValue(); } @Override public A onUnionType(List> fields) { return this.getReturnValue(); } @Override public A onFieldAccess(A base, String fieldName) { return this.getReturnValue(); } @Override public A onProjection(A base, String[] fieldNames) { return this.getReturnValue(); } @Override public A onProjectionByType(A base, A type) { return this.getReturnValue(); } @Override public A onApplication(A base, List args) { return this.getReturnValue(); } @Override public A onOperatorApplication(Operator operator, A lhs, A rhs) { return this.getReturnValue(); } @Override public A onIf(A predicate, A thenValue, A elseValue) { return this.getReturnValue(); } @Override public A onAnnotated(A base, A type) { return this.getReturnValue(); } @Override public A onAssert(A base) { return this.getReturnValue(); } @Override public A onMerge(A handlers, A union, A type) { return this.getReturnValue(); } @Override public A onToMap(A base, A type) { return this.getReturnValue(); } @Override public A onWith(A base, String[] path, A value) { return this.getReturnValue(); } @Override public A onMissingImport(Expr.ImportMode mode, byte[] hash) { return this.getReturnValue(); } @Override public A onEnvImport(String value, Expr.ImportMode mode, byte[] hash) { return this.getReturnValue(); } @Override public A onLocalImport(Path path, Expr.ImportMode mode, byte[] hash) { return this.getReturnValue(); } @Override public A onClasspathImport(Path path, Expr.ImportMode mode, byte[] hash) { return this.getReturnValue(); } @Override public A onRemoteImport(URI url, A using, Expr.ImportMode mode, byte[] hash) { return this.getReturnValue(); } } /** * Represents a property of Dhall expressions. * *

This is a convenience class designed to help with implementations that have a default value * for most cases. */ public class Property extends Constant { public Property() { super(true); } public Boolean onLambda(String name, Boolean type, Boolean result) { return type && result; } public Boolean onPi(String name, Boolean type, Boolean result) { return type && result; } public Boolean onLet(List> bindings, Boolean body) { if (!body) { return false; } else { for (Expr.LetBinding binding : bindings) { if (!binding.getValue() || (binding.hasType() && !binding.getType())) { return false; } } return true; } } public Boolean onText(String[] parts, List interpolated) { for (Boolean value : interpolated) { if (!value) { return false; } } return true; } public Boolean onNonEmptyList(List values) { for (Boolean value : values) { if (!value) { return false; } } return true; } public Boolean onEmptyList(Boolean type) { return type; } public Boolean onRecord(List> fields) { for (Entry entry : fields) { if (!entry.getValue()) { return false; } } return true; } public Boolean onRecordType(List> fields) { for (Entry entry : fields) { if (!entry.getValue()) { return false; } } return true; } public Boolean onUnionType(List> fields) { for (Entry entry : fields) { if (entry.getValue() != null && !entry.getValue()) { return false; } } return true; } public Boolean onFieldAccess(Boolean base, String fieldName) { return base; } public Boolean onProjection(Boolean base, String[] fieldNames) { return base; } public Boolean onProjectionByType(Boolean base, Boolean type) { return base && type; } public Boolean onApplication(Boolean base, List args) { if (!base) { return false; } for (Boolean value : args) { if (!value) { return false; } } return true; } public Boolean onOperatorApplication(Operator operator, Boolean lhs, Boolean rhs) { return lhs && rhs; } public Boolean onIf(Boolean predicate, Boolean thenValue, Boolean elseValue) { return predicate && thenValue && elseValue; } public Boolean onAnnotated(Boolean base, Boolean type) { return base && type; } public Boolean onAssert(Boolean base) { return base; } public Boolean onMerge(Boolean handlers, Boolean union, Boolean type) { return handlers && union && (type == null || type); } public Boolean onToMap(Boolean base, Boolean type) { return base && (type == null || type); } public Boolean onToMap(Boolean base, String[] path, Boolean value) { return base && value; } public Boolean onLocalImport(Path path, Expr.ImportMode mode, byte[] hash) { return true; } public Boolean onRemoteImport(URI url, Boolean using, Expr.ImportMode mode, byte[] hash) { return true; } public Boolean onEnvImport(String value, Expr.ImportMode mode, byte[] hash) { return true; } public Boolean onMissingImport(Expr.ImportMode mode, byte[] hash) { return true; } } /** * Represents an identity function. * *

This is a convenience class designed to help with implementations that only need to change a * small number of cases. */ public abstract class Identity extends NoPrepareEvents { public Expr onNote(Expr base, Source source) { return Expr.makeNote(base, source); } public Expr onNatural(Expr self, BigInteger value) { return self; } public Expr onInteger(Expr self, BigInteger value) { return self; } public Expr onDouble(Expr self, double value) { return self; } public Expr onDate(Expr self, int year, int month, int day) { return self; } public Expr onTime(Expr self, int hour, int minute, int second, BigDecimal fractional) { return self; } public Expr onTimeZone(Expr self, int minutes) { return self; } public Expr onBuiltIn(Expr self, String name) { return self; } public Expr onIdentifier(Expr self, String name, long index) { return self; } public Expr onLambda(String name, Expr type, Expr result) { return Expr.makeLambda(name, type, result); } public Expr onPi(String name, Expr type, Expr result) { return Expr.makePi(name, type, result); } public Expr onLet(List> bindings, Expr body) { return Expr.makeLet(bindings, body); } public Expr onText(String[] parts, List interpolated) { return Expr.makeTextLiteral(parts, interpolated); } public Expr onNonEmptyList(List values) { return Expr.makeNonEmptyListLiteral(values); } public Expr onEmptyList(Expr type) { return Expr.makeEmptyListLiteral(type); } public Expr onRecord(List> fields) { return Expr.makeRecordLiteral(fields); } public Expr onRecordType(List> fields) { return Expr.makeRecordType(fields); } public Expr onUnionType(List> fields) { return Expr.makeUnionType(fields); } public Expr onFieldAccess(Expr base, String fieldName) { return Expr.makeFieldAccess(base, fieldName); } public Expr onProjection(Expr base, String[] fieldNames) { return Expr.makeProjection(base, fieldNames); } public Expr onProjectionByType(Expr base, Expr type) { return Expr.makeProjectionByType(base, type); } public Expr onApplication(Expr base, List args) { return Expr.makeApplication(base, args); } public Expr onOperatorApplication(Operator operator, Expr lhs, Expr rhs) { return Expr.makeOperatorApplication(operator, lhs, rhs); } public Expr onIf(Expr predicate, Expr thenValue, Expr elseValue) { return Expr.makeIf(predicate, thenValue, elseValue); } public Expr onAnnotated(Expr base, Expr type) { return Expr.makeAnnotated(base, type); } public Expr onAssert(Expr base) { return Expr.makeAssert(base); } public Expr onMerge(Expr handlers, Expr union, Expr type) { return Expr.makeMerge(handlers, union, type); } public Expr onToMap(Expr base, Expr type) { return Expr.makeToMap(base, type); } public Expr onWith(Expr base, String[] path, Expr value) { return Expr.makeWith(base, path, value); } public Expr onMissingImport(Expr.ImportMode mode, byte[] hash) { return Expr.makeMissingImport(mode, hash); } public Expr onEnvImport(String value, Expr.ImportMode mode, byte[] hash) { return Expr.makeEnvImport(value, mode, hash); } public Expr onLocalImport(Path path, Expr.ImportMode mode, byte[] hash) { return Expr.makeLocalImport(path, mode, hash); } public Expr onClasspathImport(Path path, Expr.ImportMode mode, byte[] hash) { return Expr.makeClasspathImport(path, mode, hash); } public Expr onRemoteImport(URI url, Expr using, Expr.ImportMode mode, byte[] hash) { return Expr.makeRemoteImport(url, using, mode, hash); } } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/binary/CborDecodingVisitor.java ================================================ package org.dhallj.core.binary; import org.dhallj.cbor.Reader; import org.dhallj.cbor.Visitor; import org.dhallj.core.Expr; import org.dhallj.core.Operator; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * Decodes CBOR expressions corresponding to encoded Dhall expressions. * *

Note that e.g. a negative integer by itself is an error, but a single float by itself is * allowed. */ final class CborDecodingVisitor implements Visitor { private final Reader reader; CborDecodingVisitor(Reader reader) { this.reader = reader; } @Override public Expr onUnsignedInteger(BigInteger value) { return Expr.makeIdentifier("_", value.longValue()); } @Override public Expr onNegativeInteger(BigInteger value) { return notExpected("Negative integer " + value); } @Override public Expr onByteString(byte[] value) { return notExpected("ByteString"); } @Override public Expr onTextString(String value) { return Expr.makeBuiltIn(value); } @Override public Expr onVariableArray(BigInteger length, String name) { if (length.intValue() != 2) { throw new DecodingException("Variables must be encoded in an array of length 2"); } else if (name.equals("_")) { throw new DecodingException("Variables cannot be explicitly named _"); } else { BigInteger idx = this.reader.readPositiveBigNum(); return Expr.makeIdentifier(name, idx.longValue()); } } @Override public Expr onArray(BigInteger length, BigInteger tagI) { int tag = tagI.intValue(); switch (tag) { case Label.APPLICATION: return readFnApplication(length); case Label.LAMBDA: return readFunction(length); case Label.PI: return readPi(length); case Label.OPERATOR_APPLICATION: return readOperator(length); case Label.LIST: return readList(length); case Label.SOME: return readSome(length); case Label.MERGE: return readMerge(length); case Label.RECORD_TYPE: return readRecordType(length); case Label.RECORD_LITERAL: return readRecordLiteral(length); case Label.FIELD_ACCESS: return readFieldAccess(length); case Label.PROJECTION: return readProjection(length); case Label.UNION_TYPE: return readUnion(length); case Label.IF: return readIf(length); case Label.NATURAL: return readNatural(length); case Label.INTEGER: return readInteger(length); case Label.TEXT: return readTextLiteral(length); case Label.ASSERT: return readAssert(length); case Label.IMPORT: return readImport(length); case Label.LET: return readLet(length); case Label.ANNOTATED: return readTypeAnnotation(length); case Label.TO_MAP: return readMap(length); case Label.EMPTY_LIST_WITH_ABSTRACT_TYPE: return readEmptyListAbstractType(length); case Label.DATE: return readDate(length); case Label.TIME: return readTime(length); case Label.TIME_ZONE: return readTimeZone(length); default: throw new DecodingException("Array tag " + Integer.toString(tag) + " undefined"); } } @Override public Expr onMap(BigInteger size) { return notExpected("Map"); } @Override public Expr onFalse() { return Expr.Constants.FALSE; } @Override public Expr onTrue() { return Expr.Constants.TRUE; } @Override public Expr onNull() { return null; } @Override public Expr onHalfFloat(float value) { return Expr.makeDoubleLiteral(value); } @Override public Expr onSingleFloat(float value) { return Expr.makeDoubleLiteral(value); } @Override public Expr onDoubleFloat(double value) { return Expr.makeDoubleLiteral(value); } @Override public Expr onTag() { // TODO return notExpected("Tag"); } private Expr readFnApplication(BigInteger length) { if (length.longValue() < 3) { throw new DecodingException("Function application must have at least one argument"); } Expr fn = readExpr(); ArrayList args = new ArrayList<>(); for (int i = 0; i < length.longValue() - 2; i++) { Expr arg = readExpr(); args.add(arg); } return Expr.makeApplication(fn, args); } private Expr readFunction(BigInteger length) { long len = length.longValue(); if (len == 3) { Expr tpe = readExpr(); Expr result = readExpr(); return Expr.makeLambda("_", tpe, result); } else if (len == 4) { String param = this.reader.readNullableTextString(); if (param.equals("_")) { throw new DecodingException(("Illegal explicit bound variable '_' in function")); } Expr tpe = readExpr(); Expr resultTpe = readExpr(); return Expr.makeLambda(param, tpe, resultTpe); } else { throw new DecodingException("Function types must be encoded in an array of length 3 or 4"); } } private Expr readPi(BigInteger length) { long len = length.longValue(); if (len == 3) { Expr tpe = readExpr(); Expr resultTpe = readExpr(); return Expr.makePi(tpe, resultTpe); } else if (len == 4) { String param = this.reader.readNullableTextString(); if (param.equals("_")) { throw new DecodingException(("Illegal explicit bound variable '_' in pi type")); } Expr tpe = readExpr(); Expr resultTpe = readExpr(); return Expr.makePi(param, tpe, resultTpe); } else { throw new DecodingException("Pi types must be encoded in an array of length 3 or 4"); } } private Expr readOperator(BigInteger length) { if (length.longValue() != 4) { throw new DecodingException("Operator application must be encoded in an array of length 4"); } int operatorLabel = this.reader.readUnsignedInteger().intValue(); Expr lhs = readExpr(); Expr rhs = readExpr(); Operator operator = Operator.fromLabel(operatorLabel); if (operator != null) { return Expr.makeOperatorApplication(operator, lhs, rhs); } else { throw new DecodingException( "Operator tag " + Integer.toString(operatorLabel) + " is undefined"); } } private Expr readList(BigInteger length) { Expr tpe = readExpr(); if (length.intValue() == 2) { if (tpe == null) { throw new DecodingException("Type must be specified if list is empty"); } else { return Expr.makeEmptyListLiteral(Expr.makeApplication(Expr.Constants.LIST, tpe)); } } else { if (tpe == null) { List exprs = new ArrayList<>(); for (int i = 2; i < length.longValue(); i++) { exprs.add(readExpr()); } return Expr.makeNonEmptyListLiteral(exprs); } else { throw new DecodingException("Non-empty lists must not have a type annotation"); } } } private Expr readEmptyListAbstractType(BigInteger length) { Expr tpe = readExpr(); if (length.intValue() == 2) { if (tpe == null) { throw new DecodingException("Type must be specified if list is empty"); } else { return Expr.makeEmptyListLiteral(tpe); } } else { throw new DecodingException("List of abstract type must be empty"); } } private Expr readSome(BigInteger length) { int len = length.intValue(); if (len != 3) { throw new DecodingException("Some must be encoded in an array of length 3"); } else { // The spec currently says "Some expressions store the type (if present) and their value", but // we ignore the type, and (I think) it should always be null. readExpr(); Expr value = readExpr(); return Expr.makeApplication(Expr.Constants.SOME, value); } } private Expr readMerge(BigInteger length) { int len = length.intValue(); if (len == 3) { Expr l = readExpr(); Expr r = readExpr(); return Expr.makeMerge(l, r); } else if (len == 4) { Expr l = readExpr(); Expr r = readExpr(); Expr tpe = readExpr(); return Expr.makeMerge(l, r, tpe); } else { throw new DecodingException("Merge must be encoded in an array of length 3 or 4"); } } private Expr readMap(BigInteger length) { int len = length.intValue(); if (len == 2) { Expr e = readExpr(); return Expr.makeToMap(e); } else if (len == 3) { Expr e = readExpr(); Expr tpe = readExpr(); return Expr.makeToMap(e, tpe); } else { throw new DecodingException("ToMap must be encoded in an array of length 2 or 3"); } } private Expr readWith(BigInteger length) { int len = length.intValue(); if (len == 4) { Expr base = readExpr(); int pathLen = this.reader.readArrayStart().intValue(); String[] path = new String[pathLen]; for (int i = 0; i < pathLen; i += 1) { path[i] = this.reader.readNullableTextString(); } Expr value = readExpr(); return Expr.makeWith(base, path, value); } else { throw new DecodingException("with must be encoded in an array of length 4"); } } private Expr readRecordType(BigInteger length) { long len = length.longValue(); if (len != 2) { throw new DecodingException("Record literal must be encoded in an array of length 2"); } else { Map entries = this.reader.readMap(this); return Expr.makeRecordType(entries.entrySet()); } } private Expr readRecordLiteral(BigInteger length) { long len = length.longValue(); if (len != 2) { throw new DecodingException("Record literal must be encoded in an array of length 2"); } else { Map entries = this.reader.readMap(this); return Expr.makeRecordLiteral(entries.entrySet()); } } private Expr readFieldAccess(BigInteger length) { int len = length.intValue(); if (len != 3) { throw new DecodingException("Field access must be encoded in array of length 3"); } else { Expr e = readExpr(); String field = this.reader.readNullableTextString(); return Expr.makeFieldAccess(e, field); } } private Expr readProjection(BigInteger length) { long len = length.longValue(); Expr e = readExpr(); if (len == 2) { return Expr.makeProjection(e, new String[0]); } else { // This is horrible but so is the encoding of record projections - we don't know whether // we expect an array or Strings next String first = this.reader.tryReadTextString(); if (first != null) { List fields = new ArrayList<>(); fields.add(first); for (int i = 3; i < len; i++) { fields.add(this.reader.tryReadTextString()); } return Expr.makeProjection(e, fields.toArray(new String[fields.size()])); } else { // It was actually an array int innerLen = this.reader.readArrayStart().intValue(); if (innerLen != 1) { throw new DecodingException( "Type for type projection must be encoded in an array of length 1"); } else { Expr tpe = readExpr(); return Expr.makeProjectionByType(e, tpe); } } } } private Expr readUnion(BigInteger length) { int len = length.intValue(); if (len != 2) { throw new DecodingException("Union must be encoded in array of length 2"); } else { Map entries = this.reader.readMap(this); return Expr.makeUnionType(entries.entrySet()); } } private Expr readIf(BigInteger length) { int len = length.intValue(); if (len != 4) { throw new DecodingException("If must be encoded in an array of length 4"); } else { Expr cond = readExpr(); Expr ifE = readExpr(); Expr elseE = readExpr(); return Expr.makeIf(cond, ifE, elseE); } } private Expr readTypeAnnotation(BigInteger length) { int len = length.intValue(); if (len != 3) { throw new DecodingException("Type annotation must be encoded in array of length 3"); } else { Expr e = readExpr(); Expr tpe = readExpr(); return Expr.makeAnnotated(e, tpe); } } private Expr readLet(BigInteger length) { return readLet(length.longValue()); } private Expr readLet(long len) { if (len == 5) { String name = this.reader.readNullableTextString(); Expr tpe = readExpr(); Expr value = readExpr(); Expr body = readExpr(); return Expr.makeLet(name, tpe, value, body); } else { String name = this.reader.readNullableTextString(); Expr tpe = readExpr(); Expr value = readExpr(); return Expr.makeLet(name, tpe, value, readLet(len - 3)); } } private Expr readImport(BigInteger length) { byte[] hash = this.reader.readNullableByteString(); Expr.ImportMode mode = readMode(); int tag = this.reader.readUnsignedInteger().intValue(); switch (tag) { case Label.IMPORT_TYPE_REMOTE_HTTP: Expr httpUsing = readExpr(); return readRemoteImport(length, mode, hash, "http:/", httpUsing); case Label.IMPORT_TYPE_REMOTE_HTTPS: Expr httpsUsing = readExpr(); return readRemoteImport(length, mode, hash, "https:/", httpsUsing); case Label.IMPORT_TYPE_LOCAL_ABSOLUTE: return readLocalImport(length, mode, hash, "/"); case Label.IMPORT_TYPE_LOCAL_HERE: return readLocalImport(length, mode, hash, "./"); case Label.IMPORT_TYPE_LOCAL_PARENT: return readLocalImport(length, mode, hash, "../"); case Label.IMPORT_TYPE_LOCAL_HOME: return readLocalImport(length, mode, hash, "~"); case Label.IMPORT_TYPE_ENV: return readEnvImport(length, mode, hash); case Label.IMPORT_TYPE_MISSING: return Expr.makeMissingImport(mode, hash); case Label.IMPORT_TYPE_CLASSPATH: return readClasspathImport(length, mode, hash, "/"); default: throw new DecodingException("Import type " + Integer.toString(tag) + " is undefined"); } } private Expr.ImportMode readMode() { int m = this.reader.readUnsignedInteger().intValue(); if (m == 0) { return Expr.ImportMode.CODE; } else if (m == 1) { return Expr.ImportMode.RAW_TEXT; } else if (m == 2) { return Expr.ImportMode.LOCATION; } else { throw new DecodingException("Import mode " + Integer.toString(m) + " is undefined"); } } private Expr readLocalImport( BigInteger length, Expr.ImportMode mode, byte[] hash, String prefix) { Path path = Paths.get(prefix); int len = length.intValue(); for (int i = 4; i < len; i++) { path = path.resolve(this.reader.readNullableTextString()); } return Expr.makeLocalImport(path, mode, hash); } private Expr readClasspathImport( BigInteger length, Expr.ImportMode mode, byte[] hash, String prefix) { Path path = Paths.get(prefix); int len = length.intValue(); for (int i = 4; i < len; i++) { path = path.resolve(this.reader.readNullableTextString()); } return Expr.makeClasspathImport(path, mode, hash); } private Expr readRemoteImport( BigInteger length, Expr.ImportMode mode, byte[] hash, String prefix, Expr using) { StringBuilder builder = new StringBuilder(prefix); int len = length.intValue(); for (int i = 5; i < len - 1; i++) { builder.append("/"); builder.append(this.reader.readNullableTextString()); } String query = this.reader.readNullableTextString(); if (query != null) { builder.append("?"); builder.append(query); } try { return Expr.makeRemoteImport(new URI(builder.toString()), using, mode, hash); } catch (URISyntaxException cause) { throw new DecodingException("Invalid URL in remote import", cause); } } private Expr readEnvImport(BigInteger length, Expr.ImportMode mode, byte[] hash) { String value = this.reader.readNullableTextString(); return Expr.makeEnvImport(value, mode, hash); } private Expr readAssert(BigInteger length) { long len = length.longValue(); if (len != 2) { throw new DecodingException("Assert must be encoded in array of length 2"); } else { Expr e = readExpr(); return Expr.makeAssert(e); } } private Expr readTextLiteral(BigInteger length) { List lits = new ArrayList<>(); List exprs = new ArrayList<>(); String lit = this.reader.readNullableTextString(); lits.add(lit); for (int i = 2; i < length.longValue(); i += 2) { Expr e = readExpr(); exprs.add(e); lit = this.reader.readNullableTextString(); lits.add(lit); } return Expr.makeTextLiteral(lits.toArray(new String[0]), exprs.toArray(new Expr[0])); } private Expr readInteger(BigInteger length) { return Expr.makeIntegerLiteral(this.reader.readBigNum()); } private Expr readNatural(BigInteger length) { return Expr.makeNaturalLiteral(this.reader.readPositiveBigNum()); } private Expr readDate(BigInteger length) { long len = length.longValue(); if (len != 4) { throw new DecodingException("Date must be encoded in array of length 4"); } else { BigInteger year = this.reader.readUnsignedInteger(); BigInteger month = this.reader.readUnsignedInteger(); BigInteger day = this.reader.readUnsignedInteger(); return Expr.makeDateLiteral(year.intValue(), month.intValue(), day.intValue()); } } private Expr readTime(BigInteger length) { long len = length.longValue(); if (len != 4) { throw new DecodingException("Time zone must be encoded in array of length 4"); } else { BigInteger hour = this.reader.readUnsignedInteger(); BigInteger minute = this.reader.readUnsignedInteger(); BigDecimal rawSeconds = this.reader.readBigDecimal(); int seconds = rawSeconds.intValue(); BigDecimal fractional = rawSeconds.subtract(new BigDecimal(seconds)); return Expr.makeTimeLiteral(hour.intValue(), minute.intValue(), seconds, fractional); } } private Expr readTimeZone(BigInteger length) { long len = length.longValue(); if (len != 4) { throw new DecodingException("Time zone must be encoded in array of length 4"); } else { boolean positive = readExpr() != Expr.Constants.FALSE; BigInteger hour = this.reader.readUnsignedInteger(); BigInteger minute = this.reader.readUnsignedInteger(); int value = hour.intValue() * 60 + minute.intValue(); if (!positive) { value = -value; } return Expr.makeTimeZoneLiteral(value); } } private Expr readExpr() { return this.reader.nextSymbol(this); } private Expr notExpected(String msg) { throw new DecodingException( "Expected String or UnsignedInteger as first element of array. Found " + msg); } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/binary/Decode.java ================================================ package org.dhallj.core.binary; import org.dhallj.cbor.Reader; import org.dhallj.core.Expr; public class Decode { public static final Expr decode(byte[] bytes) { Reader reader = new Reader.ByteArrayReader(bytes); // TODO check: if identifier then must be builtin using Expr.Constants.isBuiltInConstant Expr e = reader.nextSymbol(new CborDecodingVisitor(reader)); return e; } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/binary/DecodingException.java ================================================ package org.dhallj.core.binary; import org.dhallj.core.DhallException; public class DecodingException extends DhallException { public DecodingException(String message) { super(message); } public DecodingException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/binary/Encode.java ================================================ package org.dhallj.core.binary; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URI; import java.nio.file.Path; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import org.dhallj.cbor.Writer; import org.dhallj.core.Expr; import org.dhallj.core.Operator; import org.dhallj.core.Source; import org.dhallj.core.Visitor; public final class Encode implements Visitor { private final Writer writer; public Encode(Writer writer) { this.writer = writer; } public boolean sortFields() { return true; } public boolean flattenToMapLists() { return false; } public Void onNote(Void base, Source source) { return base; } public Void onNatural(Expr self, BigInteger value) { this.writer.writeArrayStart(2); this.writer.writeLong(Label.NATURAL); this.writer.writeBigInteger(value); return null; } public Void onInteger(Expr self, BigInteger value) { this.writer.writeArrayStart(2); this.writer.writeLong(Label.INTEGER); this.writer.writeBigInteger(value); return null; } public Void onDouble(Expr self, double value) { this.writer.writeDouble(value); return null; } public Void onDate(Expr self, int year, int month, int day) { this.writer.writeArrayStart(4); this.writer.writeLong(Label.DATE); this.writer.writeLong(year); this.writer.writeLong(month); this.writer.writeLong(day); return null; } public Void onTime(Expr self, int hour, int minute, int second, BigDecimal fractional) { this.writer.writeArrayStart(4); this.writer.writeLong(Label.TIME); this.writer.writeLong(hour); this.writer.writeLong(minute); this.writer.writeBigDecimal(fractional.add(BigDecimal.valueOf(second))); return null; } public Void onTimeZone(Expr self, int minutes) { boolean sign = minutes >= 0; this.writer.writeArrayStart(4); this.writer.writeLong(Label.TIME_ZONE); this.writer.writeBoolean(sign); int m = Math.abs(minutes); this.writer.writeLong(m / 60); this.writer.writeLong(m % 60); return null; } public Void onBuiltIn(Expr self, String name) { if (name.equals("True")) { this.writer.writeBoolean(true); } else if (name.equals("False")) { this.writer.writeBoolean(false); } else { this.writer.writeString(name); } return null; } public Void onIdentifier(Expr self, String name, long index) { if (name.equals("_")) { this.writer.writeLong(index); } else { this.writer.writeArrayStart(2); this.writer.writeString(name); this.writer.writeLong(index); } return null; } public void bind(String param, Expr type) {} public boolean prepareLambda(String name, Expr type) { if (name.equals("_")) { this.writer.writeArrayStart(3); this.writer.writeLong(Label.LAMBDA); } else { this.writer.writeArrayStart(4); this.writer.writeLong(Label.LAMBDA); this.writer.writeString(name); } return true; } public Void onLambda(String name, Void type, Void result) { return null; } public boolean preparePi(String name, Expr type) { if (name.equals("_")) { this.writer.writeArrayStart(3); this.writer.writeLong(Label.PI); } else { this.writer.writeArrayStart(4); this.writer.writeLong(Label.PI); this.writer.writeString(name); } return true; } public Void onPi(String name, Void type, Void result) { return null; } public boolean prepareLet(int size) { this.writer.writeArrayStart(2 + size * 3); this.writer.writeLong(Label.LET); return true; } public boolean prepareLetBinding(String name, Expr type) { this.writer.writeString(name); if (type == null) { this.writer.writeNull(); } return true; } public Void onLet(List> bindings, Void body) { return null; } private static final String unescapeText(String input) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < input.length(); i += 1) { if (input.charAt(i) == '\\') { i += 1; char next = input.charAt(i); if (next == '"') { builder.append('"'); } else if (next == '\\') { builder.append('\\'); } else if (next == 'b') { builder.append('\b'); } else if (next == 'f') { builder.append('\f'); } else if (next == 'n') { builder.append('\n'); } else if (next == 'r') { builder.append('\r'); } else if (next == 't') { builder.append('\t'); } else { builder.append('\\'); builder.append(next); } } else { builder.append(input.charAt(i)); } } return builder.toString(); } public boolean prepareText(int size) { this.writer.writeArrayStart(size * 2); this.writer.writeLong(Label.TEXT); return true; } public boolean prepareTextPart(String part) { this.writer.writeString(unescapeText(part)); return true; } public Void onText(String[] parts, List interpolated) { return null; } public boolean prepareNonEmptyList(int size) { this.writer.writeArrayStart(size + 2); this.writer.writeLong(Label.LIST); this.writer.writeNull(); return true; } public boolean prepareNonEmptyListElement(int index) { return true; } public Void onNonEmptyList(final List values) { return null; } public boolean prepareEmptyList(Expr type) { final Expr listElementType = Expr.Util.getListArg(type); this.writer.writeArrayStart(2); this.writer.writeLong( (listElementType != null) ? Label.LIST : Label.EMPTY_LIST_WITH_ABSTRACT_TYPE); if (listElementType != null) { // We have to recurse explicitly. listElementType.accept(this); return false; } else { return true; } } public Void onEmptyList(Void type) { return null; } public boolean prepareRecord(int size) { this.writer.writeArrayStart(2); this.writer.writeLong(Label.RECORD_LITERAL); this.writer.writeMapStart(size); return true; } public boolean prepareRecordField(String name, Expr type, int index) { this.writer.writeString(name); return true; } public Void onRecord(final List> fields) { return null; } public boolean prepareRecordType(int size) { this.writer.writeArrayStart(2); this.writer.writeLong(Label.RECORD_TYPE); this.writer.writeMapStart(size); return true; } public boolean prepareRecordTypeField(String name, Expr type, int index) { this.writer.writeString(name); return true; } public Void onRecordType(final List> fields) { return null; } public boolean prepareUnionType(int size) { this.writer.writeArrayStart(2); this.writer.writeLong(Label.UNION_TYPE); this.writer.writeMapStart(size); return true; } public boolean prepareUnionTypeField(String name, Expr type, int index) { this.writer.writeString(name); if (type == null) { this.writer.writeNull(); } return true; } public Void onUnionType(final List> fields) { return null; } public boolean prepareFieldAccess(Expr base, String fieldName) { this.writer.writeArrayStart(3); this.writer.writeLong(Label.FIELD_ACCESS); return true; } public Void onFieldAccess(Void base, final String fieldName) { this.writer.writeString(fieldName); return null; } public boolean prepareProjection(int size) { this.writer.writeArrayStart(size + 2); this.writer.writeLong(Label.PROJECTION); return true; } public Void onProjection(Void base, final String[] fieldNames) { for (String fieldName : fieldNames) { this.writer.writeString(fieldName); } return null; } public boolean prepareProjectionByType() { this.writer.writeArrayStart(3); this.writer.writeLong(Label.PROJECTION); return true; } public boolean prepareProjectionByType(Expr type) { this.writer.writeArrayStart(1); return true; } public Void onProjectionByType(Void base, Void type) { return null; } public boolean prepareApplication(Expr base, int size) { String asBuiltIn = Expr.Util.asBuiltIn(base); if (asBuiltIn != null && asBuiltIn.equals("Some")) { /** * This is a kind of weird case that I don't think should ever occur in well-typed code, but * we need this special casing to pass the {@code SomeXYZ} parser test. */ if (size > 1) { this.writer.writeArrayStart(size + 1); this.writer.writeLong(Label.APPLICATION); } this.writer.writeArrayStart(3); this.writer.writeLong(Label.SOME); this.writer.writeNull(); return false; } else { this.writer.writeArrayStart(size + 2); this.writer.writeLong(Label.APPLICATION); return true; } } public Void onApplication(Void base, final List args) { return null; } public boolean prepareOperatorApplication(final Operator operator) { this.writer.writeArrayStart(4); this.writer.writeLong(Label.OPERATOR_APPLICATION); this.writer.writeLong(operator.getLabel()); return true; } public Void onOperatorApplication(Operator operator, Void lhs, Void rhs) { return null; } public boolean prepareIf() { this.writer.writeArrayStart(4); this.writer.writeLong(Label.IF); return true; } public Void onIf(Void predicate, Void thenValue, Void elseValue) { return null; } public boolean prepareAnnotated(Expr type) { this.writer.writeArrayStart(3); this.writer.writeLong(Label.ANNOTATED); return true; } public Void onAnnotated(Void base, Void type) { return null; } public boolean prepareAssert() { this.writer.writeArrayStart(2); this.writer.writeLong(Label.ASSERT); return true; } public Void onAssert(Void base) { return null; } public boolean prepareMerge(Expr type) { this.writer.writeArrayStart((type == null) ? 3 : 4); this.writer.writeLong(Label.MERGE); return true; } public Void onMerge(Void handlers, Void union, Void type) { return null; } public boolean prepareToMap(Expr type) { this.writer.writeArrayStart((type == null) ? 2 : 3); this.writer.writeLong(Label.TO_MAP); return true; } public Void onToMap(Void base, Void type) { return null; } public boolean prepareWith(String[] path) { this.writer.writeArrayStart(4); this.writer.writeLong(Label.WITH); return true; } public boolean prepareWithValue(String[] path) { this.writer.writeArrayStart(path.length); for (int i = 0; i < path.length; i += 1) { this.writer.writeString(path[i]); } return true; } public Void onWith(Void base, String[] path, Void value) { return null; } private final int modeLabel(Expr.ImportMode mode) { if (mode.equals(Expr.ImportMode.RAW_TEXT)) { return 1; } else if (mode.equals(Expr.ImportMode.LOCATION)) { return 2; } else { return 0; } } private static final byte[] multihash(byte[] hash) { byte[] bytes = new byte[34]; // The label for SHA-256. bytes[0] = 18; // The digest size. bytes[1] = 32; System.arraycopy(hash, 0, bytes, 2, 32); return bytes; } public Void onMissingImport(final Expr.ImportMode mode, final byte[] hash) { this.writer.writeArrayStart(4); this.writer.writeLong(Label.IMPORT); if (hash == null) { this.writer.writeNull(); } else { this.writer.writeByteString(multihash(hash)); } this.writer.writeLong(modeLabel(mode)); this.writer.writeLong(Label.IMPORT_TYPE_MISSING); return null; } public Void onEnvImport(final String value, final Expr.ImportMode mode, final byte[] hash) { this.writer.writeArrayStart(5); this.writer.writeLong(Label.IMPORT); if (hash == null) { this.writer.writeNull(); } else { this.writer.writeByteString(multihash(hash)); } this.writer.writeLong(modeLabel(mode)); this.writer.writeLong(Label.IMPORT_TYPE_ENV); this.writer.writeString(value); return null; } private static final int pathLabel(Path path) { if (path.isAbsolute()) { return Label.IMPORT_TYPE_LOCAL_ABSOLUTE; } else { String first = path.iterator().next().toString(); if (first.equals(".")) { return Label.IMPORT_TYPE_LOCAL_HERE; } else if (first.equals("..")) { return Label.IMPORT_TYPE_LOCAL_PARENT; } else if (first.equals("~")) { return Label.IMPORT_TYPE_LOCAL_HOME; } } return -1; } public Void onLocalImport(final Path path, final Expr.ImportMode mode, final byte[] hash) { int size = 4 + path.getNameCount() - (path.isAbsolute() ? 0 : 1); this.writer.writeArrayStart(size); this.writer.writeLong(Label.IMPORT); if (hash == null) { this.writer.writeNull(); } else { this.writer.writeByteString(multihash(hash)); } this.writer.writeLong(modeLabel(mode)); this.writer.writeLong(pathLabel(path)); Iterator parts = path.iterator(); if (!path.isAbsolute()) { parts.next(); } while (parts.hasNext()) { this.writer.writeString(parts.next().toString()); } return null; } @Override public Void onClasspathImport(Path path, Expr.ImportMode mode, byte[] hash) { int size = 4 + path.getNameCount(); this.writer.writeArrayStart(size); this.writer.writeLong(Label.IMPORT); if (hash == null) { this.writer.writeNull(); } else { this.writer.writeByteString(multihash(hash)); } this.writer.writeLong(modeLabel(mode)); this.writer.writeLong(Label.IMPORT_TYPE_CLASSPATH); Iterator parts = path.iterator(); while (parts.hasNext()) { this.writer.writeString(parts.next().toString()); } return null; } private static final int urlLabel(URI url) { if (url.getScheme().equals("https")) { return Label.IMPORT_TYPE_REMOTE_HTTPS; } else if (url.getScheme().equals("http")) { return Label.IMPORT_TYPE_REMOTE_HTTP; } else { return -1; } } private static final List getUrlPathParts(URI url) { final List parts = new ArrayList(); // TODO: verify that we don't need the raw versions here (currently escaped octets are decoded). parts.add(url.getAuthority()); String[] pathParts = url.getPath().split("/"); if (pathParts.length == 1) { parts.add(""); } else { for (int i = 1; i < pathParts.length; i++) { parts.add(pathParts[i]); } } return parts; } public boolean prepareRemoteImport(URI url, Expr using, Expr.ImportMode mode, byte[] hash) { final List parts = getUrlPathParts(url); this.writer.writeArrayStart(parts.size() + 6); this.writer.writeLong(Label.IMPORT); if (hash == null) { this.writer.writeNull(); } else { this.writer.writeByteString(multihash(hash)); } this.writer.writeLong(modeLabel(mode)); this.writer.writeLong(urlLabel(url)); if (using == null) { this.writer.writeNull(); } return true; } public Void onRemoteImport(URI url, Void using, Expr.ImportMode mode, byte[] hash) { List parts = getUrlPathParts(url); for (String part : parts) { this.writer.writeString(part); } if (url.getQuery() == null) { this.writer.writeNull(); } else { this.writer.writeString(url.getQuery()); } return null; } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/binary/Label.java ================================================ package org.dhallj.core.binary; final class Label { public static final int APPLICATION = 0; public static final int LAMBDA = 1; public static final int PI = 2; public static final int OPERATOR_APPLICATION = 3; public static final int LIST = 4; public static final int SOME = 5; public static final int MERGE = 6; public static final int RECORD_TYPE = 7; public static final int RECORD_LITERAL = 8; public static final int FIELD_ACCESS = 9; public static final int PROJECTION = 10; public static final int UNION_TYPE = 11; public static final int IF = 14; public static final int NATURAL = 15; public static final int INTEGER = 16; public static final int TEXT = 18; public static final int ASSERT = 19; public static final int IMPORT = 24; public static final int LET = 25; public static final int ANNOTATED = 26; public static final int TO_MAP = 27; public static final int EMPTY_LIST_WITH_ABSTRACT_TYPE = 28; public static final int WITH = 29; public static final int DATE = 30; public static final int TIME = 31; public static final int TIME_ZONE = 32; public static final int IMPORT_TYPE_REMOTE_HTTP = 0; public static final int IMPORT_TYPE_REMOTE_HTTPS = 1; public static final int IMPORT_TYPE_LOCAL_ABSOLUTE = 2; public static final int IMPORT_TYPE_LOCAL_HERE = 3; public static final int IMPORT_TYPE_LOCAL_PARENT = 4; public static final int IMPORT_TYPE_LOCAL_HOME = 5; public static final int IMPORT_TYPE_ENV = 6; public static final int IMPORT_TYPE_MISSING = 7; // Allows CBOR tiny field encoding but leaves room for Dhall spec to expand import types public static final int IMPORT_TYPE_CLASSPATH = 23; } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/converters/JsonConverter.java ================================================ package org.dhallj.core.converters; import java.math.BigInteger; import java.util.List; import java.util.Map.Entry; import org.dhallj.core.Expr; import org.dhallj.core.Visitor; public final class JsonConverter extends Visitor.Constant { private final JsonHandler handler; private final boolean escapeStrings; public JsonConverter(JsonHandler handler, boolean escapeStrings) { super(false); this.handler = handler; this.escapeStrings = escapeStrings; } public JsonConverter(JsonHandler handler) { this(handler, true); } public static final String toCompactString(Expr expr) { JsonHandler.CompactStringPrinter handler = new JsonHandler.CompactStringPrinter(); boolean isConverted = expr.accept(new JsonConverter(handler)); if (isConverted) { return handler.toString(); } else { return null; } } private static final String escape(String input) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < input.length(); i++) { char c = input.charAt(i); if (c == '\\') { char next = input.charAt(++i); if (next == '"') { builder.append("\\\""); } else if (next == '$') { builder.append("$"); } else { builder.append(c); builder.append(next); } } else if (c == '"') { builder.append("\\\""); } else { builder.append(c); } } return builder.toString(); } @Override public boolean sortFields() { return false; } @Override public boolean flattenToMapLists() { return true; } @Override public Boolean onNatural(Expr self, BigInteger value) { this.handler.onNumber(value); return true; } @Override public Boolean onInteger(Expr self, BigInteger value) { this.handler.onNumber(value); return true; } @Override public Boolean onDouble(Expr self, double value) { this.handler.onDouble(value); return true; } @Override public Boolean onBuiltIn(Expr self, String name) { if (name.equals("True")) { this.handler.onBoolean(true); return true; } else if (name.equals("False")) { this.handler.onBoolean(false); return true; } else { return false; } } @Override public Boolean onText(String[] parts, List interpolated) { if (parts.length == 1) { if (this.escapeStrings) { this.handler.onString(escape(parts[0])); } else { this.handler.onString(parts[0]); } return true; } else { return false; } } @Override public boolean prepareNonEmptyList(int size) { this.handler.onArrayStart(); return true; } @Override public boolean prepareNonEmptyListElement(int index) { if (index > 0) { this.handler.onArrayElementGap(); } return true; } @Override public Boolean onNonEmptyList(List values) { for (boolean value : values) { if (!value) { return false; } } this.handler.onArrayEnd(); return true; } @Override public Boolean onEmptyList(Boolean type) { this.handler.onArrayStart(); this.handler.onArrayEnd(); return true; } @Override public boolean prepareRecord(int size) { this.handler.onObjectStart(); return true; } @Override public boolean prepareRecordField(String name, Expr type, int index) { if (index > 0) { this.handler.onObjectFieldGap(); } if (this.escapeStrings) { this.handler.onObjectField(escape(name)); } else { this.handler.onObjectField(name); } return true; } @Override public Boolean onRecord(final List> fields) { for (Entry field : fields) { if (!field.getValue()) { return false; } } this.handler.onObjectEnd(); return true; } @Override public boolean prepareFieldAccess(Expr base, String fieldName) { List> asUnion = Expr.Util.asUnionType(base); if (asUnion != null) { for (Entry field : asUnion) { if (field.getKey().equals(fieldName) && field.getValue() == null) { this.handler.onString(escape(fieldName)); return false; } } } return true; } @Override public Boolean onFieldAccess(Boolean base, String fieldName) { return base == null || base; } @Override public boolean prepareApplication(Expr base, int size) { String asBuiltIn = Expr.Util.asBuiltIn(base); if (asBuiltIn != null && size == 1) { if (asBuiltIn.equals("Some")) { return false; } else if (asBuiltIn.equals("None")) { this.handler.onNull(); return false; } } else { Entry asFieldAccess = Expr.Util.asFieldAccess(base); if (asFieldAccess != null) { List> asUnion = Expr.Util.asUnionType(asFieldAccess.getKey()); if (asUnion != null) { for (Entry field : asUnion) { if (field.getKey().equals(asFieldAccess.getValue()) && field.getValue() != null) { return false; } } } } } return true; } @Override public Boolean onApplication(Boolean base, List args) { return base == null || base; } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/converters/JsonHandler.java ================================================ package org.dhallj.core.converters; import java.io.PrintWriter; import java.math.BigInteger; public interface JsonHandler { void onNull(); void onBoolean(boolean value); void onNumber(BigInteger value); void onDouble(double value); void onString(String value); void onArrayStart(); void onArrayEnd(); void onArrayElementGap(); void onObjectStart(); void onObjectEnd(); void onObjectField(String name); void onObjectFieldGap(); public static final class CompactPrinter implements JsonHandler { private final PrintWriter writer; public CompactPrinter(PrintWriter writer) { this.writer = writer; } public void onNull() { this.writer.print("null"); } public void onBoolean(boolean value) { this.writer.print(value); } public void onNumber(BigInteger value) { this.writer.print(value.toString()); } public void onDouble(double value) { this.writer.print(value); } public void onString(String value) { this.writer.printf("\"%s\"", value); } public void onArrayStart() { this.writer.print("["); } public void onArrayEnd() { this.writer.print("]"); } public void onArrayElementGap() { this.writer.print(","); } public void onObjectStart() { this.writer.print("{"); } public void onObjectEnd() { this.writer.print("}"); } public void onObjectField(String name) { this.writer.printf("\"%s\":", name); } public void onObjectFieldGap() { this.writer.print(","); } } public static final class CompactStringPrinter implements JsonHandler { private final StringBuilder builder; public CompactStringPrinter() { this.builder = new StringBuilder(); } public String toString() { return this.builder.toString(); } public void onNull() { this.builder.append("null"); } public void onBoolean(boolean value) { this.builder.append(value); } public void onNumber(BigInteger value) { this.builder.append(value.toString()); } public void onDouble(double value) { this.builder.append(value); } public void onString(String value) { this.builder.append(String.format("\"%s\"", value)); } public void onArrayStart() { this.builder.append("["); } public void onArrayEnd() { this.builder.append("]"); } public void onArrayElementGap() { this.builder.append(","); } public void onObjectStart() { this.builder.append("{"); } public void onObjectEnd() { this.builder.append("}"); } public void onObjectField(String name) { this.builder.append(String.format("\"%s\":", name)); } public void onObjectFieldGap() { this.builder.append(","); } } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/normalization/AlphaNormalize.java ================================================ package org.dhallj.core.normalization; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.dhallj.core.Expr; import org.dhallj.core.Visitor; /** * Performs alpha normalization. * *

Morally this is equivalent to the following (on non-underscore bindings): * *

 * input.increment("_").substitute(name, 0, Expr.Constants.UNDERSCORE).decrement(name);
 * 
* * The implementation here is necessary to fit the visitor API. * *

Note that this visitor maintains internal state and instances should not be reused. */ public final class AlphaNormalize extends Visitor.Identity { // We interpret any underscores as implicitly having this index added to their own. private int newUnderscoreDepth = 0; // The total number of underscores. private int underscoreDepth = 0; // We change any other name to an underscore whose index we compute from the depth we track here. private final Map> nameDepths = new HashMap>(); @Override public Expr onIdentifier(Expr self, String name, long index) { if (name.equals("_")) { return Expr.makeIdentifier(name, index + this.newUnderscoreDepth); } else { LinkedList depths = this.nameDepths.get(name); if (depths == null) { return self; } else if (index < depths.size()) { return Expr.makeIdentifier("_", underscoreDepth - depths.get((int) index)); } else { return Expr.makeIdentifier(name, index - depths.size()); } } } @Override public void bind(String name, Expr type) { this.underscoreDepth += 1; if (!name.equals("_")) { this.newUnderscoreDepth += 1; LinkedList nameDepth = this.nameDepths.get(name); if (nameDepth == null) { nameDepth = new LinkedList(); nameDepths.put(name, nameDepth); } nameDepth.push(this.underscoreDepth); } } @Override public Expr onLambda(String name, Expr type, Expr result) { this.underscoreDepth -= 1; if (!name.equals("_")) { this.newUnderscoreDepth -= 1; this.nameDepths.get(name).pop(); } return Expr.makeLambda("_", type, result); } @Override public Expr onPi(String name, Expr type, Expr result) { this.underscoreDepth -= 1; if (!name.equals("_")) { this.newUnderscoreDepth -= 1; this.nameDepths.get(name).pop(); } return Expr.makePi("_", type, result); } @Override public Expr onLet(List> bindings, Expr body) { List> newBindings = new ArrayList<>(bindings.size()); for (Expr.LetBinding binding : bindings) { String name = binding.getName(); this.underscoreDepth -= 1; if (!name.equals("_")) { this.newUnderscoreDepth -= 1; this.nameDepths.get(name).pop(); } newBindings.add(new Expr.LetBinding<>("_", binding.getType(), binding.getValue())); } return Expr.makeLet(newBindings, body); } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalize.java ================================================ package org.dhallj.core.normalization; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URI; import java.nio.file.Path; import java.util.Collections; import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.TreeSet; import org.dhallj.core.Expr; import org.dhallj.core.Operator; import org.dhallj.core.Source; import org.dhallj.core.Visitor; /** * Performs beta normalization. * *

This is a stateless visitor intended for use as a singleton. */ public final class BetaNormalize extends Visitor.NoPrepareEvents { public static final Visitor instance = new BetaNormalize(); public void bind(String name, Expr type) {} public Expr onNote(Expr base, Source source) { return base; } public Expr onNatural(Expr self, BigInteger value) { return self; } public Expr onInteger(Expr self, BigInteger value) { return self; } public Expr onDouble(Expr self, double value) { return self; } public Expr onDate(Expr self, int year, int month, int day) { return self; } public Expr onTime(Expr self, int hour, int minute, int second, BigDecimal fractional) { return self; } public Expr onTimeZone(Expr self, int minutes) { return self; } public Expr onBuiltIn(Expr self, String name) { return self; } public Expr onIdentifier(Expr self, String name, long index) { return self; } public Expr onLambda(String name, Expr type, Expr result) { return Expr.makeLambda(name, type, result); } public Expr onPi(String name, Expr type, Expr result) { return Expr.makePi(name, type, result); } public Expr onLet(List> bindings, Expr body) { Expr result = body; for (int i = bindings.size() - 1; i >= 0; i--) { Expr.LetBinding binding = bindings.get(i); String name = binding.getName(); result = result.substitute(name, binding.getValue()); } return result.accept(this); } public Expr onText(String[] parts, List interpolated) { return BetaNormalizeTextLiteral.apply(parts, interpolated); } public Expr onNonEmptyList(List values) { return Expr.makeNonEmptyListLiteral(values); } public Expr onEmptyList(Expr type) { return Expr.makeEmptyListLiteral(type); } public Expr onRecord(List> fields) { Collections.sort(fields, NormalizationUtilities.entryComparator); return Expr.makeRecordLiteral(fields); } public Expr onRecordType(List> fields) { Collections.sort(fields, NormalizationUtilities.entryComparator); return Expr.makeRecordType(fields); } public Expr onUnionType(List> fields) { Collections.sort(fields, NormalizationUtilities.entryComparator); return Expr.makeUnionType(fields); } public Expr onFieldAccess(Expr base, String fieldName) { return BetaNormalizeFieldAccess.apply(base, fieldName); } public Expr onProjection(Expr base, String[] fieldNames) { return BetaNormalizeProjection.apply(base, fieldNames); } public Expr onProjectionByType(Expr base, Expr arg) { List> argAsRecordType = Expr.Util.asRecordType(arg); Set keys = new TreeSet<>(); for (Entry entry : argAsRecordType) { keys.add(entry.getKey()); } return Expr.makeProjection(base, keys.toArray(new String[keys.size()])).accept(this); } public Expr onApplication(Expr base, List args) { return BetaNormalizeApplication.apply(base, args); } public Expr onOperatorApplication(Operator operator, Expr lhs, Expr rhs) { return BetaNormalizeOperatorApplication.apply(operator, lhs, rhs); } public Expr onIf(Expr predicate, Expr thenValue, Expr elseValue) { return BetaNormalizeIf.apply(predicate, thenValue, elseValue); } public Expr onAnnotated(Expr base, Expr type) { return base; } public Expr onAssert(Expr type) { return Expr.makeAssert(type); } public Expr onMerge(Expr handlers, Expr union, Expr type) { return BetaNormalizeMerge.apply(handlers, union, type); } public Expr onToMap(Expr base, Expr type) { return BetaNormalizeToMap.apply(base, type); } public Expr onWith(Expr base, String[] path, Expr value) { return BetaNormalizeWith.apply(base, path, value); } public Expr onMissingImport(Expr.ImportMode mode, byte[] hash) { return Expr.makeMissingImport(mode, hash); } public Expr onEnvImport(String value, Expr.ImportMode mode, byte[] hash) { return Expr.makeEnvImport(value, mode, hash); } public Expr onLocalImport(Path path, Expr.ImportMode mode, byte[] hash) { return Expr.makeLocalImport(path, mode, hash); } @Override public Expr onClasspathImport(Path path, Expr.ImportMode mode, byte[] hash) { return Expr.makeClasspathImport(path, mode, hash); } public Expr onRemoteImport(URI url, Expr using, Expr.ImportMode mode, byte[] hash) { return Expr.makeRemoteImport(url, using, mode, hash); } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeApplication.java ================================================ package org.dhallj.core.normalization; import java.math.BigInteger; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map.Entry; import org.dhallj.core.Expr; import org.dhallj.core.Operator; final class BetaNormalizeApplication { private static Expr applyLambdas(Expr base, final List args) { Expr currentLambda = null; Expr current = base; int i = 0; while (current != null && i < args.size()) { current = Expr.Util.applyAsLambda(current, args.get(i)); if (current == null) { break; } else { currentLambda = current.accept(BetaNormalize.instance); i += 1; } } if (currentLambda != null) { for (int j = i; j < args.size(); j++) { currentLambda = Expr.makeApplication(currentLambda, args.get(j)); } currentLambda = currentLambda.accept(BetaNormalize.instance); } return currentLambda; } private static final Expr booleanToExpr(boolean value) { return value ? Expr.Constants.TRUE : Expr.Constants.FALSE; } static final Expr apply(Expr base, List args) { String builtIn = Expr.Util.asBuiltIn(base); if (builtIn != null) { if (builtIn.equals("Natural/fold") && args.size() >= 4) { Expr result = naturalFold(args); if (result != null) { return result; } } else if (builtIn.equals("List/fold") && args.size() >= 5) { Expr result = listFold(args); if (result != null) { return result; } } else if (builtIn.equals("Text/replace") && args.size() >= 3) { Expr result = textReplace(args); if (result != null) { return result; } } else if (args.size() == 1) { Expr result = arity1(builtIn, args.get(0)); if (result != null) { return result; } } else if (args.size() == 2) { Expr result = arity2(builtIn, args.get(0), args.get(1)); if (result != null) { return result; } } } else { Expr result = applyLambdas(base, args); if (result != null) { return result; } } return Expr.makeApplication(base, args); } private static final Entry indexField = new SimpleImmutableEntry<>("index", Expr.Constants.NATURAL); private static final Expr indexedRecordType(Expr type) { Entry[] fields = {indexField, new SimpleImmutableEntry<>("value", type)}; return Expr.makeRecordType(fields); } private static final BigInteger TWO = new BigInteger("2"); private static final boolean isBigIntegerEven(BigInteger value) { return value.mod(TWO).equals(BigInteger.ZERO); } private static final boolean isBigIntegerNatural(BigInteger value) { return value.compareTo(BigInteger.ZERO) >= 0; } private static final Expr arity1(String identifier, Expr arg) { if (identifier.equals("Natural/isZero")) { BigInteger argAsNaturalLiteral = Expr.Util.asNaturalLiteral(arg); if (argAsNaturalLiteral != null) { return booleanToExpr(argAsNaturalLiteral.equals(BigInteger.ZERO)); } } else if (identifier.equals("Natural/even")) { BigInteger argAsNaturalLiteral = Expr.Util.asNaturalLiteral(arg); if (argAsNaturalLiteral != null) { return booleanToExpr(isBigIntegerEven(argAsNaturalLiteral)); } } else if (identifier.equals("Natural/odd")) { BigInteger argAsNaturalLiteral = Expr.Util.asNaturalLiteral(arg); if (argAsNaturalLiteral != null) { return booleanToExpr(!isBigIntegerEven(argAsNaturalLiteral)); } } else if (identifier.equals("Natural/toInteger")) { BigInteger argAsNaturalLiteral = Expr.Util.asNaturalLiteral(arg); if (argAsNaturalLiteral != null) { return Expr.makeIntegerLiteral(argAsNaturalLiteral); } } else if (identifier.equals("Natural/show")) { BigInteger argAsNaturalLiteral = Expr.Util.asNaturalLiteral(arg); if (argAsNaturalLiteral != null) { return Expr.makeTextLiteral(argAsNaturalLiteral.toString()); } } else if (identifier.equals("Integer/negate")) { BigInteger argAsIntegerLiteral = Expr.Util.asIntegerLiteral(arg); if (argAsIntegerLiteral != null) { return Expr.makeIntegerLiteral(argAsIntegerLiteral.negate()); } } else if (identifier.equals("Integer/clamp")) { BigInteger argAsIntegerLiteral = Expr.Util.asIntegerLiteral(arg); if (argAsIntegerLiteral != null) { if (isBigIntegerNatural(argAsIntegerLiteral)) { return Expr.makeNaturalLiteral(argAsIntegerLiteral); } else { return Expr.makeNaturalLiteral(BigInteger.ZERO); } } } else if (identifier.equals("Integer/toDouble")) { BigInteger argAsIntegerLiteral = Expr.Util.asIntegerLiteral(arg); if (argAsIntegerLiteral != null) { return Expr.makeDoubleLiteral(argAsIntegerLiteral.doubleValue()); } } else if (identifier.equals("Integer/show")) { BigInteger argAsIntegerLiteral = Expr.Util.asIntegerLiteral(arg); if (argAsIntegerLiteral != null) { String sign = isBigIntegerNatural(argAsIntegerLiteral) ? "+" : ""; return Expr.makeTextLiteral(sign + argAsIntegerLiteral.toString()); } } else if (identifier.equals("Double/show")) { Double argAsDoubleLiteral = Expr.Util.asDoubleLiteral(arg); if (argAsDoubleLiteral != null) { return Expr.makeTextLiteral(argAsDoubleLiteral.toString()); } } else if (identifier.equals("Text/show")) { String argAsSimpleTextLiteral = Expr.Util.asSimpleTextLiteral(arg); if (argAsSimpleTextLiteral != null) { return Expr.makeTextLiteral(Expr.Util.escapeText(argAsSimpleTextLiteral, true)); } } else if (identifier.equals("Natural/build")) { return Expr.makeApplication( Expr.makeApplication( Expr.makeApplication(arg, Expr.Constants.NATURAL), Expr.makeLambda( "x", Expr.Constants.NATURAL, Expr.makeOperatorApplication( Operator.PLUS, Expr.makeIdentifier("x"), Expr.makeNaturalLiteral(BigInteger.ONE)))), Expr.makeNaturalLiteral(BigInteger.ZERO)) .accept(BetaNormalize.instance); } // None matched, so we can't simplify. return null; } private static final Expr[] prependValue = new Expr[] {Expr.makeIdentifier("a")}; private static final Expr prependExpr = Expr.makeOperatorApplication( Operator.LIST_APPEND, Expr.makeNonEmptyListLiteral(prependValue), Expr.makeIdentifier("as")); private static final Expr arity2(String identifier, Expr arg1, Expr arg2) { if (identifier.equals("List/build")) { Expr listA = Expr.makeApplication(Expr.Constants.LIST, arg1); return Expr.makeApplication( Expr.makeApplication( Expr.makeApplication(arg2, listA), Expr.makeLambda( "a", arg1, Expr.makeLambda( "as", Expr.makeApplication(Expr.Constants.LIST, arg1.increment("a")), prependExpr))), Expr.makeEmptyListLiteral(listA)) .accept(BetaNormalize.instance); } else if (identifier.equals("Natural/subtract")) { BigInteger firstAsNaturalLiteral = Expr.Util.asNaturalLiteral(arg1); BigInteger secondAsNaturalLiteral = Expr.Util.asNaturalLiteral(arg2); if (firstAsNaturalLiteral != null) { if (secondAsNaturalLiteral != null) { if (firstAsNaturalLiteral.compareTo(secondAsNaturalLiteral) < 0) { return Expr.makeNaturalLiteral(secondAsNaturalLiteral.subtract(firstAsNaturalLiteral)); } else { return Expr.Constants.ZERO; } } else { if (firstAsNaturalLiteral.equals(BigInteger.ZERO)) { return arg2; } } } else { if (secondAsNaturalLiteral != null && secondAsNaturalLiteral.equals(BigInteger.ZERO)) { return Expr.Constants.ZERO; } } if (arg1.equivalent(arg2)) { return Expr.Constants.ZERO; } } else if (identifier.equals("List/length")) { List argAsListLiteral = Expr.Util.asListLiteral(arg2); if (argAsListLiteral != null) { return Expr.makeNaturalLiteral(BigInteger.valueOf(argAsListLiteral.size())); } } else if (identifier.equals("List/reverse")) { List argAsListLiteral = Expr.Util.asListLiteral(arg2); if (argAsListLiteral != null) { List result = new ArrayList<>(argAsListLiteral); Collections.reverse(result); if (result.isEmpty()) { return arg2; } else { return Expr.makeNonEmptyListLiteral(result); } } } else if (identifier.equals("List/head")) { List argAsListLiteral = Expr.Util.asListLiteral(arg2); if (argAsListLiteral != null) { if (argAsListLiteral.isEmpty()) { return Expr.makeApplication(Expr.Constants.NONE, arg1); } else { return Expr.makeApplication(Expr.Constants.SOME, argAsListLiteral.get(0)); } } } else if (identifier.equals("List/last")) { List argAsListLiteral = Expr.Util.asListLiteral(arg2); if (argAsListLiteral != null) { if (argAsListLiteral.isEmpty()) { return Expr.makeApplication(Expr.Constants.NONE, arg1); } else { return Expr.makeApplication( Expr.Constants.SOME, argAsListLiteral.get(argAsListLiteral.size() - 1)); } } } else if (identifier.equals("List/indexed")) { List argAsListLiteral = Expr.Util.asListLiteral(arg2); if (argAsListLiteral != null) { List result = new ArrayList<>(argAsListLiteral.size()); int i = 0; for (Expr value : argAsListLiteral) { List> fields = new ArrayList<>(); fields.add( new SimpleImmutableEntry<>( "index", Expr.makeNaturalLiteral(BigInteger.valueOf(i++)))); fields.add(new SimpleImmutableEntry<>("value", value)); result.add(Expr.makeRecordLiteral(fields)); } if (result.isEmpty()) { return Expr.makeEmptyListLiteral( Expr.makeApplication(Expr.Constants.LIST, indexedRecordType(arg1))); } else { return Expr.makeNonEmptyListLiteral(result); } } } // None matched, so we can't simplify. return null; } private static List drop(List args, int count) { List result = new ArrayList(args.size() - count); for (int i = count; i < args.size(); i++) { result.add(args.get(i)); } return result; } private static final Expr naturalFold(List args) { BigInteger firstAsNaturalLiteral = Expr.Util.asNaturalLiteral(args.get(0)); if (firstAsNaturalLiteral != null) { Expr applied = null; if (firstAsNaturalLiteral.equals(BigInteger.ZERO)) { applied = args.get(3); } else { applied = Expr.makeApplication( args.get(2), Expr.makeApplication( Expr.makeApplication( Expr.makeApplication( Expr.makeApplication( Expr.Constants.NATURAL_FOLD, Expr.makeNaturalLiteral( firstAsNaturalLiteral.subtract(BigInteger.ONE))), args.get(1)), args.get(2)), args.get(3))) .accept(BetaNormalize.instance); } if (applied == null) { return null; } if (args.size() == 4) { return applied; } else { return Expr.makeApplication(applied, drop(args, 4)).accept(BetaNormalize.instance); } } return null; } private static final Expr listFold(List args) { List listArg = Expr.Util.asListLiteral(args.get(1)); if (listArg != null) { Expr applied = null; if (!listArg.isEmpty()) { Expr head = listArg.get(0); Expr tail; if (listArg.size() == 1) { tail = Expr.makeEmptyListLiteral(Expr.makeApplication(Expr.Constants.LIST, args.get(0))); } else { List listArgTail = new ArrayList<>(listArg); listArgTail.remove(0); tail = Expr.makeNonEmptyListLiteral(listArgTail); } applied = Expr.makeApplication( Expr.makeApplication(args.get(3), head), Expr.makeApplication( Expr.makeApplication( Expr.makeApplication( Expr.makeApplication( Expr.makeApplication(Expr.Constants.LIST_FOLD, args.get(0)), tail), args.get(2)), args.get(3)), args.get(4))) .accept(BetaNormalize.instance); } else { applied = args.get(4); } if (args.size() == 5) { return applied; } else { return Expr.makeApplication(applied, drop(args, 5)).accept(BetaNormalize.instance); } } return null; } private static final Expr textReplace(List args) { String needle = Expr.Util.asSimpleTextLiteral(args.get(0)); if (needle != null) { if (needle.length() == 0) { return args.get(2); } else { String haystack = Expr.Util.asSimpleTextLiteral(args.get(2)); if (haystack != null) { String pattern = java.util.regex.Pattern.quote(needle); String[] parts = haystack.split(pattern, -1); Expr[] interpolated = new Expr[parts.length - 1]; Expr replacement = args.get(1); for (int i = 0; i < parts.length - 1; i += 1) { interpolated[i] = replacement; } return Expr.makeTextLiteral(parts, interpolated).accept(BetaNormalize.instance); } } } return null; } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeFieldAccess.java ================================================ package org.dhallj.core.normalization; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import org.dhallj.core.Expr; import org.dhallj.core.ExternalVisitor; import org.dhallj.core.Operator; final class BetaNormalizeFieldAccess extends ExternalVisitor.Constant { private final String fieldName; public BetaNormalizeFieldAccess(String fieldName) { super(null); this.fieldName = fieldName; } static final Expr apply(Expr base, String fieldName) { Expr result = base.accept(new BetaNormalizeFieldAccess(fieldName)); if (result != null) { return result; } else { return Expr.makeFieldAccess(base, fieldName); } } @Override public Expr onRecord(Iterable> fields, int size) { return NormalizationUtilities.lookup(fields, fieldName); } @Override public Expr onProjection(Expr base, String[] fieldNames0) { return Expr.makeFieldAccess(base, fieldName).accept(BetaNormalize.instance); } @Override public Expr onOperatorApplication(Operator operator, Expr lhs, Expr rhs) { if (operator.equals(Operator.PREFER)) { List> lhsFields = Expr.Util.asRecordLiteral(lhs); if (lhsFields != null) { Entry lhsFound = NormalizationUtilities.lookupEntry(lhsFields, fieldName); if (lhsFound != null) { List> singleton = new ArrayList(); singleton.add(lhsFound); return Expr.makeFieldAccess( Expr.makeOperatorApplication(Operator.PREFER, Expr.makeRecordLiteral(singleton), rhs), fieldName); } else { return Expr.makeFieldAccess(rhs, fieldName); } } else { List> rhsFields = Expr.Util.asRecordLiteral(rhs); if (rhsFields != null) { Expr rhsFound = NormalizationUtilities.lookup(rhsFields, fieldName); if (rhsFound != null) { return rhsFound; } else { return Expr.makeFieldAccess(lhs, fieldName).accept(BetaNormalize.instance); } } } } else if (operator.equals(Operator.COMBINE)) { List> lhsFields = Expr.Util.asRecordLiteral(lhs); if (lhsFields != null) { Entry lhsFound = NormalizationUtilities.lookupEntry(lhsFields, fieldName); if (lhsFound != null) { List> singleton = new ArrayList<>(); singleton.add(lhsFound); return Expr.makeFieldAccess( Expr.makeOperatorApplication( Operator.COMBINE, Expr.makeRecordLiteral(singleton), rhs), fieldName); } else { return Expr.makeFieldAccess(rhs, fieldName); } } else { List> rhsFields = Expr.Util.asRecordLiteral(rhs); if (rhsFields != null) { Entry rhsFound = NormalizationUtilities.lookupEntry(rhsFields, fieldName); if (rhsFound != null) { List> singleton = new ArrayList<>(); singleton.add(rhsFound); return Expr.makeFieldAccess( Expr.makeOperatorApplication( Operator.COMBINE, lhs, Expr.makeRecordLiteral(singleton)), fieldName); } else { return Expr.makeFieldAccess(lhs, fieldName).accept(BetaNormalize.instance); } } } } return null; } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeIf.java ================================================ package org.dhallj.core.normalization; import org.dhallj.core.Expr; final class BetaNormalizeIf { static final Expr apply(Expr predicate, Expr thenValue, Expr elseValue) { Boolean predicateAsBool = Expr.Util.asBoolLiteral(predicate); if (predicateAsBool != null) { return (predicateAsBool) ? thenValue : elseValue; } else { Boolean thenAsBool = Expr.Util.asBoolLiteral(thenValue); Boolean elseAsBool = Expr.Util.asBoolLiteral(elseValue); if (thenAsBool != null && elseAsBool != null && thenAsBool && !elseAsBool) { return predicate; } else if (thenValue.equivalent(elseValue)) { return thenValue; } else { return Expr.makeIf(predicate, thenValue, elseValue); } } } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeMerge.java ================================================ package org.dhallj.core.normalization; import java.util.List; import java.util.Map.Entry; import org.dhallj.core.Expr; import org.dhallj.core.ExternalVisitor; import org.dhallj.core.Operator; final class BetaNormalizeMerge extends ExternalVisitor.Constant { private final List> handlers; private BetaNormalizeMerge(List> handlers) { super(null); this.handlers = handlers; } static final Expr apply(Expr handlers, Expr union, Expr type) { List> fields = Expr.Util.asRecordLiteral(handlers); Expr result = union.accept(new BetaNormalizeMerge(fields)); if (result != null) { return result.accept(BetaNormalize.instance); } else { return Expr.makeMerge(handlers, union, type); } } @Override public Expr onFieldAccess(Expr base, String fieldName) { List> baseAsUnion = Expr.Util.asUnionType(base); if (baseAsUnion != null) { return merge(this.handlers, fieldName); } else { return null; } } @Override public Expr onApplication(Expr base, Expr arg) { Entry baseAsFieldAccess = Expr.Util.asFieldAccess(base); if (baseAsFieldAccess != null) { List> accessedAsUnion = Expr.Util.asUnionType(baseAsFieldAccess.getKey()); if (accessedAsUnion != null) { return merge(this.handlers, baseAsFieldAccess.getValue(), arg); } else { return null; } } else { String baseAsBuiltIn = Expr.Util.asBuiltIn(base); if (baseAsBuiltIn != null) { if (baseAsBuiltIn.equals("Some")) { return merge(this.handlers, baseAsBuiltIn, arg); } else if (baseAsBuiltIn.equals("None")) { return merge(this.handlers, baseAsBuiltIn); } } return null; } } private static Expr merge(List> handlers, String fieldName) { return NormalizationUtilities.lookup(handlers, fieldName); } private static Expr merge(List> handlers, String fieldName, Expr arg) { Expr handler = NormalizationUtilities.lookup(handlers, fieldName); if (handler != null) { return Expr.makeApplication(handler, arg); } else { return null; } } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeOperatorApplication.java ================================================ package org.dhallj.core.normalization; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import org.dhallj.core.Expr; import org.dhallj.core.Operator; final class BetaNormalizeOperatorApplication { static final Expr apply(Operator operator, Expr lhs, Expr rhs) { if (operator.isBoolOperator()) { Boolean lhsAsBool = Expr.Util.asBoolLiteral(lhs); Boolean rhsAsBool = Expr.Util.asBoolLiteral(rhs); if (operator.equals(Operator.OR)) { if (lhsAsBool != null) { return lhsAsBool ? lhs : rhs; } else if (rhsAsBool != null) { return rhsAsBool ? rhs : lhs; } else if (lhs.equivalent(rhs)) { return lhs; } } else if (operator.equals(Operator.AND)) { if (lhsAsBool != null) { return lhsAsBool ? rhs : lhs; } else if (rhsAsBool != null) { return rhsAsBool ? lhs : rhs; } else if (lhs.equivalent(rhs)) { return lhs; } } else if (operator.equals(Operator.EQUALS)) { if (lhsAsBool != null && lhsAsBool) { return rhs; } else if (rhsAsBool != null && rhsAsBool) { return lhs; } else if (lhs.equivalent(rhs)) { return Expr.Constants.TRUE; } } else if (operator.equals(Operator.NOT_EQUALS)) { if (lhsAsBool != null && !lhsAsBool) { return rhs; } else if (rhsAsBool != null && !rhsAsBool) { return lhs; } else if (lhs.equivalent(rhs)) { return Expr.Constants.FALSE; } } } else if (operator.equals(Operator.PLUS)) { BigInteger lhsAsNaturalLiteral = Expr.Util.asNaturalLiteral(lhs); BigInteger rhsAsNaturalLiteral = Expr.Util.asNaturalLiteral(rhs); if (lhsAsNaturalLiteral != null) { if (rhsAsNaturalLiteral != null) { return Expr.makeNaturalLiteral(lhsAsNaturalLiteral.add(rhsAsNaturalLiteral)); } else if (lhsAsNaturalLiteral.equals(BigInteger.ZERO)) { return rhs; } } else if (rhsAsNaturalLiteral != null && rhsAsNaturalLiteral.equals(BigInteger.ZERO)) { return lhs; } } else if (operator.equals(Operator.TIMES)) { BigInteger lhsAsNaturalLiteral = Expr.Util.asNaturalLiteral(lhs); BigInteger rhsAsNaturalLiteral = Expr.Util.asNaturalLiteral(rhs); if (lhsAsNaturalLiteral != null) { if (rhsAsNaturalLiteral != null) { return Expr.makeNaturalLiteral(lhsAsNaturalLiteral.multiply(rhsAsNaturalLiteral)); } else if (lhsAsNaturalLiteral.equals(BigInteger.ZERO)) { return lhs; } else if (lhsAsNaturalLiteral.equals(BigInteger.ONE)) { return rhs; } } else if (rhsAsNaturalLiteral != null) { if (rhsAsNaturalLiteral.equals(BigInteger.ZERO)) { return rhs; } else if (rhsAsNaturalLiteral.equals(BigInteger.ONE)) { return lhs; } } } else if (operator.equals(Operator.TEXT_APPEND)) { String[] parts = {"", "", ""}; List interpolated = new ArrayList(2); interpolated.add(lhs); interpolated.add(rhs); return Expr.makeTextLiteral(parts, interpolated).accept(BetaNormalize.instance); } else if (operator.equals(Operator.LIST_APPEND)) { List lhsAsListLiteral = Expr.Util.asListLiteral(lhs); List rhsAsListLiteral = Expr.Util.asListLiteral(rhs); if (lhsAsListLiteral != null) { if (lhsAsListLiteral.isEmpty()) { return rhs; } else if (rhsAsListLiteral != null) { List result = new ArrayList(lhsAsListLiteral.size() + rhsAsListLiteral.size()); result.addAll(lhsAsListLiteral); result.addAll(rhsAsListLiteral); return Expr.makeNonEmptyListLiteral(result); } } else if (rhsAsListLiteral != null && rhsAsListLiteral.isEmpty()) { return lhs; } } else if (operator.equals(Operator.PREFER)) { List> lhsAsRecordLiteral = Expr.Util.asRecordLiteral(lhs); List> rhsAsRecordLiteral = Expr.Util.asRecordLiteral(rhs); if (lhsAsRecordLiteral != null) { if (rhsAsRecordLiteral != null) { Map asMap = new TreeMap<>(); for (Entry entry : lhsAsRecordLiteral) { asMap.put(entry.getKey(), entry.getValue()); } for (Entry entry : rhsAsRecordLiteral) { asMap.put(entry.getKey(), entry.getValue()); } return Expr.makeRecordLiteral(asMap.entrySet()); } else if (!lhsAsRecordLiteral.iterator().hasNext()) { return rhs; } } else if (rhsAsRecordLiteral != null && !rhsAsRecordLiteral.iterator().hasNext()) { return lhs; } else if (lhs.equivalent(rhs)) { return rhs; } } else if (operator.equals(Operator.COMPLETE)) { return Expr.Util.desugarComplete(lhs, rhs).accept(BetaNormalize.instance); } else if (operator.equals(Operator.COMBINE)) { List> firstAsRecordLiteral = Expr.Util.asRecordLiteral(lhs); List> secondAsRecordLiteral = Expr.Util.asRecordLiteral(rhs); if (firstAsRecordLiteral != null) { if (secondAsRecordLiteral != null) { return mergeRecursive(lhs, rhs, firstAsRecordLiteral, secondAsRecordLiteral) .accept(BetaNormalize.instance); } else { if (!firstAsRecordLiteral.iterator().hasNext()) { return rhs; } } } else if (secondAsRecordLiteral != null && !secondAsRecordLiteral.iterator().hasNext()) { return lhs; } else { return Expr.makeOperatorApplication(Operator.COMBINE, lhs, rhs); } } else if (operator.equals(Operator.COMBINE_TYPES)) { List> firstAsRecordType = Expr.Util.asRecordType(lhs); List> secondAsRecordType = Expr.Util.asRecordType(rhs); if (firstAsRecordType != null) { if (secondAsRecordType != null) { return mergeTypesRecursive(lhs, rhs, firstAsRecordType, secondAsRecordType) .accept(BetaNormalize.instance); } else { if (!firstAsRecordType.iterator().hasNext()) { return rhs; } } } else if (secondAsRecordType != null && !secondAsRecordType.iterator().hasNext()) { return lhs; } else { return Expr.makeOperatorApplication(Operator.COMBINE_TYPES, lhs, rhs); } } return Expr.makeOperatorApplication(operator, lhs, rhs); } private static final Expr mergeRecursive( Expr first, Expr second, List> firstAsRecordLiteral, List> secondAsRecordLiteral) { if (firstAsRecordLiteral != null && secondAsRecordLiteral != null) { Map asMap = new TreeMap<>(); for (Entry entry : firstAsRecordLiteral) { asMap.put(entry.getKey(), entry.getValue()); } for (Entry entry : secondAsRecordLiteral) { String key = entry.getKey(); Expr value = entry.getValue(); Expr currentValue = asMap.get(key); if (currentValue == null) { asMap.put(key, entry.getValue()); } else { asMap.put( key, mergeRecursive( currentValue, value, Expr.Util.asRecordType(currentValue), Expr.Util.asRecordType(value))); } } return Expr.makeRecordLiteral(asMap.entrySet()); } else { return Expr.makeOperatorApplication(Operator.COMBINE, first, second); } } private static final Expr mergeTypesRecursive( Expr first, Expr second, List> firstAsRecordType, List> secondAsRecordType) { if (firstAsRecordType != null && secondAsRecordType != null) { Map asMap = new TreeMap<>(); for (Entry entry : firstAsRecordType) { asMap.put(entry.getKey(), entry.getValue()); } for (Entry entry : secondAsRecordType) { String key = entry.getKey(); Expr value = entry.getValue(); Expr currentValue = asMap.get(key); if (currentValue == null) { asMap.put(key, entry.getValue()); } else { asMap.put( key, mergeTypesRecursive( currentValue, value, Expr.Util.asRecordType(currentValue), Expr.Util.asRecordType(value))); } } return Expr.makeRecordType(asMap.entrySet()); } else { return Expr.makeOperatorApplication(Operator.COMBINE, first, second); } } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeProjection.java ================================================ package org.dhallj.core.normalization; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import org.dhallj.core.Expr; import org.dhallj.core.ExternalVisitor; import org.dhallj.core.Operator; final class BetaNormalizeProjection extends ExternalVisitor.Constant { private final String[] fieldNames; private BetaNormalizeProjection(String[] fieldNames) { super(null); this.fieldNames = fieldNames; } static final Expr apply(Expr base, final String[] fieldNames) { if (fieldNames.length == 0) { return Expr.Constants.EMPTY_RECORD_LITERAL; } Expr result = base.accept(new BetaNormalizeProjection(fieldNames)); if (result != null) { return result; } else { // We have to sort the field names if we can't reduce. String[] newFieldNames = new String[fieldNames.length]; System.arraycopy(fieldNames, 0, newFieldNames, 0, fieldNames.length); Arrays.sort(newFieldNames); return Expr.makeProjection(base, newFieldNames); } } @Override public Expr onRecord(Iterable> fields, int size) { Set fieldNameSet = new HashSet(this.fieldNames.length); for (String fieldName : this.fieldNames) { fieldNameSet.add(fieldName); } Map selected = new TreeMap(); for (Entry entry : fields) { if (fieldNameSet.contains(entry.getKey())) { selected.put(entry.getKey(), entry.getValue()); } } return Expr.makeRecordLiteral(selected.entrySet()); } @Override public Expr onProjection(Expr base, String[] fieldNames) { return Expr.makeProjection(base, this.fieldNames).accept(BetaNormalize.instance); } @Override public Expr onOperatorApplication(Operator operator, Expr lhs, Expr rhs) { if (operator.equals(Operator.PREFER)) { List> rhsFields = Expr.Util.asRecordLiteral(rhs); if (rhsFields != null) { Set rhsKeys = new HashSet(); Set leftFields = new TreeSet(); Set rightFields = new TreeSet(); for (Entry entry : rhsFields) { rhsKeys.add(entry.getKey()); } for (String fieldName : this.fieldNames) { if (rhsKeys.contains(fieldName)) { rightFields.add(fieldName); } else { leftFields.add(fieldName); } } return Expr.makeOperatorApplication( Operator.PREFER, Expr.makeProjection(lhs, leftFields.toArray(new String[leftFields.size()])), Expr.makeProjection(rhs, rightFields.toArray(new String[leftFields.size()]))) .accept(BetaNormalize.instance); } } return null; } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeTextLiteral.java ================================================ package org.dhallj.core.normalization; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.dhallj.core.Expr; import org.dhallj.core.ExternalVisitor; final class BetaNormalizeTextLiteral { static final Expr apply(String[] parts, List interpolated) { if (parts.length == 1) { return Expr.makeTextLiteral(parts[0]); } else { int partsSize = 0; for (String part : parts) { partsSize += part.length(); } int c = 0; if (partsSize == 0) { Expr notEmptyString = null; boolean tooMany = false; Iterator it = interpolated.iterator(); while (it.hasNext() && !tooMany) { Expr next = it.next(); String nextAsSimpleTextLiteral = Expr.Util.asSimpleTextLiteral(next); if (nextAsSimpleTextLiteral == null || nextAsSimpleTextLiteral.length() != 0) { if (notEmptyString == null) { notEmptyString = next; } else { tooMany = true; } } } if (!tooMany && notEmptyString != null) { return notEmptyString; } } List newParts = new ArrayList<>(parts.length); List newInterpolated = new ArrayList<>(parts.length - 1); newParts.add(parts[0]); boolean wasInlined = false; int partIndex = 1; for (Expr expr : interpolated) { wasInlined = expr.accept(new InlineInterpolatedTextLiteral(newParts, newInterpolated)); if (!wasInlined) { newInterpolated.add(expr); newParts.add(parts[partIndex++]); } else { int lastIndex = newParts.size() - 1; String lastPart = newParts.get(lastIndex); newParts.set(lastIndex, lastPart + parts[partIndex++]); } } return Expr.makeTextLiteral(newParts.toArray(new String[newParts.size()]), newInterpolated); } } private static final class InlineInterpolatedTextLiteral extends ExternalVisitor.Constant { private final List newParts; private final List newInterpolated; InlineInterpolatedTextLiteral(List newParts, List newInterpolated) { super(false); this.newParts = newParts; this.newInterpolated = newInterpolated; } @Override public Boolean onText(String[] parts, Iterable interpolated) { int lastIndex = newParts.size() - 1; String lastPart = newParts.get(lastIndex); newParts.set(lastIndex, lastPart + parts[0]); Iterator it = interpolated.iterator(); for (int i = 1; i < parts.length; i++) { newInterpolated.add(it.next()); newParts.add(parts[i]); } return true; } } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeToMap.java ================================================ package org.dhallj.core.normalization; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import org.dhallj.core.Expr; final class BetaNormalizeToMap { static final Expr apply(Expr base, Expr type) { List> baseAsRecordLiteral = Expr.Util.asRecordLiteral(base); if (baseAsRecordLiteral != null) { if (baseAsRecordLiteral.size() == 0) { return Expr.makeEmptyListLiteral(type); } else { Expr[] result = new Expr[baseAsRecordLiteral.size()]; for (int i = 0; i < baseAsRecordLiteral.size(); i++) { Entry entry = baseAsRecordLiteral.get(i); result[i] = makeRecord(entry.getKey(), entry.getValue()); } return Expr.makeNonEmptyListLiteral(result); } } else { return Expr.makeToMap(base, type); } } private static final String escape(String input) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < input.length(); i++) { char c = input.charAt(i); if (c == '\\') { char next = input.charAt(++i); if (next == '\\') { builder.append("\\\\"); } else if (next == 'n') { builder.append("\\\\n"); } else if (next == '$') { builder.append("\\\\\\$"); } else { builder.append("\\\\"); builder.append(next); } } else { builder.append(c); } } return builder.toString(); } private static final Expr makeRecord(String key, Expr value) { Entry[] fields = { new SimpleImmutableEntry<>( Expr.Constants.MAP_KEY_FIELD_NAME, Expr.makeTextLiteral(escape(key))), new SimpleImmutableEntry<>(Expr.Constants.MAP_VALUE_FIELD_NAME, value) }; return Expr.makeRecordLiteral(fields); } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/normalization/BetaNormalizeWith.java ================================================ package org.dhallj.core.normalization; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Map.Entry; import org.dhallj.core.Expr; final class BetaNormalizeWith { static final Expr apply(Expr base, String[] path, Expr value) { List> baseAsRecordLiteral = Expr.Util.asRecordLiteral(base); if (baseAsRecordLiteral != null) { List> result = new ArrayList<>(); boolean found = false; for (Entry field : baseAsRecordLiteral) { String key = field.getKey(); if (key.equals(path[0])) { found = true; Expr newValue; if (path.length == 1) { newValue = value; } else { String[] remainingPath = new String[path.length - 1]; System.arraycopy(path, 1, remainingPath, 0, path.length - 1); newValue = Expr.makeWith(field.getValue(), remainingPath, value) .accept(BetaNormalize.instance); } result.add(new SimpleImmutableEntry<>(key, newValue)); } else { result.add(field); } } if (!found) { Expr newValue; if (path.length == 1) { newValue = value; } else { String[] remainingPath = new String[path.length - 1]; System.arraycopy(path, 1, remainingPath, 0, path.length - 1); newValue = Expr.makeWith(Expr.Constants.EMPTY_RECORD_LITERAL, remainingPath, value) .accept(BetaNormalize.instance); } result.add(new SimpleImmutableEntry<>(path[0], newValue)); } Entry[] resultArray = result.toArray(new Entry[result.size()]); Arrays.sort(resultArray, entryComparator); return Expr.makeRecordLiteral(resultArray); } else { return Expr.makeWith(base, path, value); } } /** Java 8 introduce {@code comparingByKey}, but we can roll our own pretty easily. */ private static final Comparator> entryComparator = new Comparator>() { public int compare(Entry a, Entry b) { return a.getKey().compareTo(b.getKey()); } }; } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/normalization/NormalizationUtils.java ================================================ package org.dhallj.core.normalization; import java.util.Comparator; import java.util.Map.Entry; import org.dhallj.core.Expr; /** Static utility classes and methods for internal use. */ final class NormalizationUtilities { static final A lookup(Iterable> entries, String key) { for (Entry entry : entries) { if (entry.getKey().equals(key)) { return entry.getValue(); } } return null; } static final Entry lookupEntry(Iterable> entries, String key) { for (Entry entry : entries) { if (entry.getKey().equals(key)) { return entry; } } return null; } static final Comparator> entryComparator = new Comparator>() { public int compare(Entry a, Entry b) { return a.getKey().compareTo(b.getKey()); } }; } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/normalization/Shift.java ================================================ package org.dhallj.core.normalization; import java.math.BigInteger; import java.net.URI; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import org.dhallj.core.Expr; import org.dhallj.core.Operator; import org.dhallj.core.Source; import org.dhallj.core.Visitor; /** * Shifts all instances of a variable. * *

Note that this visitor maintains internal state and instances should not be reused. */ public final class Shift extends Visitor.Identity { private final int change; private final String name; private int cutoff = 0; public Shift(boolean isIncrement, String name) { this.change = isIncrement ? 1 : -1; this.name = name; } @Override public Expr onIdentifier(Expr self, String name, long index) { if (name.equals(this.name) && index >= this.cutoff) { return Expr.makeIdentifier(name, index + this.change); } else { return self; } } @Override public void bind(String name, Expr type) { if (name.equals(this.name)) { this.cutoff += 1; } } @Override public Expr onLambda(String name, Expr type, Expr result) { if (name.equals(this.name)) { this.cutoff -= 1; } return Expr.makeLambda(name, type, result); } @Override public Expr onPi(String name, Expr type, Expr result) { if (name.equals(this.name)) { this.cutoff -= 1; } return Expr.makePi(name, type, result); } @Override public Expr onLet(List> bindings, Expr body) { for (Expr.LetBinding binding : bindings) { if (binding.getName().equals(this.name)) { this.cutoff -= 1; } } return Expr.makeLet(bindings, body); } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/normalization/Substitute.java ================================================ package org.dhallj.core.normalization; import java.util.ArrayDeque; import java.util.Deque; import java.util.List; import org.dhallj.core.Expr; import org.dhallj.core.Visitor; /** * Substitutes an expression for all instances of a variable in another expression. * *

Note that this visitor maintains internal state and instances should not be reused. */ public final class Substitute extends Visitor.Identity { private final String name; private int index = 0; private final Deque replacementStack = new ArrayDeque<>(); public Substitute(String name, Expr replacement) { this.name = name; this.replacementStack.push(replacement); } @Override public void bind(String name, Expr type) { this.replacementStack.push(this.replacementStack.peekFirst().increment(name)); if (name.equals(this.name)) { this.index += 1; } } @Override public Expr onIdentifier(Expr self, String name, long index) { if (name.equals(this.name)) { if (index == this.index) { return this.replacementStack.peekFirst(); } else if (index > this.index) { return Expr.makeIdentifier(name, index - 1); } else { return self; } } else { return self; } } @Override public Expr onLambda(String name, Expr type, Expr result) { this.replacementStack.pop(); if (name.equals(this.name)) { this.index -= 1; } return Expr.makeLambda(name, type, result); } @Override public Expr onPi(String name, Expr type, Expr result) { this.replacementStack.pop(); if (name.equals(this.name)) { this.index -= 1; } return Expr.makePi(name, type, result); } @Override public Expr onLet(List> bindings, Expr body) { for (Expr.LetBinding binding : bindings) { String name = binding.getName(); this.replacementStack.pop(); if (name.equals(this.name)) { this.index -= 1; } } return Expr.makeLet(bindings, body); } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/typechecking/BuiltInTypes.java ================================================ package org.dhallj.core.typechecking; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.dhallj.core.Expr; import org.dhallj.core.Expr.Constants; final class BuiltInTypes { static final Expr getType(String name) { return mappings.get(name); } private static final int SIZE = 34; private static final Map mappings = new HashMap<>(SIZE); static { Expr typeToType = Expr.makePi(Constants.TYPE, Constants.TYPE); Expr naturalToBool = Expr.makePi(Constants.NATURAL, Constants.BOOL); Expr naturalToNatural = Expr.makePi(Constants.NATURAL, Constants.NATURAL); Expr _natural = Expr.makeIdentifier("natural"); Expr naturalType = Expr.makePi( "natural", Constants.TYPE, Expr.makePi( "succ", Expr.makePi(_natural, _natural), Expr.makePi("zero", _natural, _natural))); Expr _a = Expr.makeIdentifier("a"); Expr listA = Expr.makeApplication(Constants.LIST, _a); Expr optionalA = Expr.makeApplication(Constants.OPTIONAL, _a); Expr listAToOptionalA = Expr.makePi("a", Expr.Constants.TYPE, Expr.makePi(listA, optionalA)); Expr _list = Expr.makeIdentifier("list"); Expr listType = Expr.makePi( "list", Constants.TYPE, Expr.makePi( "cons", Expr.makePi(_a, Expr.makePi(_list, _list)), Expr.makePi("nil", _list, _list))); mappings.put("Kind", Constants.SORT); mappings.put("Type", Constants.KIND); mappings.put("Bool", Constants.TYPE); mappings.put("True", Constants.BOOL); mappings.put("False", Constants.BOOL); mappings.put("Natural", Constants.TYPE); mappings.put("Integer", Constants.TYPE); mappings.put("Text", Constants.TYPE); mappings.put("Double", Constants.TYPE); mappings.put("List", typeToType); mappings.put("Optional", typeToType); mappings.put( "None", Expr.makePi( "A", Constants.TYPE, Expr.makeApplication(Constants.OPTIONAL, Expr.makeIdentifier("A")))); mappings.put( "Text/replace", Expr.makePi( "needle", Constants.TEXT, Expr.makePi( "replacement", Constants.TEXT, Expr.makePi("haystack", Constants.TEXT, Constants.TEXT)))); mappings.put("Text/show", Expr.makePi(Constants.TEXT, Constants.TEXT)); mappings.put("Natural/build", Expr.makePi(naturalType, Constants.NATURAL)); mappings.put("Natural/fold", Expr.makePi(Constants.NATURAL, naturalType)); mappings.put("Natural/isZero", naturalToBool); mappings.put("Natural/even", naturalToBool); mappings.put("Natural/odd", naturalToBool); mappings.put("Natural/toInteger", Expr.makePi(Constants.NATURAL, Constants.INTEGER)); mappings.put("Natural/show", Expr.makePi(Constants.NATURAL, Constants.TEXT)); mappings.put("Natural/subtract", Expr.makePi(Constants.NATURAL, naturalToNatural)); mappings.put("Integer/show", Expr.makePi(Constants.INTEGER, Constants.TEXT)); mappings.put("Integer/toDouble", Expr.makePi(Constants.INTEGER, Constants.DOUBLE)); mappings.put("Integer/negate", Expr.makePi(Constants.INTEGER, Constants.INTEGER)); mappings.put("Integer/clamp", Expr.makePi(Constants.INTEGER, Constants.NATURAL)); mappings.put("Double/show", Expr.makePi(Constants.DOUBLE, Constants.TEXT)); mappings.put("List/build", Expr.makePi("a", Constants.TYPE, Expr.makePi(listType, listA))); mappings.put("List/fold", Expr.makePi("a", Constants.TYPE, Expr.makePi(listA, listType))); mappings.put( "List/length", Expr.makePi("a", Expr.Constants.TYPE, Expr.makePi(listA, Constants.NATURAL))); mappings.put("List/head", listAToOptionalA); mappings.put("List/last", listAToOptionalA); Entry[] indexedRecordFields = { new SimpleImmutableEntry<>("index", Constants.NATURAL), new SimpleImmutableEntry<>("value", _a) }; mappings.put( "List/indexed", Expr.makePi( "a", Constants.TYPE, Expr.makePi( listA, Expr.makeApplication(Constants.LIST, Expr.makeRecordType(indexedRecordFields))))); mappings.put("List/reverse", Expr.makePi("a", Constants.TYPE, Expr.makePi(listA, listA))); mappings.put("Some", Constants.SOME); } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/typechecking/CheckEquivalence.java ================================================ package org.dhallj.core.typechecking; import org.dhallj.core.Expr; import org.dhallj.core.Operator; import org.dhallj.core.ExternalVisitor; final class CheckEquivalence extends ExternalVisitor.Constant { public static final ExternalVisitor instance = new CheckEquivalence(); CheckEquivalence() { super(null); } @Override public Boolean onOperatorApplication(Operator operator, Expr lhs, Expr rhs) { return operator.equals(Operator.EQUIVALENT) && lhs.equivalent(rhs); } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/typechecking/Context.java ================================================ package org.dhallj.core.typechecking; import org.dhallj.core.Expr; final class Context { private final String key; private final Expr value; private final Context tail; Context(String key, Expr value, Context tail) { this.key = key; this.value = value; this.tail = tail; } public Expr lookup(String targetKey, long index) { if (this.key != null && this.key.equals(targetKey)) { if (index == 0) { return this.value; } else { if (this.tail == null) { return null; } else { return this.tail.lookup(targetKey, index - 1); } } } else { if (this.tail == null) { return null; } else { return this.tail.lookup(targetKey, index); } } } public Context insert(String key, Expr value) { return new Context(key, value, this); } public Context increment(String name) { if (this.key == null) { return this; } else { return new Context(this.key, this.value.increment(name), this.tail.increment(name)); } } public Context decrement(String name) { if (this.key == null) { return this; } else { return new Context(this.key, this.value.decrement(name), this.tail.decrement(name)); } } public static final Context EMPTY = new Context(null, null, null); } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/typechecking/NonNegativeIndices.java ================================================ package org.dhallj.core.typechecking; import org.dhallj.core.Expr; import org.dhallj.core.Visitor; final class NonNegativeIndices extends Visitor.Property { public static final Visitor instance = new NonNegativeIndices(); @Override public Boolean onIdentifier(Expr self, String name, long index) { return index >= 0; } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/typechecking/TypeCheck.java ================================================ package org.dhallj.core.typechecking; 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.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import org.dhallj.core.Expr; import org.dhallj.core.ExternalVisitor; import org.dhallj.core.Operator; import org.dhallj.core.Source; import org.dhallj.core.Expr.Constants; import org.dhallj.core.normalization.BetaNormalize; public final class TypeCheck implements ExternalVisitor { private Context context; public TypeCheck(Context context) { this.context = context; } public TypeCheck() { this(Context.EMPTY); } @Override public final Expr onNote(Expr base, Source source) { return base.accept(this); } @Override public final Expr onNatural(BigInteger value) { return Constants.NATURAL; } @Override public final Expr onInteger(BigInteger value) { return Constants.INTEGER; } @Override public final Expr onDouble(double value) { return Constants.DOUBLE; } @Override public final Expr onDate(int year, int month, int day) { return Constants.DATE; } @Override public final Expr onTime(int hour, int minute, int second, BigDecimal fractional) { return Constants.TIME; } @Override public final Expr onTimeZone(int minutes) { return Constants.TIME_ZONE; } @Override public final Expr onBuiltIn(String name) { if (name.equals("Sort")) { throw TypeCheckFailure.makeSortError(); } else { Expr type = BuiltInTypes.getType(name); if (type != null) { return type; } else { throw TypeCheckFailure.makeUnboundVariableError(name); } } } @Override public final Expr onIdentifier(String name, long index) { Expr fromContext = this.context.lookup(name, index); if (fromContext != null) { return fromContext; } else { throw TypeCheckFailure.makeUnboundVariableError(name); } } @Override public final Expr onLambda(String param, Expr input, Expr result) { Expr inputType = input.accept(this); if (Universe.fromExpr(inputType) != null) { Context unshiftedContext = this.context; Expr inputNormalized = input.accept(BetaNormalize.instance); this.context = this.context.insert(param, inputNormalized).increment(param); Expr resultType = result.accept(this); this.context = unshiftedContext; return Expr.makePi(param, inputNormalized, resultType); } else { throw TypeCheckFailure.makeLambdaInputError(inputType); } } @Override public final Expr onPi(String param, Expr input, Expr result) { Expr inputType = input.accept(this); Context unshiftedContext = this.context; this.context = this.context.insert(param, input).increment(param); Expr resultType = result.accept(this); this.context = unshiftedContext; Universe inputTypeUniverse = Universe.fromExpr(inputType); if (inputTypeUniverse == null) { throw TypeCheckFailure.makePiInputError(inputType); } Universe resultTypeUniverse = Universe.fromExpr(resultType); if (resultTypeUniverse == null) { throw TypeCheckFailure.makePiOutputError(resultType); } return Universe.functionCheck(inputTypeUniverse, resultTypeUniverse).toExpr(); } @Override public final Expr onLet(String name, Expr type, Expr value, Expr body) { Expr valueType = value.accept(this); if (type != null) { // We must confirm that the annotation is well-typed. type.accept(this); if (!type.equivalent(valueType)) { throw TypeCheckFailure.makeAnnotationError(type, valueType); } } return body.substitute(name, value.accept(BetaNormalize.instance)).accept(this); } @Override public final Expr onText(String[] parts, Iterable interpolated) { for (Expr expr : interpolated) { Expr exprType = expr.accept(this); if (!isText(exprType)) { throw TypeCheckFailure.makeInterpolationError(expr, exprType); } } return Constants.TEXT; } @Override public final Expr onNonEmptyList(Iterable values, int size) { Iterator it = values.iterator(); Expr firstType = it.next().accept(this); if (isType(firstType.accept(this))) { while (it.hasNext()) { Expr elementType = it.next().accept(this); if (!elementType.equivalent(firstType)) { throw TypeCheckFailure.makeListTypeMismatchError(firstType, elementType); } } return Expr.makeApplication(Expr.Constants.LIST, firstType); } else { throw TypeCheckFailure.makeListTypeError(firstType); } } @Override public final Expr onEmptyList(Expr type) { // We verify that the type is well-typed. type.accept(this); Expr typeNormalized = type.accept(BetaNormalize.instance); Expr elementType = Expr.Util.getListArg(typeNormalized); if (elementType != null && isType(elementType.accept(this))) { return Expr.makeApplication(Constants.LIST, elementType); } else { throw TypeCheckFailure.makeListTypeError(elementType); } } @Override public final Expr onRecord(Iterable> fields, int size) { if (size == 0) { return Constants.EMPTY_RECORD_TYPE; } else { Map fieldTypes = new TreeMap<>(); for (Entry field : fields) { fieldTypes.put( field.getKey(), field.getValue().accept(this).accept(BetaNormalize.instance)); } Expr recordType = Expr.makeRecordType(fieldTypes.entrySet()); // The inferred type must also be well-typed. recordType.accept(this); return recordType; } } @Override public final Expr onRecordType(Iterable> fields, int size) { // Need to check for duplicates here; see: https://github.com/travisbrown/dhallj/issues/6 Set fieldNamesSeen = new HashSet<>(size); Universe max = Universe.TYPE; for (Entry field : fields) { String fieldName = field.getKey(); if (!fieldNamesSeen.add(fieldName)) { throw TypeCheckFailure.makeFieldDuplicateError(fieldName); } Expr type = field.getValue().accept(this); Universe universe = Universe.fromExpr(type); if (universe != null) { max = max.max(universe); } else { throw TypeCheckFailure.makeFieldTypeError(fieldName, type); } } return max.toExpr(); } @Override public final Expr onUnionType(Iterable> fields, int size) { Set fieldNamesSeen = new HashSet<>(size); Universe max = Universe.TYPE; for (Entry field : fields) { String fieldName = field.getKey(); if (!fieldNamesSeen.add(fieldName)) { throw TypeCheckFailure.makeAlternativeDuplicateError(fieldName); } Expr alternativeType = field.getValue(); if (alternativeType != null) { Expr type = alternativeType.accept(this); Universe universe = Universe.fromExpr(type); if (universe != null) { max = max.max(universe); } else { throw TypeCheckFailure.makeAlternativeTypeError(fieldName, type); } } } return max.toExpr(); } @Override public final Expr onFieldAccess(Expr base, String fieldName) { Expr baseType = base.accept(this); List> fields = Expr.Util.asRecordType(baseType); if (fields != null) { for (Entry field : fields) { if (field.getKey().equals(fieldName)) { return field.getValue(); } } throw TypeCheckFailure.makeFieldAccessRecordMissingError(fieldName); } else { Expr baseNormalized = base.accept(BetaNormalize.instance); List> alternatives = Expr.Util.asUnionType(baseNormalized); if (alternatives != null) { for (Entry alternative : alternatives) { if (alternative.getKey().equals(fieldName)) { if (alternative.getValue() == null) { return baseNormalized; } else { return Expr.makePi(fieldName, alternative.getValue(), baseNormalized); } } } throw TypeCheckFailure.makeFieldAccessUnionMissingError(fieldName); } else { throw TypeCheckFailure.makeFieldAccessError(); } } } @Override public final Expr onProjection(Expr base, String[] fieldNames) { List> fields = Expr.Util.asRecordType(base.accept(this)); if (fields != null) { Map fieldMap = new HashMap<>(); for (Entry field : fields) { fieldMap.put(field.getKey(), field.getValue()); } List> newFields = new ArrayList(); List missing = null; for (String fieldName : fieldNames) { Expr value = fieldMap.remove(fieldName); if (value == null) { if (missing == null) { missing = new ArrayList<>(); } missing.add(fieldName); } else { newFields.add(new SimpleImmutableEntry<>(fieldName, value)); } } if (missing == null) { return Expr.makeRecordType(newFields); } else { throw TypeCheckFailure.makeFieldAccessRecordMissingError(missing.get(0)); } } else { throw TypeCheckFailure.makeProjectionError(); } } @Override public final Expr onProjectionByType(Expr base, Expr type) { List> fields = Expr.Util.asRecordType(base.accept(this)); if (fields == null) { throw TypeCheckFailure.makeProjectionError(); } else { List> projected = Expr.Util.asRecordType(type.accept(BetaNormalize.instance)); if (projected == null) { throw TypeCheckFailure.makeProjectionError(); } else { Map fieldMap = new HashMap<>(); for (Entry field : fields) { fieldMap.put(field.getKey(), field.getValue()); } for (Entry projectedEntry : projected) { String fieldName = projectedEntry.getKey(); Expr value = fieldMap.get(fieldName); Expr projectedValue = projectedEntry.getValue(); if (value == null || !value.equivalent(projectedValue)) { throw TypeCheckFailure.makeFieldAccessRecordMissingError(fieldName); } } return Expr.makeRecordType(projected); } } } @Override public final Expr onApplication(Expr base, Expr arg) { Expr baseType = base.accept(this); Expr argType = arg.accept(this); Expr result = baseType.accept(new TypeCheckApplication(arg, argType, this)); if (result != null) { return result; } else { throw TypeCheckFailure.makeApplicationError(base, arg); } } @Override public final Expr onOperatorApplication(Operator operator, Expr lhs, Expr rhs) { Expr lhsType = lhs.accept(this); Expr rhsType = rhs.accept(this); switch (operator) { case OR: case AND: case EQUALS: case NOT_EQUALS: if (isBool(lhsType) && isBool(rhsType)) { return Constants.BOOL; } else { throw TypeCheckFailure.makeOperatorError(operator); } case PLUS: case TIMES: if (isNatural(lhsType) && isNatural(rhsType)) { return Constants.NATURAL; } else { throw TypeCheckFailure.makeOperatorError(operator); } case TEXT_APPEND: if (isText(lhsType) && isText(rhsType)) { return Constants.TEXT; } else { throw TypeCheckFailure.makeOperatorError(operator); } case LIST_APPEND: Expr lhsListElementType = Expr.Util.getListArg(lhsType); Expr rhsListElementType = Expr.Util.getListArg(rhsType); if (lhsListElementType != null && rhsListElementType != null) { if (lhsListElementType.equivalent(rhsListElementType)) { return lhsType; } else { throw TypeCheckFailure.makeListAppendError(lhsListElementType, rhsListElementType); } } else { throw TypeCheckFailure.makeOperatorError(operator); } case COMBINE: Expr combineTypes = Expr.makeOperatorApplication(Operator.COMBINE_TYPES, lhsType, rhsType); // Type-check the type-level version, although we don't use the result; try { combineTypes.accept(this); } catch (TypeCheckFailure e) { throw TypeCheckFailure.makeOperatorError(operator); } return combineTypes.accept(BetaNormalize.instance); case PREFER: List> lhsTypeRecordType = Expr.Util.asRecordType(lhsType); List> rhsTypeRecordType = Expr.Util.asRecordType(rhsType); if (lhsTypeRecordType != null && rhsTypeRecordType != null) { return Expr.makeRecordType(prefer(lhsTypeRecordType, rhsTypeRecordType)); } else { throw TypeCheckFailure.makeOperatorError(operator); } case COMBINE_TYPES: List> lhsRecordType = Expr.Util.asRecordType(lhs.accept(BetaNormalize.instance)); List> rhsRecordType = Expr.Util.asRecordType(rhs.accept(BetaNormalize.instance)); if (lhsRecordType != null && rhsRecordType != null) { if (isType(rhsType) && !rhsRecordType.iterator().hasNext()) { return lhsType; } else { Universe lhsTypeUniverse = Universe.fromExpr(lhsType); Universe rhsTypeUniverse = Universe.fromExpr(rhsType); if (lhsTypeUniverse != null && rhsTypeUniverse != null) { // TODO: report collisions correctly. checkRecursiveTypeMerge(lhsRecordType, rhsRecordType); return lhsTypeUniverse.max(rhsTypeUniverse).toExpr(); } else { throw TypeCheckFailure.makeOperatorError(operator); } } } else { throw TypeCheckFailure.makeOperatorError(operator); } case IMPORT_ALT: // TODO: Confirm that this is correct. return lhsType.accept(this); case EQUIVALENT: Expr lhsTypeType = lhsType.accept(this); Expr rhsTypeType = rhsType.accept(this); if (lhsTypeType != null && rhsTypeType != null && isType(lhsTypeType) && isType(rhsTypeType)) { if (lhsType.equivalent(rhsType)) { return Constants.TYPE; } else { throw TypeCheckFailure.makeEquivalenceError(lhsType, rhsType); } } else { throw TypeCheckFailure.makeOperatorError(operator); } case COMPLETE: return Expr.Util.desugarComplete(lhs, rhs).accept(this); default: return null; } } @Override public final Expr onIf(Expr predicate, Expr thenValue, Expr elseValue) { Expr predicateType = predicate.accept(this); if (isBool(predicateType)) { Expr thenType = thenValue.accept(this); Expr elseType = elseValue.accept(this); if (thenType.equals(Expr.Constants.SORT) || elseType.equals(Expr.Constants.SORT)) { throw TypeCheckFailure.makeSortError(); } if (thenType.equivalent(elseType)) { return thenType; } else { throw TypeCheckFailure.makeIfBranchTypeMismatchError(thenType, elseType); } } else { throw TypeCheckFailure.makeIfPredicateError(predicateType); } } @Override public final Expr onAnnotated(Expr base, Expr type) { Expr inferredType = base.accept(this); if (inferredType.equivalent(type)) { return inferredType; } else { throw TypeCheckFailure.makeAnnotationError(type, inferredType); } } @Override public final Expr onAssert(Expr base) { Expr baseType = base.accept(this); if (isType(baseType)) { Expr normalized = base.accept(BetaNormalize.instance); Boolean isEquivalent = normalized.accept(CheckEquivalence.instance); if (isEquivalent != null && isEquivalent) { return normalized; } } throw TypeCheckFailure.makeAssertError(base); } @Override public final Expr onMerge(Expr handlers, Expr union, Expr type) { Expr handlersType = handlers.accept(this); List> handlersTypeFields = Expr.Util.asRecordType(handlersType); if (handlersTypeFields == null) { // The handlers argument is not a record. throw TypeCheckFailure.makeMergeHandlersTypeError(handlersType); } else { Expr unionType = union.accept(this); List> unionTypeFields = Expr.Util.asUnionType(unionType); if (unionTypeFields != null) { Expr inferredType = getMergeInferredType(handlersTypeFields, unionTypeFields); if (inferredType != null) { if (type == null || inferredType.equivalent(type)) { return inferredType; } else { throw TypeCheckFailure.makeMergeInvalidAnnotationError(type, inferredType); } } else if (type != null) { return type; } else { // Both were empty (this shouldn't happen). throw TypeCheckFailure.makeMergeUnionTypeError(type); } } else { Expr optionalElementType = Expr.Util.getOptionalArg(unionType); if (optionalElementType == null) { throw TypeCheckFailure.makeMergeUnionTypeError(unionType); } else { Expr inferredType = getMergeInferredType( handlersTypeFields, makeOptionalConstructors(optionalElementType)); if (inferredType != null) { if (type == null || inferredType.equivalent(type)) { return inferredType; } else { throw TypeCheckFailure.makeMergeInvalidAnnotationError(type, inferredType); } } else if (type != null) { return type; } else { // Both were empty (this shouldn't happen). throw TypeCheckFailure.makeMergeUnionTypeError(type); } } } } } @Override public final Expr onToMap(Expr base, Expr type) { Expr baseType = base.accept(this); List> baseAsRecord = Expr.Util.asRecordType(baseType); if (baseAsRecord == null) { throw TypeCheckFailure.makeToMapTypeError(baseType); } else { Expr firstType = null; for (Entry entry : baseAsRecord) { Expr fieldType = entry.getValue(); if (!isType(fieldType.accept(this))) { throw TypeCheckFailure.makeToMapRecordKindError(fieldType); } else { if (firstType == null) { firstType = fieldType; } else { if (!fieldType.equivalent(firstType)) { throw TypeCheckFailure.makeToMapRecordTypeMismatchError(firstType, fieldType); } } } } // The input record is non-empty. if (firstType != null) { Entry[] inferredTypeFields = { new SimpleImmutableEntry<>(Constants.MAP_KEY_FIELD_NAME, Constants.TEXT), new SimpleImmutableEntry<>(Constants.MAP_VALUE_FIELD_NAME, firstType) }; Expr inferredType = Expr.makeApplication(Constants.LIST, Expr.makeRecordType(inferredTypeFields)); if (type == null || type.equivalent(inferredType)) { return inferredType; } else { throw TypeCheckFailure.makeToMapResultTypeMismatchError(type, inferredType); } } else { if (type == null) { throw TypeCheckFailure.makeToMapMissingAnnotationError(); } else { Expr typeType = type.accept(this); if (!isType(typeType)) { throw TypeCheckFailure.makeToMapInvalidAnnotationError(type); } else { Expr typeNormalized = type.accept(BetaNormalize.instance); Expr listElementType = Expr.Util.getListArg(typeNormalized); if (listElementType == null) { throw TypeCheckFailure.makeToMapInvalidAnnotationError(type); } else { List> typeFields = Expr.Util.asRecordType(listElementType); if (typeFields == null) { throw TypeCheckFailure.makeToMapInvalidAnnotationError(type); } else { boolean sawKey = false; boolean sawValue = false; for (Entry typeField : typeFields) { if (sawKey && sawValue) { throw TypeCheckFailure.makeToMapInvalidAnnotationError(type); } else { if (typeField.getKey().equals(Constants.MAP_KEY_FIELD_NAME)) { if (isText(typeField.getValue())) { sawKey = true; } else { throw TypeCheckFailure.makeToMapInvalidAnnotationError(type); } } else if (typeField.getKey().equals(Constants.MAP_VALUE_FIELD_NAME)) { sawValue = true; } else { throw TypeCheckFailure.makeToMapInvalidAnnotationError(type); } } } if (sawKey && sawValue) { return typeNormalized; } else { throw TypeCheckFailure.makeToMapInvalidAnnotationError(type); } } } } } } } } @Override public final Expr onWith(Expr base, String[] path, Expr value) { List> baseAsRecordLiteral = Expr.Util.asRecordLiteral(base); if (baseAsRecordLiteral == null) { throw TypeCheckFailure.makeWithTypeError(base.accept(this)); } else { List> result = new ArrayList<>(); boolean found = false; for (Entry field : baseAsRecordLiteral) { String key = field.getKey(); if (key.equals(path[0])) { found = true; Expr newValue; if (path.length == 1) { newValue = value.accept(this); } else { String[] remainingPath = new String[path.length - 1]; System.arraycopy(path, 1, remainingPath, 0, path.length - 1); newValue = Expr.makeWith(field.getValue(), remainingPath, value).accept(this); } result.add(new SimpleImmutableEntry<>(key, newValue)); } else { result.add(new SimpleImmutableEntry<>(key, field.getValue().accept(this))); } } if (!found) { Expr newValue; if (path.length == 1) { newValue = value.accept(this); } else { String[] remainingPath = new String[path.length - 1]; System.arraycopy(path, 1, remainingPath, 0, path.length - 1); newValue = Expr.makeWith(Expr.Constants.EMPTY_RECORD_LITERAL, remainingPath, value).accept(this); } result.add(new SimpleImmutableEntry<>(path[0], newValue)); } Entry[] resultArray = result.toArray(new Entry[result.size()]); Arrays.sort(resultArray, entryComparator); return Expr.makeRecordType(resultArray); } } @Override public final Expr onMissingImport(Expr.ImportMode mode, byte[] hash) { throw TypeCheckFailure.makeUnresolvedImportError(); } @Override public final Expr onEnvImport(String value, Expr.ImportMode mode, byte[] hash) { throw TypeCheckFailure.makeUnresolvedImportError(); } @Override public final Expr onLocalImport(Path path, Expr.ImportMode mode, byte[] hash) { throw TypeCheckFailure.makeUnresolvedImportError(); } @Override public final Expr onClasspathImport(Path path, Expr.ImportMode mode, byte[] hash) { throw TypeCheckFailure.makeUnresolvedImportError(); } @Override public final Expr onRemoteImport(URI url, Expr using, Expr.ImportMode mode, byte[] hash) { throw TypeCheckFailure.makeUnresolvedImportError(); } static final boolean isBool(Expr expr) { String asBuiltIn = Expr.Util.asBuiltIn(expr); return asBuiltIn != null && asBuiltIn.equals("Bool"); } static final boolean isText(Expr expr) { String asBuiltIn = Expr.Util.asBuiltIn(expr); return asBuiltIn != null && asBuiltIn.equals("Text"); } static final boolean isList(Expr expr) { String asBuiltIn = Expr.Util.asBuiltIn(expr); return asBuiltIn != null && asBuiltIn.equals("List"); } static final boolean isNatural(Expr expr) { String asBuiltIn = Expr.Util.asBuiltIn(expr); return asBuiltIn != null && asBuiltIn.equals("Natural"); } static final boolean isOptional(Expr expr) { String asBuiltIn = Expr.Util.asBuiltIn(expr); return asBuiltIn != null && asBuiltIn.equals("Optional"); } static final boolean isType(Expr expr) { String asBuiltIn = Expr.Util.asBuiltIn(expr); return asBuiltIn != null && asBuiltIn.equals("Type"); } private final void checkRecursiveTypeMerge( List> lhs, List> rhs) { Map lhsMap = new HashMap<>(); for (Entry entry : lhs) { lhsMap.put(entry.getKey(), entry.getValue()); } for (Entry entry : rhs) { Expr rhsValue = entry.getValue(); Expr lhsValue = lhsMap.get(entry.getKey()); if (lhsValue != null) { Expr.makeOperatorApplication(Operator.COMBINE_TYPES, lhsValue, rhsValue).accept(this); } } } private static final List> makeOptionalConstructors(Expr type) { List> constructors = new ArrayList<>(); constructors.add((Entry) new SimpleImmutableEntry<>("None", (Expr) null)); constructors.add((Entry) new SimpleImmutableEntry<>("Some", type)); return constructors; } private static final Expr getMergeInferredType( List> handlerTypes, List> constructors) { Map handlerTypeMap = new HashMap<>(); for (Entry handlerTypeField : handlerTypes) { handlerTypeMap.put(handlerTypeField.getKey(), handlerTypeField.getValue()); } Expr resultType = null; for (Entry constructor : constructors) { String fieldName = constructor.getKey(); Expr handlerType = handlerTypeMap.remove(fieldName); if (handlerType == null) { throw TypeCheckFailure.makeMergeHandlerMissingError(fieldName); } else { final Expr constructorType = constructor.getValue(); if (constructorType == null) { // We have an empty constructor. if (resultType == null) { // This is the first constructor. resultType = handlerType; } else { // We check that the handler type is the same as previous result types. if (!handlerType.equivalent(resultType)) { throw TypeCheckFailure.makeMergeHandlerTypeMismatchError(resultType, handlerType); } } } else { // We have a constructor with a type, so we have to make sure the handler is a function. Expr handlerResultType = handlerType.accept( new ExternalVisitor.Constant(null) { public Expr onPi(String name, Expr input, Expr result) { if (!input.equivalent(constructorType)) { throw TypeCheckFailure.makeMergeHandlerTypeInvalidError( constructorType, input); } else { Expr inferredResultType = result.decrement(name); if (!inferredResultType.accept(NonNegativeIndices.instance)) { throw TypeCheckFailure.makeMergeHandlerTypeDisallowedError( inferredResultType); } return inferredResultType; } } }); if (handlerResultType == null) { throw TypeCheckFailure.makeMergeHandlerTypeNotFunctionError( fieldName, constructorType, handlerType); } else if (resultType == null) { resultType = handlerResultType; } else { // We check that the handler result type is the same as previous result types. if (!handlerResultType.equivalent(resultType)) { throw TypeCheckFailure.makeMergeHandlerTypeMismatchError( resultType, handlerResultType); } } } } } if (handlerTypeMap.isEmpty()) { return resultType; } else { throw TypeCheckFailure.makeMergeHandlerUnusedError(handlerTypeMap.keySet().iterator().next()); } } private static final Entry[] prefer( List> base, List> updates) { Map updateMap = new LinkedHashMap<>(); for (Entry field : updates) { updateMap.put(field.getKey(), field.getValue()); } List> result = new ArrayList<>(); for (Entry field : base) { String key = field.getKey(); Expr inUpdates = updateMap.remove(key); if (inUpdates == null) { result.add(field); } else { result.add(new SimpleImmutableEntry<>(key, inUpdates)); } } for (Entry field : updateMap.entrySet()) { result.add(field); } Entry[] resultArray = result.toArray(new Entry[result.size()]); Arrays.sort(resultArray, entryComparator); return resultArray; } /** Java 8 introduce {@code comparingByKey}, but we can roll our own pretty easily. */ private static final Comparator> entryComparator = new Comparator>() { public int compare(Entry a, Entry b) { return a.getKey().compareTo(b.getKey()); } }; } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/typechecking/TypeCheckApplication.java ================================================ package org.dhallj.core.typechecking; import org.dhallj.core.Expr; import org.dhallj.core.ExternalVisitor; final class TypeCheckApplication extends ExternalVisitor.Constant { private final Expr arg; private final Expr argType; private final TypeCheck typeCheck; TypeCheckApplication(Expr arg, Expr argType, TypeCheck typeCheck) { super(null); this.arg = arg; this.argType = argType; this.typeCheck = typeCheck; } @Override public Expr onBuiltIn(String name) { if (name.equals("Some")) { if (TypeCheck.isType(this.argType.accept(this.typeCheck))) { return Expr.makeApplication(Expr.Constants.OPTIONAL, this.argType); } else { throw TypeCheckFailure.makeSomeApplicationError(this.arg, this.argType); } } throw TypeCheckFailure.makeBuiltInApplicationError(name, this.arg, this.argType); } @Override public Expr onPi(String name, Expr input, Expr result) { if (input.equivalent(this.argType)) { return result.substitute(name, arg).normalize(); } else { throw TypeCheckFailure.makeApplicationTypeError(input, this.argType); } } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/typechecking/TypeCheckFailure.java ================================================ package org.dhallj.core.typechecking; import org.dhallj.core.DhallException; import org.dhallj.core.Expr; import org.dhallj.core.Operator; public final class TypeCheckFailure extends DhallException { @Override public Throwable fillInStackTrace() { // This is a failure type; stack traces aren't useful. return this; } TypeCheckFailure(String message) { super(message); } static final TypeCheckFailure makeSortError() { return new TypeCheckFailure("Sort has no type, kind, or sort"); } static final TypeCheckFailure makeUnboundVariableError(String name) { return new TypeCheckFailure("Unbound variable: " + name); } static final TypeCheckFailure makeOperatorError(Operator operator) { String message; if (operator == Operator.OR || operator == Operator.AND || operator == Operator.EQUALS || operator == Operator.NOT_EQUALS) { message = operator.toString() + " only works on Bools"; } else if (operator == Operator.PLUS || operator == Operator.TIMES) { message = operator.toString() + " only works on Naturals"; } else if (operator == Operator.TEXT_APPEND) { message = operator.toString() + " only works on Text"; } else if (operator == Operator.LIST_APPEND) { message = operator.toString() + " only works on Lists"; } else if (operator == Operator.COMBINE || operator == Operator.PREFER) { message = "You can only combine records"; } else if (operator == Operator.COMBINE_TYPES) { message = operator.toString() + " requires arguments that are record types"; } else if (operator == Operator.EQUIVALENT) { message = "Incomparable expression"; } else { message = "Operator error on " + operator.toString(); } return new TypeCheckFailure(message); } static final TypeCheckFailure makeListAppendError(Expr lhs, Expr rhs) { return new TypeCheckFailure("You can only append Lists with matching element types"); } static final TypeCheckFailure makeEquivalenceError(Expr lhs, Expr rhs) { return new TypeCheckFailure("You can only append Lists with matching element types"); } static final TypeCheckFailure makeInterpolationError(Expr interpolated, Expr interpolatedType) { return new TypeCheckFailure("You can only interpolate Text"); } static final TypeCheckFailure makeSomeApplicationError(Expr arg, Expr argType) { return new TypeCheckFailure("Some argument has the wrong type"); } static final TypeCheckFailure makeBuiltInApplicationError(String name, Expr arg, Expr argType) { return new TypeCheckFailure("Can't apply " + name); } static final TypeCheckFailure makeApplicationTypeError(Expr expected, Expr received) { return new TypeCheckFailure("Wrong type of function argument"); } static final TypeCheckFailure makeApplicationError(Expr base, Expr arg) { return new TypeCheckFailure("Not a function"); } static final TypeCheckFailure makeUnresolvedImportError() { return new TypeCheckFailure("Can't type-check unresolved import"); } static final TypeCheckFailure makeIfPredicateError(Expr type) { return new TypeCheckFailure("Invalid predicate for if"); } static final TypeCheckFailure makeIfBranchTypeMismatchError(Expr thenType, Expr elseType) { return new TypeCheckFailure("if branches must have matching types"); } static final TypeCheckFailure makeLambdaInputError(Expr type) { return new TypeCheckFailure("Invalid function input"); } static final TypeCheckFailure makePiInputError(Expr type) { return new TypeCheckFailure("Invalid function input"); } static final TypeCheckFailure makePiOutputError(Expr type) { return new TypeCheckFailure("Invalid function output"); } static final TypeCheckFailure makeAssertError(Expr type) { return new TypeCheckFailure("Not an equivalence"); } static final TypeCheckFailure makeFieldAccessError() { return new TypeCheckFailure("Not a record or union"); } static final TypeCheckFailure makeFieldAccessRecordMissingError(String fieldName) { return new TypeCheckFailure("Missing record field: " + fieldName); } static final TypeCheckFailure makeFieldAccessUnionMissingError(String fieldName) { return new TypeCheckFailure("Missing constructor: " + fieldName); } static final TypeCheckFailure makeProjectionError() { return new TypeCheckFailure("Not a record"); } static final TypeCheckFailure makeFieldTypeError(String fieldName, Expr type) { return new TypeCheckFailure("Invalid field type"); } static final TypeCheckFailure makeFieldDuplicateError(String fieldName) { return new TypeCheckFailure("duplicate field: " + fieldName); } static final TypeCheckFailure makeListTypeMismatchError(Expr type1, Expr type2) { return new TypeCheckFailure("List elements should all have the same type"); } static final TypeCheckFailure makeListTypeError(Expr type) { return new TypeCheckFailure("Invalid type for List"); } static final TypeCheckFailure makeAnnotationError(Expr expected, Expr received) { return new TypeCheckFailure("Expression doesn't match annotation"); } static final TypeCheckFailure makeAlternativeTypeError(String fieldName, Expr type) { return new TypeCheckFailure("Invalid alternative type"); } /** Not sure under what conditions this wouldn't be caught by the parser. */ static final TypeCheckFailure makeAlternativeDuplicateError(String fieldName) { return new TypeCheckFailure("duplicate field: " + fieldName); } static final TypeCheckFailure makeMergeHandlersTypeError(Expr type) { return new TypeCheckFailure("merge expects a record of handlers"); } static final TypeCheckFailure makeMergeUnionTypeError(Expr type) { return new TypeCheckFailure("toMap expects a union or an Optional"); } static final TypeCheckFailure makeMergeHandlerMissingError(String fieldName) { return new TypeCheckFailure("Missing handler: " + fieldName); } static final TypeCheckFailure makeMergeHandlerUnusedError(String fieldName) { return new TypeCheckFailure("Unused handler: " + fieldName); } static final TypeCheckFailure makeMergeHandlerTypeInvalidError(Expr expected, Expr type) { return new TypeCheckFailure("Wrong handler input type"); } static final TypeCheckFailure makeMergeHandlerTypeNotFunctionError( String fieldName, Expr expected, Expr type) { return new TypeCheckFailure("Handler for " + fieldName + " is not a function"); } static final TypeCheckFailure makeMergeHandlerTypeMismatchError(Expr type1, Expr type2) { return new TypeCheckFailure("Handlers should have the same output type"); } static final TypeCheckFailure makeMergeHandlerTypeDisallowedError(Expr type) { return new TypeCheckFailure("Disallowed handler type"); } static final TypeCheckFailure makeMergeInvalidAnnotationError(Expr expected, Expr inferred) { return new TypeCheckFailure("Expression doesn't match annotation"); } static final TypeCheckFailure makeToMapTypeError(Expr type) { return new TypeCheckFailure("toMap expects a record value"); } static final TypeCheckFailure makeToMapRecordKindError(Expr type) { return new TypeCheckFailure("toMap expects a record of kind Type"); } static final TypeCheckFailure makeToMapRecordTypeMismatchError(Expr type1, Expr type2) { return new TypeCheckFailure("toMap expects a homogenous record"); } static final TypeCheckFailure makeToMapResultTypeMismatchError(Expr expected, Expr inferred) { return new TypeCheckFailure("toMap result type doesn't match annotation"); } static final TypeCheckFailure makeToMapMissingAnnotationError() { return new TypeCheckFailure("An empty toMap requires a type annotation"); } static final TypeCheckFailure makeToMapInvalidAnnotationError(Expr type) { return new TypeCheckFailure("An empty toMap was annotated with an invalid type"); } static final TypeCheckFailure makeWithTypeError(Expr type) { return new TypeCheckFailure("with only works on records"); } } ================================================ FILE: modules/core/src/main/java/org/dhallj/core/typechecking/Universe.java ================================================ package org.dhallj.core.typechecking; import org.dhallj.core.Expr; public enum Universe { TYPE, KIND, SORT; public final Universe max(Universe other) { if (this == SORT || other == SORT) { return SORT; } else if (this == KIND || other == KIND) { return KIND; } else { return TYPE; } } public final Expr toExpr() { if (this == TYPE) { return Expr.Constants.TYPE; } else if (this == KIND) { return Expr.Constants.KIND; } else { return Expr.Constants.SORT; } } public static final boolean isUniverse(Expr expr) { return fromExpr(expr) != null; } public static final Universe fromExpr(Expr expr) { String name = Expr.Util.asBuiltIn(expr); if (name != null) { if (name.equals("Type")) { return TYPE; } else if (name.equals("Kind")) { return KIND; } else if (name.equals("Sort")) { return SORT; } } return null; } public static Universe functionCheck(Universe input, Universe output) { if (input == null || output == null) { return null; } if (output == Universe.TYPE) { return Universe.TYPE; } else { return input.max(output); } } } ================================================ FILE: modules/core/src/test/java/org/dhallj/cbor/CborSuite.scala ================================================ package org.dhallj.cbor import co.nstant.in.cbor.{CborBuilder, CborEncoder} import java.io.ByteArrayOutputStream import java.math.BigInteger import munit.ScalaCheckSuite import org.scalacheck.Prop class CborSuite extends ScalaCheckSuite { def roundTripDouble(value: Double): Option[Double] = { val writer = new Writer.ByteArrayWriter() writer.writeDouble(value) val bytes = writer.getBytes val reader = new Reader.ByteArrayReader(bytes) reader.nextSymbol(DoubleValueVisitor) } def encodeDoubleWithCborJava(value: Double): Array[Byte] = { val stream = new ByteArrayOutputStream() new CborEncoder(stream).encode(new CborBuilder().add(value).build()) return stream.toByteArray } property("Writer and Reader should round-trip doubles") { Prop.forAll((value: Double) => roundTripDouble(value) == Some(value)) } property("Writer should agree with cbor-java") { Prop.forAll { (value: Double) => val writer = new Writer.ByteArrayWriter() writer.writeDouble(value) writer.getBytes.sameElements(encodeDoubleWithCborJava(value)) } } test("Writer and Reader should round-trip special-case doubles") { assertEquals(roundTripDouble(0.0), Some(0.0)) assertEquals(roundTripDouble(-0.0), Some(-0.0)) assertEquals(roundTripDouble(Double.PositiveInfinity), Some(Double.PositiveInfinity)) assertEquals(roundTripDouble(Double.NegativeInfinity), Some(Double.NegativeInfinity)) assert(roundTripDouble(Double.NaN).exists(_.isNaN)) } object DoubleValueVisitor extends Visitor[Option[Double]] { def onUnsignedInteger(value: BigInteger): Option[Double] = None def onNegativeInteger(value: BigInteger): Option[Double] = None def onByteString(value: Array[Byte]): Option[Double] = None def onTextString(value: String): Option[Double] = None def onVariableArray(length: BigInteger, name: String): Option[Double] = None def onArray(length: BigInteger, tagI: BigInteger): Option[Double] = None def onMap(size: BigInteger): Option[Double] = None def onFalse: Option[Double] = None def onTrue: Option[Double] = None def onNull: Option[Double] = None def onHalfFloat(value: Float): Option[Double] = Some(value.toDouble) def onSingleFloat(value: Float): Option[Double] = Some(value.toDouble) def onDoubleFloat(value: Double): Option[Double] = Some(value) def onTag: Option[Double] = None } } ================================================ FILE: modules/core/src/test/java/org/dhallj/cbor/HalfFloatSuite.scala ================================================ package org.dhallj.cbor import munit.FunSuite import org.scalacheck.Prop class HalfFloatSuite extends FunSuite { def roundTripInt(x: Int): Int = HalfFloat.toFloat(HalfFloat.fromFloat(x.toFloat)).toInt test("HalfFloat conversions round-trip integers with abs <= 2048") { 0.to(2048).foreach { x => assertEquals(roundTripInt(x), x) assertEquals(roundTripInt(-x), -x) } } test("HalfFloat conversions round-trip even integers with abs <= 4096") { 1.to(1024).foreach { x => assertEquals(roundTripInt((x * 2) + 2048), (x * 2) + 2048) assertEquals(roundTripInt(-((x * 2) + 2048)), -((x * 2) + 2048)) assertEquals(roundTripInt((x * 2) + 2048 - 1), (x * 2) + 2048) assertEquals(roundTripInt(-((x * 2) + 2048 - 1)), -((x * 2) + 2048)) } } test("HalfFloat conversions round-trip through float for all values") { 0.until(1 << 16).foreach { x => val asFloat = HalfFloat.toFloat(x) if (!asFloat.isNaN) { assertEquals(HalfFloat.fromFloat(asFloat), x) } } } } ================================================ FILE: modules/imports/README.md ================================================ # Imports A reference implementation of import resolution using [Cats Effect](https://typelevel.org/cats-effect/). ================================================ FILE: modules/imports/src/main/scala/org/dhallj/imports/Canonicalization.scala ================================================ package org.dhallj.imports import java.nio.file.{Path, Paths} import cats.{ApplicativeError, MonadError} import cats.implicits._ import cats.effect.Sync import org.dhallj.core.DhallException.ResolutionFailure import org.dhallj.imports.ImportContext._ import scala.collection.JavaConverters._ object Canonicalization { def canonicalize[F[_]](imp: ImportContext)(implicit F: ApplicativeError[F, Throwable]): F[ImportContext] = imp match { case Remote(uri, headers) => F.pure(Remote(uri.normalize, headers)) case Local(path) => LocalFile[F](path).map(_.canonicalize.toPath).map(Local) case Classpath(path) => LocalFile[F](path).map(_.canonicalize.toPath).map(Classpath) case i => F.pure(i) } def canonicalize[F[_]](parent: ImportContext, child: ImportContext)(implicit F: MonadError[F, Throwable] ): F[ImportContext] = parent match { case Remote(uri, headers) => child match { //A transitive relative import is parsed as local but is resolved as a remote import // eg https://github.com/dhall-lang/dhall-lang/blob/master/Prelude/Integer/add has a local import but we still //need to resolve this as a remote import //Also note that if the path is absolute then this violates referential sanity but we handle that elsewhere case Local(path) => if (path.isAbsolute) canonicalize[F](child) else canonicalize[F](Remote(uri.resolve(path.toString), headers)) case _ => canonicalize[F](child) } case Local(path) => child match { case Local(path2) => for { parent <- LocalFile[F](path) c <- LocalFile[F](path2) } yield Local(parent.chain(c).canonicalize.toPath) case _ => canonicalize[F](child) } //TODO - determine semantics of classpath imports case Classpath(path) => child match { //Also note that if the path is absolute then this violates referential sanity but we handle that elsewhere case Local(path2) => for { parent <- LocalFile[F](path) c <- LocalFile[F](path2) } yield Classpath(parent.chain(c).canonicalize.toPath) case _ => canonicalize[F](child) } case _ => canonicalize[F](child) } private case class LocalFile(dirs: LocalDirs, filename: String) { def toPath: Path = { def toPath(l: List[String]) = "/" + l.intercalate("/") val s: String = dirs.ds match { case Nil => "" case l @ (h :: t) => h match { case h if h == "." || h == ".." || h == "~" => s"$h${toPath(t)}" case _ => toPath(l) } } Paths.get(s"$s/$filename") } def chain(other: LocalFile): LocalFile = LocalFile.chain(this, other) def canonicalize: LocalFile = LocalFile.canonicalize(this) } private object LocalFile { def apply[F[_]](path: Path)(implicit F: ApplicativeError[F, Throwable]): F[LocalFile] = path.iterator().asScala.toList.map(_.toString) match { case Nil => F.raiseError(new ResolutionFailure("This shouldn't happen - / can't import a dhall expression")) case l => F.pure(LocalFile(LocalDirs(l.take(l.length - 1)), l.last)) } def canonicalize(f: LocalFile): LocalFile = LocalFile(f.dirs.canonicalize, f.filename.stripPrefix("\"").stripSuffix("\"")) def chain(lhs: LocalFile, rhs: LocalFile): LocalFile = LocalFile(LocalDirs.chain(lhs.dirs, rhs.dirs), rhs.filename) } private case class LocalDirs(ds: List[String]) { def isRelative: Boolean = ds.nonEmpty && (ds.head == "." || ds.head == "..") def canonicalize: LocalDirs = LocalDirs.canonicalize(this) def chain(other: LocalDirs): LocalDirs = LocalDirs.chain(this, other) } private object LocalDirs { def chain(lhs: LocalDirs, rhs: LocalDirs): LocalDirs = if (rhs.isRelative) LocalDirs(lhs.ds ++ rhs.ds) else rhs def canonicalize(d: LocalDirs): LocalDirs = d.ds match { case Nil => d case l => LocalDirs(canonicalize(l.map(_.stripPrefix("\"").stripSuffix("\"")))) } def canonicalize(l: List[String]): List[String] = l.tail .foldLeft(List(l.head)) { (acc, next) => next match { case "." => acc case ".." => acc.tail case o => o :: acc } } .reverse } } ================================================ FILE: modules/imports/src/main/scala/org/dhallj/imports/CorsComplianceCheck.scala ================================================ package org.dhallj.imports import java.net.URI import cats.effect.Sync import org.dhallj.core.DhallException.ResolutionFailure import org.http4s.Headers import org.typelevel.ci.CIString object CorsComplianceCheck { def apply[F[_]](parent: ImportContext, child: ImportContext, headers: Headers)(implicit F: Sync[F]): F[Unit] = parent match { case ImportContext.Remote(uri, _) => child match { case ImportContext.Remote(uri2, _) => if (sameOrigin(uri, uri2)) F.unit else headers .get(CIString("Access-Control-Allow-Origin")) .fold( F.raiseError[Unit]( new ResolutionFailure( s"CORS compliance failure - No Access-Control-Allow-Origin header for import $uri2 from $uri" ) ) ) { h => if (h.head.value.trim == "*" || sameOrigin(new URI(h.head.value), uri)) F.unit else F.raiseError( new ResolutionFailure( s"CORS compliance failure - ${h.head.value.trim} is invalid for import $uri2 from $uri" ) ) } case _ => F.unit } case _ => F.unit } private def sameOrigin(uri: URI, uri2: URI): Boolean = uri.getScheme == uri2.getScheme && uri.getAuthority == uri2.getAuthority && uri.getPort == uri2.getPort } ================================================ FILE: modules/imports/src/main/scala/org/dhallj/imports/ImportCache.scala ================================================ package org.dhallj.imports import cats.Applicative import cats.effect.Async import cats.implicits._ import java.nio.file.{Files, Path, Paths} import org.dhallj.core.DhallException.ResolutionFailure trait ImportCache[F[_]] { def get(key: Array[Byte]): F[Option[Array[Byte]]] def put(key: Array[Byte], value: Array[Byte]): F[Unit] } object ImportCache { /* * Improves the ergonomics when resolving imports if we don't have to check * if the cache exists. So if we fail to construct an imports cache, * we warn and return this instead. */ class NoopImportCache[F[_]](implicit F: Applicative[F]) extends ImportCache[F] { override def get(key: Array[Byte]): F[Option[Array[Byte]]] = F.pure(None) override def put(key: Array[Byte], value: Array[Byte]): F[Unit] = F.unit } private class Impl[F[_]](rootDir: Path)(implicit F: Async[F]) extends ImportCache[F] { override def get(key: Array[Byte]): F[Option[Array[Byte]]] = { val p = path(key) if (Files.exists(p)) { F.delay(Some(Files.readAllBytes(p))) } else { F.pure(None) } } override def put(key: Array[Byte], value: Array[Byte]): F[Unit] = F.delay(Files.write(path(key), value)) private def path(key: Array[Byte]): Path = rootDir.resolve(s"1220${toHex(key)}") private def toHex(bs: Array[Byte]): String = { val sb = new StringBuilder for (b <- bs) { sb.append(String.format("%02x", Byte.box(b))) } sb.toString } } def apply[F[_] <: AnyRef](rootDir: Path)(implicit F: Async[F]): F[Option[ImportCache[F]]] = for { _ <- if (!Files.exists(rootDir)) F.delay(Files.createDirectories(rootDir)).void else F.unit perms <- F.delay(Files.isReadable(rootDir) && Files.isWritable(rootDir)) } yield (if (perms) Some(new Impl[F](rootDir)) else None) def apply[F[_] <: AnyRef](cacheName: String)(implicit F: Async[F]): F[ImportCache[F]] = { def makeCacheFromEnvVar(env: String, relativePath: String): F[Option[ImportCache[F]]] = for { envValO <- F.delay(sys.env.get(env)) cache <- envValO.fold(F.pure(Option.empty[ImportCache[F]]))(envVal => for { rootDir <- F.pure(Paths.get(envVal, relativePath, cacheName)) c <- apply(rootDir) } yield c ) } yield cache def backupCache = for { cacheO <- if (isWindows) makeCacheFromEnvVar("LOCALAPPDATA", "") else makeCacheFromEnvVar("HOME", ".cache") cache <- F.fromOption(cacheO, new ResolutionFailure("Failed to create cache")) } yield cache def isWindows = System.getProperty("os.name").toLowerCase.contains("windows") for { xdgCache <- makeCacheFromEnvVar("XDG_CACHE_HOME", "") cache <- xdgCache.fold(backupCache)(F.pure) } yield cache } } ================================================ FILE: modules/imports/src/main/scala/org/dhallj/imports/ImportContext.scala ================================================ package org.dhallj.imports import java.net.URI import java.nio.file.Path import org.dhallj.core.Expr sealed abstract class ImportContext extends Product with Serializable object ImportContext { case object Missing extends ImportContext case class Env(value: String) extends ImportContext case class Local(absolutePath: Path) extends ImportContext case class Classpath(absolutePath: Path) extends ImportContext case class Remote(url: URI, using: Expr) extends ImportContext } ================================================ FILE: modules/imports/src/main/scala/org/dhallj/imports/ReferentialSanityCheck.scala ================================================ package org.dhallj.imports import cats.effect.Sync import org.dhallj.core.DhallException.ResolutionFailure object ReferentialSanityCheck { def apply[F[_]](parent: ImportContext, child: ImportContext)(implicit F: Sync[F]): F[Unit] = parent match { case ImportContext.Remote(uri, _) => child match { case ImportContext.Remote(_, _) => F.unit case ImportContext.Missing => F.unit case ImportContext.Local(path) => F.raiseError( new ResolutionFailure( s"Referential sanity violation - remote import $uri cannot reference local import $path" ) ) case ImportContext.Classpath(path) => F.raiseError( new ResolutionFailure( s"Referential sanity violation - remote import $uri cannot reference classpath import $path" ) ) case ImportContext.Env(v) => F.raiseError( new ResolutionFailure(s"Referential sanity violation - remote import $uri cannot reference env import $v") ) } case ImportContext.Missing => F.raiseError(new ResolutionFailure(s"Missing import cannot reference import $child")) case _ => F.unit } } ================================================ FILE: modules/imports/src/main/scala/org/dhallj/imports/ResolveImports.scala ================================================ package org.dhallj.imports import java.nio.file.{Path, Paths} import cats.effect.Async import org.dhallj.core.Expr import org.http4s.client.Client object Resolver { def resolve[F[_] <: AnyRef](expr: Expr)(implicit Client: Client[F], F: Async[F]): F[Expr] = resolveRelativeTo[F](cwd)(expr) def resolveRelativeTo[F[_] <: AnyRef](relativeTo: Path)( expr: Expr )(implicit Client: Client[F], F: Async[F]): F[Expr] = F.flatMap(ResolveImportsVisitor[F](relativeTo))(expr.accept(_)) def resolve[F[_] <: AnyRef]( semanticCache: ImportCache[F], semiSemanticCache: ImportCache[F] )(expr: Expr)(implicit Client: Client[F], F: Async[F]): F[Expr] = resolveRelativeTo[F](semanticCache, semiSemanticCache)(cwd)(expr) def resolveRelativeTo[F[_] <: AnyRef]( semanticCache: ImportCache[F], semiSemanticCache: ImportCache[F] )(relativeTo: Path)(expr: Expr)(implicit Client: Client[F], F: Async[F]): F[Expr] = expr.accept(ResolveImportsVisitor[F](semanticCache, semiSemanticCache, relativeTo)) def resolve[F[_] <: AnyRef]( semanticCache: ImportCache[F] )(expr: Expr)(implicit Client: Client[F], F: Async[F]): F[Expr] = resolveRelativeTo[F](semanticCache)(cwd)(expr) def resolveRelativeTo[F[_] <: AnyRef]( semanticCache: ImportCache[F] )(relativeTo: Path)(expr: Expr)(implicit Client: Client[F], F: Async[F]): F[Expr] = expr.accept(ResolveImportsVisitor[F](semanticCache, new ImportCache.NoopImportCache[F], relativeTo)) private def cwd: Path = Paths.get(".") final class Ops(val expr: Expr) extends AnyVal { def resolveImports[F[_] <: AnyRef](implicit Client: Client[F], F: Async[F]): F[Expr] = resolve[F](expr) def resolveImportsRelativeTo[F[_] <: AnyRef](relativeTo: Path)(implicit Client: Client[F], F: Async[F]): F[Expr] = resolveRelativeTo[F](relativeTo)(expr) } } ================================================ FILE: modules/imports/src/main/scala/org/dhallj/imports/ResolveImportsVisitor.scala ================================================ package org.dhallj.imports import java.net.URI import java.nio.file.{Files, Path, Paths} import java.security.MessageDigest import cats.Apply import cats.data.NonEmptyList import cats.effect.{Async, Concurrent} import cats.implicits._ import org.dhallj.cats.LiftVisitor import org.dhallj.core.DhallException.ResolutionFailure import org.dhallj.core.Expr.ImportMode import org.dhallj.core.Expr.Util.typeCheck import org.dhallj.core._ import org.dhallj.core.binary.Decode import org.dhallj.imports.Canonicalization.canonicalize import org.dhallj.imports.ImportContext.Local import org.dhallj.parser.DhallParser import org.http4s.Status.Successful import org.http4s.Uri.unsafeFromString import org.http4s.client.Client import org.http4s.{EntityDecoder, Request} import scala.collection.mutable.{Map => MMap} //TODO proper error handling final private class ResolveImportsVisitor[F[_] <: AnyRef]( semanticCache: ImportCache[F], semiSemanticCache: ImportCache[F], parents: NonEmptyList[ImportContext] )(implicit Client: Client[F], F: Async[F] ) extends LiftVisitor[F](F, true) { def this( semanticCache: ImportCache[F], semiSemanticCache: ImportCache[F], parents: List[ImportContext] )(implicit Client: Client[F], F: Async[F]) = { this(semanticCache, semiSemanticCache, NonEmptyList.fromListUnsafe(parents)) } private var duplicateImportCache: MMap[(ImportContext, ImportMode), Expr] = MMap.empty override def onOperatorApplication(operator: Operator, lhs: F[Expr], rhs: F[Expr]): F[Expr] = if (operator == Operator.IMPORT_ALT) lhs.handleErrorWith { case e: ResolutionFailure if e.isAbsentImport => rhs case other => F.raiseError(other) } else { F.map2(lhs, rhs)(Expr.makeOperatorApplication(operator, _, _)) } override def onLocalImport(path: Path, mode: Expr.ImportMode, hash: Array[Byte]): F[Expr] = onImport(ImportContext.Local(path), mode, hash) override def onClasspathImport(path: Path, mode: Expr.ImportMode, hash: Array[Byte]): F[Expr] = onImport(ImportContext.Classpath(path), mode, hash) override def onRemoteImport(url: URI, using: F[Expr], mode: Expr.ImportMode, hash: Array[Byte]): F[Expr] = if (using.ne(null)) using >>= (u => onImport(ImportContext.Remote(url, u), mode, hash)) else onImport(ImportContext.Remote(url, null), mode, hash) override def onEnvImport(value: String, mode: Expr.ImportMode, hash: Array[Byte]): F[Expr] = onImport(ImportContext.Env(value), mode, hash) override def onMissingImport(mode: Expr.ImportMode, hash: Array[Byte]): F[Expr] = onImport(ImportContext.Missing, mode, hash) private[this] def checkHash(encoded: Array[Byte], expected: Array[Byte]): Boolean = { val hashed = MessageDigest.getInstance("SHA-256").digest(encoded) hashed.sameElements(expected) } private def onImport(i: ImportContext, mode: Expr.ImportMode, hash: Array[Byte]): F[Expr] = { //TODO check that equality is sensibly defined for URI and Path def rejectCyclicImports(imp: ImportContext, parents: NonEmptyList[ImportContext]): F[Unit] = if (parents.exists(_ == imp)) F.raiseError[Unit](new ResolutionFailure(s"Cyclic import - $imp is already imported in chain $parents")) else F.unit def importLocation(imp: ImportContext): F[Expr] = imp match { case ImportContext.Local(path) => makeLocation("Local", path.toString) // Cannot support this and remain spec-compliant as result type must be case ImportContext.Classpath(path) => F.raiseError(new ResolutionFailure("Importing classpath as location is not supported")) case ImportContext.Remote(uri, _) => makeLocation("Remote", uri.toString) case ImportContext.Env(value) => makeLocation("Environment", value) case ImportContext.Missing => F.pure(Expr.makeFieldAccess(Expr.Constants.LOCATION_TYPE, "Missing")) } def makeLocation(field: String, value: String): F[Expr] = F.pure( Expr.makeApplication(Expr.makeFieldAccess(Expr.Constants.LOCATION_TYPE, field), Expr.makeTextLiteral(value)) ) def checkHashesMatch(encoded: Array[Byte], expected: Array[Byte]): F[Unit] = { if (checkHash(encoded, expected)) F.unit else F.raiseError(new ResolutionFailure("Cached expression does not match its hash")) } def loadWithSemanticCache(imp: ImportContext, mode: ImportMode, hash: Array[Byte]): F[Expr] = if (hash == null) loadWithSemiSemanticCache(imp, mode, hash) else for { cached <- semanticCache.get(hash) e <- cached match { case Some(bs) if checkHash(bs, hash) => F.delay(Decode.decode(bs)) case _ => for { e <- loadWithSemiSemanticCache(imp, mode, hash) n = e.normalize.alphaNormalize bs = n.getEncodedBytes _ <- checkHashesMatch(bs, hash) _ <- semanticCache.put(hash, bs) } yield n } } yield e def fetch(imp: ImportContext): F[String] = imp match { case ImportContext.Env(value) => for { vO <- F.delay(sys.env.get(value)) v <- vO.fold( F.raiseError[String](new ResolutionFailure(s"Missing import - env import $value undefined", true)) )(F.pure) } yield v case ImportContext.Local(path) => for { v <- { if (Files.exists(path)) { F.delay(new String(Files.readAllBytes(path))) } else { F.raiseError(new ResolutionFailure(s"Missing import - file $path does not exist", true)) } } } yield v case ImportContext.Classpath(path) => for { v <- F.delay(getClass.getResourceAsStream(path.toString)).flatMap { stream => if (stream.ne(null)) { F.delay(scala.io.Source.fromInputStream(stream).mkString) } else { F.raiseError[String](new ResolutionFailure(s"Missing import - resource $path does not exist", true)) } } } yield v case ImportContext.Remote(uri, using) => for { headers <- F.fromOption(ToHeaders(`using`), new ResolutionFailure("Invalid using clause")) req <- F.pure(Request[F](uri = unsafeFromString(uri.toString), headers = headers)) resp <- Client.fetch[String](req) { case Successful(resp) => for { s <- EntityDecoder.decodeText[F](resp) _ <- if (parents.nonEmpty) CorsComplianceCheck(parents.head, imp, resp.headers) else F.unit } yield s case _ => F.raiseError[String]( new ResolutionFailure(s"Missing import - cannot resolve $uri", true) ) } } yield resp case ImportContext.Missing => F.raiseError(new ResolutionFailure("Missing import - cannot resolve missing", true)) } def loadWithSemiSemanticCache(imp: ImportContext, mode: ImportMode, hash: Array[Byte]): F[Expr] = mode match { case ImportMode.LOCATION => F.raiseError(new ResolutionFailure("Unreachable - location imports already handled")) case ImportMode.RAW_TEXT => for { text <- fetch(imp) } yield Expr.makeTextLiteral(text) case ImportMode.CODE => for { text <- fetch(imp) parsed <- F.delay(DhallParser.parse(text)) resolved <- { val v = new ResolveImportsVisitor[F](semanticCache, semiSemanticCache, imp :: parents) v.duplicateImportCache = this.duplicateImportCache parsed.accept(v) } semiHash = MessageDigest.getInstance("SHA-256").digest(resolved.getEncodedBytes) cached <- semiSemanticCache.get(semiHash) expr <- cached match { case Some(bs) => F.delay(Decode.decode(bs)) case None => for { _ <- F.delay(typeCheck(resolved)) //TODO substitutions here? normalized <- F.delay(resolved.normalize) _ <- semiSemanticCache.put(semiHash, normalized.getEncodedBytes) } yield normalized } } yield expr } def resolve(imp: ImportContext, mode: ImportMode, hash: Array[Byte]): F[Expr] = { val p = (imp, mode) if (duplicateImportCache.contains(p)) { val cached = duplicateImportCache.get(p).get if (hash == null) { F.delay(cached) } else { checkHashesMatch(cached.getEncodedBytes, hash).as(cached) } } else { for { e <- loadWithSemanticCache(imp, mode, hash) _ <- F.delay(duplicateImportCache.put(p, e)) } yield e } } def importNonLocation(imp: ImportContext, mode: ImportMode, hash: Array[Byte]) = for { _ <- ReferentialSanityCheck(parents.head, imp) _ <- rejectCyclicImports(imp, parents) r <- resolve(imp, mode, hash) } yield r for { imp <- canonicalize[F](parents.head, i)(F) result <- if (mode == ImportMode.LOCATION) importLocation(imp) else importNonLocation(imp, mode, hash) } yield result } } private object ResolveImportsVisitor { def apply[F[_] <: AnyRef: Async: Client](relativeTo: Path): F[ResolveImportsVisitor[F]] = Apply[F].map2(ImportCache[F]("dhall"), ImportCache[F]("dhallj"))(apply[F](_, _, relativeTo)) def apply[F[_] <: AnyRef: Async: Client](semanticCache: ImportCache[F], semiSemanticCache: ImportCache[F], relativeTo: Path ): ResolveImportsVisitor[F] = //We add a placeholder filename "package.dhall" for the base directory as a Local import must have a filename new ResolveImportsVisitor[F](semanticCache, semiSemanticCache, NonEmptyList.one(Local(relativeTo.resolve("package.dhall"))) ) def apply[F[_] <: AnyRef: Async: Client](semanticCache: ImportCache[F], relativeTo: Path): ResolveImportsVisitor[F] = apply[F](semanticCache, new ImportCache.NoopImportCache[F], relativeTo) private def cwd: Path = Paths.get(".") def apply[F[_] <: AnyRef: Async: Client]: F[ResolveImportsVisitor[F]] = apply[F](cwd) def apply[F[_] <: AnyRef: Async: Client](semanticCache: ImportCache[F], semiSemanticCache: ImportCache[F] ): ResolveImportsVisitor[F] = apply[F](semanticCache, semiSemanticCache, cwd) def apply[F[_] <: AnyRef: Async: Client](semanticCache: ImportCache[F]): ResolveImportsVisitor[F] = apply[F](semanticCache, cwd) } ================================================ FILE: modules/imports/src/main/scala/org/dhallj/imports/ToHeaders.scala ================================================ package org.dhallj.imports import java.util.AbstractMap.SimpleImmutableEntry import java.util.Map.Entry import org.dhallj.core.Expr import org.dhallj.core.Expr.Util.{asListLiteral, asRecordLiteral, asSimpleTextLiteral} import org.dhallj.core.typechecking.TypeCheckFailure import org.http4s.{Header, Headers} import org.typelevel.ci.CIString import scala.collection.JavaConverters._ object ToHeaders { private val validType1: Expr = Expr.makeApplication( Expr.Constants.LIST, Expr.makeRecordType( Array[Entry[String, Expr]]( new SimpleImmutableEntry("mapKey", Expr.Constants.TEXT), new SimpleImmutableEntry("mapValue", Expr.Constants.TEXT) ) ) ) private val validType2: Expr = Expr.makeApplication( Expr.Constants.LIST, Expr.makeRecordType( Array[Entry[String, Expr]]( new SimpleImmutableEntry("header", Expr.Constants.TEXT), new SimpleImmutableEntry("value", Expr.Constants.TEXT) ) ) ) private def isValidType(expr: Expr): Boolean = { try { val tpe = Expr.Util.typeCheck(expr) tpe == validType1 || tpe == validType2 } catch { case _: TypeCheckFailure => false } } // See https://discourse.dhall-lang.org/t/valid-expressions-for-using-headers/205 // For the moment, this is consistent with the Haskell implementation def apply(expr: Expr): Option[Headers] = if (expr eq null) Some(Headers.empty) else { if (!isValidType(expr)) { None } else { //TODO do we need to .accept(this) on expr? val e = expr.normalize val l = asListLiteral(e) if (l eq null) { Some(Headers.empty) } else { val hs: List[Header.Raw] = l.asScala.toList.flatMap { e => // e should have type `List { header : Text, value Text }` // or `List { mapKey : Text, mapValue Text }` val r = asRecordLiteral(e) if (r eq null) { None } else { if (r.size == 2) { val map = r.asScala.map(e => e.getKey -> e.getValue).toMap if (map.contains("header") && map.contains("value")) { val key = asSimpleTextLiteral(map("header")) val value = asSimpleTextLiteral(map("value")) if ((key ne null) && (value ne null)) { Some(Header.Raw(CIString(key), value)) } else None } else if (map.contains("mapKey") && map.contains("mapValue")) { val key = asSimpleTextLiteral(map("mapKey")) val value = asSimpleTextLiteral(map("mapValue")) if ((key ne null) && (value ne null)) { Some(Header.Raw(CIString(key), value)) } else None } else None } else None } } Some(Headers(hs)) } } } } ================================================ FILE: modules/imports/src/main/scala/org/dhallj/imports/syntax/package.scala ================================================ package org.dhallj.imports import org.dhallj.core.Expr import scala.language.implicitConversions package object syntax { implicit def toResolveImportOps(expr: Expr): Resolver.Ops = new Resolver.Ops(expr) } ================================================ FILE: modules/imports/src/test/resources/alternate/other.dhall ================================================ let x = 2 in x ================================================ FILE: modules/imports/src/test/resources/alternate/package.dhall ================================================ let x = 1 in x ================================================ FILE: modules/imports/src/test/resources/cache-write/package.dhall ================================================ let x = 2 in x ================================================ FILE: modules/imports/src/test/resources/cyclic/other.dhall ================================================ let x = /cyclic/package.dhall in x ================================================ FILE: modules/imports/src/test/resources/cyclic/package.dhall ================================================ let x = ./other.dhall in x ================================================ FILE: modules/imports/src/test/resources/cyclic-relative-paths/other.dhall ================================================ let x = ./package.dhall in x ================================================ FILE: modules/imports/src/test/resources/cyclic-relative-paths/package.dhall ================================================ let x = ./other.dhall in x ================================================ FILE: modules/imports/src/test/resources/hashed/package.dhall ================================================ let x = 1 in x ================================================ FILE: modules/imports/src/test/resources/local/package.dhall ================================================ let x = 1 in x ================================================ FILE: modules/imports/src/test/resources/local-local-absolute/package.dhall ================================================ let x = /local-local-absolute-2/package.dhall in x ================================================ FILE: modules/imports/src/test/resources/local-local-absolute-2/package.dhall ================================================ let x = 1 in x ================================================ FILE: modules/imports/src/test/resources/local-local-relative/other.dhall ================================================ let x = 1 in x ================================================ FILE: modules/imports/src/test/resources/local-local-relative/package.dhall ================================================ let x = ./other.dhall in x ================================================ FILE: modules/imports/src/test/resources/local-remote/package.dhall ================================================ let any = https://raw.githubusercontent.com/dhall-lang/dhall-lang/master/Prelude/List/any in any ================================================ FILE: modules/imports/src/test/resources/multiple-imports/other.dhall ================================================ let x = 1 in x ================================================ FILE: modules/imports/src/test/resources/multiple-imports/other2.dhall ================================================ let x = 2 in x ================================================ FILE: modules/imports/src/test/resources/multiple-imports/package.dhall ================================================ let x = ./other.dhall let y = ./other2.dhall in [x,y] ================================================ FILE: modules/imports/src/test/resources/text-import/package.dhall ================================================ let x = 1 in x ================================================ FILE: modules/imports/src/test/scala/org/dhallj/imports/CanonicalizationSuite.scala ================================================ package org.dhallj.imports import java.net.URI import java.nio.file.Paths import cats.effect.IO import cats.effect.unsafe.implicits.global import munit.FunSuite import org.dhallj.imports.Canonicalization.canonicalize import org.dhallj.imports.ImportContext._ import org.dhallj.parser.DhallParser.parse /** * https://github.com/dhall-lang/dhall-lang/blob/master/standard/imports.md#canonicalization-of-directories */ class CanonicalizationSuite extends FunSuite { val headers1 = parse("/headers1.dhall") val headers2 = parse("/headers2.dhall") test("Imports - Local, empty path") { assertEquals(canonicalize[IO](Local(Paths.get("/foo.dhall"))).unsafeRunSync(), Local(Paths.get("/foo.dhall"))) } test("Imports - quoted") { assertEquals(canonicalize[IO](Local(Paths.get("/\"foo\"/\"bar.dhall\""))).unsafeRunSync(), Local(Paths.get("/foo/bar.dhall")) ) } test("Paths - Trailing .") { assertEquals(canonicalize[IO](Local(Paths.get("/foo/./bar.dhall"))).unsafeRunSync(), Local(Paths.get("/foo/bar.dhall")) ) } test("Paths - Trailing ..") { assertEquals(canonicalize[IO](Local(Paths.get("/foo/bar/../baz.dhall"))).unsafeRunSync(), Local(Paths.get("/foo/baz.dhall")) ) } //TODO determine whether spec is correct on this test("Paths - Root ..") { assertEquals(canonicalize[IO](Local(Paths.get("/.."))).unsafeRunSync(), Local(Paths.get("/.."))) } test("Chaining - local / , local /") { assertEquals( canonicalize[IO](Local(Paths.get("/foo/bar.dhall")), Local(Paths.get("/bar/baz.dhall"))).unsafeRunSync(), Local(Paths.get("/bar/baz.dhall")) ) } test("Chaining - local / , local ~") { assertEquals( canonicalize[IO](Local(Paths.get("/foo/bar.dhall")), Local(Paths.get("~/bar/baz.dhall"))).unsafeRunSync(), Local(Paths.get("~/bar/baz.dhall")) ) } test("Chaining - local / , local .") { assertEquals( canonicalize[IO](Local(Paths.get("/foo/bar.dhall")), Local(Paths.get("./baz.dhall"))).unsafeRunSync(), Local(Paths.get("/foo/baz.dhall")) ) } test("Chaining - local / , local ..") { assertEquals( canonicalize[IO](Local(Paths.get("./foo/x/bar.dhall")), Local(Paths.get("../bar/baz.dhall"))).unsafeRunSync(), Local(Paths.get("./foo/bar/baz.dhall")) ) } test("Chaining - local ~ , local /") { assertEquals( canonicalize[IO](Local(Paths.get("~/foo/bar.dhall")), Local(Paths.get("/baz.dhall"))).unsafeRunSync(), Local(Paths.get("/baz.dhall")) ) } test("Chaining - local ~ , local ~") { assertEquals( canonicalize[IO](Local(Paths.get("~/foo/bar.dhall")), Local(Paths.get("~/baz.dhall"))).unsafeRunSync(), Local(Paths.get("~/baz.dhall")) ) } test("Chaining - local ~ , local .") { assertEquals( canonicalize[IO](Local(Paths.get("~/foo/bar.dhall")), Local(Paths.get("./baz.dhall"))).unsafeRunSync(), Local(Paths.get("~/foo/baz.dhall")) ) } test("Chaining - local ~ , local ..") { assertEquals( canonicalize[IO](Local(Paths.get("~/foo/bar.dhall")), Local(Paths.get("../baz.dhall"))).unsafeRunSync(), Local(Paths.get("~/baz.dhall")) ) } test("Chaining - local . , local /") { assertEquals( canonicalize[IO](Local(Paths.get("./foo/bar.dhall")), Local(Paths.get("/baz.dhall"))).unsafeRunSync(), Local(Paths.get("/baz.dhall")) ) } test("Chaining - local . , local ~") { assertEquals( canonicalize[IO](Local(Paths.get("./foo/bar.dhall")), Local(Paths.get("~/baz.dhall"))).unsafeRunSync(), Local(Paths.get("~/baz.dhall")) ) } test("Chaining - local . , local .") { assertEquals( canonicalize[IO](Local(Paths.get("./foo/bar.dhall")), Local(Paths.get("./baz.dhall"))).unsafeRunSync(), Local(Paths.get("./foo/baz.dhall")) ) } test("Chaining - local . , local ..") { assertEquals( canonicalize[IO](Local(Paths.get("./foo/bar.dhall")), Local(Paths.get("../baz.dhall"))).unsafeRunSync(), Local(Paths.get("./baz.dhall")) ) } test("Chaining - local .. , local /") { assertEquals( canonicalize[IO](Local(Paths.get("../foo/bar.dhall")), Local(Paths.get("/baz.dhall"))).unsafeRunSync(), Local(Paths.get("/baz.dhall")) ) } test("Chaining - local .. , local ~") { assertEquals( canonicalize[IO](Local(Paths.get("../foo/bar.dhall")), Local(Paths.get("~/baz.dhall"))).unsafeRunSync(), Local(Paths.get("~/baz.dhall")) ) } test("Chaining - local .. , local .") { assertEquals( canonicalize[IO](Local(Paths.get("../foo/bar.dhall")), Local(Paths.get("./baz.dhall"))).unsafeRunSync(), Local(Paths.get("../foo/baz.dhall")) ) } test("Chaining - local .. , local ..") { assertEquals( canonicalize[IO](Local(Paths.get("../foo/bar.dhall")), Local(Paths.get("../baz.dhall"))).unsafeRunSync(), Local(Paths.get("../baz.dhall")) ) } test("Chaining - local / remote") { assertEquals( canonicalize[IO](Local(Paths.get("/foo/bar.dhall")), Remote(new URI("http://foo.org/bar.dhall"), headers1)) .unsafeRunSync(), Remote(new URI("http://foo.org/bar.dhall"), headers1) ) } test("Chaining - remote / remote absolute") { assertEquals( canonicalize[IO](Remote(new URI("http://foo.org/bar.dhall"), headers1), Remote(new URI("https://bar.com/bar/baz.dhall"), headers2) ).unsafeRunSync(), Remote((new URI("https://bar.com/bar/baz.dhall")), headers2) ) } test("Chaining - remote / local relative") { assertEquals( canonicalize[IO](Remote(new URI("http://foo.org/bar.dhall"), headers1), Local(Paths.get("./baz.dhall"))) .unsafeRunSync(), Remote(new URI("http://foo.org/baz.dhall"), headers1) ) } //This is actually prohibited by the sanity check but we don't worry about it here test("Chaining - remote / local absolute") { assertEquals( canonicalize[IO](Remote(new URI("http://foo.org/bar.dhall"), headers1), Local(Paths.get("/baz.dhall"))) .unsafeRunSync(), Local(Paths.get("/baz.dhall")) ) } } ================================================ FILE: modules/imports/src/test/scala/org/dhallj/imports/CorsComplianceCheckSuite.scala ================================================ package org.dhallj.imports import java.net.URI import java.nio.file.Paths import cats.effect.IO import cats.effect.unsafe.implicits.global import munit.FunSuite import org.dhallj.imports.ImportContext._ import org.http4s.{Header, Headers} import org.typelevel.ci._ class CorsComplianceCheckSuite extends FunSuite { val fooOrigin = new URI("http://foo.org/foo.dhall") val fooOrigin8080 = new URI("http://foo.org:8080/foo.dhall") val fooOrigin2 = new URI("http://foo.org/baz.dhall") val barOrigin = new URI("http://bar.org/bar.dhall") val localPath = Paths.get("/foo/bar.dhall") test("Remote - same origin") { CorsComplianceCheck[IO](Remote(fooOrigin, null), Remote(fooOrigin2, null), Headers.empty).unsafeRunSync() } test("Remote - different origin, allow *") { CorsComplianceCheck[IO](Remote(fooOrigin, null), Remote(barOrigin, null), Headers(Header.Raw(ci"Access-Control-Allow-Origin", "*")) ).unsafeRunSync() } test("Remote - different origin, allow parent authority") { CorsComplianceCheck[IO](Remote(fooOrigin, null), Remote(barOrigin, null), Headers(Header.Raw(ci"Access-Control-Allow-Origin", "http://foo.org")) ).unsafeRunSync() } test("Remote - different origin".fail) { CorsComplianceCheck[IO](Remote(fooOrigin, null), Remote(barOrigin, null), Headers.empty).unsafeRunSync() } test("Remote - different origin, cors parent different authority".fail) { CorsComplianceCheck[IO](Remote(fooOrigin, null), Remote(barOrigin, null), Headers(Header.Raw(ci"Access-Control-Allow-Origin", "http://bar.org")) ).unsafeRunSync() } test("Remote - different origin, cors parent different scheme".fail) { CorsComplianceCheck[IO](Remote(fooOrigin, null), Remote(barOrigin, null), Headers(Header.Raw(ci"Access-Control-Allow-Origin", "https://foo.org")) ).unsafeRunSync() } test("Remote - different origin, cors parent different port".fail) { CorsComplianceCheck[IO](Remote(fooOrigin, null), Remote(barOrigin, null), Headers(Header.Raw(ci"Access-Control-Allow-Origin", "http://foo.org:8080")) ).unsafeRunSync() } test("Remote - different origin, cors parent different port 2".fail) { CorsComplianceCheck[IO](Remote(fooOrigin8080, null), Remote(barOrigin, null), Headers(Header.Raw(ci"Access-Control-Allow-Origin", "http://foo.org")) ).unsafeRunSync() } test("Local") { CorsComplianceCheck[IO](Local(localPath), Remote(fooOrigin2, null), Headers.empty).unsafeRunSync() } test("Env") { CorsComplianceCheck[IO](Env("foo"), Remote(fooOrigin2, null), Headers.empty).unsafeRunSync() } } ================================================ FILE: modules/imports/src/test/scala/org/dhallj/imports/ImportCacheSuite.scala ================================================ package org.dhallj.imports import java.nio.file.{Files, Path} import cats.effect.IO import cats.effect.unsafe.implicits.global import munit.FunSuite class ImportCacheSuite extends FunSuite { val rootDir = FunFixture[(ImportCache[IO], Path)]( setup = { test => val rootDir = Files.createTempDirectory(test.name).resolve("dhall") ImportCache[IO](rootDir).unsafeRunSync().get -> rootDir }, teardown = { case (_, rootDir) => } ) val key = "0f86d".getBytes val bytes: Array[Byte] = "test".getBytes rootDir.test("Get-if-absent") { case (cache, _) => val prog = cache.get(key) assertEquals(prog.unsafeRunSync(), None) } rootDir.test("Get-if-present") { case (cache, _) => val prog = cache.put(key, bytes) >> cache.get(key) assert(prog.unsafeRunSync().exists(_.sameElements(bytes))) } } ================================================ FILE: modules/imports/src/test/scala/org/dhallj/imports/ImportResolutionSuite.scala ================================================ package org.dhallj.imports import java.security.MessageDigest import cats.effect.{IO, Ref, Resource} import cats.effect.unsafe.implicits.global import cats.implicits._ import munit.FunSuite import org.dhallj.core.Expr import org.dhallj.core.binary.Decode import org.dhallj.imports.syntax._ import org.dhallj.parser.DhallParser.parse import org.http4s.blaze.client._ import org.http4s.client._ class ImportResolutionSuite extends FunSuite { implicit val client: Resource[IO, Client[IO]] = BlazeClientBuilder[IO](scala.concurrent.ExecutionContext.global).resource test("Classpath import") { val expr = parse("let x = classpath:/local/package.dhall in x") val expected = parse("let x = 1 in x").normalize assert(resolve(expr) == expected) } test("Quoted import") { val expr = parse("let x = classpath:/\"local\"/\"package.dhall\" in x") val expected = parse("let x = 1 in x").normalize assert(resolve(expr) == expected) } test("Classpath -> classpath relative import") { val expr = parse("let x = classpath:/local-local-relative/package.dhall in x") val expected = parse("let x = 1 in x").normalize assert(resolve(expr) == expected) } test("Classpath -> classpath absolute import") { val expr = parse("let x = classpath:/local-local-absolute/package.dhall in x") val expected = parse("let x = 1 in x").normalize assert(resolve(expr) == expected) } test("Classpath -> remote import") { val expr = parse("let any = classpath:/local-remote/package.dhall in any Natural Natural/even [2,3,5]") val expected = parse("True").normalize assert(resolve(expr) == expected) } test("Remote import") { val expr = parse( "let any = https://raw.githubusercontent.com/dhall-lang/dhall-lang/master/Prelude/List/any in any Natural Natural/even [2,3,5]" ) val expected = parse("True").normalize assert(resolve(expr) == expected) } //TODO - dependent on https://github.com/travisbrown/dhallj/issues/34 test("Quoted remote import".fail) { val expr = parse( "let any = https://raw.githubusercontent.com/\"dhall-lang\"/\"dhall-lang\"/\"master\"/\"Prelude\"/\"List\"/\"any\" in any Natural Natural/even [2,3,5]" ) val expected = parse("True").normalize assert(resolve(expr) == expected) } test("Multiple imports") { val expr = parse("let x = classpath:/multiple-imports/package.dhall in x") val expected = parse("let x = [1,2] in x").normalize assert(resolve(expr) == expected) } test("Import as text") { val expr = parse("let x = classpath:/text-import/package.dhall as Text in x") val expected = parse("\"let x = 1 in x\"").normalize assert(resolve(expr) == expected) } test("Import as local location") { val expr = parse("let x = /foo/bar.dhall as Location in x") val expected = parse( "< Local : Text | Remote : Text | Environment : Text | Missing >.Local \"/foo/bar.dhall\"" ).normalize assert(resolve(expr) == expected) } test("Import as remote location") { val expr = parse("let x = http://example.com/foo.dhall as Location in x") val expected = parse( "< Local : Text | Remote : Text | Environment : Text | Missing >.Remote \"http://example.com/foo.dhall\"" ).normalize assert(resolve(expr) == expected) } test("Import as env location") { val expr = parse("let x = env:foo as Location in x") val expected = parse("< Local : Text | Remote : Text | Environment : Text | Missing >.Environment \"foo\"").normalize assert(resolve(expr) == expected) } test("Cyclic imports".fail) { val expr = parse("let x = classpath:/cyclic-relative-paths/package.dhall in x") val expected = parse("True").normalize assert(resolve(expr) == expected) } test("Cyclic imports - all relative".fail) { val expr = parse("let x = classpath:/cyclic-relative-paths/package.dhall in x") val expected = parse("True").normalize assert(resolve(expr) == expected) } test("Alternate imports - first succeeds") { val expr = parse("let x = classpath:/alternate/package.dhall ? classpath:/alternate/other.dhall in x") val expected = parse("let x = 1 in x").normalize assert(resolve(expr) == expected) } test("Alternate imports - first fails") { val expr = parse("let x = classpath:/alternate/not_present.dhall ? classpath:/alternate/package.dhall in x") val expected = parse("let x = 1 in x").normalize assert(resolve(expr) == expected) } test("Valid hash") { val expr = parse( "classpath:/hashed/package.dhall sha256:d60d8415e36e86dae7f42933d3b0c4fe3ca238f057fba206c7e9fbf5d784fe15" ) val expected = parse("let x = 1 in x").normalize.alphaNormalize assert(resolve(expr) == expected) } test("Invalid hash".fail) { val expr = parse( "let x = classpath:/hashed/package.dhall sha256:e60d8415e36e86dae7f42933d3b0c4fe3ca238f057fba206c7e9fbf5d784fe15 in x" ) val expected = parse("let x = 1 in x").normalize.alphaNormalize assert(resolve(expr) == expected) } test("Read from cache, cached value present") { val cache = InMemoryCache() val expected = parse("let x = 2 in x") val encoded = expected.normalize.alphaNormalize.getEncodedBytes val hash = MessageDigest.getInstance("SHA-256").digest(encoded) val expr = parse( "let x = classpath:/does-not-exist sha256:4caf97e8c445d4d4b5c5b992973e098ed4ae88a355915f5a59db640a589bc9cb in x" ) assert((cache.put(hash, encoded) >> resolveWithCustomCache(cache, expr)).unsafeRunSync() == expected) } test("Read from cache, incorrect hash".fail) { val cache = InMemoryCache() val cached = parse("let x = 1 in x") val expected = parse("let x = 2 in x") val encoded = cached.normalize.alphaNormalize.getEncodedBytes val hash = MessageDigest .getInstance("SHA-256") .digest(expected.normalize.getEncodedBytes) //Hash doesn't match what is stored val expr = parse( "let x = classpath:/does-not-exist sha256:4caf97e8c445d4d4b5c5b992973e098ed4ae88a355915f5a59db640a589bc9cb in x" ) assert((cache.put(hash, encoded) >> resolveWithCustomCache(cache, expr)).unsafeRunSync() == expected) } test("Write to cache") { val cache = InMemoryCache() val expected = parse("let x = 2 in x") val encoded = expected.normalize.alphaNormalize.getEncodedBytes val hash = MessageDigest.getInstance("SHA-256").digest(encoded) val expr = parse( "let x = classpath:/cache-write/package.dhall sha256:4caf97e8c445d4d4b5c5b992973e098ed4ae88a355915f5a59db640a589bc9cb in x" ) val prog = resolveWithCustomCache(cache, expr) >> cache.get(hash) assert(prog.unsafeRunSync() match { case None => false case Some(bs) => Decode.decode(bs) == expected }) } private def resolve(e: Expr): Expr = client .use { c => implicit val http: Client[IO] = c e.resolveImports[IO].map(_.normalize) } .unsafeRunSync() private def resolveWithCustomCache(cache: ImportCache[IO], e: Expr): IO[Expr] = client.use { c => implicit val http: Client[IO] = c Resolver.resolve(cache)(e) } private case class InMemoryCache() extends ImportCache[IO] { private val store: Ref[IO, Map[List[Byte], Array[Byte]]] = Ref.unsafe(Map.empty) override def get(key: Array[Byte]): IO[Option[Array[Byte]]] = store.get.map(_.get(key.toList)) override def put(key: Array[Byte], value: Array[Byte]): IO[Unit] = store.update(_ + (key.toList -> value)) } } ================================================ FILE: modules/imports/src/test/scala/org/dhallj/imports/ReferentialSanityCheckSuite.scala ================================================ package org.dhallj.imports import java.net.URI import java.nio.file.Paths import cats.effect.IO import cats.effect.unsafe.implicits.global import munit.FunSuite import org.dhallj.imports.ImportContext._ class ReferentialSanityCheckSuite extends FunSuite { private val someUri = new URI("http://example.com/foo.dhall") private val somePath = Paths.get("/foo.dhall") test("Remote imports local".fail) { ReferentialSanityCheck[IO](Remote(someUri, null), Local(somePath)).unsafeRunSync() } test("Remote imports env".fail) { ReferentialSanityCheck[IO](Remote(someUri, null), Env("foo")).unsafeRunSync() } test("Remote imports remote") { ReferentialSanityCheck[IO](Remote(someUri, null), Remote(someUri, null)).unsafeRunSync() } test("Remote imports missing") { ReferentialSanityCheck[IO](Remote(someUri, null), Missing).unsafeRunSync() } test("Remote imports classpath".fail) { ReferentialSanityCheck[IO](Remote(someUri, null), Classpath(somePath)).unsafeRunSync() } test("Local imports local") { ReferentialSanityCheck[IO](Local(somePath), Local(somePath)).unsafeRunSync() } test("Local imports env") { ReferentialSanityCheck[IO](Local(somePath), Env("foo")).unsafeRunSync() } test("Local imports remote") { ReferentialSanityCheck[IO](Local(somePath), Remote(someUri, null)).unsafeRunSync() } test("Local imports missing") { ReferentialSanityCheck[IO](Local(somePath), Missing).unsafeRunSync() } test("Env imports local") { ReferentialSanityCheck[IO](Env("foo"), Local(somePath)).unsafeRunSync() } test("Env imports env") { ReferentialSanityCheck[IO](Env("foo"), Env("foo")).unsafeRunSync() } test("Env imports remote") { ReferentialSanityCheck[IO](Env("foo"), Remote(someUri, null)).unsafeRunSync() } test("Env imports missing") { ReferentialSanityCheck[IO](Env("foo"), Missing).unsafeRunSync() } } ================================================ FILE: modules/imports/src/test/scala/org/dhallj/imports/ToHeadersSuite.scala ================================================ package org.dhallj.imports import munit.FunSuite import org.dhallj.core.Expr import org.http4s.{Header, Headers} import org.typelevel.ci._ import scala.collection.JavaConverters._ class ToHeadersSuite extends FunSuite { test("Success case") { val expr = Expr.makeNonEmptyListLiteral( Array( Expr.makeRecordLiteral( Map("header" -> Expr.makeTextLiteral("foo"), "value" -> Expr.makeTextLiteral("bar")).asJava.entrySet() ), Expr.makeRecordLiteral( Map("header" -> Expr.makeTextLiteral("baz"), "value" -> Expr.makeTextLiteral("x")).asJava.entrySet() ) ) ) val expected = Headers( List( Header.Raw(ci"foo", "bar"), Header.Raw(ci"baz", "x") ) ) assertEquals(ToHeaders(expr), Some(expected)) } test("Success case 2") { val expr = Expr.makeNonEmptyListLiteral( Array( Expr.makeRecordLiteral( Map("mapKey" -> Expr.makeTextLiteral("foo"), "mapValue" -> Expr.makeTextLiteral("bar")).asJava.entrySet() ), Expr.makeRecordLiteral( Map("mapKey" -> Expr.makeTextLiteral("baz"), "mapValue" -> Expr.makeTextLiteral("x")).asJava.entrySet() ) ) ) val expected = Headers( List( Header.Raw(ci"foo", "bar"), Header.Raw(ci"baz", "x") ) ) assertEquals(ToHeaders(expr), Some(expected)) } test("Failure case 1") { val expr = Expr.makeNonEmptyListLiteral( Array( Expr.makeRecordLiteral( Map("header" -> Expr.makeTextLiteral("foo"), "mapValue" -> Expr.makeTextLiteral("bar")).asJava.entrySet() ), Expr.makeRecordLiteral( Map("mapKey" -> Expr.makeTextLiteral("baz"), "value" -> Expr.makeTextLiteral("x")).asJava.entrySet() ) ) ) assertEquals(ToHeaders(expr), None) } test("Failure case 2") { val expr = Expr.makeTextLiteral("foo") assertEquals(ToHeaders(expr), None) } } ================================================ FILE: modules/imports-mini/src/main/java/org/dhallj/imports/mini/ResolutionVisitor.java ================================================ package org.dhallj.imports.mini; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import org.dhallj.core.DhallException.ParsingFailure; import org.dhallj.core.Expr; import org.dhallj.core.Operator; import org.dhallj.core.Visitor; import org.dhallj.parser.DhallParser; abstract class ResolutionVisitor extends Visitor.Identity { private final Path currentPath; protected final boolean integrityChecks; ResolutionVisitor(Path currentPath, boolean integrityChecks) { this.currentPath = currentPath; this.integrityChecks = integrityChecks; } protected abstract String readContents(Path path) throws IOException, URISyntaxException; protected abstract ResolutionVisitor withCurrentPath(Path newCurrentPath); public void bind(String name, Expr type) {} @Override public Expr onOperatorApplication(Operator operator, Expr lhs, Expr rhs) { if (operator.equals(Operator.IMPORT_ALT)) { return lhs; } else { return Expr.makeOperatorApplication(operator, lhs, rhs); } } @Override public Expr onMissingImport(Expr.ImportMode mode, byte[] hash) { Expr result; if (mode.equals(Expr.ImportMode.LOCATION)) { result = Expr.makeFieldAccess(Expr.Constants.LOCATION_TYPE, "Missing"); } else { throw new Missing(); } return checkHash(result, hash); } @Override public Expr onEnvImport(String name, Expr.ImportMode mode, byte[] hash) { Expr result; if (mode.equals(Expr.ImportMode.LOCATION)) { result = Expr.makeApplication( Expr.makeFieldAccess(Expr.Constants.LOCATION_TYPE, "Environment"), Expr.makeTextLiteral(name)); } else { String value = System.getenv(name); if (value != null) { if (mode.equals(Expr.ImportMode.RAW_TEXT)) { result = Expr.makeTextLiteral(value); } else { try { result = DhallParser.parse(value).accept(this); } catch (ParsingFailure underlying) { throw new WrappedParsingFailure(name, underlying); } } } else { throw new MissingEnv(name); } } return checkHash(result, hash); } @Override public Expr onLocalImport(Path path, Expr.ImportMode mode, byte[] hash) { Expr result; if (mode.equals(Expr.ImportMode.LOCATION)) { result = Expr.makeApplication( Expr.makeFieldAccess(Expr.Constants.LOCATION_TYPE, "Local"), Expr.makeTextLiteral(path.toString())); } else { Path resolvedPath = (currentPath == null) ? path : currentPath.resolveSibling(path); String contents; try { contents = this.readContents(resolvedPath); } catch (IOException underlying) { throw new WrappedIOException(resolvedPath, underlying); } catch (URISyntaxException underlying) { throw new WrappedIOException(resolvedPath, underlying); } if (mode.equals(Expr.ImportMode.RAW_TEXT)) { result = Expr.makeTextLiteral(contents); } else { try { result = DhallParser.parse(contents).accept(this.withCurrentPath(resolvedPath)); } catch (ParsingFailure underlying) { throw new WrappedParsingFailure(path.toString(), underlying); } } } return checkHash(result, hash); } @Override public Expr onRemoteImport(URI url, Expr using, Expr.ImportMode mode, byte[] hash) { Expr result; if (mode.equals(Expr.ImportMode.LOCATION)) { result = Expr.makeApplication( Expr.makeFieldAccess(Expr.Constants.LOCATION_TYPE, "Remote"), Expr.makeTextLiteral(url.toString())); } else { throw new UnsupportedOperationException("Remote import resolution not currently supported"); } return checkHash(result, hash); } private final Expr checkHash(Expr result, byte[] expected) { if (expected != null && this.integrityChecks) { byte[] received = result.normalize().alphaNormalize().getHashBytes(); if (!Arrays.equals(received, expected)) { throw new IntegrityCheckException(expected, received); } } return result; } static final class Filesystem extends ResolutionVisitor { Filesystem(Path currentPath, boolean integrityChecks) { super(currentPath, integrityChecks); } protected ResolutionVisitor withCurrentPath(Path newCurrentPath) { return new Filesystem(newCurrentPath, this.integrityChecks); } protected String readContents(Path path) throws IOException, URISyntaxException { return new String(Files.readAllBytes(path)); } } static final class Resources extends ResolutionVisitor { private final ClassLoader classLoader; Resources(Path currentPath, boolean integrityChecks, ClassLoader classLoader) { super(currentPath, integrityChecks); this.classLoader = classLoader; } protected ResolutionVisitor withCurrentPath(Path newCurrentPath) { return new Resources(newCurrentPath, this.integrityChecks, this.classLoader); } protected String readContents(Path path) throws IOException, URISyntaxException { return new String( Files.readAllBytes(Paths.get(this.classLoader.getResource(path.toString()).toURI()))); } } static final class WrappedParsingFailure extends RuntimeException { String location; ParsingFailure underlying; WrappedParsingFailure(String location, ParsingFailure underlying) { super(String.format("Can't parse import: %s", location), underlying); this.location = location; this.underlying = underlying; } } static final class WrappedIOException extends RuntimeException { Path path; Exception underlying; WrappedIOException(Path path, Exception underlying) { super(String.format("Missing file %s", path), underlying); this.path = path; this.underlying = underlying; } } static final class Missing extends RuntimeException { Missing() { super("No valid imports"); } } static final class MissingEnv extends RuntimeException { String name; MissingEnv(String name) { super(String.format("Missing environment variable %s", name)); this.name = name; } } static final class IntegrityCheckException extends RuntimeException { final byte[] expected; final byte[] received; IntegrityCheckException(byte[] expected, byte[] received) { super( String.format( "Import integrity check failed (received: %s)", Expr.Util.encodeHashBytes(received))); this.expected = expected; this.received = received; } } } ================================================ FILE: modules/imports-mini/src/main/java/org/dhallj/imports/mini/Resolver.java ================================================ package org.dhallj.imports.mini; import org.dhallj.core.DhallException.ResolutionFailure; import org.dhallj.core.Expr; import java.nio.file.Path; public final class Resolver { public static final Expr resolve(Expr expr, boolean integrityChecks, Path currentPath) throws ResolutionFailure { return resolveWithVisitor(expr, new ResolutionVisitor.Filesystem(currentPath, integrityChecks)); } public static final Expr resolve(Expr expr, boolean integrityChecks) throws ResolutionFailure { return resolve(expr, integrityChecks, null); } public static final Expr resolve(Expr expr) throws ResolutionFailure { return resolve(expr, true); } public static final Expr resolveFromResources( Expr expr, boolean integrityChecks, Path currentPath, ClassLoader classLoader) throws ResolutionFailure { return resolveWithVisitor( expr, new ResolutionVisitor.Resources(currentPath, integrityChecks, classLoader)); } public static final Expr resolveFromResources( Expr expr, boolean integrityChecks, Path currentPath) throws ResolutionFailure { return resolveFromResources( expr, integrityChecks, currentPath, Resolver.class.getClassLoader()); } public static final Expr resolveFromResources(Expr expr, boolean integrityChecks) throws ResolutionFailure { return resolveFromResources(expr, integrityChecks, null); } public static final Expr resolveFromResources(Expr expr) throws ResolutionFailure { return resolveFromResources(expr, true); } private static final Expr resolveWithVisitor(Expr expr, ResolutionVisitor visitor) throws ResolutionFailure { Expr result; try { result = expr.accept(visitor); } catch (ResolutionVisitor.WrappedParsingFailure e) { throw new ResolutionFailure(e.getMessage(), e.underlying); } catch (ResolutionVisitor.WrappedIOException e) { throw new ResolutionFailure(e.getMessage(), e.underlying); } catch (ResolutionVisitor.Missing e) { throw new ResolutionFailure(e.getMessage()); } catch (ResolutionVisitor.MissingEnv e) { throw new ResolutionFailure(e.getMessage()); } catch (ResolutionVisitor.IntegrityCheckException e) { throw new ResolutionFailure(e.getMessage()); } catch (UnsupportedOperationException e) { throw new ResolutionFailure(e.getMessage()); } return result; } } ================================================ FILE: modules/javagen/src/main/java/org/dhallj/javagen/Code.scala ================================================ package org.dhallj.javagen case class Code(content: String, defs: Vector[Code] = Vector.empty) { final private def replace(in: String, target: Int, newName: String): String = { var last = 0 var next = in.indexOf(Code.marker, last) val builder = new StringBuilder() while (next >= 0) { builder.append(in.substring(last, next)) val index = in.substring(next + Code.marker.length, next + Code.marker.length + Code.indexLength).toInt if (index == target) { builder.append(newName) } else { builder.append(in.substring(next, next + Code.marker.length + Code.indexLength)) } last = next + Code.marker.length + Code.indexLength next = in.indexOf(Code.marker, last) } builder.append(in.substring(last)) return builder.toString() } def mapContent(f: String => String): Code = Code(f(Code.makeIdentifier(0)), Vector(this)) def merge(other: Code)(f: (String, String) => String): Code = Option(other) match { case Some(otherValue) => Code(f(Code.makeIdentifier(0), Code.makeIdentifier(1)), Vector(this, other)) case None => this.copy(content = f(content, "null")) } def merge(other0: Code, other1: Code)(f: (String, String, String) => String): Code = Option(other1) match { case Some(otherValue) => Code(f(Code.makeIdentifier(0), Code.makeIdentifier(1), Code.makeIdentifier(2)), Vector(this, other0, other1)) case None => Code(f(Code.makeIdentifier(0), Code.makeIdentifier(1), "null"), Vector(this, other0)) } protected def toFields(known: Map[Code, (String, String)]): (String, Map[Code, (String, String)]) = known.get(this) match { case Some((name, impl)) => (name, known) case None => val (newContent, newKnown) = this.defs.zipWithIndex.foldLeft((this.content, known)) { case ((accContent, accKnown), (code, i)) => val (childName, newKnown) = code.toFields(accKnown) (this.replace(accContent, i, childName), newKnown) } val nextName = Code.makeFieldName(newKnown.size) (nextName, newKnown.updated(this, (nextName, newContent))) } def toClassDef(packageName: String, className: String): String = { val (topLevelFieldName, fields) = toFields(Map.empty) val fieldDefs = fields.values.toList .sortBy(_._1) .map { case (name, impl) => s" private static final Expr $name = $impl;" } .mkString("\n") s"""package $packageName; | |import java.math.BigInteger; |import java.util.AbstractMap.SimpleImmutableEntry; |import java.util.ArrayList; |import java.util.List; |import java.util.Map.Entry; |import org.dhallj.core.Expr; |import org.dhallj.core.Operator; | |public final class $className { |$fieldDefs | |public static final Expr instance = $topLevelFieldName; |} |""".stripMargin } } object Code { private[javagen] val marker: String = "__" private[javagen] val indexLength: Int = 4 private[javagen] def makeIdentifier(n: Int): String = String.format(s"%s%0${indexLength}d", marker, Int.box(n)) private[javagen] def makeFieldName(n: Int): String = f"f$n%06d" def mergeAll(other: Vector[Code])(f: Vector[String] => String): Code = Code(f(0.until(other.size).map(makeIdentifier).toVector), other) def mergeAll[A](other: Vector[A])(extract: A => Option[Code])(f: Vector[(A, String)] => String): Code = { var i = -1 val values = other.map { value => if (extract(value).isDefined) { i += 1 (value, makeIdentifier(i)) } else { (value, "null") } } Code(f(values), other.flatMap(extract(_))) } } ================================================ FILE: modules/javagen/src/main/java/org/dhallj/javagen/ToCodeVisitor.scala ================================================ package org.dhallj.javagen import java.math.BigDecimal import java.math.BigInteger import java.net.URI import java.nio.file.Path import java.util.{List => JList} import java.util.Map.Entry import org.dhallj.core.Expr import org.dhallj.core.Operator import org.dhallj.core.Source import org.dhallj.core.Visitor import scala.collection.JavaConverters._ object ToCodeVisitor { val instance: Visitor[Code] = new ToCodeVisitor } final class ToCodeVisitor extends Visitor.NoPrepareEvents[Code] { private val constants = Set("Natural", "Integer", "Double", "True", "False", "Type", "List", "Text") private def unsupported: Nothing = throw new RuntimeException("Java generation only supported for fully-interpreted expressions") def onNote(base: Code, source: Source): Code = base def onNatural(self: Expr, value: BigInteger): Code = Code(s"""Expr.makeNaturalLiteral(new BigInteger("$value"))""") def onInteger(self: Expr, value: BigInteger): Code = Code(s"""Expr.makeIntegerLiteral(new BigInteger("$value"))""") def onDouble(self: Expr, value: Double): Code = Code(s"""Expr.makeDoubleLiteral($value)""") def onDate(self: Expr, year: Int, month: Int, day: Int): Code = Code(s"Expr.makeDateLiteral($year, $month, $day)") def onTime(self: Expr, hour: Int, minute: Int, second: Int, fractional: BigDecimal): Code = { if (fractional.equals(BigDecimal.ZERO)) { Code(s"Expr.makeTimeLiteral($hour, $minute, $second, BigDecimal.ZERO)") } else { Code(s"Expr.makeTimeLiteral($hour, $minute, $second, new BigDecimal($fractional))") } } def onTimeZone(self: Expr, seconds: Int): Code = Code(s"Expr.makeTimeZoneLiteral($seconds)") def onBuiltIn(self: Expr, name: String): Code = Code( if (constants(name)) { s"""Expr.Constants.${name.toUpperCase}""" } else { s"""Expr.makeBuiltIn("$name")""" } ) def onIdentifier(self: Expr, name: String, index: Long): Code = Code(s"""Expr.makeIdentifier("$name", $index)""") def onLambda(name: String, tpe: Code, result: Code): Code = result.merge(tpe) { case (resultContent, tpeContent) => s"""Expr.makeLambda("$name", $tpeContent, $resultContent)""" } def onPi(name: String, tpe: Code, result: Code): Code = result.merge(tpe) { case (resultContent, tpeContent) => s"""Expr.makePi("$name", $tpeContent, $resultContent)""" } def onLet(bindings: JList[Expr.LetBinding[Code]], body: Code): Code = unsupported private def escape(input: String): String = input.replace("\"", "\\\"").replace("\\\\", "\\\\\\\\") def onText(parts: Array[String], interpolated: JList[Code]): Code = if (parts.length == 1) { Code(s"""Expr.makeTextLiteral("${escape(parts(0))}")""") } else { Code.mergeAll(interpolated.asScala.toVector) { ids => val partArray = parts.map(part => "\"" + escape(part) + "\"").mkString(", ") s"Expr.makeTextLiteral(new String[] {$partArray}, new Expr[] {${ids.mkString(", ")}})" } } def onNonEmptyList(values: JList[Code]): Code = Code.mergeAll(values.asScala.toVector) { ids => s"Expr.makeNonEmptyListLiteral(new Expr[] {${ids.mkString(", ")}})" } def onEmptyList(tpe: Code): Code = tpe.mapContent(tpeContent => s"Expr.makeEmptyListLiteral($tpeContent)") private def forFields(constructor: String, fields: JList[Entry[String, Code]]): Code = if (fields.isEmpty) { constructor match { case "makeRecordLiteral" => Code("Expr.Constants.EMPTY_RECORD_LITERAL") case "makeRecordType" => Code("Expr.Constants.EMPTY_RECORD_TYPE") case "makeUnionType" => Code("Expr.Constants.EMPTY_UNION_TYPE") } } else { Code.mergeAll(fields.asScala.toVector)(entry => Option(entry.getValue)) { pairs => val entries = pairs .map { case (entry, id) => s"""new SimpleImmutableEntry("${entry.getKey}", $id)""" } .mkString(", ") s"Expr.$constructor(new Entry[] {$entries})" } } def onRecord(fields: JList[Entry[String, Code]]): Code = forFields("makeRecordLiteral", fields) def onRecordType(fields: JList[Entry[String, Code]]): Code = forFields("makeRecordType", fields) def onUnionType(fields: JList[Entry[String, Code]]): Code = forFields("makeUnionType", fields) def onFieldAccess(base: Code, fieldName: String): Code = base.mapContent(baseContent => s"""Expr.makeFieldAccess($baseContent, "$fieldName")""") def onProjection(base: Code, fieldNames: Array[String]): Code = { val fieldNameArray = fieldNames.map(fieldName => "\"" + fieldName + "\"").mkString(", ") base.mapContent(baseContent => s"""Expr.makeProjection($baseContent, new String[] {${fieldNameArray}})""") } def onProjectionByType(base: Code, tpe: Code): Code = base.merge(tpe) { case (baseContent, tpeContent) => s"""Expr.makeProjectionByType($baseContent, $tpeContent)""" } def onApplication(base: Code, args: JList[Code]): Code = Code.mergeAll(base +: args.asScala.toVector) { case head +: tail => s"Expr.makeApplication($head, new Expr[] {${tail.mkString(", ")}})" } def onOperatorApplication(operator: Operator, lhs: Code, rhs: Code): Code = { val operatorCode = operator match { case Operator.OR => "Operator.OR" case Operator.AND => "Operator.AND" case Operator.EQUALS => "Operator.EQUALS" case Operator.NOT_EQUALS => "Operator.NOT_EQUALS" case Operator.PLUS => "Operator.PLUS" case Operator.TIMES => "Operator.TIMES" case Operator.TEXT_APPEND => "Operator.TEXT_APPEND" case Operator.LIST_APPEND => "Operator.LIST_APPEND" case Operator.COMBINE => "Operator.COMBINE" case Operator.PREFER => "Operator.PREFER" case Operator.COMBINE_TYPES => "Operator.COMBINE_TYPES" case Operator.IMPORT_ALT => "Operator.IMPORT_ALT" case Operator.EQUIVALENT => "Operator.EQUIVALENT" case Operator.COMPLETE => "Operator.COMPLETE" } lhs.merge(rhs) { case (lhsContent, rhsContent) => s"Expr.makeOperatorApplication($operatorCode, $lhsContent, $rhsContent)" } } def onIf(predicate: Code, thenValue: Code, elseValue: Code): Code = { if (elseValue == null) throw new RuntimeException(predicate.toString()); predicate.merge(thenValue, elseValue) { case (predicateContent, thenValueContent, elseValueContent) => s"""Expr.makeIf($predicateContent, $thenValueContent, $elseValueContent)""" } } def onAnnotated(base: Code, tpe: Code): Code = unsupported def onAssert(base: Code): Code = unsupported def onMerge(handlers: Code, union: Code, tpe: Code): Code = handlers.merge(union, tpe) { case (handlersContent, unionContent, tpeContent) => s"""Expr.makeMerge($handlersContent, $unionContent, $tpeContent)""" } def onToMap(base: Code, tpe: Code): Code = unsupported def onWith(base: Code, path: Array[String], value: Code): Code = unsupported def onMissingImport(mode: Expr.ImportMode, hash: Array[Byte]): Code = unsupported def onEnvImport(value: String, mode: Expr.ImportMode, hash: Array[Byte]): Code = unsupported def onLocalImport(path: Path, mode: Expr.ImportMode, hash: Array[Byte]): Code = unsupported def onClasspathImport(path: Path, mode: Expr.ImportMode, hash: Array[Byte]): Code = unsupported def onRemoteImport(url: URI, `using`: Code, mode: Expr.ImportMode, hash: Array[Byte]): Code = unsupported } ================================================ FILE: modules/javagen/src/main/java/org/dhallj/javagen/package.scala ================================================ package org.dhallj import org.dhallj.core.Expr package object javagen { def toJavaCode(expr: Expr, packageName: String, className: String): String = expr.accept(ToCodeVisitor.instance).toClassDef(packageName, className) } ================================================ FILE: modules/jawn/src/main/scala/org/dhallj/jawn/FacadeHandler.scala ================================================ package org.dhallj.jawn import java.math.BigInteger import java.util.{ArrayDeque, Deque} import org.dhallj.core.converters.JsonHandler import org.typelevel.jawn.{FContext, Facade} class FacadeHandler[J](facade: Facade[J]) extends JsonHandler { final protected[this] val stack: Deque[FContext[J]] = new ArrayDeque() final protected[this] val position: Int = 0 protected[this] def addValue(value: J): Unit = { if (stack.isEmpty) { stack.push(facade.singleContext(position)) } stack.peek.add(value, position) } final def result: J = stack.pop().finish(position) final def onNull(): Unit = addValue(facade.jnull(position)) final def onBoolean(value: Boolean): Unit = addValue(if (value) facade.jtrue(position) else facade.jfalse(position)) def onNumber(value: BigInteger): Unit = addValue(facade.jnum(value.toString(), -1, -1, position)) def onDouble(value: Double): Unit = if (java.lang.Double.isFinite(value)) { val asString = java.lang.Double.toString(value) addValue(facade.jnum(asString, asString.indexOf('.'), asString.indexOf('E'), position)) } else { addValue(facade.jnull(position)) } def onString(value: String): Unit = addValue(facade.jstring(value, position)) final def onArrayStart(): Unit = stack.push(facade.arrayContext(position)) final def onArrayEnd(): Unit = { val current = stack.pop() if (stack.isEmpty) { stack.push(current) } else { stack.peek.add(current.finish(position), position) } } final def onArrayElementGap(): Unit = () final def onObjectStart(): Unit = stack.push(facade.objectContext(position)) final def onObjectEnd(): Unit = { val current = stack.pop() if (stack.isEmpty) { stack.push(current) } else { stack.peek.add(current.finish(position), position) } } final def onObjectField(name: String): Unit = stack.peek.add(name, position) final def onObjectFieldGap(): Unit = () } ================================================ FILE: modules/jawn/src/main/scala/org/dhallj/jawn/JawnConverter.scala ================================================ package org.dhallj.jawn import org.dhallj.core.Expr import org.dhallj.core.converters.JsonConverter import org.typelevel.jawn.Facade class JawnConverter[J](facade: Facade[J]) { def apply(expr: Expr): Option[J] = { val handler = new FacadeHandler(facade) val wasConverted = expr.accept(new JsonConverter(handler)) if (wasConverted) Some(handler.result) else None } } ================================================ FILE: modules/jawn/src/test/scala/org/dhallj/jawn/JawnConverterSuite.scala ================================================ package org.dhallj.jawn import io.circe.Json import io.circe.jawn.CirceSupportParser import io.circe.syntax._ import munit.ScalaCheckSuite import org.dhallj.ast._ import org.dhallj.core.Expr import org.dhallj.parser.DhallParser import org.scalacheck.Prop class JawnConverterSuite extends ScalaCheckSuite { val converter = new JawnConverter(CirceSupportParser.facade) 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) -> ) 1).bar]").normalize() val Right(json1) = io.circe.jawn.parse("""["bar"]""") assert(converter(expr1) == Some(json1)) } test("convert unions") { val expr1 = DhallParser.parse("[.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/parser/BUILD ================================================ load("@com_google_j2cl//build_defs:rules.bzl", "j2cl_library") package( default_visibility = ["//visibility:public"], ) j2cl_library( name = "parser", srcs = glob([ "src/main/java/org/dhallj/parser/*.java", "src/main/java/org/dhallj/parser/support/*.java", "target/javacc/*.java", ]), deps = [ "//javascript/jre:java_io", "//javascript/jre:java_net", "//javascript/jre:java_nio_file", "//modules/core", ], ) ================================================ FILE: modules/parser/src/main/java/org/dhallj/parser/DhallParser.java ================================================ package org.dhallj.parser; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import org.dhallj.core.Expr; import org.dhallj.parser.support.Parser; /** Parses text input into Dhall expressions. */ public final class DhallParser { private static final Charset UTF_8 = Charset.forName("UTF-8"); public static Expr.Parsed parse(String input) { return Parser.parse(input); } public static Expr.Parsed parse(InputStream input) throws IOException { return parse(input, UTF_8); } public static Expr.Parsed parse(InputStream input, Charset charset) throws IOException { return Parser.parse(input, charset); } } ================================================ FILE: modules/parser/src/main/java/org/dhallj/parser/support/Comment.java ================================================ package org.dhallj.parser.support; final class Comment { private final String content; private final int beginLine; private final int beginColumn; private final int endLine; private final int endColumn; Comment(String content, int beginLine, int beginColumn, int endLine, int endColumn) { this.content = content; this.beginLine = beginLine; this.beginColumn = beginColumn; this.endLine = endLine; this.endColumn = endColumn; } public String getContent() { return this.content; } public final int getBeginLine() { return this.beginLine; } public final int getBeginColumn() { return this.beginColumn; } public final int getEndLine() { return this.endLine; } public final int getEndColumn() { return this.endColumn; } } ================================================ FILE: modules/parser/src/main/java/org/dhallj/parser/support/LetBinding.java ================================================ package org.dhallj.parser.support; import org.dhallj.core.Expr; final class LetBinding { final String name; final Expr.Parsed type; final Expr.Parsed value; final String text0; final String text1; final String text2; final int beginLine; final int beginColumn; LetBinding( String name, Expr.Parsed type, Expr.Parsed value, String text0, String text1, String text2, int beginLine, int beginColumn) { this.name = name; this.type = type; this.value = value; this.text0 = text0; this.text1 = text1; this.text2 = text2; this.beginLine = beginLine; this.beginColumn = beginColumn; } } ================================================ FILE: modules/parser/src/main/java/org/dhallj/parser/support/OperatorPrecedenceTable.java ================================================ package org.dhallj.parser.support; import org.dhallj.core.Operator; final class OperatorPrecedenceTable implements JavaCCParserConstants { private static final int MAX_OPERATOR_TOKEN_KIND = 256; private static final int[] table = new int[MAX_OPERATOR_TOKEN_KIND]; static { for (int i = 0; i < MAX_OPERATOR_TOKEN_KIND; i++) { table[i] = -1; } table[OR] = Operator.OR.getPrecedence(); table[AND] = Operator.AND.getPrecedence(); table[EQUALS] = Operator.EQUALS.getPrecedence(); table[NOT_EQUALS] = Operator.NOT_EQUALS.getPrecedence(); table[PLUS] = Operator.PLUS.getPrecedence(); table[TIMES] = Operator.TIMES.getPrecedence(); table[TEXT_APPEND] = Operator.TEXT_APPEND.getPrecedence(); table[LIST_APPEND] = Operator.LIST_APPEND.getPrecedence(); table[COMBINE] = Operator.COMBINE.getPrecedence(); table[PREFER] = Operator.PREFER.getPrecedence(); table[COMBINE_TYPES] = Operator.COMBINE_TYPES.getPrecedence(); table[IMPORT_ALT] = Operator.IMPORT_ALT.getPrecedence(); table[EQUIVALENT] = Operator.EQUIVALENT.getPrecedence(); } static final int get(int tokenKind) { if (tokenKind >= MAX_OPERATOR_TOKEN_KIND) { return -1; } else { return table[tokenKind]; } } } ================================================ FILE: modules/parser/src/main/java/org/dhallj/parser/support/Parser.java ================================================ package org.dhallj.parser.support; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import org.dhallj.core.DhallException.ParsingFailure; import org.dhallj.core.Expr; /** Wrapper for the JavaCC-generated parser. */ public final class Parser { public static Expr.Parsed parse(String input) { try { return new JavaCCParser(new StringProvider(input)).TOP_LEVEL(); } catch (ParseException underlying) { throw new ParsingFailure(underlying.getMessage(), underlying); } catch (TokenMgrException underlying) { throw new ParsingFailure(underlying.getMessage(), underlying); } } public static Expr.Parsed parse(InputStream input, Charset charset) throws IOException { try { return new JavaCCParser(new StreamProvider(input, charset.name())).TOP_LEVEL(); } catch (ParseException underlying) { throw new ParsingFailure(underlying.getMessage(), underlying); } catch (TokenMgrException underlying) { throw new ParsingFailure(underlying.getMessage(), underlying); } } } ================================================ FILE: modules/parser/src/main/java/org/dhallj/parser/support/ParsingHelpers.java ================================================ package org.dhallj.parser.support; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URI; import java.nio.file.Path; import java.nio.file.Paths; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Set; import org.dhallj.core.DhallException.ParsingFailure; import org.dhallj.core.Expr; import org.dhallj.core.Operator; import org.dhallj.core.Source; final class ParsingHelpers { private static Source sourceFromToken(Token token) { return Source.fromString( token.image, token.beginLine, token.beginColumn, token.endLine, token.endColumn); } private static Source sourceFromTokens(Token token1, Token token2) { return Source.fromString( token1.image + token2.image, token1.beginLine, token1.beginColumn, token2.endLine, token2.endColumn); } static final Expr.Parsed makeDoubleLiteral(Token token) { double parsed = Double.parseDouble(token.image); if (Double.isInfinite(parsed) && !token.image.equals("Infinity") && !token.image.equals("-Infinity")) { throw new ParsingFailure("double out of bounds"); } return new Expr.Parsed(Expr.makeDoubleLiteral(parsed), sourceFromToken(token)); } static final Expr.Parsed makeNaturalLiteral(Token token) { BigInteger value = token.image.startsWith("0x") ? new BigInteger(token.image.substring(2), 16) : new BigInteger(token.image); return new Expr.Parsed(Expr.makeNaturalLiteral(value), sourceFromToken(token)); } static final Expr.Parsed makeIntegerLiteral(Token token) { BigInteger value; if (token.image.startsWith("0x")) { value = new BigInteger(token.image.substring(2), 16); } else if (token.image.startsWith("-0x")) { value = new BigInteger(token.image.substring(3), 16).negate(); } else { value = new BigInteger(token.image); } return new Expr.Parsed(Expr.makeIntegerLiteral(value), sourceFromToken(token)); } static final boolean isValidDate(int year, int month, int day) { if (month > 0 && month <= 12) { if (day > 0 && day < 29) { return true; } else if (day == 29) { // Deal with leap days. if (month == 2) { if (year % 4 != 0) { return false; } else if (year % 100 != 0) { return true; } else if (year % 400 != 0) { return false; } else { return true; } } else { return true; } } else if (day == 30) { return month != 2; } else if (day == 31) { return month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12; } else { return false; } } else { return false; } } static final Expr.Parsed makeDateLiteral(Token token) { int year = Integer.parseInt(token.image.substring(0, 4)); int month = Integer.parseInt(token.image.substring(5, 7)); int day = Integer.parseInt(token.image.substring(8, 10)); if (!isValidDate(year, month, day)) { throw new ParsingFailure("Invalid temporal literal"); } return new Expr.Parsed(Expr.makeDateLiteral(year, month, day), sourceFromToken(token)); } static final Expr.Parsed makeTimeZoneLiteral(Token token) { boolean positive = token.image.charAt(0) == '+'; int hour = Integer.parseInt(token.image.substring(1, 3)); int minute = Integer.parseInt(token.image.substring(4, 6)); if (hour > 23 || minute > 59) { throw new ParsingFailure("Invalid temporal literal"); } int seconds = hour * 60 + minute; int value = positive ? seconds : -seconds; return new Expr.Parsed(Expr.makeTimeZoneLiteral(value), sourceFromToken(token)); } static final Expr.Parsed makeTimeLiteral(Token token, Token timeZone) { int hour = Integer.parseInt(token.image.substring(0, 2)); int minute = Integer.parseInt(token.image.substring(3, 5)); int second = Integer.parseInt(token.image.substring(6, 8)); BigDecimal fractional; if (hour > 23 || minute > 59 || second > 59) { throw new ParsingFailure("Invalid temporal literal"); } if (token.image.length() > 8) { fractional = new BigDecimal(token.image.substring(8)); } else { fractional = BigDecimal.ZERO; } Expr time = Expr.makeTimeLiteral(hour, minute, second, fractional); if (timeZone != null) { int value; if (timeZone.image.equals("z") || timeZone.image.equals("Z")) { value = 0; } else if (timeZone.image.startsWith("+") || timeZone.image.startsWith("-")) { boolean positive = timeZone.image.charAt(0) == '+'; int tzHour = Integer.parseInt(timeZone.image.substring(1, 3)); int tzMinute = Integer.parseInt(timeZone.image.substring(4, 6)); if (tzHour > 23 || tzMinute > 59) { throw new ParsingFailure("Invalid temporal literal"); } int seconds = tzHour * 60 + tzMinute; value = positive ? seconds : -seconds; } else { // Necessary to work around generated code size limits. throw new ParsingFailure("Invalid temporal offset: " + timeZone.image); } List> fields = new ArrayList>(2); fields.add(new SimpleImmutableEntry<>("time", time)); fields.add(new SimpleImmutableEntry<>("timeZone", Expr.makeTimeZoneLiteral(value))); return new Expr.Parsed(Expr.makeRecordLiteral(fields), sourceFromTokens(token, timeZone)); } else { return new Expr.Parsed(time, sourceFromToken(token)); } } static final Expr.Parsed makeDateTimeLiteral(Token token, Token timeZone) { int year = Integer.parseInt(token.image.substring(0, 4)); int month = Integer.parseInt(token.image.substring(5, 7)); int day = Integer.parseInt(token.image.substring(8, 10)); int hour = Integer.parseInt(token.image.substring(11, 13)); int minute = Integer.parseInt(token.image.substring(14, 16)); int second = Integer.parseInt(token.image.substring(17, 19)); BigDecimal fractional; if (!isValidDate(year, month, day) || hour > 23 || minute > 59 || second > 59) { throw new ParsingFailure("Invalid temporal literal"); } if (token.image.length() > 19) { fractional = new BigDecimal(token.image.substring(19)); } else { fractional = BigDecimal.ZERO; } List> fields = new ArrayList>(2); fields.add(new SimpleImmutableEntry<>("date", Expr.makeDateLiteral(year, month, day))); fields.add( new SimpleImmutableEntry<>("time", Expr.makeTimeLiteral(hour, minute, second, fractional))); if (timeZone != null) { int value; if (timeZone.image.equals("z") || timeZone.image.equals("Z")) { value = 0; } else if (timeZone.image.startsWith("+") || timeZone.image.startsWith("-")) { boolean positive = timeZone.image.charAt(0) == '+'; int tzHour = Integer.parseInt(timeZone.image.substring(1, 3)); int tzMinute = Integer.parseInt(timeZone.image.substring(4, 6)); if (tzHour > 23 || tzMinute > 59) { throw new ParsingFailure("Invalid temporal literal"); } int seconds = tzHour * 60 + tzMinute; value = positive ? seconds : -seconds; } else { // Necessary to work around generated code size limits. throw new ParsingFailure("Invalid temporal offset: " + timeZone.image); } fields.add(new SimpleImmutableEntry<>("timeZone", Expr.makeTimeZoneLiteral(value))); } Source source = timeZone == null ? sourceFromToken(token) : sourceFromTokens(token, timeZone); return new Expr.Parsed(Expr.makeRecordLiteral(fields), source); } private static String unescapeText(String in) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < in.length(); i++) { if (in.charAt(i) == '\\') { i += 1; char next = in.charAt(i); if (next == '"' || next == '$' || next == '/') { builder.append(next); } else if (next == 'u') { char escapeFirst = in.charAt(i + 1); if (escapeFirst == '{') { int len = 0; while (in.charAt(i + 2 + len) != '}') { len += 1; } int code = Integer.parseInt(in.substring(i + 2, i + 2 + len), 16); builder.appendCodePoint(code); i += len + 2; } else { int code = Integer.parseInt(in.substring(i + 1, i + 5), 16); builder.append((char) code); i += 4; } } else { builder.append('\\'); builder.append(next); } } else { builder.append(in.charAt(i)); } } return builder.toString(); } static final Expr.Parsed makeTextLiteral( List> chunks, Token first, Token last) { // TODO: fix source. Source source = Source.fromString("", first.beginLine, first.beginColumn, last.endLine, last.endColumn); List parts = new ArrayList<>(1); List interpolated = new ArrayList<>(); boolean lastWasInterpolated = true; for (Entry chunk : chunks) { if (chunk.getKey() == null) { if (lastWasInterpolated) { parts.add(""); } interpolated.add(chunk.getValue()); lastWasInterpolated = true; } else { parts.add(unescapeText(chunk.getKey())); lastWasInterpolated = false; } } if (interpolated.size() == parts.size()) { parts.add(""); } return new Expr.Parsed( Expr.makeTextLiteral(parts.toArray(new String[parts.size()]), (List) interpolated), source); } static final void dedent(String[] input) { List candidate = null; String[][] partLines = new String[input.length][]; for (int i = 0; i < input.length; i += 1) { String part = input[i].replace("\r\n", "\n"); String[] lines = part.split("\n", -1); partLines[i] = lines; for (int j = (i == 0) ? 0 : 1; j < lines.length; j += 1) { String line = lines[j]; if (line.length() > 0 || j == lines.length - 1) { if (candidate == null) { candidate = new ArrayList<>(); for (int k = 0; k < line.length(); k += 1) { char c = line.charAt(k); if (c == ' ' || c == '\t') { candidate.add(c); } else { break; } } } else { for (int k = 0; k < candidate.size(); k += 1) { if (k == line.length() || line.charAt(k) != candidate.get(k).charValue()) { candidate = candidate.subList(0, k); break; } } } } } } int stripCount = candidate == null ? 0 : candidate.size(); if (stripCount == 0) { for (int i = 0; i < input.length; i += 1) { input[i] = reEscape(input[i]); } } else { StringBuilder builder = new StringBuilder(); for (int i = 0; i < input.length; i += 1) { builder.setLength(0); String[] lines = partLines[i]; for (int j = 0; j < lines.length; j += 1) { if (lines[j].length() != 0) { if (i > 0 && j == 0) { builder.append(lines[j]); } else { builder.append(lines[j].substring(stripCount)); } } if (j < lines.length - 1) { builder.append("\n"); } } input[i] = reEscape(builder.toString()); } } } static final String reEscape(String input) { return input.replace("\\", "\\\\").replace("\n", "\\n").replace("\t", "\\t"); } static final Expr.Parsed makeSingleQuotedTextLiteral( List> chunks, Token first) { // TODO: fix source. Source source = sourceFromToken(first); Collections.reverse(chunks); List parts = new ArrayList<>(1); List interpolated = new ArrayList<>(); for (Entry chunk : chunks) { if (chunk.getKey() == null) { if (parts.isEmpty()) { parts.add(""); } interpolated.add(chunk.getValue()); } else { if (parts.size() > interpolated.size()) { parts.set(parts.size() - 1, parts.get(parts.size() - 1) + chunk.getKey()); } else { parts.add(chunk.getKey()); } } } if (interpolated.size() == parts.size()) { parts.add(""); } String[] partArray = parts.toArray(new String[parts.size()]); dedent(partArray); return new Expr.Parsed(Expr.makeTextLiteral(partArray, (List) interpolated), source); } static final Expr.Parsed makeApplication(Expr.Parsed base, Expr.Parsed arg, Token whsp) { return new Expr.Parsed(Expr.makeApplication(base, arg), new ESESource(base, whsp.image, arg)); } static final Expr.Parsed makeOperatorApplication( Operator operator, Expr.Parsed lhs, Expr.Parsed rhs, String operatorString, Token whsp0, Token whsp1) { StringBuilder builder = new StringBuilder(); if (whsp0 != null) { builder.append(whsp0.image); } builder.append(operatorString); if (whsp1 != null) { builder.append(whsp1.image); } Source source = new ESESource(lhs, builder.toString(), rhs); return new Expr.Parsed(Expr.makeOperatorApplication(operator, lhs, rhs), source); } static final Expr.Parsed makeAnnotated( Expr.Parsed base, Expr.Parsed tpe, Token whsp0, Token whsp1) { StringBuilder builder = new StringBuilder(); if (whsp0 != null) { builder.append(whsp0.image); } builder.append(':'); builder.append(whsp1.image); Source source = new ESESource(base, builder.toString(), tpe); return new Expr.Parsed(Expr.makeAnnotated(base, tpe), source); } static final Expr.Parsed makeToMap( Expr.Parsed base, Expr.Parsed tpe, Token first, Token whsp0, Token whsp1, Token whsp2) { StringBuilder builder = new StringBuilder(); if (whsp1 != null) { builder.append(whsp1.image); } if (whsp2 != null) { builder.append(":"); builder.append(whsp2.image); } Source source; if (tpe != null) { source = new SESESource( first.image + whsp0.image, base, builder.toString(), tpe, first.beginLine, first.beginColumn); } else { source = new SESource(first.image + whsp0.image, base, first.beginLine, first.beginColumn); } return new Expr.Parsed(Expr.makeToMap(base, tpe), source); } static final Expr.Parsed makeToMap(Expr.Parsed base, Token first, Token whsp) { Source source = new SESource(first.image + whsp.image, base, first.beginLine, first.beginColumn); return new Expr.Parsed(Expr.makeToMap(base), source); } static final Expr.Parsed makeMerge( Expr.Parsed left, Expr.Parsed right, Expr.Parsed tpe, Token first, Token whsp0, Token whsp1, Token whsp2, Token whsp3) { Source source; if (tpe != null) { StringBuilder builder = new StringBuilder(); if (whsp2 != null) { builder.append(whsp2.image); } builder.append(":"); builder.append(whsp3.image); source = new SESESESource( first.image + whsp0.image, left, whsp1.image, right, builder.toString(), tpe, first.beginLine, first.beginColumn); } else { source = new SESESource( first.image + whsp0.image, left, whsp1.image, right, first.beginLine, first.beginColumn); } return new Expr.Parsed(Expr.makeMerge(left, right, tpe), source); } static final Expr.Parsed makeLambda( String param, Expr.Parsed input, Expr.Parsed result, Token first) { // TODO: text is empty. Source source = Source.fromString( "", first.beginLine, first.beginColumn, result.getSource().getEndLine(), result.getSource().getEndColumn()); return new Expr.Parsed(Expr.makeLambda(param, input, result), source); } static final Expr.Parsed makePi( String param, Expr.Parsed input, Expr.Parsed result, Token first) { // TODO: text is empty. Source source = Source.fromString( "", first.beginLine, first.beginColumn, result.getSource().getEndLine(), result.getSource().getEndColumn()); return new Expr.Parsed(Expr.makePi(param, input, result), source); } static final Expr.Parsed makePi(Expr.Parsed input, Expr.Parsed result) { // TODO: text is empty. Source source = Source.fromString( "", input.getSource().getBeginLine(), input.getSource().getBeginColumn(), result.getSource().getEndLine(), result.getSource().getEndColumn()); return new Expr.Parsed(Expr.makePi(input, result), source); } static final Expr.Parsed makeIf( Expr.Parsed cond, Expr.Parsed thenValue, Expr.Parsed elseValue, Token first) { // TODO: text is empty. Source source = Source.fromString( "", first.beginLine, first.beginColumn, elseValue.getSource().getEndLine(), elseValue.getSource().getEndColumn()); return new Expr.Parsed(Expr.makeIf(cond, thenValue, elseValue), source); } static final Expr.Parsed makeLet(List bindings, Expr.Parsed body, String whsp) { Collections.reverse(bindings); Expr.Parsed current = body; String extraText2 = whsp; for (LetBinding binding : bindings) { Source source; if (binding.type == null) { source = new SESESource( binding.text1, binding.value, binding.text2 + extraText2, current, binding.beginLine, binding.beginColumn); } else { source = new SESESESource( binding.text0, binding.type, binding.text1, binding.value, binding.text2 + extraText2, current, binding.beginLine, binding.beginColumn); } current = new Expr.Parsed(Expr.makeLet(binding.name, binding.type, binding.value, current), source); extraText2 = ""; } return current; } static Expr.Parsed makeAssert(Expr.Parsed base, Token first, Token whsp0, Token whsp1) { StringBuilder builder = new StringBuilder("assert"); if (whsp0 != null) { builder.append(whsp0.image); } builder.append(':'); builder.append(whsp1.image); Source source = new SESource(builder.toString(), base, first.beginLine, first.beginColumn); return new Expr.Parsed(Expr.makeAssert(base), source); } static Expr.Parsed makeFieldAccess( Expr.Parsed base, String fieldName, Token whsp0, Token whsp1, int endLine, int endColumn) { StringBuilder builder = new StringBuilder(); if (whsp0 != null) { builder.append(whsp0.image); } builder.append('.'); if (whsp1 != null) { builder.append(whsp1.image); } builder.append(fieldName); Source source = new ESSource(base, builder.toString(), endLine, endColumn); return new Expr.Parsed(Expr.makeFieldAccess(base, fieldName), source); } static Expr.Parsed makeProjection( Expr.Parsed base, List fieldNames, int endLine, int endColumn) { // TODO: text is empty. Source source = Source.fromString( "", base.getSource().getBeginLine(), base.getSource().getBeginColumn(), endLine, endColumn); return new Expr.Parsed( Expr.makeProjection(base, fieldNames.toArray(new String[fieldNames.size()])), source); } static Expr.Parsed makeProjectionByType( Expr.Parsed base, Expr.Parsed tpe, int endLine, int endColumn) { // TODO: text is empty. Source source = Source.fromString( "", base.getSource().getBeginLine(), base.getSource().getBeginColumn(), endLine, endColumn); return new Expr.Parsed(Expr.makeProjectionByType(base, tpe), source); } private static final boolean isBuiltIn(String input) { return input.charAt(0) != '`' && Expr.Constants.isBuiltIn(input); } private static final String unescapeLabel(String input) { return (input.charAt(0) != '`') ? input : input.substring(1, input.length() - 1); } static final Expr.Parsed makeBuiltInOrIdentifier(Token value) { if (isBuiltIn(value.image)) { return new Expr.Parsed(Expr.makeBuiltIn(value.image), sourceFromToken(value)); } else { return new Expr.Parsed( Expr.makeIdentifier(unescapeLabel(value.image)), sourceFromToken(value)); } } static final Expr.Parsed makeIdentifier(Token value, Token whsp0, Token whsp1, Token index) { StringBuilder builder = new StringBuilder(); builder.append(value.image); if (whsp0 != null) { builder.append(whsp0.image); } builder.append("@"); if (whsp1 != null) { builder.append(whsp1.image); } builder.append(index.image); Source source = Source.fromString( builder.toString(), value.beginLine, value.beginColumn, index.endLine, index.endColumn); long indexValue = index.image.startsWith("0x") ? Long.parseLong(index.image.substring(2), 16) : Long.parseLong(index.image); return new Expr.Parsed(Expr.makeIdentifier(unescapeLabel(value.image), indexValue), source); } static final Expr.Parsed makeRecordLiteral( List, Expr.Parsed>> fields, Token first, Token last) { // TODO: Get actual last token in all cases. int endLine = (last == null) ? first.endLine : last.endLine; int endColumn = (last == null) ? first.endColumn : last.endColumn; // TODO: text is empty. Source source = Source.fromString("", first.beginLine, first.beginColumn, endLine, endColumn); List> dedotted = new ArrayList<>(fields.size()); for (Entry, Expr.Parsed> entry : fields) { List parts = entry.getKey(); String firstPart = parts.remove(0); Expr maybePunnedValue = entry.getValue(); Expr value; if (maybePunnedValue == null) { // Record puns can't be dotted. value = Expr.makeIdentifier(firstPart); } else { value = maybePunnedValue; } if (parts.isEmpty()) { dedotted.add(new SimpleImmutableEntry<>(firstPart, value)); } else { Collections.reverse(parts); Expr current = value; for (String part : parts) { current = Expr.makeRecordLiteral(part, current); } dedotted.add(new SimpleImmutableEntry<>(firstPart, current)); } } List> desugared = new ArrayList<>(dedotted.size()); Set seen = new HashSet(); for (int i = 0; i < dedotted.size(); i++) { Entry entry = dedotted.get(i); String key = entry.getKey(); if (!seen.contains(key)) { Expr current = entry.getValue(); for (int j = i + 1; j < dedotted.size(); j++) { Entry other = dedotted.get(j); if (other.getKey().equals(entry.getKey())) { current = Expr.makeOperatorApplication(Operator.COMBINE, current, other.getValue()); } } desugared.add(new SimpleImmutableEntry<>(key, current)); seen.add(key); } } return new Expr.Parsed(Expr.makeRecordLiteral(desugared), source); } static final Expr.Parsed makeRecordType( List> fields, Token first, Token last) { // TODO: Get actual last token in all cases. int endLine = (last == null) ? first.endLine : last.endLine; int endColumn = (last == null) ? first.endColumn : last.endColumn; // TODO: text is empty. Source source = Source.fromString("", first.beginLine, first.beginColumn, endLine, endColumn); return new Expr.Parsed(Expr.makeRecordType((List) fields), source); } static final Expr.Parsed makeUnionType( List> fields, Token first, Token last) { // TODO: Get actual last token in all cases. int endLine = (last == null) ? first.endLine : last.endLine; int endColumn = (last == null) ? first.endColumn : last.endColumn; // TODO: text is empty. Source source = Source.fromString("", first.beginLine, first.beginColumn, endLine, endColumn); return new Expr.Parsed(Expr.makeUnionType((List) fields), source); } static final Expr.Parsed makeWith(Expr base, List path, Expr.Parsed arg, Token first) { // TODO: source isn't correct. Source source = sourceFromToken(first); return new Expr.Parsed(Expr.makeWith(base, path.toArray(new String[path.size()]), arg), source); } static final Expr.Parsed makeNonEmptyListLiteral( List values, List other, Token first, Token last) { Source source = new InterspersedSource( other, values, first.beginLine, first.beginColumn, last.endLine, last.endColumn); return new Expr.Parsed( Expr.makeNonEmptyListLiteral(values.toArray(new Expr.Parsed[values.size()])), source); } static final Expr.Parsed makeEmptyListLiteral(Expr.Parsed tpe, String other, Token first) { Source source = new SESource(other, tpe, first.beginLine, first.beginColumn); return new Expr.Parsed(Expr.makeEmptyListLiteral(tpe), source); } static final Expr.Parsed makeParenthesized(Expr.Parsed value, Token first, Token last) { Source source = new SESSource( "(", value, ")", first.beginLine, first.beginColumn, last.endLine, last.endColumn); return new Expr.Parsed(value, source); } static final Expr.Parsed makeImport( Token type, Token hashToken, Token modeToken, Expr.Parsed using) { // TODO: fix. Source source = sourceFromToken(type); byte[] hash = (hashToken == null) ? null : Expr.Util.decodeHashBytes(hashToken.image.substring(7)); Expr value = null; Expr.ImportMode mode = (modeToken == null) ? Expr.ImportMode.CODE : (modeToken.image.equals("Text") ? Expr.ImportMode.RAW_TEXT : Expr.ImportMode.LOCATION); if (type.image.equals("missing")) { value = Expr.makeMissingImport(mode, hash); } else if (type.image.startsWith("http")) { try { value = Expr.makeRemoteImport(new URI(type.image), using, mode, hash); } catch (java.net.URISyntaxException e) { throw new ParsingFailure("Invalid URL", e); } } else if (type.image.startsWith("env:")) { value = Expr.makeEnvImport(type.image.substring(4), mode, hash); } else if (type.image.startsWith("classpath:")) { value = Expr.makeClasspathImport(Paths.get(type.image.substring(10)), mode, hash); } else { try { value = Expr.makeLocalImport(Paths.get(type.image), mode, hash); } catch (java.nio.file.InvalidPathException e) { throw new ParsingFailure("Invalid path", e); } } return new Expr.Parsed(value, source); } private static final class ESESource extends Source { private final Expr.Parsed i0; private final String i1; private final Expr.Parsed i2; ESESource(Expr.Parsed i0, String i1, Expr.Parsed i2) { super( i0.getSource().getBeginLine(), i0.getSource().getBeginColumn(), i2.getSource().getEndLine(), i2.getSource().getEndColumn()); this.i0 = i0; this.i1 = i1; this.i2 = i2; } public final void printText(StringBuilder builder) { this.i0.getSource().printText(builder); builder.append(this.i1); this.i2.getSource().printText(builder); } } private static final class ESSource extends Source { private final Expr.Parsed i0; private final String i1; ESSource(Expr.Parsed i0, String i1, int endLine, int endColumn) { super(i0.getSource().getBeginLine(), i0.getSource().getBeginColumn(), endLine, endColumn); this.i0 = i0; this.i1 = i1; } public final void printText(StringBuilder builder) { this.i0.getSource().printText(builder); builder.append(this.i1); } } private static final class SESource extends Source { private final String i0; private final Expr.Parsed i1; SESource(String i0, Expr.Parsed i1, int beginLine, int beginColumn) { super(beginLine, beginColumn, i1.getSource().getEndLine(), i1.getSource().getEndColumn()); this.i0 = i0; this.i1 = i1; } public final void printText(StringBuilder builder) { builder.append(this.i0); this.i1.getSource().printText(builder); } } private static final class SESSource extends Source { private final String i0; private final Expr.Parsed i1; private final String i2; SESSource( String i0, Expr.Parsed i1, String i2, int beginLine, int beginColumn, int endLine, int endColumn) { super(beginLine, beginColumn, endLine, endColumn); this.i0 = i0; this.i1 = i1; this.i2 = i2; } public final void printText(StringBuilder builder) { builder.append(this.i0); this.i1.getSource().printText(builder); builder.append(this.i2); } } private static final class SESESource extends Source { private final String i0; private final Expr.Parsed i1; private final String i2; private final Expr.Parsed i3; SESESource( String i0, Expr.Parsed i1, String i2, Expr.Parsed i3, int beginLine, int beginColumn) { super(beginLine, beginColumn, i3.getSource().getEndLine(), i3.getSource().getEndColumn()); this.i0 = i0; this.i1 = i1; this.i2 = i2; this.i3 = i3; } public final void printText(StringBuilder builder) { builder.append(this.i0); this.i1.getSource().printText(builder); builder.append(this.i2); this.i3.getSource().printText(builder); } } private static final class SESESESource extends Source { private final String i0; private final Expr.Parsed i1; private final String i2; private final Expr.Parsed i3; private final String i4; private final Expr.Parsed i5; SESESESource( String i0, Expr.Parsed i1, String i2, Expr.Parsed i3, String i4, Expr.Parsed i5, int beginLine, int beginColumn) { super(beginLine, beginColumn, i5.getSource().getEndLine(), i5.getSource().getEndColumn()); this.i0 = i0; this.i1 = i1; this.i2 = i2; this.i3 = i3; this.i4 = i4; this.i5 = i5; } public final void printText(StringBuilder builder) { builder.append(this.i0); this.i1.getSource().printText(builder); builder.append(this.i2); this.i3.getSource().printText(builder); builder.append(this.i4); this.i5.getSource().printText(builder); } } private static final class InterspersedSource extends Source { private final List i0; private final List i1; InterspersedSource( List i0, List i1, int beginLine, int beginColumn, int endLine, int endColumn) { super(beginLine, beginColumn, endLine, endColumn); this.i0 = i0; this.i1 = i1; } public final void printText(StringBuilder builder) { Iterator ii0 = i0.iterator(); Iterator ii1 = i1.iterator(); while (ii0.hasNext() && ii1.hasNext()) { builder.append(ii0.next()); ii1.next().getSource().printText(builder); } if (ii0.hasNext()) { builder.append(ii0.next()); } } } } ================================================ FILE: modules/parser/src/main/java/org/dhallj/parser/support/WhitespaceManager.java ================================================ package org.dhallj.parser.support; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * Special tokenization for whitespace. * *

Dhall's definition of whitespace doesn't fit cleanly with JavaCC's model, so if we want no * consecutive whitespace tokens, we have to tokenize ourselves. * *

This is roughly working, except for the sources, but desperately needs clean-up. */ final class WhitespaceManager { private final List comments = new ArrayList(); private char curr = 0; private boolean advance(SimpleCharStream stream) { try { this.curr = stream.readChar(); return false; } catch (IOException e) { return true; } } private void advanceNotEof(SimpleCharStream stream) { if (advance(stream)) { fail(this.curr, stream); } } private static boolean isCommentChar(char c) { return (c >= '\u0020' && c <= '\ud7ff') || (c >= '\ue000' && c <= '\ufffd') || (c == '\t'); } private static void fail(int current, SimpleCharStream stream) { throw new TokenMgrException( false, 0, stream.getEndLine(), stream.getEndColumn(), stream.GetImage(), current, TokenMgrException.LEXICAL_ERROR); } List consume(SimpleCharStream stream, char first) { if (first == '{') { return consumeWithComments(stream, true); } else if (first == '-') { return consumeWithComments(stream, false); } else { if (advance(stream)) { return null; } else if (this.curr == '{' || this.curr == '-') { boolean startsWithBlock = this.curr == '{'; if (advance(stream)) { stream.backup(1); return null; } else if (this.curr == '-') { return consumeWithComments(stream, startsWithBlock); } else { stream.backup(2); return null; } } else { stream.backup(1); return null; } } } private Comment consumeLineComment(SimpleCharStream stream) { int commentLen = 2; int commentBeginLine = stream.getBeginLine(); int commentBeginColumn = stream.getBeginColumn(); while (true) { advanceNotEof(stream); if (isCommentChar(this.curr)) { commentLen += 1; } else if (this.curr == '\n') { break; } else if (this.curr == '\r') { if (advance(stream) || this.curr != '\n') { fail(this.curr, stream); } break; } else if (Character.isHighSurrogate(this.curr)) { if (advance(stream) || !Character.isLowSurrogate(this.curr)) { fail(this.curr, stream); } commentLen += 2; } else { fail(this.curr, stream); } } return new Comment( new String(stream.GetSuffix(commentLen)), commentBeginLine, commentBeginColumn, stream.getEndLine(), stream.getEndColumn()); } private Comment consumeBlockComment(SimpleCharStream stream) { int blockCommentLevel = 1; int commentLen = 2; int commentBeginLine = stream.getBeginLine(); int commentBeginColumn = stream.getBeginColumn(); advanceNotEof(stream); do { if (this.curr == '-') { advanceNotEof(stream); if (this.curr == '}') { blockCommentLevel -= 1; } else { commentLen += 1; advanceNotEof(stream); } } else if (this.curr == '{') { advanceNotEof(stream); if (this.curr == '-') { blockCommentLevel += 1; } else { commentLen += 1; advanceNotEof(stream); } } else if (isCommentChar(this.curr) || this.curr == '\n') { commentLen += 1; advanceNotEof(stream); } else if (this.curr == '\r') { if (advance(stream) || this.curr != '\n') { fail(this.curr, stream); } commentLen += 1; advanceNotEof(stream); } else if (Character.isHighSurrogate(this.curr)) { if (advance(stream) || !Character.isLowSurrogate(this.curr)) { fail(this.curr, stream); } commentLen += 2; advanceNotEof(stream); } else { fail(this.curr, stream); } } while (blockCommentLevel > 0); return new Comment( new String(stream.GetSuffix(commentLen)), commentBeginLine, commentBeginColumn, stream.getEndLine(), stream.getEndColumn()); } private List consumeWithComments(SimpleCharStream stream, boolean startsWithBlock) { comments.clear(); if (startsWithBlock) { comments.add(consumeBlockComment(stream)); } else { comments.add(consumeLineComment(stream)); } while (true) { if (advance(stream)) { return comments; } switch (this.curr) { case ' ': case '\t': case '\n': break; case '{': advance(stream); if (this.curr == '-') { comments.add(consumeBlockComment(stream)); } else { stream.backup(2); return comments; } break; case '-': advance(stream); if (this.curr == '-') { comments.add(consumeLineComment(stream)); } else { stream.backup(2); return comments; } break; case '\r': advanceNotEof(stream); if (this.curr != '\n') { stream.backup(2); return comments; } break; default: stream.backup(1); return null; } } } } ================================================ FILE: modules/parser/src/main/java/org/dhallj/parser/support/package-info.java ================================================ /** * Support classes generated by JavaCC. * *

Please do not use the contents of this package directly! */ package org.dhallj.parser.support; ================================================ FILE: modules/parser/src/main/javacc/JavaCCParser.jj ================================================ options { JDK_VERSION="1.8"; JAVA_TEMPLATE_TYPE = "modern"; SUPPORT_CLASS_VISIBILITY_PUBLIC = false; UNICODE_INPUT = true; JAVA_UNICODE_ESCAPE = false; } PARSER_BEGIN(JavaCCParser) package org.dhallj.parser.support; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.List; import java.util.Map; import org.dhallj.core.Operator; import org.dhallj.core.Expr; final class JavaCCParser { private static final String trimLabel(String input) { return (input.charAt(0) != '`') ? input : input.substring(1, input.length() - 1); } } PARSER_END(JavaCCParser) TOKEN_MGR_DECLS : { Deque interpolationState = new ArrayDeque<>(); Deque braceDepth = new ArrayDeque<>(); List comments = new ArrayList<>(); WhitespaceManager whitespace = new WhitespaceManager(); } TOKEN: { { List cs = whitespace.consume(input_stream, matchedToken.image.charAt(0)); if (cs != null) { comments.addAll(cs); } } | )? (("." ()?) | )) | (("-")? "Infinity") | "NaN"> | > | )*) | ("0x" )> | | | | | | | | | | | | | | | | | | | | "_") ( | | "_" | "-" | "/" )*> | | " | "\u2192"> | | | | | | | | | | | | | | : WITHIN_DOUBLE_QUOTE | : WITHIN_SINGLE_QUOTE | | | | | { if (braceDepth.isEmpty()) { braceDepth.push(0); }; braceDepth.push(braceDepth.pop() + 1); ; } | { int currentBraceDepth = braceDepth.pop() - 1; boolean currentInterpolationState = false; if (currentBraceDepth >= 0) { braceDepth.push(currentBraceDepth); } else { currentInterpolationState = interpolationState.pop(); } SwitchTo((currentBraceDepth >= 0) ? DEFAULT : (currentInterpolationState) ? WITHIN_DOUBLE_QUOTE : WITHIN_SINGLE_QUOTE); } | | "> | | | | | | ){64}> | | ("\"" "\""))> | "@")? (":" ()*)? ("?" )?> | | | | > | > | <#DIGIT: ["0"-"9"]> | <#DIGIT2: > | <#DIGIT4: > | <#DIGITS: ()+> | <#HEX_DIGIT: | ["A"-"F"] | ["a"-"f"]> | <#HEX_DIGITS: ()+> | <#SIGN: "+" | "-"> | <#EXPONENT: ("E" | "e") ()? > | <#ALPHA: ["A"-"Z"] | ["a"-"z"]> | <#ASCII: ["\u0020"-"\u007f"]> | <#VALID_NON_ASCII: ["\u0080"-"\uD7FF"] | (["\ud800"-"\udbff"] ["\udc00"-"\udfff"]) | ["\ue000"-"\ufffd"]> | <#BASH_ENV_VAR: ( | "_") ( | | "_")*> | <#POSIX_ENV_VAR: ("\u0020" | "\u0021" | ["\u0023"-"\u003c"] | ["\u003e"-"\u005b"] | ["\u005d"-"\u007e"] | ("\\" (["\"", "\\", "a", "b", "f", "n", "r", "t", "v"])))+> | <#PCT_ENCODED: "%" > | <#SUB_DELIM: ["!", "$", "&", "'", "*", "+", ";", "="]> | <#UNRESERVED: | | ["-", ".", "_", "~"]> | <#PCHAR: | | | ":" | "@"> | <#SEGMENT: ()*> | <#USER_INFO: ( | | | ":")*> | <#QUERY: ( | "/" | "?")*> | <#DOMAIN_LABEL: ( | )+ (("-")+ ( | )+)*> | <#DOMAIN: ("." )* (".")?> | <#IPV4: "." "." "." > | <#H16: (){1,4}> | <#LS32: ( ":" ) | > | <#IPV6: (( ":"){6} ) | ("::" ( ":"){5} ) | (()? "::" ( ":"){4} ) | (( (":" ){0, 1})? "::" ( ":"){3} ) | (( (":" ){0, 2})? "::" ( ":"){2} ) | (( (":" ){0, 3})? "::" ":" ) | (( (":" ){0, 4})? "::" ) | (( (":" ){0, 5})? "::" ) | (( (":" ){0, 6})? "::") > | <#IPVFUTURE: "v" ()+ "." ( | | ":")+> | <#DEC_OCTET: ("25" ["0"-"5"]) | ("2" ["0"-"4"] ) | ("1" ) | (["1"-"9"] ) | > | <#HOST: | | ("[" ( | ) "]")> | <#PATH_CHARACTER: ["\u0021", "\u003d", "\u007c", "\u007e"] | ["\u0024"-"\u0027"] | ["\u002a"-"\u002b"] | ["\u002d"-"\u002e"] | ["\u0030"-"\u003b"] | ["\u0040"-"\u005a"] | ["\u0040"-"\u005a"] | ["\u005e"-"\u007a"]> | <#QUOTED_PATH_CHARACTER: ["\u0020"-"\u0021"] | ["\u0023"-"\u002e"] | ["\u0030"-"\u007f"] | > | <#PATH_COMPONENT: "/" (()+ | ("\"" ()+ "\""))> | <#URL_PATH_COMPONENT: "/" (()+)> | <#PATH: ()+> | <#PARENT_PATH: ".." > | <#HERE_PATH: "." > | <#HOME_PATH: "~" > | <#URL_PATH: ( | ("/") )*> | | | "\t")+ ("\n" | "\r\n")> | ":" > | "-" "-" > | ":" ":" ("." ()+)?> | ("T" | "t") > } TOKEN: { { interpolationState.push(true); braceDepth.push(0); SwitchTo(DEFAULT); } | | ) | )+> | <#UNICODE_ESCAPE: | ("{" "}")> | <#UNBRACED_ESCAPE: (( | "A" | "B" | "C" | "a" | "b" | "c") (){3}) | (("D" | "d") ["0"-"7"] ) | (("E" | "e") (){3}) | (("F" | "f") ("A" | "B" | "C" | "D" | "a" | "b" | "c" | "d")) > | <#BRACED_ESCAPE: ("0")* > | <#BRACED_CODEPOINT: ((["0"-"9"] | ["A"-"F"] | ["a"-"f"] | "10") ) | | (){3}> | <#UNICODE_SUFFIX: (( | ["A"-"E"] | ["a"-"e"]) (){3}) | (("F" | "f") ( | ["A"-"D"] | ["a"-"d"]))> } TOKEN: { : DEFAULT } TOKEN: { { interpolationState.push(false); braceDepth.push(0); SwitchTo(DEFAULT); } | | | | | "\t" | "\n" | "\r\n")> } TOKEN: { : DEFAULT } String ANY_LABEL_OR_SOME(): { Token token; } { (token= | token= | token= | token= | token= | token=) { return trimLabel(token.image); } } String NAME_BINDING(): { Token token; } { (token= | token= | token= | token=) { return trimLabel(token.image); } } Map.Entry DOUBLE_QUOTE_CHUNK(): { StringBuilder builder = null; Token token0 = null; Token token1 = null; Expr.Parsed expr = null; } { ( ( expr=COMPLETE_EXPRESSION() ) | ( (token0= | token0=) (LOOKAHEAD(2) (token1= | token1=) { if (builder == null) { builder = new StringBuilder(token0.image); } builder.append(token1.image); } )* ) ) { if (builder == null) { return new SimpleImmutableEntry<>(token0 == null ? null : token0.image, expr); } else { return new SimpleImmutableEntry<>(builder.toString(), expr); } } } Expr.Parsed DOUBLE_QUOTE_LITERAL(): { List> chunks = new ArrayList<>(1); Map.Entry current; Token first; Token last; } { first= (current=DOUBLE_QUOTE_CHUNK() { chunks.add(current); })* last= { return ParsingHelpers.makeTextLiteral(chunks, first, last); } } List> SINGLE_QUOTE_CONTINUE(): { List> continuation = null; Token token = null; Expr.Parsed expr = null; } { ( expr=COMPLETE_EXPRESSION() continuation=SINGLE_QUOTE_CONTINUE() | token= continuation=SINGLE_QUOTE_CONTINUE() | token= continuation=SINGLE_QUOTE_CONTINUE() | token= continuation=SINGLE_QUOTE_CONTINUE() | ) { if (continuation == null) { return new ArrayList>(); } else { String value = null; if (token != null) { value = token.image; if (value.equals("'''")) { value = "''"; } else if (value.equals("''${")) { value = "${"; } } continuation.add(new SimpleImmutableEntry<>(value, expr)); return continuation; } } } Expr.Parsed SINGLE_QUOTE_LITERAL(): { List> chunks; Token first; } { ( first= chunks=SINGLE_QUOTE_CONTINUE() ) { return ParsingHelpers.makeSingleQuotedTextLiteral(chunks, first); } } Expr.Parsed IDENTIFIER(): { Token value; Token whsp0 = null; Token whsp1 = null; Token index = null; } { ( ( (value= | value=) ( LOOKAHEAD(2) [whsp0=] [whsp1=] index= )? ) | value= | value= | value= ) { return (index == null) ? ParsingHelpers.makeBuiltInOrIdentifier(value) : ParsingHelpers.makeIdentifier(value, whsp0, whsp1, index); } } Expr.Parsed NON_EMPTY_LIST_LITERAL(): { Token first; Token last; Token t0 = null; Token t1 = null; Token t2 = null; List other = new ArrayList<>(); StringBuilder currentOther = new StringBuilder("["); List values = new ArrayList<>(); Expr.Parsed current; } { ( first= (t0= { currentOther.append(t0.image); })? (t0= { currentOther.append(t0.image); } (t0= { currentOther.append(t0.image); })?)? current=BASE_EXPRESSION() { values.add(current); other.add(currentOther.toString()); currentOther.setLength(0); } (t0= { currentOther.append(t0.image); })? ( t0= { currentOther.append(t0.image); } (t0= { currentOther.append(t0.image); })? ( current=BASE_EXPRESSION() { values.add(current); other.add(currentOther.toString()); currentOther.setLength(0); } (t0= { currentOther.append(t0.image); })? )? )* last= ) { currentOther.append(']'); other.add(currentOther.toString()); return ParsingHelpers.makeNonEmptyListLiteral(values, other, first, last); } } List, Expr.Parsed>> RECORD_LITERAL_ENTRY(String firstLabel): { List, Expr.Parsed>> values = new ArrayList<>(); List, Expr.Parsed>> next = null; List current = new ArrayList<>(); current.add(firstLabel); Expr.Parsed expr; } { ( ( ( ( | (( [] firstLabel=ANY_LABEL_OR_SOME() { current.add(firstLabel); })+ [] ) ) [] expr=BASE_EXPRESSION() { values.add(new SimpleImmutableEntry<>(current, expr)); current = new ArrayList<>(); } [] ) | ({} { values.add(new SimpleImmutableEntry, Expr.Parsed>(current, null)); current = new ArrayList<>(); }) ) ( [] ( firstLabel=ANY_LABEL_OR_SOME() { current.add(firstLabel); } [] ( ( ( | (( [] firstLabel=ANY_LABEL_OR_SOME() { current.add(firstLabel); })+ [] ) ) [] expr=BASE_EXPRESSION() { values.add(new SimpleImmutableEntry<>(current, expr)); current = new ArrayList<>(); } [] ) | ({} { values.add(new SimpleImmutableEntry, Expr.Parsed>(current, null)); current = new ArrayList<>(); }) ) )? )* ) { return values; } } List> RECORD_TYPE_ENTRY(String firstLabel): { List> values = new ArrayList<>(); List> next = null; Expr.Parsed expr; } { ( expr=BASE_EXPRESSION() { values.add(new SimpleImmutableEntry<>(firstLabel, expr)); } [] ( [] ( firstLabel=ANY_LABEL_OR_SOME() [] expr=BASE_EXPRESSION() { values.add(new SimpleImmutableEntry<>(firstLabel, expr)); } [] )? )* ) { return values; } } Expr.Parsed RECORD_LITERAL_OR_TYPE(): { Token first; Token last = null; String firstLabel = null; List, Expr.Parsed>> literalValues = null; List> typeValues = null; } { ( first= [] ( [])? ( last= | ( [] [ []] last= { literalValues = new ArrayList<>(); }) | ( firstLabel=ANY_LABEL_OR_SOME() [] (typeValues=RECORD_TYPE_ENTRY(firstLabel) | literalValues=RECORD_LITERAL_ENTRY(firstLabel)) ) ) ) { if (literalValues != null) { return ParsingHelpers.makeRecordLiteral(literalValues, first, last); } else if (typeValues != null) { return ParsingHelpers.makeRecordType(typeValues, first, last); } else { return ParsingHelpers.makeRecordType(new ArrayList>(), first, last); } } } Expr.Parsed UNION_TYPE(): { Token first; Token last; String label; Expr.Parsed type = null; List> typeValues = new ArrayList<>(); } { ( first= [] [ []] ( last= | ( ( label=ANY_LABEL_OR_SOME() [] ( [ type=BASE_EXPRESSION() []] { typeValues.add(new SimpleImmutableEntry<>(label, type)); type = null; } ( [] ( label=ANY_LABEL_OR_SOME() [] [ type=BASE_EXPRESSION() []] { typeValues.add(new SimpleImmutableEntry<>(label, type)); type = null; } )? )* ) ) last= ) ) ) { return ParsingHelpers.makeUnionType(typeValues, first, last); } } Expr.Parsed PARENTHESIZED_EXPRESSION(): { Expr.Parsed value; Token first; Token last; } { (first= value=COMPLETE_EXPRESSION() last=) { return ParsingHelpers.makeParenthesized(value, first, last); } } Expr.Parsed DOUBLE_LITERAL(): { Token token; } { token= { return ParsingHelpers.makeDoubleLiteral(token); }} Expr.Parsed INTEGER_LITERAL(): { Token token; } { token= { return ParsingHelpers.makeIntegerLiteral(token); }} Expr.Parsed NATURAL_LITERAL(): { Token token; } { token= { return ParsingHelpers.makeNaturalLiteral(token); }} Expr.Parsed DATE_LITERAL(): { Token token; } { token= { return ParsingHelpers.makeDateLiteral(token); }} Expr.Parsed TIME_ZONE_LITERAL(): { Token token; } { token= { return ParsingHelpers.makeTimeZoneLiteral(token); }} Expr.Parsed TIME_LITERAL(): { Token token; Token timeZone = null; } { (token= [(timeZone=) | (timeZone=)]) { // Matching the "Z" in this way is a little convoluted but seems necessary to work around generated code size limits. return ParsingHelpers.makeTimeLiteral(token, timeZone); } } Expr.Parsed DATE_TIME_LITERAL(): { Token token; Token timeZone = null; } { (token= [(timeZone=) | (timeZone=)]) { // Matching the "Z" in this way is a little convoluted but seems necessary to work around generated code size limits. return ParsingHelpers.makeDateTimeLiteral(token, timeZone); } } Expr.Parsed TEMPORAL_LITERAL(): { Expr.Parsed expr; } { ( expr=DATE_LITERAL() | expr=TIME_ZONE_LITERAL() | expr=TIME_LITERAL() | expr=DATE_TIME_LITERAL() ) { return expr; } } Expr.Parsed PRIMITIVE_EXPRESSION(): { Expr.Parsed expr; } { ( expr=DOUBLE_LITERAL() | expr=NATURAL_LITERAL() | expr=INTEGER_LITERAL() | expr=TEMPORAL_LITERAL() | expr=DOUBLE_QUOTE_LITERAL() | expr=SINGLE_QUOTE_LITERAL() | expr=RECORD_LITERAL_OR_TYPE() | expr=UNION_TYPE() | expr=NON_EMPTY_LIST_LITERAL() | expr=IDENTIFIER() | expr=PARENTHESIZED_EXPRESSION() ) { return expr; } } Expr.Parsed FIELD_ACCESS_EXPRESSION(Expr.Parsed base, Token whsp0, Token whsp1): { Token token; } { (token= | token= | token= | token= | token=) { return ParsingHelpers.makeFieldAccess(base, trimLabel(token.image), whsp0, whsp1, token.endLine, token.endColumn); } } Expr.Parsed PROJECTION_EXPRESSION(Expr.Parsed base, Token whsp0, Token whsp): { String current; List labels = new ArrayList(); Token last; } { ( [] [ []] ( current=ANY_LABEL_OR_SOME() { labels.add(current); } [] ( [] (current=ANY_LABEL_OR_SOME() { labels.add(current); } [])?)* )? last= ) { return ParsingHelpers.makeProjection(base, labels, last.endLine, last.endColumn); } } Expr.Parsed PROJECTION_BY_TYPE_EXPRESSION(Expr.Parsed base, Token whsp0, Token whsp): { Expr.Parsed expr; Token last; } { ( [] expr=BASE_EXPRESSION() [] last=) { return ParsingHelpers.makeProjectionByType(base, expr, last.endLine, last.endColumn); } } Expr.Parsed SELECTOR_EXPRESSION(): { Expr.Parsed base; Token whsp0 = null; Token whsp1 = null; } { ( base=PRIMITIVE_EXPRESSION() ( LOOKAHEAD(2) [whsp0=] [whsp1=] ( base=FIELD_ACCESS_EXPRESSION(base, whsp0, whsp1) | base=PROJECTION_EXPRESSION(base, whsp0, whsp1) | base=PROJECTION_BY_TYPE_EXPRESSION(base, whsp0, whsp1) ) { whsp0 = null; whsp1 = null; } )* ) { return base; } } Expr.Parsed COMPLETION_EXPRESSION(): { Expr.Parsed base; Expr.Parsed completion = null; Token operatorToken; Token whsp0 = null; Token whsp1 = null; } { ( base=SELECTOR_EXPRESSION() ( LOOKAHEAD(2) [whsp0=] operatorToken= [whsp1=] completion=SELECTOR_EXPRESSION() { base = ParsingHelpers.makeOperatorApplication(Operator.COMPLETE, base, completion, operatorToken.image, whsp0, whsp1); } )? ) { return base; } } Expr.Parsed IMPORT(): { Token token = null; Token hash = null; Token asValue = null; Expr.Parsed using = null; } { ( ( token= | token= | (token= (LOOKAHEAD(2) [] using=IMPORT_EXPRESSION())?) | token= | token= ) (LOOKAHEAD(2) hash=)? (LOOKAHEAD(2) [] (asValue= | asValue=))? ) { return ParsingHelpers.makeImport(token, hash, asValue, using); } } Expr.Parsed IMPORT_EXPRESSION(): { Expr.Parsed expr; } { (expr=IMPORT() | expr=COMPLETION_EXPRESSION()) { return expr; } } Expr.Parsed APPLICATION_EXPRESSION(): { Token first = null; Token whsp0; Token whsp1; Expr.Parsed base = null; Expr.Parsed current0; Expr.Parsed current1; Token whsp2 = null; Token whsp3 = null; Expr.Parsed expr; Expr.Parsed other; Expr.Parsed type = null; } { ( ( (first= whsp0= expr=IMPORT_EXPRESSION() whsp1= other=IMPORT_EXPRESSION() (LOOKAHEAD(2) [whsp2=] whsp3= type=BASE_EXPRESSION() )?) { base = ParsingHelpers.makeMerge(expr, other, type, first, whsp0, whsp1, whsp2, whsp3); } | (first= whsp0= current0=IMPORT_EXPRESSION() { base = ParsingHelpers.makeApplication(ParsingHelpers.makeBuiltInOrIdentifier(first), current0, whsp0); }) | (first= whsp0= expr=IMPORT_EXPRESSION() (LOOKAHEAD(2) [whsp2=] whsp3= type=BASE_EXPRESSION() )?) { base = ParsingHelpers.makeToMap(expr, type, first, whsp0, whsp2, whsp3); } | base=IMPORT_EXPRESSION() ) ( LOOKAHEAD(2) whsp0= current0=IMPORT_EXPRESSION() { base = ParsingHelpers.makeApplication(base, current0, whsp0); } )* ) { return base; } } Map.Entry OPERATOR(): { Token token; Token whsp = null; Operator operator; } { ( token= { operator = Operator.OR; } | token= { operator = Operator.AND; } | token= { operator = Operator.EQUALS; } | token= { operator = Operator.NOT_EQUALS; } | (token= whsp=) { operator = Operator.PLUS; } | token= { operator = Operator.TIMES; } | token= { operator = Operator.TEXT_APPEND; } | token= { operator = Operator.LIST_APPEND; } | token= { operator = Operator.COMBINE; } | token= { operator = Operator.PREFER; } | token= { operator = Operator.COMBINE_TYPES; } | (token= whsp=) { operator = Operator.IMPORT_ALT; } | token= { operator = Operator.EQUIVALENT; } ) { if (whsp == null) { return new SimpleImmutableEntry<>(token.image, operator); } else { return new SimpleImmutableEntry<>(token.image + whsp.image, operator); } } } Expr.Parsed OPERATOR_EXPRESSION(int minPredecence): { Expr.Parsed base; Expr.Parsed arg; Map.Entry operator; Map.Entry, Expr.Parsed> current; Token whsp0 = null; Token whsp1 = null; } { ( base=APPLICATION_EXPRESSION() (LOOKAHEAD({ getToken(1).kind == WHSP && getToken(2).kind == WITH && minPredecence < 1 }) whsp0= current=WITH_ENTRY() { base = ParsingHelpers.makeWith(base, current.getKey(), current.getValue(), whsp0); } )* (LOOKAHEAD({ (getToken(1).kind == WHSP && OperatorPrecedenceTable.get(getToken(2).kind) >= minPredecence) || OperatorPrecedenceTable.get(getToken(1).kind) >= minPredecence }) [whsp0=] operator=OPERATOR() [whsp1=] arg=OPERATOR_EXPRESSION(operator.getValue().getPrecedence() + 1) { base = ParsingHelpers.makeOperatorApplication(operator.getValue(), base, arg, operator.getKey(), whsp0, whsp1); whsp0 = null; })* ) { return base; } } Map.Entry, Expr.Parsed> WITH_ENTRY(): { List labels = new ArrayList(); String current; Expr.Parsed expr; } { ( current=ANY_LABEL_OR_SOME() { labels.add(current); } (LOOKAHEAD(2) [] [] current=ANY_LABEL_OR_SOME() { labels.add(current); })* [] [] expr=OPERATOR_EXPRESSION(1) ) { return new SimpleImmutableEntry<>(labels, expr); } } Expr.Parsed EMPTY_LIST_LITERAL(): { Expr.Parsed type; Token first; Token t0; StringBuilder builder = new StringBuilder("["); } { ( first= (t0= { builder.append(t0.image); })? ( { builder.append(','); } (t0= { builder.append(t0.image); })?)? { builder.append(']'); } (t0= { builder.append(t0.image); })? t0= { builder.append(':'); builder.append(t0.image); } type=BASE_EXPRESSION() ) { return ParsingHelpers.makeEmptyListLiteral(type, builder.toString(), first); } } Expr.Parsed LAMBDA_EXPRESSION(): { Expr.Parsed type; Expr.Parsed body; Token first; String name; } { ( first= [] [] name=NAME_BINDING() [] type=BASE_EXPRESSION() [] [] [] body=BASE_EXPRESSION() ) { return ParsingHelpers.makeLambda(name, type, body, first); } } LetBinding LET_BINDING(): { Token first; Token whsp; String name; Expr.Parsed type = null; Expr.Parsed value; StringBuilder builder = new StringBuilder(); String text0 = null; String text2 = null; } { ( first= whsp= name=NAME_BINDING() { builder.append(first.image); builder.append(whsp.image); builder.append(name); } (whsp= { builder.append(whsp.image); })? ( whsp= { builder.append(':'); builder.append(whsp.image); text0 = builder.toString(); builder.setLength(0); } type=BASE_EXPRESSION() (whsp= { builder.append(whsp.image); })? )? { builder.append('='); } (whsp= { builder.append(whsp.image); })? value=BASE_EXPRESSION() (whsp= { text2 = whsp.image; })? ) { return new LetBinding(name, type, value, text0, builder.toString(), text2, first.beginLine, first.beginColumn); } } Expr.Parsed LET_EXPRESSION(): { LetBinding current = null; List bindings = new ArrayList<>(); Expr.Parsed body; Token whsp; } { ( (current=LET_BINDING() { bindings.add(current); })+ whsp= body=BASE_EXPRESSION() ) { return ParsingHelpers.makeLet(bindings, body, whsp.image); } } Expr.Parsed IF_EXPRESSION(): { Token first; Expr.Parsed predicate; Expr.Parsed thenValue; Expr.Parsed elseValue; } { ( first= predicate=BASE_EXPRESSION() [] thenValue=BASE_EXPRESSION() [] elseValue=BASE_EXPRESSION() ) { return ParsingHelpers.makeIf(predicate, thenValue, elseValue, first); } } Expr.Parsed FORALL_EXPRESSION(): { Token first; String name; Expr.Parsed input; Expr.Parsed result; } { ( first= [] [] name=NAME_BINDING() [] input=BASE_EXPRESSION() [] [] [] result=BASE_EXPRESSION() ) { return ParsingHelpers.makePi(name, input, result, first); } } Expr.Parsed ASSERT_EXPRESSION(): { Token first; Token whsp0 = null; Token whsp1 = null; Expr.Parsed value; } { (first= [whsp0=] whsp1= value=BASE_EXPRESSION()) { return ParsingHelpers.makeAssert(value, first, whsp0, whsp1); } } Expr.Parsed FUNCTION_TYPE_OR_ANNOTATED_EXPRESSION(): { Expr.Parsed base; Expr.Parsed type = null; Expr.Parsed result = null; Token whsp0 = null; Token whsp1 = null; } { ( base=OPERATOR_EXPRESSION(0) (LOOKAHEAD(2) [whsp0=] (( [] result=BASE_EXPRESSION()) | ( whsp1= type=BASE_EXPRESSION())) )? ) { if (type == null) { if (result == null) { return base; } else { return ParsingHelpers.makePi(base, result); } } else { return ParsingHelpers.makeAnnotated(base, type, whsp0, whsp1); } } } Expr.Parsed BASE_EXPRESSION(): { Expr.Parsed expr; } { ( expr=LAMBDA_EXPRESSION() | expr=IF_EXPRESSION() | expr=LET_EXPRESSION() | expr=FORALL_EXPRESSION() | LOOKAHEAD(EMPTY_LIST_LITERAL()) expr=EMPTY_LIST_LITERAL() | expr=ASSERT_EXPRESSION() | expr=FUNCTION_TYPE_OR_ANNOTATED_EXPRESSION() ) { return expr; } } Expr.Parsed COMPLETE_EXPRESSION(): { Expr.Parsed expr; } { ([] expr=BASE_EXPRESSION() []) { return expr; }} Expr.Parsed TOP_LEVEL(): { Expr.Parsed expr; } { (()* expr=COMPLETE_EXPRESSION() ) { return expr; }} ================================================ FILE: modules/parser/src/test/scala/org/dhallj/parser/DhallParserSuite.scala ================================================ package org.dhallj.parser import java.net.URI import java.io.FileInputStream import java.nio.charset.StandardCharsets import java.nio.file.Paths import munit.{FunSuite, Ignore} import org.dhallj.core.DhallException.ParsingFailure import org.dhallj.core.Expr import org.dhallj.core.Expr.ImportMode class DhallParserSuite extends FunSuite() { test("parse empty list with annotation on element type") { val expected = Expr.makeEmptyListLiteral( Expr.makeAnnotated(Expr.makeApplication(Expr.Constants.LIST, Expr.Constants.NATURAL), Expr.Constants.TYPE) ) // Output from dhall-haskell. val expectedBytes: Array[Byte] = Array(-126, 24, 28, -125, 24, 26, -125, 0, 100, 76, 105, 115, 116, 103, 78, 97, 116, 117, 114, 97, 108, 100, 84, 121, 112, 101) val parsed = DhallParser.parse("[]: List Natural: Type") assert(parsed == expected) assert(parsed.getEncodedBytes.sameElements(expectedBytes)) } test("parse toMap with empty record with annotation on type") { // Output from dhall-haskell. val expectedBytes: Array[Byte] = Array(-125, 24, 27, -126, 7, -96, -125, 24, 26, -125, 0, 100, 76, 105, 115, 116, -126, 7, -94, 102, 109, 97, 112, 75, 101, 121, 100, 84, 101, 120, 116, 104, 109, 97, 112, 86, 97, 108, 117, 101, 100, 66, 111, 111, 108, 100, 84, 121, 112, 101) val parsed = DhallParser.parse("toMap {}: List { mapKey : Text, mapValue: Bool }: Type") assert(parsed.getEncodedBytes.sameElements(expectedBytes)) } test("parse toMap with empty non-record with annotation on type") { // Output from dhall-haskell. val expectedBytes: Array[Byte] = Array(-125, 24, 27, -126, 8, -95, 97, 97, -11, -125, 24, 26, -125, 0, 100, 76, 105, 115, 116, -126, 7, -94, 102, 109, 97, 112, 75, 101, 121, 100, 84, 101, 120, 116, 104, 109, 97, 112, 86, 97, 108, 117, 101, 100, 66, 111, 111, 108, 100, 84, 121, 112, 101) val parsed = DhallParser.parse("toMap {a=True}: List { mapKey : Text, mapValue: Bool }: Type") assert(parsed.getEncodedBytes.sameElements(expectedBytes)) } test("parse merge with annotation on type") { // Output from dhall-haskell. val expectedBytes: Array[Byte] = Array(-124, 6, -126, 8, -95, 97, 97, -126, 15, 1, -125, 9, -126, 11, -95, 97, 97, -10, 97, 97, -125, 24, 26, 103, 78, 97, 116, 117, 114, 97, 108, 100, 84, 121, 112, 101) val parsed = DhallParser.parse("merge {a=1} .a: Natural: Type") assert(parsed.getEncodedBytes.sameElements(expectedBytes)) } test("parse IPv6 address") { val expected = Expr.makeRemoteImport(new URI("https://[0:0:0:0:0:0:0:1]/"), null, Expr.ImportMode.CODE, null) assert(DhallParser.parse("https://[0:0:0:0:0:0:0:1]/") == expected) } test("parse $ in double-quoted text literals") { val expected = Expr.makeTextLiteral("$ $ $100 $ $") assert(DhallParser.parse("""let x = "100" in "$ $ $${x} $ $" """) == expected) } test("parse # in double-quoted text literals") { val expected = Expr.makeTextLiteral("# # # $ % ^ #") assert(DhallParser.parse(""""# # # $ % ^ #"""") == expected) } test("parse classpath import") { val expected = Expr.makeClasspathImport(Paths.get("/foo/bar.dhall"), ImportMode.RAW_TEXT, null) assert(DhallParser.parse("classpath:/foo/bar.dhall as Text") == expected) } test("fail on URLs with quoted paths") { intercept[ParsingFailure](DhallParser.parse("https://example.com/foo/\"bar?baz\"?qux")) } test("fail on non-UTF-8 input") { val stream = new FileInputStream("dhall-lang/tests/parser/failure/nonUtf8.dhall") intercept[ParsingFailure](DhallParser.parse(stream, StandardCharsets.UTF_16)) } test("handle single-quoted escape sequences") { val expected = Expr.makeTextLiteral("foo '' bar ${ baz '''' qux") val input = """'' foo ''' bar ''${ baz '''''' qux''""" assertEquals(DhallParser.parse(input): Expr, expected) } } ================================================ FILE: modules/prelude/src/main/java/org/dhallj/prelude/Prelude.java ================================================ package org.dhallj.prelude; import java.math.BigInteger; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import org.dhallj.core.Expr; import org.dhallj.core.Operator; public final class Prelude { private static final Expr f000000 = Expr.makeBuiltIn("List/fold"); private static final Expr f000001 = Expr.makeBuiltIn("Bool"); private static final Expr f000002 = Expr.makeIdentifier("xs", 0); private static final Expr f000003 = Expr.makeIdentifier("l", 0); private static final Expr f000004 = Expr.makeIdentifier("r", 0); private static final Expr f000005 = Expr.makeOperatorApplication(Operator.AND, f000003, f000004); private static final Expr f000006 = Expr.makeLambda("r", f000001, f000005); private static final Expr f000007 = Expr.makeLambda("l", f000001, f000006); private static final Expr f000008 = Expr.Constants.TRUE; private static final Expr f000009 = Expr.makeApplication(f000000, new Expr[] {f000001, f000002, f000001, f000007, f000008}); private static final Expr f000010 = Expr.Constants.LIST; private static final Expr f000011 = Expr.makeApplication(f000010, new Expr[] {f000001}); private static final Expr f000012 = Expr.makeLambda("xs", f000011, f000009); private static final Expr f000013 = Expr.makeIdentifier("f", 0); private static final Expr f000014 = Expr.Constants.FALSE; private static final Expr f000015 = Expr.makeApplication(f000013, new Expr[] {f000001, f000008, f000014}); private static final Expr f000016 = Expr.makeIdentifier("bool", 0); private static final Expr f000017 = Expr.makePi("false", f000016, f000016); private static final Expr f000018 = Expr.makePi("true", f000016, f000017); private static final Expr f000019 = Expr.Constants.TYPE; private static final Expr f000020 = Expr.makePi("bool", f000019, f000018); private static final Expr f000021 = Expr.makeLambda("f", f000020, f000015); private static final Expr f000022 = Expr.makeIdentifier("x", 0); private static final Expr f000023 = Expr.makeIdentifier("y", 0); private static final Expr f000024 = Expr.makeOperatorApplication(Operator.EQUALS, f000022, f000023); private static final Expr f000025 = Expr.makeLambda("y", f000001, f000024); private static final Expr f000026 = Expr.makeLambda("x", f000001, f000025); private static final Expr f000027 = Expr.makeApplication(f000000, new Expr[] {f000001, f000002, f000001, f000026, f000008}); private static final Expr f000028 = Expr.makeLambda("xs", f000011, f000027); private static final Expr f000029 = Expr.makeIdentifier("b", 0); private static final Expr f000030 = Expr.makeIdentifier("true", 0); private static final Expr f000031 = Expr.makeIdentifier("false", 0); private static final Expr f000032 = Expr.makeIf(f000029, f000030, f000031); private static final Expr f000033 = Expr.makeLambda("false", f000016, f000032); private static final Expr f000034 = Expr.makeLambda("true", f000016, f000033); private static final Expr f000035 = Expr.makeLambda("bool", f000019, f000034); private static final Expr f000036 = Expr.makeLambda("b", f000001, f000035); private static final Expr f000037 = Expr.makeOperatorApplication(Operator.EQUALS, f000029, f000014); private static final Expr f000038 = Expr.makeLambda("b", f000001, f000037); private static final Expr f000039 = Expr.makeOperatorApplication(Operator.NOT_EQUALS, f000022, f000023); private static final Expr f000040 = Expr.makeLambda("y", f000001, f000039); private static final Expr f000041 = Expr.makeLambda("x", f000001, f000040); private static final Expr f000042 = Expr.makeApplication(f000000, new Expr[] {f000001, f000002, f000001, f000041, f000014}); private static final Expr f000043 = Expr.makeLambda("xs", f000011, f000042); private static final Expr f000044 = Expr.makeOperatorApplication(Operator.OR, f000003, f000004); private static final Expr f000045 = Expr.makeLambda("r", f000001, f000044); private static final Expr f000046 = Expr.makeLambda("l", f000001, f000045); private static final Expr f000047 = Expr.makeApplication(f000000, new Expr[] {f000001, f000002, f000001, f000046, f000014}); private static final Expr f000048 = Expr.makeLambda("xs", f000011, f000047); private static final Expr f000049 = Expr.makeTextLiteral("True"); private static final Expr f000050 = Expr.makeTextLiteral("False"); private static final Expr f000051 = Expr.makeIf(f000029, f000049, f000050); private static final Expr f000052 = Expr.makeLambda("b", f000001, f000051); private static final Expr f000053 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("and", f000012), new SimpleImmutableEntry("build", f000021), new SimpleImmutableEntry("even", f000028), new SimpleImmutableEntry("fold", f000036), new SimpleImmutableEntry("not", f000038), new SimpleImmutableEntry("odd", f000043), new SimpleImmutableEntry("or", f000048), new SimpleImmutableEntry("show", f000052) }); private static final Expr f000054 = Expr.makeBuiltIn("Double/show"); private static final Expr f000055 = Expr.makeRecordLiteral(new Entry[] {new SimpleImmutableEntry("show", f000054)}); private static final Expr f000056 = Expr.makeIdentifier("g", 0); private static final Expr f000057 = Expr.makeApplication(f000013, new Expr[] {f000022}); private static final Expr f000058 = Expr.makeApplication(f000056, new Expr[] {f000057}); private static final Expr f000059 = Expr.makeIdentifier("A", 0); private static final Expr f000060 = Expr.makeLambda("x", f000059, f000058); private static final Expr f000061 = Expr.makeIdentifier("C", 0); private static final Expr f000062 = Expr.makeIdentifier("B", 0); private static final Expr f000063 = Expr.makePi("_", f000062, f000061); private static final Expr f000064 = Expr.makeLambda("g", f000063, f000060); private static final Expr f000065 = Expr.makePi("_", f000059, f000062); private static final Expr f000066 = Expr.makeLambda("f", f000065, f000064); private static final Expr f000067 = Expr.makeLambda("C", f000019, f000066); private static final Expr f000068 = Expr.makeLambda("B", f000019, f000067); private static final Expr f000069 = Expr.makeLambda("A", f000019, f000068); private static final Expr f000070 = Expr.makeIdentifier("a", 0); private static final Expr f000071 = Expr.makeLambda("x", f000070, f000022); private static final Expr f000072 = Expr.makeLambda("a", f000019, f000071); private static final Expr f000073 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("compose", f000069), new SimpleImmutableEntry("identity", f000072) }); private static final Expr f000074 = Expr.makeBuiltIn("Natural/isZero"); private static final Expr f000075 = Expr.makeBuiltIn("Integer/clamp"); private static final Expr f000076 = Expr.makeIdentifier("n", 0); private static final Expr f000077 = Expr.makeApplication(f000075, new Expr[] {f000076}); private static final Expr f000078 = Expr.makeApplication(f000074, new Expr[] {f000077}); private static final Expr f000079 = Expr.makeBuiltIn("Integer/negate"); private static final Expr f000080 = Expr.makeApplication(f000079, new Expr[] {f000076}); private static final Expr f000081 = Expr.makeApplication(f000075, new Expr[] {f000080}); private static final Expr f000082 = Expr.makeIf(f000078, f000081, f000077); private static final Expr f000083 = Expr.Constants.INTEGER; private static final Expr f000084 = Expr.makeLambda("n", f000083, f000082); private static final Expr f000085 = Expr.makeIdentifier("m", 0); private static final Expr f000086 = Expr.makeApplication(f000079, new Expr[] {f000085}); private static final Expr f000087 = Expr.makeApplication(f000075, new Expr[] {f000086}); private static final Expr f000088 = Expr.makeApplication(f000074, new Expr[] {f000087}); private static final Expr f000089 = Expr.makeBuiltIn("Natural/subtract"); private static final Expr f000090 = Expr.makeApplication(f000079, new Expr[] {f000086}); private static final Expr f000091 = Expr.makeApplication(f000075, new Expr[] {f000090}); private static final Expr f000092 = Expr.makeApplication(f000089, new Expr[] {f000081, f000091}); private static final Expr f000093 = Expr.makeApplication(f000074, new Expr[] {f000092}); private static final Expr f000094 = Expr.makeBuiltIn("Natural/toInteger"); private static final Expr f000095 = Expr.makeApplication(f000089, new Expr[] {f000091, f000081}); private static final Expr f000096 = Expr.makeApplication(f000094, new Expr[] {f000095}); private static final Expr f000097 = Expr.makeApplication(f000079, new Expr[] {f000096}); private static final Expr f000098 = Expr.makeApplication(f000094, new Expr[] {f000092}); private static final Expr f000099 = Expr.makeIf(f000093, f000097, f000098); private static final Expr f000100 = Expr.makeOperatorApplication(Operator.PLUS, f000091, f000077); private static final Expr f000101 = Expr.makeApplication(f000094, new Expr[] {f000100}); private static final Expr f000102 = Expr.makeIf(f000078, f000099, f000101); private static final Expr f000103 = Expr.makeOperatorApplication(Operator.PLUS, f000087, f000081); private static final Expr f000104 = Expr.makeApplication(f000094, new Expr[] {f000103}); private static final Expr f000105 = Expr.makeApplication(f000079, new Expr[] {f000104}); private static final Expr f000106 = Expr.makeApplication(f000089, new Expr[] {f000087, f000077}); private static final Expr f000107 = Expr.makeApplication(f000074, new Expr[] {f000106}); private static final Expr f000108 = Expr.makeApplication(f000089, new Expr[] {f000077, f000087}); private static final Expr f000109 = Expr.makeApplication(f000094, new Expr[] {f000108}); private static final Expr f000110 = Expr.makeApplication(f000079, new Expr[] {f000109}); private static final Expr f000111 = Expr.makeApplication(f000094, new Expr[] {f000106}); private static final Expr f000112 = Expr.makeIf(f000107, f000110, f000111); private static final Expr f000113 = Expr.makeIf(f000078, f000105, f000112); private static final Expr f000114 = Expr.makeIf(f000088, f000102, f000113); private static final Expr f000115 = Expr.makeLambda("n", f000083, f000114); private static final Expr f000116 = Expr.makeLambda("m", f000083, f000115); private static final Expr f000117 = Expr.makeApplication(f000075, new Expr[] {f000029}); private static final Expr f000118 = Expr.makeApplication(f000075, new Expr[] {f000070}); private static final Expr f000119 = Expr.makeApplication(f000089, new Expr[] {f000117, f000118}); private static final Expr f000120 = Expr.makeApplication(f000074, new Expr[] {f000119}); private static final Expr f000121 = Expr.makeApplication(f000089, new Expr[] {f000118, f000117}); private static final Expr f000122 = Expr.makeApplication(f000074, new Expr[] {f000121}); private static final Expr f000123 = Expr.makeOperatorApplication(Operator.AND, f000120, f000122); private static final Expr f000124 = Expr.makeApplication(f000079, new Expr[] {f000029}); private static final Expr f000125 = Expr.makeApplication(f000075, new Expr[] {f000124}); private static final Expr f000126 = Expr.makeApplication(f000079, new Expr[] {f000070}); private static final Expr f000127 = Expr.makeApplication(f000075, new Expr[] {f000126}); private static final Expr f000128 = Expr.makeApplication(f000089, new Expr[] {f000125, f000127}); private static final Expr f000129 = Expr.makeApplication(f000074, new Expr[] {f000128}); private static final Expr f000130 = Expr.makeApplication(f000089, new Expr[] {f000127, f000125}); private static final Expr f000131 = Expr.makeApplication(f000074, new Expr[] {f000130}); private static final Expr f000132 = Expr.makeOperatorApplication(Operator.AND, f000129, f000131); private static final Expr f000133 = Expr.makeOperatorApplication(Operator.AND, f000123, f000132); private static final Expr f000134 = Expr.makeLambda("b", f000083, f000133); private static final Expr f000135 = Expr.makeLambda("a", f000083, f000134); private static final Expr f000136 = Expr.makeApplication(f000075, new Expr[] {f000022}); private static final Expr f000137 = Expr.makeApplication(f000074, new Expr[] {f000136}); private static final Expr f000138 = Expr.makeApplication(f000079, new Expr[] {f000023}); private static final Expr f000139 = Expr.makeApplication(f000075, new Expr[] {f000138}); private static final Expr f000140 = Expr.makeApplication(f000074, new Expr[] {f000139}); private static final Expr f000141 = Expr.makeApplication(f000079, new Expr[] {f000022}); private static final Expr f000142 = Expr.makeApplication(f000075, new Expr[] {f000141}); private static final Expr f000143 = Expr.makeApplication(f000089, new Expr[] {f000142, f000139}); private static final Expr f000144 = Expr.makeApplication(f000074, new Expr[] {f000143}); private static final Expr f000145 = Expr.makeOperatorApplication(Operator.OR, f000140, f000144); private static final Expr f000146 = Expr.makeApplication(f000075, new Expr[] {f000023}); private static final Expr f000147 = Expr.makeApplication(f000089, new Expr[] {f000146, f000136}); private static final Expr f000148 = Expr.makeApplication(f000074, new Expr[] {f000147}); private static final Expr f000149 = Expr.makeIf(f000137, f000145, f000148); private static final Expr f000150 = Expr.makeOperatorApplication(Operator.EQUALS, f000149, f000014); private static final Expr f000151 = Expr.makeLambda("y", f000083, f000150); private static final Expr f000152 = Expr.makeLambda("x", f000083, f000151); private static final Expr f000153 = Expr.makeApplication(f000074, new Expr[] {f000146}); private static final Expr f000154 = Expr.makeApplication(f000074, new Expr[] {f000142}); private static final Expr f000155 = Expr.makeApplication(f000089, new Expr[] {f000139, f000142}); private static final Expr f000156 = Expr.makeApplication(f000074, new Expr[] {f000155}); private static final Expr f000157 = Expr.makeOperatorApplication(Operator.OR, f000154, f000156); private static final Expr f000158 = Expr.makeApplication(f000089, new Expr[] {f000136, f000146}); private static final Expr f000159 = Expr.makeApplication(f000074, new Expr[] {f000158}); private static final Expr f000160 = Expr.makeIf(f000153, f000157, f000159); private static final Expr f000161 = Expr.makeLambda("y", f000083, f000160); private static final Expr f000162 = Expr.makeLambda("x", f000083, f000161); private static final Expr f000163 = Expr.makeOperatorApplication(Operator.EQUALS, f000160, f000014); private static final Expr f000164 = Expr.makeLambda("y", f000083, f000163); private static final Expr f000165 = Expr.makeLambda("x", f000083, f000164); private static final Expr f000166 = Expr.makeLambda("y", f000083, f000149); private static final Expr f000167 = Expr.makeLambda("x", f000083, f000166); private static final Expr f000168 = Expr.makeApplication(f000075, new Expr[] {f000085}); private static final Expr f000169 = Expr.makeApplication(f000074, new Expr[] {f000168}); private static final Expr f000170 = Expr.makeOperatorApplication(Operator.TIMES, f000087, f000081); private static final Expr f000171 = Expr.makeApplication(f000094, new Expr[] {f000170}); private static final Expr f000172 = Expr.makeOperatorApplication(Operator.TIMES, f000087, f000077); private static final Expr f000173 = Expr.makeApplication(f000094, new Expr[] {f000172}); private static final Expr f000174 = Expr.makeApplication(f000079, new Expr[] {f000173}); private static final Expr f000175 = Expr.makeIf(f000078, f000171, f000174); private static final Expr f000176 = Expr.makeOperatorApplication(Operator.TIMES, f000168, f000081); private static final Expr f000177 = Expr.makeApplication(f000094, new Expr[] {f000176}); private static final Expr f000178 = Expr.makeApplication(f000079, new Expr[] {f000177}); private static final Expr f000179 = Expr.makeOperatorApplication(Operator.TIMES, f000168, f000077); private static final Expr f000180 = Expr.makeApplication(f000094, new Expr[] {f000179}); private static final Expr f000181 = Expr.makeIf(f000078, f000178, f000180); private static final Expr f000182 = Expr.makeIf(f000169, f000175, f000181); private static final Expr f000183 = Expr.makeLambda("n", f000083, f000182); private static final Expr f000184 = Expr.makeLambda("m", f000083, f000183); private static final Expr f000185 = Expr.makeApplication(f000074, new Expr[] {f000081}); private static final Expr f000186 = Expr.makeOperatorApplication(Operator.EQUALS, f000185, f000014); private static final Expr f000187 = Expr.makeLambda("n", f000083, f000186); private static final Expr f000188 = Expr.makeLambda("n", f000083, f000185); private static final Expr f000189 = Expr.makeLambda("n", f000083, f000078); private static final Expr f000190 = Expr.makeOperatorApplication(Operator.EQUALS, f000078, f000014); private static final Expr f000191 = Expr.makeLambda("n", f000083, f000190); private static final Expr f000192 = Expr.makeBuiltIn("Integer/show"); private static final Expr f000193 = Expr.makeApplication(f000089, new Expr[] {f000081, f000087}); private static final Expr f000194 = Expr.makeApplication(f000074, new Expr[] {f000193}); private static final Expr f000195 = Expr.makeApplication(f000089, new Expr[] {f000087, f000081}); private static final Expr f000196 = Expr.makeApplication(f000094, new Expr[] {f000195}); private static final Expr f000197 = Expr.makeApplication(f000079, new Expr[] {f000196}); private static final Expr f000198 = Expr.makeApplication(f000094, new Expr[] {f000193}); private static final Expr f000199 = Expr.makeIf(f000194, f000197, f000198); private static final Expr f000200 = Expr.makeOperatorApplication(Operator.PLUS, f000087, f000077); private static final Expr f000201 = Expr.makeApplication(f000094, new Expr[] {f000200}); private static final Expr f000202 = Expr.makeIf(f000078, f000199, f000201); private static final Expr f000203 = Expr.makeOperatorApplication(Operator.PLUS, f000168, f000081); private static final Expr f000204 = Expr.makeApplication(f000094, new Expr[] {f000203}); private static final Expr f000205 = Expr.makeApplication(f000079, new Expr[] {f000204}); private static final Expr f000206 = Expr.makeApplication(f000089, new Expr[] {f000168, f000077}); private static final Expr f000207 = Expr.makeApplication(f000074, new Expr[] {f000206}); private static final Expr f000208 = Expr.makeApplication(f000089, new Expr[] {f000077, f000168}); private static final Expr f000209 = Expr.makeApplication(f000094, new Expr[] {f000208}); private static final Expr f000210 = Expr.makeApplication(f000079, new Expr[] {f000209}); private static final Expr f000211 = Expr.makeApplication(f000094, new Expr[] {f000206}); private static final Expr f000212 = Expr.makeIf(f000207, f000210, f000211); private static final Expr f000213 = Expr.makeIf(f000078, f000205, f000212); private static final Expr f000214 = Expr.makeIf(f000169, f000202, f000213); private static final Expr f000215 = Expr.makeLambda("n", f000083, f000214); private static final Expr f000216 = Expr.makeLambda("m", f000083, f000215); private static final Expr f000217 = Expr.makeBuiltIn("Integer/toDouble"); private static final Expr f000218 = Expr.makeBuiltIn("Some"); private static final Expr f000219 = Expr.makeApplication(f000218, new Expr[] {f000077}); private static final Expr f000220 = Expr.makeBuiltIn("None"); private static final Expr f000221 = Expr.Constants.NATURAL; private static final Expr f000222 = Expr.makeApplication(f000220, new Expr[] {f000221}); private static final Expr f000223 = Expr.makeIf(f000185, f000219, f000222); private static final Expr f000224 = Expr.makeLambda("n", f000083, f000223); private static final Expr f000225 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("abs", f000084), new SimpleImmutableEntry("add", f000116), new SimpleImmutableEntry("clamp", f000075), new SimpleImmutableEntry("equal", f000135), new SimpleImmutableEntry("greaterThan", f000152), new SimpleImmutableEntry("greaterThanEqual", f000162), new SimpleImmutableEntry("lessThan", f000165), new SimpleImmutableEntry("lessThanEqual", f000167), new SimpleImmutableEntry("multiply", f000184), new SimpleImmutableEntry("negate", f000079), new SimpleImmutableEntry("negative", f000187), new SimpleImmutableEntry("nonNegative", f000188), new SimpleImmutableEntry("nonPositive", f000189), new SimpleImmutableEntry("positive", f000191), new SimpleImmutableEntry("show", f000192), new SimpleImmutableEntry("subtract", f000216), new SimpleImmutableEntry("toDouble", f000217), new SimpleImmutableEntry("toNatural", f000224) }); private static final Expr f000226 = Expr.Constants.TEXT; private static final Expr f000227 = Expr.makeUnionType( new Entry[] { new SimpleImmutableEntry("Inline", null), new SimpleImmutableEntry("Nested", f000226) }); private static final Expr f000228 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("contents", f000070), new SimpleImmutableEntry("field", f000226), new SimpleImmutableEntry("nesting", f000227) }); private static final Expr f000229 = Expr.makeLambda("a", f000019, f000228); private static final Expr f000230 = Expr.makeIdentifier("JSON", 0); private static final Expr f000231 = Expr.makeApplication(f000010, new Expr[] {f000230}); private static final Expr f000232 = Expr.makePi("_", f000231, f000230); private static final Expr f000233 = Expr.makePi("_", f000001, f000230); private static final Expr f000234 = Expr.Constants.DOUBLE; private static final Expr f000235 = Expr.makePi("_", f000234, f000230); private static final Expr f000236 = Expr.makePi("_", f000083, f000230); private static final Expr f000237 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("mapKey", f000226), new SimpleImmutableEntry("mapValue", f000230) }); private static final Expr f000238 = Expr.makeApplication(f000010, new Expr[] {f000237}); private static final Expr f000239 = Expr.makePi("_", f000238, f000230); private static final Expr f000240 = Expr.makePi("_", f000226, f000230); private static final Expr f000241 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("array", f000232), new SimpleImmutableEntry("bool", f000233), new SimpleImmutableEntry("double", f000235), new SimpleImmutableEntry("integer", f000236), new SimpleImmutableEntry("null", f000230), new SimpleImmutableEntry("object", f000239), new SimpleImmutableEntry("string", f000240) }); private static final Expr f000242 = Expr.makePi("json", f000241, f000230); private static final Expr f000243 = Expr.makePi("JSON", f000019, f000242); private static final Expr f000244 = Expr.makeIdentifier("json", 0); private static final Expr f000245 = Expr.makeFieldAccess(f000244, "array"); private static final Expr f000246 = Expr.makeApplication(f000022, new Expr[] {f000230, f000244}); private static final Expr f000247 = Expr.makeNonEmptyListLiteral(new Expr[] {f000246}); private static final Expr f000248 = Expr.makeIdentifier("as", 0); private static final Expr f000249 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000247, f000248); private static final Expr f000250 = Expr.makeLambda("as", f000231, f000249); private static final Expr f000251 = Expr.makeLambda("x", f000243, f000250); private static final Expr f000252 = Expr.makeEmptyListLiteral(f000231); private static final Expr f000253 = Expr.makeApplication(f000000, new Expr[] {f000243, f000022, f000231, f000251, f000252}); private static final Expr f000254 = Expr.makeApplication(f000245, new Expr[] {f000253}); private static final Expr f000255 = Expr.makeLambda("json", f000241, f000254); private static final Expr f000256 = Expr.makeLambda("JSON", f000019, f000255); private static final Expr f000257 = Expr.makeApplication(f000010, new Expr[] {f000243}); private static final Expr f000258 = Expr.makeLambda("x", f000257, f000256); private static final Expr f000259 = Expr.makeFieldAccess(f000244, "bool"); private static final Expr f000260 = Expr.makeApplication(f000259, new Expr[] {f000022}); private static final Expr f000261 = Expr.makeLambda("json", f000241, f000260); private static final Expr f000262 = Expr.makeLambda("JSON", f000019, f000261); private static final Expr f000263 = Expr.makeLambda("x", f000001, f000262); private static final Expr f000264 = Expr.makeFieldAccess(f000244, "double"); private static final Expr f000265 = Expr.makeApplication(f000264, new Expr[] {f000022}); private static final Expr f000266 = Expr.makeLambda("json", f000241, f000265); private static final Expr f000267 = Expr.makeLambda("JSON", f000019, f000266); private static final Expr f000268 = Expr.makeLambda("x", f000234, f000267); private static final Expr f000269 = Expr.makeFieldAccess(f000244, "integer"); private static final Expr f000270 = Expr.makeApplication(f000269, new Expr[] {f000022}); private static final Expr f000271 = Expr.makeLambda("json", f000241, f000270); private static final Expr f000272 = Expr.makeLambda("JSON", f000019, f000271); private static final Expr f000273 = Expr.makeLambda("x", f000083, f000272); private static final Expr f000274 = Expr.makeIdentifier("key", 0); private static final Expr f000275 = Expr.makeIdentifier("value", 0); private static final Expr f000276 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("mapKey", f000274), new SimpleImmutableEntry("mapValue", f000275) }); private static final Expr f000277 = Expr.makeLambda("value", f000226, f000276); private static final Expr f000278 = Expr.makeLambda("key", f000226, f000277); private static final Expr f000279 = Expr.makeIdentifier("v", 0); private static final Expr f000280 = Expr.makeLambda("value", f000279, f000276); private static final Expr f000281 = Expr.makeLambda("key", f000226, f000280); private static final Expr f000282 = Expr.makeLambda("v", f000019, f000281); private static final Expr f000283 = Expr.makeApplication(f000094, new Expr[] {f000022}); private static final Expr f000284 = Expr.makeApplication(f000269, new Expr[] {f000283}); private static final Expr f000285 = Expr.makeLambda("json", f000241, f000284); private static final Expr f000286 = Expr.makeLambda("JSON", f000019, f000285); private static final Expr f000287 = Expr.makeLambda("x", f000221, f000286); private static final Expr f000288 = Expr.makeFieldAccess(f000244, "null"); private static final Expr f000289 = Expr.makeLambda("json", f000241, f000288); private static final Expr f000290 = Expr.makeLambda("JSON", f000019, f000289); private static final Expr f000291 = Expr.makeFieldAccess(f000244, "object"); private static final Expr f000292 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("mapKey", f000226), new SimpleImmutableEntry("mapValue", f000243) }); private static final Expr f000293 = Expr.makeFieldAccess(f000022, "mapKey"); private static final Expr f000294 = Expr.makeFieldAccess(f000022, "mapValue"); private static final Expr f000295 = Expr.makeApplication(f000294, new Expr[] {f000230, f000244}); private static final Expr f000296 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("mapKey", f000293), new SimpleImmutableEntry("mapValue", f000295) }); private static final Expr f000297 = Expr.makeNonEmptyListLiteral(new Expr[] {f000296}); private static final Expr f000298 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000297, f000248); private static final Expr f000299 = Expr.makeLambda("as", f000238, f000298); private static final Expr f000300 = Expr.makeLambda("x", f000292, f000299); private static final Expr f000301 = Expr.makeEmptyListLiteral(f000238); private static final Expr f000302 = Expr.makeApplication(f000000, new Expr[] {f000292, f000022, f000238, f000300, f000301}); private static final Expr f000303 = Expr.makeApplication(f000291, new Expr[] {f000302}); private static final Expr f000304 = Expr.makeLambda("json", f000241, f000303); private static final Expr f000305 = Expr.makeLambda("JSON", f000019, f000304); private static final Expr f000306 = Expr.makeApplication(f000010, new Expr[] {f000292}); private static final Expr f000307 = Expr.makeLambda("x", f000306, f000305); private static final Expr f000308 = Expr.makeIdentifier("old", 0); private static final Expr f000309 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("isNull", f000001), new SimpleImmutableEntry("value", f000230) }); private static final Expr f000310 = Expr.makeFieldAccess(f000022, "value"); private static final Expr f000311 = Expr.makeNonEmptyListLiteral(new Expr[] {f000310}); private static final Expr f000312 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000311, f000248); private static final Expr f000313 = Expr.makeLambda("as", f000231, f000312); private static final Expr f000314 = Expr.makeLambda("x", f000309, f000313); private static final Expr f000315 = Expr.makeApplication(f000000, new Expr[] {f000309, f000002, f000231, f000314, f000252}); private static final Expr f000316 = Expr.makeApplication(f000245, new Expr[] {f000315}); private static final Expr f000317 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("isNull", f000014), new SimpleImmutableEntry("value", f000316) }); private static final Expr f000318 = Expr.makeApplication(f000010, new Expr[] {f000309}); private static final Expr f000319 = Expr.makeLambda("xs", f000318, f000317); private static final Expr f000320 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("isNull", f000014), new SimpleImmutableEntry("value", f000260) }); private static final Expr f000321 = Expr.makeLambda("x", f000001, f000320); private static final Expr f000322 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("isNull", f000014), new SimpleImmutableEntry("value", f000265) }); private static final Expr f000323 = Expr.makeLambda("x", f000234, f000322); private static final Expr f000324 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("isNull", f000014), new SimpleImmutableEntry("value", f000270) }); private static final Expr f000325 = Expr.makeLambda("x", f000083, f000324); private static final Expr f000326 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("isNull", f000008), new SimpleImmutableEntry("value", f000288) }); private static final Expr f000327 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("mapKey", f000226), new SimpleImmutableEntry("mapValue", f000309) }); private static final Expr f000328 = Expr.makeIdentifier("keyValues", 0); private static final Expr f000329 = Expr.makeFieldAccess(f000294, "isNull"); private static final Expr f000330 = Expr.makeProjection(f000022, new String[] {"mapKey"}); private static final Expr f000331 = Expr.makeFieldAccess(f000294, "value"); private static final Expr f000332 = Expr.makeRecordLiteral( new Entry[] {new SimpleImmutableEntry("mapValue", f000331)}); private static final Expr f000333 = Expr.makeOperatorApplication(Operator.COMBINE, f000330, f000332); private static final Expr f000334 = Expr.makeNonEmptyListLiteral(new Expr[] {f000333}); private static final Expr f000335 = Expr.makeIf(f000329, f000301, f000334); private static final Expr f000336 = Expr.makeNonEmptyListLiteral(new Expr[] {f000070}); private static final Expr f000337 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000336, f000248); private static final Expr f000338 = Expr.makeLambda("as", f000238, f000337); private static final Expr f000339 = Expr.makeLambda("a", f000237, f000338); private static final Expr f000340 = Expr.makeApplication(f000000, new Expr[] {f000237, f000335, f000238, f000339}); private static final Expr f000341 = Expr.makeLambda("x", f000327, f000340); private static final Expr f000342 = Expr.makeApplication(f000000, new Expr[] {f000327, f000328, f000238, f000341, f000301}); private static final Expr f000343 = Expr.makeApplication(f000291, new Expr[] {f000342}); private static final Expr f000344 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("isNull", f000014), new SimpleImmutableEntry("value", f000343) }); private static final Expr f000345 = Expr.makeApplication(f000010, new Expr[] {f000327}); private static final Expr f000346 = Expr.makeLambda("keyValues", f000345, f000344); private static final Expr f000347 = Expr.makeFieldAccess(f000244, "string"); private static final Expr f000348 = Expr.makeApplication(f000347, new Expr[] {f000022}); private static final Expr f000349 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("isNull", f000014), new SimpleImmutableEntry("value", f000348) }); private static final Expr f000350 = Expr.makeLambda("x", f000226, f000349); private static final Expr f000351 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("array", f000319), new SimpleImmutableEntry("bool", f000321), new SimpleImmutableEntry("double", f000323), new SimpleImmutableEntry("integer", f000325), new SimpleImmutableEntry("null", f000326), new SimpleImmutableEntry("object", f000346), new SimpleImmutableEntry("string", f000350) }); private static final Expr f000352 = Expr.makeApplication(f000308, new Expr[] {f000309, f000351}); private static final Expr f000353 = Expr.makeFieldAccess(f000352, "value"); private static final Expr f000354 = Expr.makeLambda("json", f000241, f000353); private static final Expr f000355 = Expr.makeLambda("JSON", f000019, f000354); private static final Expr f000356 = Expr.makeLambda("old", f000243, f000355); private static final Expr f000357 = Expr.makeApplication(f000010, new Expr[] {f000226}); private static final Expr f000358 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("head", f000226), new SimpleImmutableEntry("tail", f000357) }); private static final Expr f000359 = Expr.makeLambda("x", f000358, f000022); private static final Expr f000360 = Expr.makeEmptyListLiteral(f000357); private static final Expr f000361 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000022), new SimpleImmutableEntry("tail", f000360) }); private static final Expr f000362 = Expr.makeLambda("x", f000226, f000361); private static final Expr f000363 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("Complex", f000359), new SimpleImmutableEntry("Simple", f000362) }); private static final Expr f000364 = Expr.makeUnionType( new Entry[] { new SimpleImmutableEntry("Complex", f000358), new SimpleImmutableEntry("Simple", f000226) }); private static final Expr f000365 = Expr.makeFieldAccess(f000364, "Simple"); private static final Expr f000366 = Expr.makeTextLiteral("[]"); private static final Expr f000367 = Expr.makeApplication(f000365, new Expr[] {f000366}); private static final Expr f000368 = Expr.makeFieldAccess(f000364, "Complex"); private static final Expr f000369 = Expr.makeIdentifier("inputs", 0); private static final Expr f000370 = Expr.makeFieldAccess(f000369, "head"); private static final Expr f000371 = Expr.makeMerge(f000363, f000370, null); private static final Expr f000372 = Expr.makeFieldAccess(f000369, "tail"); private static final Expr f000373 = Expr.makeApplication(f000010, new Expr[] {f000358}); private static final Expr f000374 = Expr.makeMerge(f000363, f000022, null); private static final Expr f000375 = Expr.makeNonEmptyListLiteral(new Expr[] {f000374}); private static final Expr f000376 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000375, f000248); private static final Expr f000377 = Expr.makeLambda("as", f000373, f000376); private static final Expr f000378 = Expr.makeLambda("x", f000364, f000377); private static final Expr f000379 = Expr.makeEmptyListLiteral(f000373); private static final Expr f000380 = Expr.makeApplication(f000000, new Expr[] {f000364, f000372, f000373, f000378, f000379}); private static final Expr f000381 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000371), new SimpleImmutableEntry("tail", f000380) }); private static final Expr f000382 = Expr.makeFieldAccess(f000371, "head"); private static final Expr f000383 = Expr.makeTextLiteral(new String[] {"", ","}, new Expr[] {f000382}); private static final Expr f000384 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000383), new SimpleImmutableEntry("tail", f000360) }); private static final Expr f000385 = Expr.makeFieldAccess(f000022, "init"); private static final Expr f000386 = Expr.makeFieldAccess(f000022, "last"); private static final Expr f000387 = Expr.makeTextLiteral(new String[] {"", ","}, new Expr[] {f000386}); private static final Expr f000388 = Expr.makeNonEmptyListLiteral(new Expr[] {f000387}); private static final Expr f000389 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000385, f000388); private static final Expr f000390 = Expr.makeRecordLiteral(new Entry[] {new SimpleImmutableEntry("tail", f000389)}); private static final Expr f000391 = Expr.makeOperatorApplication(Operator.PREFER, f000371, f000390); private static final Expr f000392 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("init", f000357), new SimpleImmutableEntry("last", f000226) }); private static final Expr f000393 = Expr.makeLambda("x", f000392, f000391); private static final Expr f000394 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000384), new SimpleImmutableEntry("Some", f000393) }); private static final Expr f000395 = Expr.makeFieldAccess(f000371, "tail"); private static final Expr f000396 = Expr.makeBuiltIn("Optional"); private static final Expr f000397 = Expr.makeApplication(f000396, new Expr[] {f000392}); private static final Expr f000398 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("init", f000360), new SimpleImmutableEntry("last", f000022) }); private static final Expr f000399 = Expr.makeApplication(f000218, new Expr[] {f000398}); private static final Expr f000400 = Expr.makeIdentifier("ny", 0); private static final Expr f000401 = Expr.makeNonEmptyListLiteral(new Expr[] {f000022}); private static final Expr f000402 = Expr.makeFieldAccess(f000400, "init"); private static final Expr f000403 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000401, f000402); private static final Expr f000404 = Expr.makeRecordLiteral(new Entry[] {new SimpleImmutableEntry("init", f000403)}); private static final Expr f000405 = Expr.makeOperatorApplication(Operator.PREFER, f000400, f000404); private static final Expr f000406 = Expr.makeApplication(f000218, new Expr[] {f000405}); private static final Expr f000407 = Expr.makeLambda("ny", f000392, f000406); private static final Expr f000408 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000399), new SimpleImmutableEntry("Some", f000407) }); private static final Expr f000409 = Expr.makeIdentifier("acc", 0); private static final Expr f000410 = Expr.makeMerge(f000408, f000409, null); private static final Expr f000411 = Expr.makeLambda("acc", f000397, f000410); private static final Expr f000412 = Expr.makeLambda("x", f000226, f000411); private static final Expr f000413 = Expr.makeApplication(f000220, new Expr[] {f000392}); private static final Expr f000414 = Expr.makeApplication(f000000, new Expr[] {f000226, f000395, f000397, f000412, f000413}); private static final Expr f000415 = Expr.makeMerge(f000394, f000414, null); private static final Expr f000416 = Expr.makeFieldAccess(f000022, "head"); private static final Expr f000417 = Expr.makeTextLiteral(new String[] {"", ","}, new Expr[] {f000416}); private static final Expr f000418 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000417), new SimpleImmutableEntry("tail", f000360) }); private static final Expr f000419 = Expr.makeIdentifier("x", 1); private static final Expr f000420 = Expr.makeOperatorApplication(Operator.PREFER, f000419, f000390); private static final Expr f000421 = Expr.makeLambda("x", f000392, f000420); private static final Expr f000422 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000418), new SimpleImmutableEntry("Some", f000421) }); private static final Expr f000423 = Expr.makeFieldAccess(f000022, "tail"); private static final Expr f000424 = Expr.makeApplication(f000000, new Expr[] {f000226, f000423, f000397, f000412, f000413}); private static final Expr f000425 = Expr.makeMerge(f000422, f000424, null); private static final Expr f000426 = Expr.makeNonEmptyListLiteral(new Expr[] {f000425}); private static final Expr f000427 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000426, f000248); private static final Expr f000428 = Expr.makeLambda("as", f000373, f000427); private static final Expr f000429 = Expr.makeLambda("x", f000358, f000428); private static final Expr f000430 = Expr.makeApplication(f000000, new Expr[] {f000358, f000385, f000373, f000429, f000379}); private static final Expr f000431 = Expr.makeNonEmptyListLiteral(new Expr[] {f000386}); private static final Expr f000432 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000430, f000431); private static final Expr f000433 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000415), new SimpleImmutableEntry("tail", f000432) }); private static final Expr f000434 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("init", f000373), new SimpleImmutableEntry("last", f000358) }); private static final Expr f000435 = Expr.makeLambda("x", f000434, f000433); private static final Expr f000436 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000381), new SimpleImmutableEntry("Some", f000435) }); private static final Expr f000437 = Expr.makeApplication(f000396, new Expr[] {f000434}); private static final Expr f000438 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("init", f000379), new SimpleImmutableEntry("last", f000022) }); private static final Expr f000439 = Expr.makeApplication(f000218, new Expr[] {f000438}); private static final Expr f000440 = Expr.makeLambda("ny", f000434, f000406); private static final Expr f000441 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000439), new SimpleImmutableEntry("Some", f000440) }); private static final Expr f000442 = Expr.makeMerge(f000441, f000409, null); private static final Expr f000443 = Expr.makeLambda("acc", f000437, f000442); private static final Expr f000444 = Expr.makeLambda("x", f000358, f000443); private static final Expr f000445 = Expr.makeApplication(f000220, new Expr[] {f000434}); private static final Expr f000446 = Expr.makeApplication(f000000, new Expr[] {f000358, f000380, f000437, f000444, f000445}); private static final Expr f000447 = Expr.makeMerge(f000436, f000446, null); private static final Expr f000448 = Expr.makeFieldAccess(f000447, "head"); private static final Expr f000449 = Expr.makeFieldAccess(f000448, "head"); private static final Expr f000450 = Expr.makeTextLiteral(new String[] {"[ ", " ]"}, new Expr[] {f000449}); private static final Expr f000451 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000450), new SimpleImmutableEntry("tail", f000360) }); private static final Expr f000452 = Expr.makeTextLiteral("["); private static final Expr f000453 = Expr.makeNonEmptyListLiteral(new Expr[] {f000449}); private static final Expr f000454 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000453, f000402); private static final Expr f000455 = Expr.makeFieldAccess(f000400, "last"); private static final Expr f000456 = Expr.makeNonEmptyListLiteral(new Expr[] {f000455}); private static final Expr f000457 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000454, f000456); private static final Expr f000458 = Expr.makeTextLiteral(new String[] {" ", ""}, new Expr[] {f000022}); private static final Expr f000459 = Expr.makeNonEmptyListLiteral(new Expr[] {f000458}); private static final Expr f000460 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000459, f000248); private static final Expr f000461 = Expr.makeLambda("as", f000357, f000460); private static final Expr f000462 = Expr.makeLambda("x", f000226, f000461); private static final Expr f000463 = Expr.makeApplication(f000000, new Expr[] {f000226, f000457, f000357, f000462, f000360}); private static final Expr f000464 = Expr.makeTextLiteral("]"); private static final Expr f000465 = Expr.makeNonEmptyListLiteral(new Expr[] {f000464}); private static final Expr f000466 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000463, f000465); private static final Expr f000467 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000452), new SimpleImmutableEntry("tail", f000466) }); private static final Expr f000468 = Expr.makeLambda("ny", f000392, f000467); private static final Expr f000469 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000451), new SimpleImmutableEntry("Some", f000468) }); private static final Expr f000470 = Expr.makeFieldAccess(f000448, "tail"); private static final Expr f000471 = Expr.makeFieldAccess(f000447, "tail"); private static final Expr f000472 = Expr.makeNonEmptyListLiteral(new Expr[] {f000416}); private static final Expr f000473 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000472, f000423); private static final Expr f000474 = Expr.makeLambda("as", f000357, f000337); private static final Expr f000475 = Expr.makeLambda("a", f000226, f000474); private static final Expr f000476 = Expr.makeApplication(f000000, new Expr[] {f000226, f000473, f000357, f000475}); private static final Expr f000477 = Expr.makeLambda("x", f000358, f000476); private static final Expr f000478 = Expr.makeApplication(f000000, new Expr[] {f000358, f000471, f000357, f000477, f000360}); private static final Expr f000479 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000470, f000478); private static final Expr f000480 = Expr.makeApplication(f000000, new Expr[] {f000226, f000479, f000397, f000412, f000413}); private static final Expr f000481 = Expr.makeMerge(f000469, f000480, null); private static final Expr f000482 = Expr.makeApplication(f000368, new Expr[] {f000481}); private static final Expr f000483 = Expr.makeApplication(f000010, new Expr[] {f000364}); private static final Expr f000484 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("head", f000364), new SimpleImmutableEntry("tail", f000483) }); private static final Expr f000485 = Expr.makeLambda("inputs", f000484, f000482); private static final Expr f000486 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000367), new SimpleImmutableEntry("Some", f000485) }); private static final Expr f000487 = Expr.makeBuiltIn("List/reverse"); private static final Expr f000488 = Expr.makeApplication(f000487, new Expr[] {f000364, f000369}); private static final Expr f000489 = Expr.makeApplication(f000396, new Expr[] {f000484}); private static final Expr f000490 = Expr.makeEmptyListLiteral(f000483); private static final Expr f000491 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000022), new SimpleImmutableEntry("tail", f000490) }); private static final Expr f000492 = Expr.makeApplication(f000218, new Expr[] {f000491}); private static final Expr f000493 = Expr.makeIdentifier("ne", 0); private static final Expr f000494 = Expr.makeFieldAccess(f000493, "tail"); private static final Expr f000495 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000494, f000401); private static final Expr f000496 = Expr.makeRecordLiteral(new Entry[] {new SimpleImmutableEntry("tail", f000495)}); private static final Expr f000497 = Expr.makeOperatorApplication(Operator.PREFER, f000493, f000496); private static final Expr f000498 = Expr.makeApplication(f000218, new Expr[] {f000497}); private static final Expr f000499 = Expr.makeLambda("ne", f000484, f000498); private static final Expr f000500 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000492), new SimpleImmutableEntry("Some", f000499) }); private static final Expr f000501 = Expr.makeMerge(f000500, f000409, null); private static final Expr f000502 = Expr.makeLambda("acc", f000489, f000501); private static final Expr f000503 = Expr.makeLambda("x", f000364, f000502); private static final Expr f000504 = Expr.makeApplication(f000220, new Expr[] {f000484}); private static final Expr f000505 = Expr.makeApplication(f000000, new Expr[] {f000364, f000488, f000489, f000503, f000504}); private static final Expr f000506 = Expr.makeMerge(f000486, f000505, null); private static final Expr f000507 = Expr.makeLambda("inputs", f000483, f000506); private static final Expr f000508 = Expr.makeTextLiteral("true"); private static final Expr f000509 = Expr.makeTextLiteral("false"); private static final Expr f000510 = Expr.makeIf(f000022, f000508, f000509); private static final Expr f000511 = Expr.makeApplication(f000365, new Expr[] {f000510}); private static final Expr f000512 = Expr.makeLambda("x", f000001, f000511); private static final Expr f000513 = Expr.makeApplication(f000054, new Expr[] {f000022}); private static final Expr f000514 = Expr.makeApplication(f000365, new Expr[] {f000513}); private static final Expr f000515 = Expr.makeLambda("x", f000234, f000514); private static final Expr f000516 = Expr.makeBuiltIn("Natural/show"); private static final Expr f000517 = Expr.makeApplication(f000516, new Expr[] {f000136}); private static final Expr f000518 = Expr.makeApplication(f000192, new Expr[] {f000022}); private static final Expr f000519 = Expr.makeIf(f000154, f000517, f000518); private static final Expr f000520 = Expr.makeApplication(f000365, new Expr[] {f000519}); private static final Expr f000521 = Expr.makeLambda("x", f000083, f000520); private static final Expr f000522 = Expr.makeTextLiteral("null"); private static final Expr f000523 = Expr.makeApplication(f000365, new Expr[] {f000522}); private static final Expr f000524 = Expr.makeTextLiteral("{}"); private static final Expr f000525 = Expr.makeApplication(f000365, new Expr[] {f000524}); private static final Expr f000526 = Expr.makeFieldAccess(f000370, "mapValue"); private static final Expr f000527 = Expr.makeMerge(f000363, f000526, null); private static final Expr f000528 = Expr.makeBuiltIn("Text/replace"); private static final Expr f000529 = Expr.makeTextLiteral("\""); private static final Expr f000530 = Expr.makeTextLiteral("\\\\\""); private static final Expr f000531 = Expr.makeTextLiteral("\b"); private static final Expr f000532 = Expr.makeTextLiteral("\\\\b"); private static final Expr f000533 = Expr.makeTextLiteral("\f"); private static final Expr f000534 = Expr.makeTextLiteral("\\\\f"); private static final Expr f000535 = Expr.makeTextLiteral("\n"); private static final Expr f000536 = Expr.makeTextLiteral("\\\\n"); private static final Expr f000537 = Expr.makeTextLiteral("\r"); private static final Expr f000538 = Expr.makeTextLiteral("\\\\r"); private static final Expr f000539 = Expr.makeTextLiteral("\t"); private static final Expr f000540 = Expr.makeTextLiteral("\\\\t"); private static final Expr f000541 = Expr.makeTextLiteral("\\\\"); private static final Expr f000542 = Expr.makeTextLiteral("\\\\\\\\"); private static final Expr f000543 = Expr.makeFieldAccess(f000370, "mapKey"); private static final Expr f000544 = Expr.makeApplication(f000528, new Expr[] {f000541, f000542, f000543}); private static final Expr f000545 = Expr.makeApplication(f000528, new Expr[] {f000539, f000540, f000544}); private static final Expr f000546 = Expr.makeApplication(f000528, new Expr[] {f000537, f000538, f000545}); private static final Expr f000547 = Expr.makeApplication(f000528, new Expr[] {f000535, f000536, f000546}); private static final Expr f000548 = Expr.makeApplication(f000528, new Expr[] {f000533, f000534, f000547}); private static final Expr f000549 = Expr.makeApplication(f000528, new Expr[] {f000531, f000532, f000548}); private static final Expr f000550 = Expr.makeApplication(f000528, new Expr[] {f000529, f000530, f000549}); private static final Expr f000551 = Expr.makeFieldAccess(f000527, "head"); private static final Expr f000552 = Expr.makeTextLiteral(new String[] {"\"", "\": ", ""}, new Expr[] {f000550, f000551}); private static final Expr f000553 = Expr.makeRecordLiteral(new Entry[] {new SimpleImmutableEntry("head", f000552)}); private static final Expr f000554 = Expr.makeOperatorApplication(Operator.PREFER, f000527, f000553); private static final Expr f000555 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("mapKey", f000226), new SimpleImmutableEntry("mapValue", f000364) }); private static final Expr f000556 = Expr.makeMerge(f000363, f000294, null); private static final Expr f000557 = Expr.makeApplication(f000528, new Expr[] {f000541, f000542, f000293}); private static final Expr f000558 = Expr.makeApplication(f000528, new Expr[] {f000539, f000540, f000557}); private static final Expr f000559 = Expr.makeApplication(f000528, new Expr[] {f000537, f000538, f000558}); private static final Expr f000560 = Expr.makeApplication(f000528, new Expr[] {f000535, f000536, f000559}); private static final Expr f000561 = Expr.makeApplication(f000528, new Expr[] {f000533, f000534, f000560}); private static final Expr f000562 = Expr.makeApplication(f000528, new Expr[] {f000531, f000532, f000561}); private static final Expr f000563 = Expr.makeApplication(f000528, new Expr[] {f000529, f000530, f000562}); private static final Expr f000564 = Expr.makeFieldAccess(f000556, "head"); private static final Expr f000565 = Expr.makeTextLiteral(new String[] {"\"", "\": ", ""}, new Expr[] {f000563, f000564}); private static final Expr f000566 = Expr.makeRecordLiteral(new Entry[] {new SimpleImmutableEntry("head", f000565)}); private static final Expr f000567 = Expr.makeOperatorApplication(Operator.PREFER, f000556, f000566); private static final Expr f000568 = Expr.makeNonEmptyListLiteral(new Expr[] {f000567}); private static final Expr f000569 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000568, f000248); private static final Expr f000570 = Expr.makeLambda("as", f000373, f000569); private static final Expr f000571 = Expr.makeLambda("x", f000555, f000570); private static final Expr f000572 = Expr.makeApplication(f000000, new Expr[] {f000555, f000372, f000373, f000571, f000379}); private static final Expr f000573 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000554), new SimpleImmutableEntry("tail", f000572) }); private static final Expr f000574 = Expr.makeTextLiteral(new String[] {"\"", "\": ", ","}, new Expr[] {f000550, f000551}); private static final Expr f000575 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000574), new SimpleImmutableEntry("tail", f000360) }); private static final Expr f000576 = Expr.makeOperatorApplication(Operator.PREFER, f000554, f000390); private static final Expr f000577 = Expr.makeLambda("x", f000392, f000576); private static final Expr f000578 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000575), new SimpleImmutableEntry("Some", f000577) }); private static final Expr f000579 = Expr.makeFieldAccess(f000527, "tail"); private static final Expr f000580 = Expr.makeApplication(f000000, new Expr[] {f000226, f000579, f000397, f000412, f000413}); private static final Expr f000581 = Expr.makeMerge(f000578, f000580, null); private static final Expr f000582 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000581), new SimpleImmutableEntry("tail", f000432) }); private static final Expr f000583 = Expr.makeLambda("x", f000434, f000582); private static final Expr f000584 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000573), new SimpleImmutableEntry("Some", f000583) }); private static final Expr f000585 = Expr.makeApplication(f000000, new Expr[] {f000358, f000572, f000437, f000444, f000445}); private static final Expr f000586 = Expr.makeMerge(f000584, f000585, null); private static final Expr f000587 = Expr.makeFieldAccess(f000586, "head"); private static final Expr f000588 = Expr.makeFieldAccess(f000587, "head"); private static final Expr f000589 = Expr.makeTextLiteral(new String[] {"{ ", " }"}, new Expr[] {f000588}); private static final Expr f000590 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000589), new SimpleImmutableEntry("tail", f000360) }); private static final Expr f000591 = Expr.makeTextLiteral("{"); private static final Expr f000592 = Expr.makeNonEmptyListLiteral(new Expr[] {f000588}); private static final Expr f000593 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000592, f000402); private static final Expr f000594 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000593, f000456); private static final Expr f000595 = Expr.makeApplication(f000000, new Expr[] {f000226, f000594, f000357, f000462, f000360}); private static final Expr f000596 = Expr.makeTextLiteral("}"); private static final Expr f000597 = Expr.makeNonEmptyListLiteral(new Expr[] {f000596}); private static final Expr f000598 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000595, f000597); private static final Expr f000599 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000591), new SimpleImmutableEntry("tail", f000598) }); private static final Expr f000600 = Expr.makeLambda("ny", f000392, f000599); private static final Expr f000601 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000590), new SimpleImmutableEntry("Some", f000600) }); private static final Expr f000602 = Expr.makeFieldAccess(f000587, "tail"); private static final Expr f000603 = Expr.makeFieldAccess(f000586, "tail"); private static final Expr f000604 = Expr.makeApplication(f000000, new Expr[] {f000358, f000603, f000357, f000477, f000360}); private static final Expr f000605 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000602, f000604); private static final Expr f000606 = Expr.makeApplication(f000000, new Expr[] {f000226, f000605, f000397, f000412, f000413}); private static final Expr f000607 = Expr.makeMerge(f000601, f000606, null); private static final Expr f000608 = Expr.makeApplication(f000368, new Expr[] {f000607}); private static final Expr f000609 = Expr.makeApplication(f000010, new Expr[] {f000555}); private static final Expr f000610 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("head", f000555), new SimpleImmutableEntry("tail", f000609) }); private static final Expr f000611 = Expr.makeLambda("inputs", f000610, f000608); private static final Expr f000612 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000525), new SimpleImmutableEntry("Some", f000611) }); private static final Expr f000613 = Expr.makeApplication(f000487, new Expr[] {f000555, f000369}); private static final Expr f000614 = Expr.makeApplication(f000396, new Expr[] {f000610}); private static final Expr f000615 = Expr.makeEmptyListLiteral(f000609); private static final Expr f000616 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000022), new SimpleImmutableEntry("tail", f000615) }); private static final Expr f000617 = Expr.makeApplication(f000218, new Expr[] {f000616}); private static final Expr f000618 = Expr.makeLambda("ne", f000610, f000498); private static final Expr f000619 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000617), new SimpleImmutableEntry("Some", f000618) }); private static final Expr f000620 = Expr.makeMerge(f000619, f000409, null); private static final Expr f000621 = Expr.makeLambda("acc", f000614, f000620); private static final Expr f000622 = Expr.makeLambda("x", f000555, f000621); private static final Expr f000623 = Expr.makeApplication(f000220, new Expr[] {f000610}); private static final Expr f000624 = Expr.makeApplication(f000000, new Expr[] {f000555, f000613, f000614, f000622, f000623}); private static final Expr f000625 = Expr.makeMerge(f000612, f000624, null); private static final Expr f000626 = Expr.makeLambda("inputs", f000609, f000625); private static final Expr f000627 = Expr.makeApplication(f000528, new Expr[] {f000541, f000542, f000022}); private static final Expr f000628 = Expr.makeApplication(f000528, new Expr[] {f000539, f000540, f000627}); private static final Expr f000629 = Expr.makeApplication(f000528, new Expr[] {f000537, f000538, f000628}); private static final Expr f000630 = Expr.makeApplication(f000528, new Expr[] {f000535, f000536, f000629}); private static final Expr f000631 = Expr.makeApplication(f000528, new Expr[] {f000533, f000534, f000630}); private static final Expr f000632 = Expr.makeApplication(f000528, new Expr[] {f000531, f000532, f000631}); private static final Expr f000633 = Expr.makeApplication(f000528, new Expr[] {f000529, f000530, f000632}); private static final Expr f000634 = Expr.makeTextLiteral(new String[] {"\"", "\""}, new Expr[] {f000633}); private static final Expr f000635 = Expr.makeApplication(f000365, new Expr[] {f000634}); private static final Expr f000636 = Expr.makeLambda("x", f000226, f000635); private static final Expr f000637 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("array", f000507), new SimpleImmutableEntry("bool", f000512), new SimpleImmutableEntry("double", f000515), new SimpleImmutableEntry("integer", f000521), new SimpleImmutableEntry("null", f000523), new SimpleImmutableEntry("object", f000626), new SimpleImmutableEntry("string", f000636) }); private static final Expr f000638 = Expr.makeApplication(f000244, new Expr[] {f000364, f000637}); private static final Expr f000639 = Expr.makeMerge(f000363, f000638, null); private static final Expr f000640 = Expr.makeFieldAccess(f000639, "head"); private static final Expr f000641 = Expr.makeNonEmptyListLiteral(new Expr[] {f000640}); private static final Expr f000642 = Expr.makeFieldAccess(f000639, "tail"); private static final Expr f000643 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000641, f000642); private static final Expr f000644 = Expr.makeTextLiteral(new String[] {"", "\n", ""}, new Expr[] {f000022, f000023}); private static final Expr f000645 = Expr.makeLambda("y", f000226, f000644); private static final Expr f000646 = Expr.makeLambda("x", f000226, f000645); private static final Expr f000647 = Expr.makeTextLiteral(""); private static final Expr f000648 = Expr.makeApplication(f000000, new Expr[] {f000226, f000643, f000226, f000646, f000647}); private static final Expr f000649 = Expr.makeLambda("json", f000243, f000648); private static final Expr f000650 = Expr.makeIdentifier("j", 0); private static final Expr f000651 = Expr.makeIdentifier("result", 0); private static final Expr f000652 = Expr.makeLambda("result", f000226, f000651); private static final Expr f000653 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("Empty", f000647), new SimpleImmutableEntry("NonEmpty", f000652) }); private static final Expr f000654 = Expr.makeUnionType( new Entry[] { new SimpleImmutableEntry("Empty", null), new SimpleImmutableEntry("NonEmpty", f000226) }); private static final Expr f000655 = Expr.makeFieldAccess(f000654, "NonEmpty"); private static final Expr f000656 = Expr.makeTextLiteral(new String[] {" ", ""}, new Expr[] {f000022}); private static final Expr f000657 = Expr.makeApplication(f000655, new Expr[] {f000656}); private static final Expr f000658 = Expr.makeTextLiteral(new String[] {" ", ",", ""}, new Expr[] {f000022, f000651}); private static final Expr f000659 = Expr.makeApplication(f000655, new Expr[] {f000658}); private static final Expr f000660 = Expr.makeLambda("result", f000226, f000659); private static final Expr f000661 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("Empty", f000657), new SimpleImmutableEntry("NonEmpty", f000660) }); private static final Expr f000662 = Expr.makeIdentifier("status", 0); private static final Expr f000663 = Expr.makeMerge(f000661, f000662, null); private static final Expr f000664 = Expr.makeLambda("status", f000654, f000663); private static final Expr f000665 = Expr.makeLambda("x", f000226, f000664); private static final Expr f000666 = Expr.makeFieldAccess(f000654, "Empty"); private static final Expr f000667 = Expr.makeApplication(f000000, new Expr[] {f000226, f000022, f000654, f000665, f000666}); private static final Expr f000668 = Expr.makeMerge(f000653, f000667, null); private static final Expr f000669 = Expr.makeTextLiteral(new String[] {"[", " ]"}, new Expr[] {f000668}); private static final Expr f000670 = Expr.makeLambda("x", f000357, f000669); private static final Expr f000671 = Expr.makeLambda("x", f000001, f000510); private static final Expr f000672 = Expr.makeIdentifier("integer", 0); private static final Expr f000673 = Expr.makeApplication(f000079, new Expr[] {f000672}); private static final Expr f000674 = Expr.makeApplication(f000075, new Expr[] {f000673}); private static final Expr f000675 = Expr.makeApplication(f000074, new Expr[] {f000674}); private static final Expr f000676 = Expr.makeApplication(f000075, new Expr[] {f000672}); private static final Expr f000677 = Expr.makeApplication(f000516, new Expr[] {f000676}); private static final Expr f000678 = Expr.makeApplication(f000192, new Expr[] {f000672}); private static final Expr f000679 = Expr.makeIf(f000675, f000677, f000678); private static final Expr f000680 = Expr.makeLambda("integer", f000083, f000679); private static final Expr f000681 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("mapKey", f000226), new SimpleImmutableEntry("mapValue", f000226) }); private static final Expr f000682 = Expr.makeBuiltIn("Text/show"); private static final Expr f000683 = Expr.makeApplication(f000682, new Expr[] {f000293}); private static final Expr f000684 = Expr.makeTextLiteral(new String[] {" ", ": ", ""}, new Expr[] {f000683, f000294}); private static final Expr f000685 = Expr.makeApplication(f000655, new Expr[] {f000684}); private static final Expr f000686 = Expr.makeTextLiteral( new String[] {" ", ": ", ",", ""}, new Expr[] {f000683, f000294, f000651}); private static final Expr f000687 = Expr.makeApplication(f000655, new Expr[] {f000686}); private static final Expr f000688 = Expr.makeLambda("result", f000226, f000687); private static final Expr f000689 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("Empty", f000685), new SimpleImmutableEntry("NonEmpty", f000688) }); private static final Expr f000690 = Expr.makeMerge(f000689, f000662, null); private static final Expr f000691 = Expr.makeLambda("status", f000654, f000690); private static final Expr f000692 = Expr.makeLambda("x", f000681, f000691); private static final Expr f000693 = Expr.makeApplication(f000000, new Expr[] {f000681, f000022, f000654, f000692, f000666}); private static final Expr f000694 = Expr.makeMerge(f000653, f000693, null); private static final Expr f000695 = Expr.makeTextLiteral(new String[] {"{", " }"}, new Expr[] {f000694}); private static final Expr f000696 = Expr.makeApplication(f000010, new Expr[] {f000681}); private static final Expr f000697 = Expr.makeLambda("x", f000696, f000695); private static final Expr f000698 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("array", f000670), new SimpleImmutableEntry("bool", f000671), new SimpleImmutableEntry("double", f000054), new SimpleImmutableEntry("integer", f000680), new SimpleImmutableEntry("null", f000522), new SimpleImmutableEntry("object", f000697), new SimpleImmutableEntry("string", f000682) }); private static final Expr f000699 = Expr.makeApplication(f000650, new Expr[] {f000226, f000698}); private static final Expr f000700 = Expr.makeLambda("j", f000243, f000699); private static final Expr f000701 = Expr.makeTextLiteral(new String[] {"- ", ""}, new Expr[] {f000382}); private static final Expr f000702 = Expr.makeApplication(f000000, new Expr[] {f000226, f000395, f000357, f000462, f000360}); private static final Expr f000703 = Expr.makeFieldAccess(f000374, "tail"); private static final Expr f000704 = Expr.makeApplication(f000000, new Expr[] {f000226, f000703, f000357, f000462, f000360}); private static final Expr f000705 = Expr.makeRecordLiteral(new Entry[] {new SimpleImmutableEntry("tail", f000704)}); private static final Expr f000706 = Expr.makeOperatorApplication(Operator.PREFER, f000374, f000705); private static final Expr f000707 = Expr.makeFieldAccess(f000374, "head"); private static final Expr f000708 = Expr.makeTextLiteral(new String[] {"- ", ""}, new Expr[] {f000707}); private static final Expr f000709 = Expr.makeRecordLiteral(new Entry[] {new SimpleImmutableEntry("head", f000708)}); private static final Expr f000710 = Expr.makeOperatorApplication(Operator.PREFER, f000706, f000709); private static final Expr f000711 = Expr.makeNonEmptyListLiteral(new Expr[] {f000710}); private static final Expr f000712 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000711, f000248); private static final Expr f000713 = Expr.makeLambda("as", f000373, f000712); private static final Expr f000714 = Expr.makeLambda("x", f000364, f000713); private static final Expr f000715 = Expr.makeApplication(f000000, new Expr[] {f000364, f000372, f000373, f000714, f000379}); private static final Expr f000716 = Expr.makeApplication(f000000, new Expr[] {f000358, f000715, f000357, f000477, f000360}); private static final Expr f000717 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000702, f000716); private static final Expr f000718 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000701), new SimpleImmutableEntry("tail", f000717) }); private static final Expr f000719 = Expr.makeApplication(f000368, new Expr[] {f000718}); private static final Expr f000720 = Expr.makeLambda("inputs", f000484, f000719); private static final Expr f000721 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000367), new SimpleImmutableEntry("Some", f000720) }); private static final Expr f000722 = Expr.makeMerge(f000721, f000505, null); private static final Expr f000723 = Expr.makeLambda("inputs", f000483, f000722); private static final Expr f000724 = Expr.makeTextLiteral(new String[] {"\"", "\":"}, new Expr[] {f000550}); private static final Expr f000725 = Expr.makeNonEmptyListLiteral(new Expr[] {f000551}); private static final Expr f000726 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000725, f000579); private static final Expr f000727 = Expr.makeApplication(f000000, new Expr[] {f000226, f000726, f000357, f000462, f000360}); private static final Expr f000728 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000724), new SimpleImmutableEntry("tail", f000727) }); private static final Expr f000729 = Expr.makeLambda("_", f000358, f000728); private static final Expr f000730 = Expr.makeIdentifier("line", 0); private static final Expr f000731 = Expr.makeTextLiteral(new String[] {"\"", "\": ", ""}, new Expr[] {f000550, f000730}); private static final Expr f000732 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000731), new SimpleImmutableEntry("tail", f000360) }); private static final Expr f000733 = Expr.makeLambda("line", f000226, f000732); private static final Expr f000734 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("Complex", f000729), new SimpleImmutableEntry("Simple", f000733) }); private static final Expr f000735 = Expr.makeMerge(f000734, f000526, null); private static final Expr f000736 = Expr.makeFieldAccess(f000735, "head"); private static final Expr f000737 = Expr.makeFieldAccess(f000735, "tail"); private static final Expr f000738 = Expr.makeTextLiteral(new String[] {"\"", "\":"}, new Expr[] {f000563}); private static final Expr f000739 = Expr.makeNonEmptyListLiteral(new Expr[] {f000564}); private static final Expr f000740 = Expr.makeFieldAccess(f000556, "tail"); private static final Expr f000741 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000739, f000740); private static final Expr f000742 = Expr.makeApplication(f000000, new Expr[] {f000226, f000741, f000357, f000462, f000360}); private static final Expr f000743 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000738), new SimpleImmutableEntry("tail", f000742) }); private static final Expr f000744 = Expr.makeLambda("_", f000358, f000743); private static final Expr f000745 = Expr.makeTextLiteral(new String[] {"\"", "\": ", ""}, new Expr[] {f000563, f000730}); private static final Expr f000746 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000745), new SimpleImmutableEntry("tail", f000360) }); private static final Expr f000747 = Expr.makeLambda("line", f000226, f000746); private static final Expr f000748 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("Complex", f000744), new SimpleImmutableEntry("Simple", f000747) }); private static final Expr f000749 = Expr.makeMerge(f000748, f000294, null); private static final Expr f000750 = Expr.makeNonEmptyListLiteral(new Expr[] {f000749}); private static final Expr f000751 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000750, f000248); private static final Expr f000752 = Expr.makeLambda("as", f000373, f000751); private static final Expr f000753 = Expr.makeLambda("x", f000555, f000752); private static final Expr f000754 = Expr.makeApplication(f000000, new Expr[] {f000555, f000372, f000373, f000753, f000379}); private static final Expr f000755 = Expr.makeApplication(f000000, new Expr[] {f000358, f000754, f000357, f000477, f000360}); private static final Expr f000756 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000737, f000755); private static final Expr f000757 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000736), new SimpleImmutableEntry("tail", f000756) }); private static final Expr f000758 = Expr.makeApplication(f000368, new Expr[] {f000757}); private static final Expr f000759 = Expr.makeLambda("inputs", f000610, f000758); private static final Expr f000760 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000525), new SimpleImmutableEntry("Some", f000759) }); private static final Expr f000761 = Expr.makeMerge(f000760, f000624, null); private static final Expr f000762 = Expr.makeLambda("inputs", f000609, f000761); private static final Expr f000763 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("array", f000723), new SimpleImmutableEntry("bool", f000512), new SimpleImmutableEntry("double", f000515), new SimpleImmutableEntry("integer", f000521), new SimpleImmutableEntry("null", f000523), new SimpleImmutableEntry("object", f000762), new SimpleImmutableEntry("string", f000636) }); private static final Expr f000764 = Expr.makeApplication(f000244, new Expr[] {f000364, f000763}); private static final Expr f000765 = Expr.makeMerge(f000363, f000764, null); private static final Expr f000766 = Expr.makeFieldAccess(f000765, "head"); private static final Expr f000767 = Expr.makeNonEmptyListLiteral(new Expr[] {f000766}); private static final Expr f000768 = Expr.makeFieldAccess(f000765, "tail"); private static final Expr f000769 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000767, f000768); private static final Expr f000770 = Expr.makeApplication(f000000, new Expr[] {f000226, f000769, f000226, f000646, f000647}); private static final Expr f000771 = Expr.makeLambda("json", f000243, f000770); private static final Expr f000772 = Expr.makeLambda("json", f000241, f000348); private static final Expr f000773 = Expr.makeLambda("JSON", f000019, f000772); private static final Expr f000774 = Expr.makeLambda("x", f000226, f000773); private static final Expr f000775 = Expr.makeIdentifier("contents", 0); private static final Expr f000776 = Expr.makeIdentifier("tagFieldName", 0); private static final Expr f000777 = Expr.makeFieldAccess(f000227, "Inline"); private static final Expr f000778 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("contents", f000775), new SimpleImmutableEntry("field", f000776), new SimpleImmutableEntry("nesting", f000777) }); private static final Expr f000779 = Expr.makeLambda("contents", f000070, f000778); private static final Expr f000780 = Expr.makeLambda("a", f000019, f000779); private static final Expr f000781 = Expr.makeLambda("tagFieldName", f000226, f000780); private static final Expr f000782 = Expr.makeFieldAccess(f000227, "Nested"); private static final Expr f000783 = Expr.makeIdentifier("contentsFieldName", 0); private static final Expr f000784 = Expr.makeApplication(f000782, new Expr[] {f000783}); private static final Expr f000785 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("contents", f000775), new SimpleImmutableEntry("field", f000776), new SimpleImmutableEntry("nesting", f000784) }); private static final Expr f000786 = Expr.makeLambda("contents", f000070, f000785); private static final Expr f000787 = Expr.makeLambda("a", f000019, f000786); private static final Expr f000788 = Expr.makeLambda("tagFieldName", f000226, f000787); private static final Expr f000789 = Expr.makeLambda("contentsFieldName", f000226, f000788); private static final Expr f000790 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("Nesting", f000227), new SimpleImmutableEntry("Tagged", f000229), new SimpleImmutableEntry("Type", f000243), new SimpleImmutableEntry("array", f000258), new SimpleImmutableEntry("bool", f000263), new SimpleImmutableEntry("double", f000268), new SimpleImmutableEntry("integer", f000273), new SimpleImmutableEntry("keyText", f000278), new SimpleImmutableEntry("keyValue", f000282), new SimpleImmutableEntry("natural", f000287), new SimpleImmutableEntry("null", f000290), new SimpleImmutableEntry("number", f000268), new SimpleImmutableEntry("object", f000307), new SimpleImmutableEntry("omitNullFields", f000356), new SimpleImmutableEntry("render", f000649), new SimpleImmutableEntry("renderCompact", f000700), new SimpleImmutableEntry("renderInteger", f000680), new SimpleImmutableEntry("renderYAML", f000771), new SimpleImmutableEntry("string", f000774), new SimpleImmutableEntry("tagInline", f000781), new SimpleImmutableEntry("tagNested", f000789) }); private static final Expr f000791 = Expr.makeOperatorApplication(Operator.AND, f000057, f000004); private static final Expr f000792 = Expr.makeLambda("r", f000001, f000791); private static final Expr f000793 = Expr.makeLambda("x", f000070, f000792); private static final Expr f000794 = Expr.makeApplication(f000000, new Expr[] {f000070, f000002, f000001, f000793, f000008}); private static final Expr f000795 = Expr.makeApplication(f000010, new Expr[] {f000070}); private static final Expr f000796 = Expr.makeLambda("xs", f000795, f000794); private static final Expr f000797 = Expr.makePi("_", f000070, f000001); private static final Expr f000798 = Expr.makeLambda("f", f000797, f000796); private static final Expr f000799 = Expr.makeLambda("a", f000019, f000798); private static final Expr f000800 = Expr.makeOperatorApplication(Operator.OR, f000057, f000004); private static final Expr f000801 = Expr.makeLambda("r", f000001, f000800); private static final Expr f000802 = Expr.makeLambda("x", f000070, f000801); private static final Expr f000803 = Expr.makeApplication(f000000, new Expr[] {f000070, f000002, f000001, f000802, f000014}); private static final Expr f000804 = Expr.makeLambda("xs", f000795, f000803); private static final Expr f000805 = Expr.makeLambda("f", f000797, f000804); private static final Expr f000806 = Expr.makeLambda("a", f000019, f000805); private static final Expr f000807 = Expr.makeBuiltIn("List/build"); private static final Expr f000808 = Expr.makeIdentifier("xss", 0); private static final Expr f000809 = Expr.makeIdentifier("a", 1); private static final Expr f000810 = Expr.makeApplication(f000010, new Expr[] {f000809}); private static final Expr f000811 = Expr.makeLambda("as", f000810, f000337); private static final Expr f000812 = Expr.makeLambda("a", f000070, f000811); private static final Expr f000813 = Expr.makeIdentifier("ys", 0); private static final Expr f000814 = Expr.makeApplication(f000000, new Expr[] {f000070, f000002, f000795, f000812, f000813}); private static final Expr f000815 = Expr.makeLambda("ys", f000795, f000814); private static final Expr f000816 = Expr.makeLambda("xs", f000795, f000815); private static final Expr f000817 = Expr.makeEmptyListLiteral(f000795); private static final Expr f000818 = Expr.makeApplication(f000000, new Expr[] {f000795, f000808, f000795, f000816, f000817}); private static final Expr f000819 = Expr.makeApplication(f000010, new Expr[] {f000795}); private static final Expr f000820 = Expr.makeLambda("xss", f000819, f000818); private static final Expr f000821 = Expr.makeLambda("a", f000019, f000820); private static final Expr f000822 = Expr.makeApplication(f000010, new Expr[] {f000029}); private static final Expr f000823 = Expr.makeLambda("as", f000822, f000337); private static final Expr f000824 = Expr.makeLambda("a", f000029, f000823); private static final Expr f000825 = Expr.makeApplication(f000000, new Expr[] {f000029, f000057, f000822, f000824}); private static final Expr f000826 = Expr.makeLambda("x", f000070, f000825); private static final Expr f000827 = Expr.makeEmptyListLiteral(f000822); private static final Expr f000828 = Expr.makeApplication(f000000, new Expr[] {f000070, f000002, f000822, f000826, f000827}); private static final Expr f000829 = Expr.makeLambda("xs", f000795, f000828); private static final Expr f000830 = Expr.makePi("_", f000070, f000822); private static final Expr f000831 = Expr.makeLambda("f", f000830, f000829); private static final Expr f000832 = Expr.makeLambda("b", f000019, f000831); private static final Expr f000833 = Expr.makeLambda("a", f000019, f000832); private static final Expr f000834 = Expr.makeLambda("l", f000795, f000003); private static final Expr f000835 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000817), new SimpleImmutableEntry("Some", f000834) }); private static final Expr f000836 = Expr.makeIdentifier("o", 0); private static final Expr f000837 = Expr.makeMerge(f000835, f000836, null); private static final Expr f000838 = Expr.makeApplication(f000396, new Expr[] {f000795}); private static final Expr f000839 = Expr.makeLambda("o", f000838, f000837); private static final Expr f000840 = Expr.makeLambda("a", f000019, f000839); private static final Expr f000841 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("index", f000221), new SimpleImmutableEntry("value", f000070) }); private static final Expr f000842 = Expr.makeBuiltIn("List/indexed"); private static final Expr f000843 = Expr.makeApplication(f000842, new Expr[] {f000070, f000002}); private static final Expr f000844 = Expr.makeFieldAccess(f000022, "index"); private static final Expr f000845 = Expr.makeApplication(f000089, new Expr[] {f000844, f000076}); private static final Expr f000846 = Expr.makeApplication(f000074, new Expr[] {f000845}); private static final Expr f000847 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000311, f000002); private static final Expr f000848 = Expr.makeIf(f000846, f000847, f000002); private static final Expr f000849 = Expr.makeLambda("xs", f000795, f000848); private static final Expr f000850 = Expr.makeLambda("x", f000841, f000849); private static final Expr f000851 = Expr.makeApplication(f000000, new Expr[] {f000841, f000843, f000795, f000850, f000817}); private static final Expr f000852 = Expr.makeLambda("xs", f000795, f000851); private static final Expr f000853 = Expr.makeLambda("a", f000019, f000852); private static final Expr f000854 = Expr.makeLambda("n", f000221, f000853); private static final Expr f000855 = Expr.makeLambda("a", f000019, f000817); private static final Expr f000856 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000401, f000002); private static final Expr f000857 = Expr.makeIf(f000057, f000856, f000002); private static final Expr f000858 = Expr.makeLambda("xs", f000795, f000857); private static final Expr f000859 = Expr.makeLambda("x", f000070, f000858); private static final Expr f000860 = Expr.makeApplication(f000000, new Expr[] {f000070, f000002, f000795, f000859, f000817}); private static final Expr f000861 = Expr.makeLambda("xs", f000795, f000860); private static final Expr f000862 = Expr.makeLambda("f", f000797, f000861); private static final Expr f000863 = Expr.makeLambda("a", f000019, f000862); private static final Expr f000864 = Expr.makeIdentifier("list", 0); private static final Expr f000865 = Expr.makePi("_", f000864, f000864); private static final Expr f000866 = Expr.makeIdentifier("cons", 0); private static final Expr f000867 = Expr.makeApplication(f000866, new Expr[] {f000003, f000022}); private static final Expr f000868 = Expr.makeApplication(f000013, new Expr[] {f000867}); private static final Expr f000869 = Expr.makeLambda("l", f000864, f000868); private static final Expr f000870 = Expr.makeLambda("f", f000865, f000869); private static final Expr f000871 = Expr.makeLambda("x", f000070, f000870); private static final Expr f000872 = Expr.makeLambda("l", f000864, f000003); private static final Expr f000873 = Expr.makeIdentifier("nil", 0); private static final Expr f000874 = Expr.makeApplication( f000000, new Expr[] {f000070, f000002, f000865, f000871, f000872, f000873}); private static final Expr f000875 = Expr.makeLambda("nil", f000864, f000874); private static final Expr f000876 = Expr.makePi("_", f000070, f000864); private static final Expr f000877 = Expr.makePi("_", f000864, f000876); private static final Expr f000878 = Expr.makeLambda("cons", f000877, f000875); private static final Expr f000879 = Expr.makeLambda("list", f000019, f000878); private static final Expr f000880 = Expr.makeLambda("xs", f000795, f000879); private static final Expr f000881 = Expr.makeLambda("a", f000019, f000880); private static final Expr f000882 = Expr.Constants.EMPTY_RECORD_TYPE; private static final Expr f000883 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("index", f000221), new SimpleImmutableEntry("value", f000882) }); private static final Expr f000884 = Expr.makeBuiltIn("Natural/fold"); private static final Expr f000885 = Expr.makeApplication(f000010, new Expr[] {f000882}); private static final Expr f000886 = Expr.Constants.EMPTY_RECORD_LITERAL; private static final Expr f000887 = Expr.makeNonEmptyListLiteral(new Expr[] {f000886}); private static final Expr f000888 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000887, f000248); private static final Expr f000889 = Expr.makeLambda("as", f000885, f000888); private static final Expr f000890 = Expr.makeEmptyListLiteral(f000885); private static final Expr f000891 = Expr.makeApplication(f000884, new Expr[] {f000076, f000885, f000889, f000890}); private static final Expr f000892 = Expr.makeApplication(f000842, new Expr[] {f000882, f000891}); private static final Expr f000893 = Expr.makeApplication(f000013, new Expr[] {f000844}); private static final Expr f000894 = Expr.makeNonEmptyListLiteral(new Expr[] {f000893}); private static final Expr f000895 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000894, f000248); private static final Expr f000896 = Expr.makeLambda("as", f000795, f000895); private static final Expr f000897 = Expr.makeLambda("x", f000883, f000896); private static final Expr f000898 = Expr.makeApplication(f000000, new Expr[] {f000883, f000892, f000795, f000897, f000817}); private static final Expr f000899 = Expr.makePi("_", f000221, f000070); private static final Expr f000900 = Expr.makeLambda("f", f000899, f000898); private static final Expr f000901 = Expr.makeLambda("a", f000019, f000900); private static final Expr f000902 = Expr.makeLambda("n", f000221, f000901); private static final Expr f000903 = Expr.makeBuiltIn("List/head"); private static final Expr f000904 = Expr.makeApplication(f000903, new Expr[] {f000070, f000851}); private static final Expr f000905 = Expr.makeLambda("xs", f000795, f000904); private static final Expr f000906 = Expr.makeLambda("a", f000019, f000905); private static final Expr f000907 = Expr.makeLambda("n", f000221, f000906); private static final Expr f000908 = Expr.makeFieldAccess(f000023, "index"); private static final Expr f000909 = Expr.makeApplication(f000884, new Expr[] {f000908, f000070, f000013, f000022}); private static final Expr f000910 = Expr.makeNonEmptyListLiteral(new Expr[] {f000909}); private static final Expr f000911 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000910, f000248); private static final Expr f000912 = Expr.makeLambda("as", f000795, f000911); private static final Expr f000913 = Expr.makeLambda("y", f000883, f000912); private static final Expr f000914 = Expr.makeApplication(f000000, new Expr[] {f000883, f000892, f000795, f000913, f000817}); private static final Expr f000915 = Expr.makeLambda("x", f000070, f000914); private static final Expr f000916 = Expr.makePi("_", f000070, f000070); private static final Expr f000917 = Expr.makeLambda("f", f000916, f000915); private static final Expr f000918 = Expr.makeLambda("a", f000019, f000917); private static final Expr f000919 = Expr.makeLambda("n", f000221, f000918); private static final Expr f000920 = Expr.makeBuiltIn("List/last"); private static final Expr f000921 = Expr.makeBuiltIn("List/length"); private static final Expr f000922 = Expr.makeNonEmptyListLiteral(new Expr[] {f000057}); private static final Expr f000923 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000922, f000248); private static final Expr f000924 = Expr.makeLambda("as", f000822, f000923); private static final Expr f000925 = Expr.makeLambda("x", f000070, f000924); private static final Expr f000926 = Expr.makeApplication(f000000, new Expr[] {f000070, f000002, f000822, f000925, f000827}); private static final Expr f000927 = Expr.makeLambda("xs", f000795, f000926); private static final Expr f000928 = Expr.makePi("_", f000070, f000029); private static final Expr f000929 = Expr.makeLambda("f", f000928, f000927); private static final Expr f000930 = Expr.makeLambda("b", f000019, f000929); private static final Expr f000931 = Expr.makeLambda("a", f000019, f000930); private static final Expr f000932 = Expr.makeApplication(f000921, new Expr[] {f000070, f000002}); private static final Expr f000933 = Expr.makeApplication(f000074, new Expr[] {f000932}); private static final Expr f000934 = Expr.makeLambda("xs", f000795, f000933); private static final Expr f000935 = Expr.makeLambda("a", f000019, f000934); private static final Expr f000936 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("false", f000795), new SimpleImmutableEntry("true", f000795) }); private static final Expr f000937 = Expr.makeIdentifier("p", 0); private static final Expr f000938 = Expr.makeFieldAccess(f000937, "false"); private static final Expr f000939 = Expr.makeFieldAccess(f000937, "true"); private static final Expr f000940 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000401, f000939); private static final Expr f000941 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("false", f000938), new SimpleImmutableEntry("true", f000940) }); private static final Expr f000942 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000401, f000938); private static final Expr f000943 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("false", f000942), new SimpleImmutableEntry("true", f000939) }); private static final Expr f000944 = Expr.makeIf(f000057, f000941, f000943); private static final Expr f000945 = Expr.makeLambda("p", f000936, f000944); private static final Expr f000946 = Expr.makeLambda("x", f000070, f000945); private static final Expr f000947 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("false", f000817), new SimpleImmutableEntry("true", f000817) }); private static final Expr f000948 = Expr.makeApplication(f000000, new Expr[] {f000070, f000002, f000936, f000946, f000947}); private static final Expr f000949 = Expr.makeLambda("xs", f000795, f000948); private static final Expr f000950 = Expr.makeLambda("f", f000797, f000949); private static final Expr f000951 = Expr.makeLambda("a", f000019, f000950); private static final Expr f000952 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000401, f000248); private static final Expr f000953 = Expr.makeLambda("as", f000795, f000952); private static final Expr f000954 = Expr.makeApplication(f000884, new Expr[] {f000076, f000795, f000953, f000817}); private static final Expr f000955 = Expr.makeLambda("x", f000070, f000954); private static final Expr f000956 = Expr.makeLambda("a", f000019, f000955); private static final Expr f000957 = Expr.makeLambda("n", f000221, f000956); private static final Expr f000958 = Expr.makeApplication(f000010, new Expr[] {f000841}); private static final Expr f000959 = Expr.makeIdentifier("kvss", 0); private static final Expr f000960 = Expr.makePi("_", f000221, f000958); private static final Expr f000961 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("count", f000221), new SimpleImmutableEntry("diff", f000960) }); private static final Expr f000962 = Expr.makeFieldAccess(f000023, "count"); private static final Expr f000963 = Expr.makeIdentifier("kvs", 0); private static final Expr f000964 = Expr.makeApplication(f000921, new Expr[] {f000841, f000963}); private static final Expr f000965 = Expr.makeOperatorApplication(Operator.PLUS, f000962, f000964); private static final Expr f000966 = Expr.makeIdentifier("kvOld", 0); private static final Expr f000967 = Expr.makeFieldAccess(f000966, "index"); private static final Expr f000968 = Expr.makeOperatorApplication(Operator.PLUS, f000967, f000076); private static final Expr f000969 = Expr.makeFieldAccess(f000966, "value"); private static final Expr f000970 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("index", f000968), new SimpleImmutableEntry("value", f000969) }); private static final Expr f000971 = Expr.makeNonEmptyListLiteral(new Expr[] {f000970}); private static final Expr f000972 = Expr.makeIdentifier("z", 0); private static final Expr f000973 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000971, f000972); private static final Expr f000974 = Expr.makeLambda("z", f000958, f000973); private static final Expr f000975 = Expr.makeLambda("kvOld", f000841, f000974); private static final Expr f000976 = Expr.makeFieldAccess(f000023, "diff"); private static final Expr f000977 = Expr.makeOperatorApplication(Operator.PLUS, f000076, f000964); private static final Expr f000978 = Expr.makeApplication(f000976, new Expr[] {f000977}); private static final Expr f000979 = Expr.makeApplication(f000000, new Expr[] {f000841, f000963, f000958, f000975, f000978}); private static final Expr f000980 = Expr.makeLambda("n", f000221, f000979); private static final Expr f000981 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("count", f000965), new SimpleImmutableEntry("diff", f000980) }); private static final Expr f000982 = Expr.makeLambda("y", f000961, f000981); private static final Expr f000983 = Expr.makeLambda("kvs", f000958, f000982); private static final Expr f000984 = Expr.makeNaturalLiteral(new BigInteger("0")); private static final Expr f000985 = Expr.makeEmptyListLiteral(f000958); private static final Expr f000986 = Expr.makeLambda("_", f000221, f000985); private static final Expr f000987 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("count", f000984), new SimpleImmutableEntry("diff", f000986) }); private static final Expr f000988 = Expr.makeApplication(f000000, new Expr[] {f000958, f000959, f000961, f000983, f000987}); private static final Expr f000989 = Expr.makeFieldAccess(f000988, "diff"); private static final Expr f000990 = Expr.makeApplication(f000989, new Expr[] {f000984}); private static final Expr f000991 = Expr.makeApplication(f000010, new Expr[] {f000958}); private static final Expr f000992 = Expr.makeLambda("kvss", f000991, f000990); private static final Expr f000993 = Expr.makeLambda("a", f000019, f000992); private static final Expr f000994 = Expr.makeOperatorApplication(Operator.EQUALS, f000846, f000014); private static final Expr f000995 = Expr.makeIf(f000994, f000847, f000002); private static final Expr f000996 = Expr.makeLambda("xs", f000795, f000995); private static final Expr f000997 = Expr.makeLambda("x", f000841, f000996); private static final Expr f000998 = Expr.makeApplication(f000000, new Expr[] {f000841, f000843, f000795, f000997, f000817}); private static final Expr f000999 = Expr.makeLambda("xs", f000795, f000998); private static final Expr f001000 = Expr.makeLambda("a", f000019, f000999); private static final Expr f001001 = Expr.makeLambda("n", f000221, f001000); private static final Expr f001002 = Expr.makeApplication(f000396, new Expr[] {f000070}); private static final Expr f001003 = Expr.makeLambda("x", f000070, f000401); private static final Expr f001004 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000817), new SimpleImmutableEntry("Some", f001003) }); private static final Expr f001005 = Expr.makeMerge(f001004, f000022, null); private static final Expr f001006 = Expr.makeApplication(f000000, new Expr[] {f000070, f001005, f000795, f000812}); private static final Expr f001007 = Expr.makeLambda("x", f001002, f001006); private static final Expr f001008 = Expr.makeApplication(f000000, new Expr[] {f001002, f000002, f000795, f001007, f000817}); private static final Expr f001009 = Expr.makeApplication(f000010, new Expr[] {f001002}); private static final Expr f001010 = Expr.makeLambda("xs", f001009, f001008); private static final Expr f001011 = Expr.makeLambda("a", f000019, f001010); private static final Expr f001012 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("_1", f000070), new SimpleImmutableEntry("_2", f000029) }); private static final Expr f001013 = Expr.makeFieldAccess(f000022, "_1"); private static final Expr f001014 = Expr.makeNonEmptyListLiteral(new Expr[] {f001013}); private static final Expr f001015 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001014, f000248); private static final Expr f001016 = Expr.makeLambda("as", f000795, f001015); private static final Expr f001017 = Expr.makeLambda("x", f001012, f001016); private static final Expr f001018 = Expr.makeApplication(f000000, new Expr[] {f001012, f000002, f000795, f001017, f000817}); private static final Expr f001019 = Expr.makeFieldAccess(f000022, "_2"); private static final Expr f001020 = Expr.makeNonEmptyListLiteral(new Expr[] {f001019}); private static final Expr f001021 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001020, f000248); private static final Expr f001022 = Expr.makeLambda("as", f000822, f001021); private static final Expr f001023 = Expr.makeLambda("x", f001012, f001022); private static final Expr f001024 = Expr.makeApplication(f000000, new Expr[] {f001012, f000002, f000822, f001023, f000827}); private static final Expr f001025 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("_1", f001018), new SimpleImmutableEntry("_2", f001024) }); private static final Expr f001026 = Expr.makeApplication(f000010, new Expr[] {f001012}); private static final Expr f001027 = Expr.makeLambda("xs", f001026, f001025); private static final Expr f001028 = Expr.makeLambda("b", f000019, f001027); private static final Expr f001029 = Expr.makeLambda("a", f000019, f001028); private static final Expr f001030 = Expr.makeIdentifier("rest", 0); private static final Expr f001031 = Expr.makeIdentifier("ix", 0); private static final Expr f001032 = Expr.makeFieldAccess(f001031, "value"); private static final Expr f001033 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("_1", f001032), new SimpleImmutableEntry("_2", f000023) }); private static final Expr f001034 = Expr.makeNonEmptyListLiteral(new Expr[] {f001033}); private static final Expr f001035 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001034, f001030); private static final Expr f001036 = Expr.makeLambda("y", f000029, f001035); private static final Expr f001037 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f001030), new SimpleImmutableEntry("Some", f001036) }); private static final Expr f001038 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("index", f000221), new SimpleImmutableEntry("value", f000029) }); private static final Expr f001039 = Expr.makeApplication(f000842, new Expr[] {f000029, f000813}); private static final Expr f001040 = Expr.makeFieldAccess(f001031, "index"); private static final Expr f001041 = Expr.makeApplication(f000089, new Expr[] {f000844, f001040}); private static final Expr f001042 = Expr.makeApplication(f000074, new Expr[] {f001041}); private static final Expr f001043 = Expr.makeIf(f001042, f000847, f000002); private static final Expr f001044 = Expr.makeLambda("xs", f000822, f001043); private static final Expr f001045 = Expr.makeLambda("x", f001038, f001044); private static final Expr f001046 = Expr.makeApplication(f000000, new Expr[] {f001038, f001039, f000822, f001045, f000827}); private static final Expr f001047 = Expr.makeApplication(f000903, new Expr[] {f000029, f001046}); private static final Expr f001048 = Expr.makeMerge(f001037, f001047, null); private static final Expr f001049 = Expr.makeLambda("rest", f001026, f001048); private static final Expr f001050 = Expr.makeLambda("ix", f000841, f001049); private static final Expr f001051 = Expr.makeEmptyListLiteral(f001026); private static final Expr f001052 = Expr.makeApplication(f000000, new Expr[] {f000841, f000843, f001026, f001050, f001051}); private static final Expr f001053 = Expr.makeLambda("ys", f000822, f001052); private static final Expr f001054 = Expr.makeLambda("b", f000019, f001053); private static final Expr f001055 = Expr.makeLambda("xs", f000795, f001054); private static final Expr f001056 = Expr.makeLambda("a", f000019, f001055); private static final Expr f001057 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("all", f000799), new SimpleImmutableEntry("any", f000806), new SimpleImmutableEntry("build", f000807), new SimpleImmutableEntry("concat", f000821), new SimpleImmutableEntry("concatMap", f000833), new SimpleImmutableEntry("default", f000840), new SimpleImmutableEntry("drop", f000854), new SimpleImmutableEntry("empty", f000855), new SimpleImmutableEntry("filter", f000863), new SimpleImmutableEntry("fold", f000000), new SimpleImmutableEntry("foldLeft", f000881), new SimpleImmutableEntry("generate", f000902), new SimpleImmutableEntry("head", f000903), new SimpleImmutableEntry("index", f000907), new SimpleImmutableEntry("indexed", f000842), new SimpleImmutableEntry("iterate", f000919), new SimpleImmutableEntry("last", f000920), new SimpleImmutableEntry("length", f000921), new SimpleImmutableEntry("map", f000931), new SimpleImmutableEntry("null", f000935), new SimpleImmutableEntry("partition", f000951), new SimpleImmutableEntry("replicate", f000957), new SimpleImmutableEntry("reverse", f000487), new SimpleImmutableEntry("shifted", f000993), new SimpleImmutableEntry("take", f001001), new SimpleImmutableEntry("unpackOptionals", f001011), new SimpleImmutableEntry("unzip", f001029), new SimpleImmutableEntry("zip", f001056) }); private static final Expr f001058 = Expr.makeUnionType( new Entry[] { new SimpleImmutableEntry("Environment", f000226), new SimpleImmutableEntry("Local", f000226), new SimpleImmutableEntry("Missing", null), new SimpleImmutableEntry("Remote", f000226) }); private static final Expr f001059 = Expr.makeRecordLiteral(new Entry[] {new SimpleImmutableEntry("Type", f001058)}); private static final Expr f001060 = Expr.makeIdentifier("k", 0); private static final Expr f001061 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("mapKey", f001060), new SimpleImmutableEntry("mapValue", f000279) }); private static final Expr f001062 = Expr.makeLambda("v", f000019, f001061); private static final Expr f001063 = Expr.makeLambda("k", f000019, f001062); private static final Expr f001064 = Expr.makeApplication(f000010, new Expr[] {f001061}); private static final Expr f001065 = Expr.makeLambda("v", f000019, f001064); private static final Expr f001066 = Expr.makeLambda("k", f000019, f001065); private static final Expr f001067 = Expr.makeEmptyListLiteral(f001064); private static final Expr f001068 = Expr.makeLambda("v", f000019, f001067); private static final Expr f001069 = Expr.makeLambda("k", f000019, f001068); private static final Expr f001070 = Expr.makeApplication(f000010, new Expr[] {f001060}); private static final Expr f001071 = Expr.makeNonEmptyListLiteral(new Expr[] {f000293}); private static final Expr f001072 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001071, f000248); private static final Expr f001073 = Expr.makeLambda("as", f001070, f001072); private static final Expr f001074 = Expr.makeLambda("x", f001061, f001073); private static final Expr f001075 = Expr.makeEmptyListLiteral(f001070); private static final Expr f001076 = Expr.makeApplication(f000000, new Expr[] {f001061, f000002, f001070, f001074, f001075}); private static final Expr f001077 = Expr.makeLambda("xs", f001064, f001076); private static final Expr f001078 = Expr.makeLambda("v", f000019, f001077); private static final Expr f001079 = Expr.makeLambda("k", f000019, f001078); private static final Expr f001080 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("mapKey", f001060), new SimpleImmutableEntry("mapValue", f000070) }); private static final Expr f001081 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("mapKey", f001060), new SimpleImmutableEntry("mapValue", f000029) }); private static final Expr f001082 = Expr.makeApplication(f000010, new Expr[] {f001081}); private static final Expr f001083 = Expr.makeApplication(f000013, new Expr[] {f000294}); private static final Expr f001084 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("mapKey", f000293), new SimpleImmutableEntry("mapValue", f001083) }); private static final Expr f001085 = Expr.makeNonEmptyListLiteral(new Expr[] {f001084}); private static final Expr f001086 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001085, f000248); private static final Expr f001087 = Expr.makeLambda("as", f001082, f001086); private static final Expr f001088 = Expr.makeLambda("x", f001080, f001087); private static final Expr f001089 = Expr.makeEmptyListLiteral(f001082); private static final Expr f001090 = Expr.makeApplication(f000000, new Expr[] {f001080, f000085, f001082, f001088, f001089}); private static final Expr f001091 = Expr.makeApplication(f000010, new Expr[] {f001080}); private static final Expr f001092 = Expr.makeLambda("m", f001091, f001090); private static final Expr f001093 = Expr.makeLambda("f", f000928, f001092); private static final Expr f001094 = Expr.makeLambda("b", f000019, f001093); private static final Expr f001095 = Expr.makeLambda("a", f000019, f001094); private static final Expr f001096 = Expr.makeLambda("k", f000019, f001095); private static final Expr f001097 = Expr.makeApplication(f000396, new Expr[] {f000279}); private static final Expr f001098 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("mapKey", f001060), new SimpleImmutableEntry("mapValue", f001097) }); private static final Expr f001099 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("mapKey", f000293), new SimpleImmutableEntry("mapValue", f000279) }); private static final Expr f001100 = Expr.makeNonEmptyListLiteral(new Expr[] {f001099}); private static final Expr f001101 = Expr.makeLambda("v", f000279, f001100); private static final Expr f001102 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f001067), new SimpleImmutableEntry("Some", f001101) }); private static final Expr f001103 = Expr.makeMerge(f001102, f000294, null); private static final Expr f001104 = Expr.makeLambda("as", f001064, f000337); private static final Expr f001105 = Expr.makeLambda("a", f001061, f001104); private static final Expr f001106 = Expr.makeApplication(f000000, new Expr[] {f001061, f001103, f001064, f001105}); private static final Expr f001107 = Expr.makeLambda("x", f001098, f001106); private static final Expr f001108 = Expr.makeApplication(f000000, new Expr[] {f001098, f000002, f001064, f001107, f001067}); private static final Expr f001109 = Expr.makeApplication(f000010, new Expr[] {f001098}); private static final Expr f001110 = Expr.makeLambda("xs", f001109, f001108); private static final Expr f001111 = Expr.makeLambda("v", f000019, f001110); private static final Expr f001112 = Expr.makeLambda("k", f000019, f001111); private static final Expr f001113 = Expr.makeApplication(f000010, new Expr[] {f000279}); private static final Expr f001114 = Expr.makeNonEmptyListLiteral(new Expr[] {f000294}); private static final Expr f001115 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001114, f000248); private static final Expr f001116 = Expr.makeLambda("as", f001113, f001115); private static final Expr f001117 = Expr.makeLambda("x", f001061, f001116); private static final Expr f001118 = Expr.makeEmptyListLiteral(f001113); private static final Expr f001119 = Expr.makeApplication(f000000, new Expr[] {f001061, f000002, f001113, f001117, f001118}); private static final Expr f001120 = Expr.makeLambda("xs", f001064, f001119); private static final Expr f001121 = Expr.makeLambda("v", f000019, f001120); private static final Expr f001122 = Expr.makeLambda("k", f000019, f001121); private static final Expr f001123 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("Entry", f001063), new SimpleImmutableEntry("Type", f001066), new SimpleImmutableEntry("empty", f001069), new SimpleImmutableEntry("keyText", f000278), new SimpleImmutableEntry("keyValue", f000282), new SimpleImmutableEntry("keys", f001079), new SimpleImmutableEntry("map", f001096), new SimpleImmutableEntry("unpackOptionals", f001112), new SimpleImmutableEntry("values", f001122) }); private static final Expr f001124 = Expr.makeApplication(f000010, new Expr[] {f000085}); private static final Expr f001125 = Expr.makePi("_", f001124, f000085); private static final Expr f001126 = Expr.makeLambda("m", f000019, f001125); private static final Expr f001127 = Expr.makeBuiltIn("Natural/build"); private static final Expr f001128 = Expr.makeApplication(f000010, new Expr[] {f000221}); private static final Expr f001129 = Expr.makeNonEmptyListLiteral(new Expr[] {f000844}); private static final Expr f001130 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001129, f000248); private static final Expr f001131 = Expr.makeLambda("as", f001128, f001130); private static final Expr f001132 = Expr.makeLambda("x", f000883, f001131); private static final Expr f001133 = Expr.makeEmptyListLiteral(f001128); private static final Expr f001134 = Expr.makeApplication(f000000, new Expr[] {f000883, f000892, f001128, f001132, f001133}); private static final Expr f001135 = Expr.makeLambda("n", f000221, f001134); private static final Expr f001136 = Expr.makeApplication(f000089, new Expr[] {f000029, f000070}); private static final Expr f001137 = Expr.makeApplication(f000074, new Expr[] {f001136}); private static final Expr f001138 = Expr.makeApplication(f000089, new Expr[] {f000070, f000029}); private static final Expr f001139 = Expr.makeApplication(f000074, new Expr[] {f001138}); private static final Expr f001140 = Expr.makeOperatorApplication(Operator.AND, f001137, f001139); private static final Expr f001141 = Expr.makeLambda("b", f000221, f001140); private static final Expr f001142 = Expr.makeLambda("a", f000221, f001141); private static final Expr f001143 = Expr.makeBuiltIn("Natural/even"); private static final Expr f001144 = Expr.makeApplication(f000089, new Expr[] {f000023, f000022}); private static final Expr f001145 = Expr.makeApplication(f000074, new Expr[] {f001144}); private static final Expr f001146 = Expr.makeOperatorApplication(Operator.EQUALS, f001145, f000014); private static final Expr f001147 = Expr.makeLambda("y", f000221, f001146); private static final Expr f001148 = Expr.makeLambda("x", f000221, f001147); private static final Expr f001149 = Expr.makeApplication(f000089, new Expr[] {f000022, f000023}); private static final Expr f001150 = Expr.makeApplication(f000074, new Expr[] {f001149}); private static final Expr f001151 = Expr.makeLambda("y", f000221, f001150); private static final Expr f001152 = Expr.makeLambda("x", f000221, f001151); private static final Expr f001153 = Expr.makeOperatorApplication(Operator.EQUALS, f001150, f000014); private static final Expr f001154 = Expr.makeLambda("y", f000221, f001153); private static final Expr f001155 = Expr.makeLambda("x", f000221, f001154); private static final Expr f001156 = Expr.makeLambda("y", f000221, f001145); private static final Expr f001157 = Expr.makeLambda("x", f000221, f001156); private static final Expr f001158 = Expr.makeIf(f001137, f000029, f000070); private static final Expr f001159 = Expr.makeLambda("b", f000221, f001158); private static final Expr f001160 = Expr.makeLambda("a", f000221, f001159); private static final Expr f001161 = Expr.makeApplication(f000000, new Expr[] {f000221, f000002, f000221, f001160, f000022}); private static final Expr f001162 = Expr.makeApplication(f000218, new Expr[] {f001161}); private static final Expr f001163 = Expr.makeLambda("x", f000221, f001162); private static final Expr f001164 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000222), new SimpleImmutableEntry("Some", f001163) }); private static final Expr f001165 = Expr.makeApplication(f000903, new Expr[] {f000221, f000002}); private static final Expr f001166 = Expr.makeMerge(f001164, f001165, null); private static final Expr f001167 = Expr.makeLambda("xs", f001128, f001166); private static final Expr f001168 = Expr.makeApplication(f000074, new Expr[] {f000022}); private static final Expr f001169 = Expr.makeIf(f001137, f000070, f000029); private static final Expr f001170 = Expr.makeLambda("b", f000221, f001169); private static final Expr f001171 = Expr.makeLambda("a", f000221, f001170); private static final Expr f001172 = Expr.makeApplication(f000000, new Expr[] {f000221, f000002, f000221, f001171, f000022}); private static final Expr f001173 = Expr.makeIf(f001168, f000022, f001172); private static final Expr f001174 = Expr.makeApplication(f000218, new Expr[] {f001173}); private static final Expr f001175 = Expr.makeLambda("x", f000221, f001174); private static final Expr f001176 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000222), new SimpleImmutableEntry("Some", f001175) }); private static final Expr f001177 = Expr.makeMerge(f001176, f001165, null); private static final Expr f001178 = Expr.makeLambda("xs", f001128, f001177); private static final Expr f001179 = Expr.makeBuiltIn("Natural/odd"); private static final Expr f001180 = Expr.makeOperatorApplication(Operator.TIMES, f000003, f000004); private static final Expr f001181 = Expr.makeLambda("r", f000221, f001180); private static final Expr f001182 = Expr.makeLambda("l", f000221, f001181); private static final Expr f001183 = Expr.makeNaturalLiteral(new BigInteger("1")); private static final Expr f001184 = Expr.makeApplication(f000000, new Expr[] {f000221, f000002, f000221, f001182, f001183}); private static final Expr f001185 = Expr.makeLambda("xs", f001128, f001184); private static final Expr f001186 = Expr.makeApplication(f000921, new Expr[] {f000221, f000002}); private static final Expr f001187 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("rest", f001128), new SimpleImmutableEntry("sorted", f001128) }); private static final Expr f001188 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("false", f001133), new SimpleImmutableEntry("true", f001133) }); private static final Expr f001189 = Expr.makeFieldAccess(f000022, "rest"); private static final Expr f001190 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("false", f001128), new SimpleImmutableEntry("true", f001128) }); private static final Expr f001191 = Expr.makeApplication(f000089, new Expr[] {f000085, f000022}); private static final Expr f001192 = Expr.makeApplication(f000074, new Expr[] {f001191}); private static final Expr f001193 = Expr.makeIf(f001192, f000941, f000943); private static final Expr f001194 = Expr.makeLambda("p", f001190, f001193); private static final Expr f001195 = Expr.makeLambda("x", f000221, f001194); private static final Expr f001196 = Expr.makeApplication(f000000, new Expr[] {f000221, f001189, f001190, f001195, f001188}); private static final Expr f001197 = Expr.makeLambda("m", f000221, f001196); private static final Expr f001198 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f001188), new SimpleImmutableEntry("Some", f001197) }); private static final Expr f001199 = Expr.makeFieldAccess(f000419, "rest"); private static final Expr f001200 = Expr.makeApplication(f000000, new Expr[] {f000221, f001199, f000221, f001171, f000022}); private static final Expr f001201 = Expr.makeIf(f001168, f000022, f001200); private static final Expr f001202 = Expr.makeApplication(f000218, new Expr[] {f001201}); private static final Expr f001203 = Expr.makeLambda("x", f000221, f001202); private static final Expr f001204 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000222), new SimpleImmutableEntry("Some", f001203) }); private static final Expr f001205 = Expr.makeApplication(f000903, new Expr[] {f000221, f001189}); private static final Expr f001206 = Expr.makeMerge(f001204, f001205, null); private static final Expr f001207 = Expr.makeMerge(f001198, f001206, null); private static final Expr f001208 = Expr.makeFieldAccess(f001207, "false"); private static final Expr f001209 = Expr.makeFieldAccess(f000022, "sorted"); private static final Expr f001210 = Expr.makeFieldAccess(f001207, "true"); private static final Expr f001211 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001209, f001210); private static final Expr f001212 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("rest", f001208), new SimpleImmutableEntry("sorted", f001211) }); private static final Expr f001213 = Expr.makeLambda("x", f001187, f001212); private static final Expr f001214 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("rest", f000002), new SimpleImmutableEntry("sorted", f001133) }); private static final Expr f001215 = Expr.makeApplication(f000884, new Expr[] {f001186, f001187, f001213, f001214}); private static final Expr f001216 = Expr.makeFieldAccess(f001215, "sorted"); private static final Expr f001217 = Expr.makeLambda("xs", f001128, f001216); private static final Expr f001218 = Expr.makeOperatorApplication(Operator.PLUS, f000003, f000004); private static final Expr f001219 = Expr.makeLambda("r", f000221, f001218); private static final Expr f001220 = Expr.makeLambda("l", f000221, f001219); private static final Expr f001221 = Expr.makeApplication(f000000, new Expr[] {f000221, f000002, f000221, f001220, f000984}); private static final Expr f001222 = Expr.makeLambda("xs", f001128, f001221); private static final Expr f001223 = Expr.makeApplication(f000094, new Expr[] {f000076}); private static final Expr f001224 = Expr.makeApplication(f000217, new Expr[] {f001223}); private static final Expr f001225 = Expr.makeLambda("n", f000221, f001224); private static final Expr f001226 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("build", f001127), new SimpleImmutableEntry("enumerate", f001135), new SimpleImmutableEntry("equal", f001142), new SimpleImmutableEntry("even", f001143), new SimpleImmutableEntry("fold", f000884), new SimpleImmutableEntry("greaterThan", f001148), new SimpleImmutableEntry("greaterThanEqual", f001152), new SimpleImmutableEntry("isZero", f000074), new SimpleImmutableEntry("lessThan", f001155), new SimpleImmutableEntry("lessThanEqual", f001157), new SimpleImmutableEntry("listMax", f001167), new SimpleImmutableEntry("listMin", f001178), new SimpleImmutableEntry("max", f001160), new SimpleImmutableEntry("min", f001171), new SimpleImmutableEntry("odd", f001179), new SimpleImmutableEntry("product", f001185), new SimpleImmutableEntry("show", f000516), new SimpleImmutableEntry("sort", f001217), new SimpleImmutableEntry("subtract", f000089), new SimpleImmutableEntry("sum", f001222), new SimpleImmutableEntry("toDouble", f001225), new SimpleImmutableEntry("toInteger", f000094) }); private static final Expr f001227 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("head", f000070), new SimpleImmutableEntry("tail", f000795) }); private static final Expr f001228 = Expr.makeLambda("a", f000019, f001227); private static final Expr f001229 = Expr.makeFieldAccess(f000002, "head"); private static final Expr f001230 = Expr.makeNonEmptyListLiteral(new Expr[] {f001229}); private static final Expr f001231 = Expr.makeFieldAccess(f000002, "tail"); private static final Expr f001232 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001230, f001231); private static final Expr f001233 = Expr.makeApplication(f000000, new Expr[] {f000070, f001232, f000001, f000793, f000008}); private static final Expr f001234 = Expr.makeLambda("xs", f001227, f001233); private static final Expr f001235 = Expr.makeLambda("f", f000797, f001234); private static final Expr f001236 = Expr.makeLambda("a", f000019, f001235); private static final Expr f001237 = Expr.makeApplication(f000000, new Expr[] {f000070, f001232, f000001, f000802, f000014}); private static final Expr f001238 = Expr.makeLambda("xs", f001227, f001237); private static final Expr f001239 = Expr.makeLambda("f", f000797, f001238); private static final Expr f001240 = Expr.makeLambda("a", f000019, f001239); private static final Expr f001241 = Expr.makeFieldAccess(f000808, "head"); private static final Expr f001242 = Expr.makeFieldAccess(f001241, "head"); private static final Expr f001243 = Expr.makeFieldAccess(f001241, "tail"); private static final Expr f001244 = Expr.makeFieldAccess(f000808, "tail"); private static final Expr f001245 = Expr.makeApplication(f000000, new Expr[] {f000070, f000473, f000795, f000812}); private static final Expr f001246 = Expr.makeLambda("x", f001227, f001245); private static final Expr f001247 = Expr.makeApplication(f000000, new Expr[] {f001227, f001244, f000795, f001246, f000817}); private static final Expr f001248 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001243, f001247); private static final Expr f001249 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f001242), new SimpleImmutableEntry("tail", f001248) }); private static final Expr f001250 = Expr.makeApplication(f000010, new Expr[] {f001227}); private static final Expr f001251 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("head", f001227), new SimpleImmutableEntry("tail", f001250) }); private static final Expr f001252 = Expr.makeLambda("xss", f001251, f001249); private static final Expr f001253 = Expr.makeLambda("a", f000019, f001252); private static final Expr f001254 = Expr.makeApplication(f000013, new Expr[] {f001229}); private static final Expr f001255 = Expr.makeFieldAccess(f001254, "head"); private static final Expr f001256 = Expr.makeFieldAccess(f001254, "tail"); private static final Expr f001257 = Expr.makeFieldAccess(f000057, "head"); private static final Expr f001258 = Expr.makeNonEmptyListLiteral(new Expr[] {f001257}); private static final Expr f001259 = Expr.makeFieldAccess(f000057, "tail"); private static final Expr f001260 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001258, f001259); private static final Expr f001261 = Expr.makeApplication(f000000, new Expr[] {f000029, f001260, f000822, f000824}); private static final Expr f001262 = Expr.makeLambda("x", f000070, f001261); private static final Expr f001263 = Expr.makeApplication(f000000, new Expr[] {f000070, f001231, f000822, f001262, f000827}); private static final Expr f001264 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001256, f001263); private static final Expr f001265 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f001255), new SimpleImmutableEntry("tail", f001264) }); private static final Expr f001266 = Expr.makeLambda("xs", f001227, f001265); private static final Expr f001267 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("head", f000029), new SimpleImmutableEntry("tail", f000822) }); private static final Expr f001268 = Expr.makePi("_", f000070, f001267); private static final Expr f001269 = Expr.makeLambda("f", f001268, f001266); private static final Expr f001270 = Expr.makeLambda("b", f000019, f001269); private static final Expr f001271 = Expr.makeLambda("a", f000019, f001270); private static final Expr f001272 = Expr.makeLambda("xs", f001227, f001229); private static final Expr f001273 = Expr.makeLambda("a", f000019, f001272); private static final Expr f001274 = Expr.makeApplication(f000074, new Expr[] {f000076}); private static final Expr f001275 = Expr.makeApplication(f000218, new Expr[] {f001229}); private static final Expr f001276 = Expr.makeApplication(f000842, new Expr[] {f000070, f001231}); private static final Expr f001277 = Expr.makeApplication(f000089, new Expr[] {f001183, f000076}); private static final Expr f001278 = Expr.makeApplication(f000089, new Expr[] {f000844, f001277}); private static final Expr f001279 = Expr.makeApplication(f000074, new Expr[] {f001278}); private static final Expr f001280 = Expr.makeIf(f001279, f000847, f000002); private static final Expr f001281 = Expr.makeLambda("xs", f000795, f001280); private static final Expr f001282 = Expr.makeLambda("x", f000841, f001281); private static final Expr f001283 = Expr.makeApplication(f000000, new Expr[] {f000841, f001276, f000795, f001282, f000817}); private static final Expr f001284 = Expr.makeApplication(f000903, new Expr[] {f000070, f001283}); private static final Expr f001285 = Expr.makeIf(f001274, f001275, f001284); private static final Expr f001286 = Expr.makeLambda("xs", f001227, f001285); private static final Expr f001287 = Expr.makeLambda("a", f000019, f001286); private static final Expr f001288 = Expr.makeLambda("n", f000221, f001287); private static final Expr f001289 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("index", f000984), new SimpleImmutableEntry("value", f001229) }); private static final Expr f001290 = Expr.makeOperatorApplication(Operator.PLUS, f000844, f001183); private static final Expr f001291 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("index", f001290), new SimpleImmutableEntry("value", f000310) }); private static final Expr f001292 = Expr.makeNonEmptyListLiteral(new Expr[] {f001291}); private static final Expr f001293 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001292, f000248); private static final Expr f001294 = Expr.makeLambda("as", f000958, f001293); private static final Expr f001295 = Expr.makeLambda("x", f000841, f001294); private static final Expr f001296 = Expr.makeApplication(f000000, new Expr[] {f000841, f001276, f000958, f001295, f000985}); private static final Expr f001297 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f001289), new SimpleImmutableEntry("tail", f001296) }); private static final Expr f001298 = Expr.makeLambda("xs", f001227, f001297); private static final Expr f001299 = Expr.makeLambda("a", f000019, f001298); private static final Expr f001300 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f001229), new SimpleImmutableEntry("Some", f000071) }); private static final Expr f001301 = Expr.makeApplication(f000920, new Expr[] {f000070, f001231}); private static final Expr f001302 = Expr.makeMerge(f001300, f001301, null); private static final Expr f001303 = Expr.makeLambda("xs", f001227, f001302); private static final Expr f001304 = Expr.makeLambda("a", f000019, f001303); private static final Expr f001305 = Expr.makeApplication(f000921, new Expr[] {f000070, f001231}); private static final Expr f001306 = Expr.makeOperatorApplication(Operator.PLUS, f001305, f001183); private static final Expr f001307 = Expr.makeLambda("xs", f001227, f001306); private static final Expr f001308 = Expr.makeLambda("a", f000019, f001307); private static final Expr f001309 = Expr.makeIdentifier("head", 0); private static final Expr f001310 = Expr.makeIdentifier("tail", 0); private static final Expr f001311 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f001309), new SimpleImmutableEntry("tail", f001310) }); private static final Expr f001312 = Expr.makeLambda("tail", f000795, f001311); private static final Expr f001313 = Expr.makeLambda("head", f000070, f001312); private static final Expr f001314 = Expr.makeLambda("a", f000019, f001313); private static final Expr f001315 = Expr.makeApplication(f000000, new Expr[] {f000070, f001231, f000822, f000925, f000827}); private static final Expr f001316 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f001254), new SimpleImmutableEntry("tail", f001315) }); private static final Expr f001317 = Expr.makeLambda("xs", f001227, f001316); private static final Expr f001318 = Expr.makeLambda("f", f000928, f001317); private static final Expr f001319 = Expr.makeLambda("b", f000019, f001318); private static final Expr f001320 = Expr.makeLambda("a", f000019, f001319); private static final Expr f001321 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f001229), new SimpleImmutableEntry("tail", f000817) }); private static final Expr f001322 = Expr.makeApplication(f000487, new Expr[] {f000070, f001231}); private static final Expr f001323 = Expr.makeApplication(f000842, new Expr[] {f000070, f001322}); private static final Expr f001324 = Expr.makeApplication(f000089, new Expr[] {f000844, f001183}); private static final Expr f001325 = Expr.makeApplication(f000074, new Expr[] {f001324}); private static final Expr f001326 = Expr.makeIf(f001325, f000847, f000002); private static final Expr f001327 = Expr.makeLambda("xs", f000795, f001326); private static final Expr f001328 = Expr.makeLambda("x", f000841, f001327); private static final Expr f001329 = Expr.makeApplication(f000000, new Expr[] {f000841, f001323, f000795, f001328, f000817}); private static final Expr f001330 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001329, f001230); private static final Expr f001331 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000023), new SimpleImmutableEntry("tail", f001330) }); private static final Expr f001332 = Expr.makeLambda("y", f000070, f001331); private static final Expr f001333 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f001321), new SimpleImmutableEntry("Some", f001332) }); private static final Expr f001334 = Expr.makeApplication(f000903, new Expr[] {f000070, f001322}); private static final Expr f001335 = Expr.makeMerge(f001333, f001334, null); private static final Expr f001336 = Expr.makeLambda("xs", f001227, f001335); private static final Expr f001337 = Expr.makeLambda("a", f000019, f001336); private static final Expr f001338 = Expr.makeFieldAccess(f000959, "head"); private static final Expr f001339 = Expr.makeFieldAccess(f001338, "head"); private static final Expr f001340 = Expr.makeFieldAccess(f001338, "tail"); private static final Expr f001341 = Expr.makeNonEmptyListLiteral(new Expr[] {f001340}); private static final Expr f001342 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("head", f000841), new SimpleImmutableEntry("tail", f000958) }); private static final Expr f001343 = Expr.makeFieldAccess(f000959, "tail"); private static final Expr f001344 = Expr.makeApplication(f000000, new Expr[] {f000841, f000473, f000958, f001295, f000985}); private static final Expr f001345 = Expr.makeNonEmptyListLiteral(new Expr[] {f001344}); private static final Expr f001346 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001345, f000248); private static final Expr f001347 = Expr.makeLambda("as", f000991, f001346); private static final Expr f001348 = Expr.makeLambda("x", f001342, f001347); private static final Expr f001349 = Expr.makeEmptyListLiteral(f000991); private static final Expr f001350 = Expr.makeApplication(f000000, new Expr[] {f001342, f001343, f000991, f001348, f001349}); private static final Expr f001351 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001341, f001350); private static final Expr f001352 = Expr.makeApplication(f000000, new Expr[] {f000958, f001351, f000961, f000983, f000987}); private static final Expr f001353 = Expr.makeFieldAccess(f001352, "diff"); private static final Expr f001354 = Expr.makeApplication(f001353, new Expr[] {f000984}); private static final Expr f001355 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f001339), new SimpleImmutableEntry("tail", f001354) }); private static final Expr f001356 = Expr.makeApplication(f000010, new Expr[] {f001342}); private static final Expr f001357 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("head", f001342), new SimpleImmutableEntry("tail", f001356) }); private static final Expr f001358 = Expr.makeLambda("kvss", f001357, f001355); private static final Expr f001359 = Expr.makeLambda("a", f000019, f001358); private static final Expr f001360 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f000022), new SimpleImmutableEntry("tail", f000817) }); private static final Expr f001361 = Expr.makeLambda("x", f000070, f001360); private static final Expr f001362 = Expr.makeLambda("a", f000019, f001361); private static final Expr f001363 = Expr.makeLambda("xs", f001227, f001232); private static final Expr f001364 = Expr.makeLambda("a", f000019, f001363); private static final Expr f001365 = Expr.makeFieldAccess(f001229, "_1"); private static final Expr f001366 = Expr.makeApplication(f000000, new Expr[] {f001012, f001231, f000795, f001017, f000817}); private static final Expr f001367 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f001365), new SimpleImmutableEntry("tail", f001366) }); private static final Expr f001368 = Expr.makeFieldAccess(f001229, "_2"); private static final Expr f001369 = Expr.makeApplication(f000000, new Expr[] {f001012, f001231, f000822, f001023, f000827}); private static final Expr f001370 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f001368), new SimpleImmutableEntry("tail", f001369) }); private static final Expr f001371 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("_1", f001367), new SimpleImmutableEntry("_2", f001370) }); private static final Expr f001372 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("head", f001012), new SimpleImmutableEntry("tail", f001026) }); private static final Expr f001373 = Expr.makeLambda("xs", f001372, f001371); private static final Expr f001374 = Expr.makeLambda("b", f000019, f001373); private static final Expr f001375 = Expr.makeLambda("a", f000019, f001374); private static final Expr f001376 = Expr.makeFieldAccess(f000813, "head"); private static final Expr f001377 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("_1", f001229), new SimpleImmutableEntry("_2", f001376) }); private static final Expr f001378 = Expr.makeFieldAccess(f000813, "tail"); private static final Expr f001379 = Expr.makeApplication(f000842, new Expr[] {f000029, f001378}); private static final Expr f001380 = Expr.makeApplication(f000000, new Expr[] {f001038, f001379, f000822, f001045, f000827}); private static final Expr f001381 = Expr.makeApplication(f000903, new Expr[] {f000029, f001380}); private static final Expr f001382 = Expr.makeMerge(f001037, f001381, null); private static final Expr f001383 = Expr.makeLambda("rest", f001026, f001382); private static final Expr f001384 = Expr.makeLambda("ix", f000841, f001383); private static final Expr f001385 = Expr.makeApplication(f000000, new Expr[] {f000841, f001276, f001026, f001384, f001051}); private static final Expr f001386 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("head", f001377), new SimpleImmutableEntry("tail", f001385) }); private static final Expr f001387 = Expr.makeLambda("ys", f001267, f001386); private static final Expr f001388 = Expr.makeLambda("b", f000019, f001387); private static final Expr f001389 = Expr.makeLambda("xs", f001227, f001388); private static final Expr f001390 = Expr.makeLambda("a", f000019, f001389); private static final Expr f001391 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("Type", f001228), new SimpleImmutableEntry("all", f001236), new SimpleImmutableEntry("any", f001240), new SimpleImmutableEntry("concat", f001253), new SimpleImmutableEntry("concatMap", f001271), new SimpleImmutableEntry("head", f001273), new SimpleImmutableEntry("index", f001288), new SimpleImmutableEntry("indexed", f001299), new SimpleImmutableEntry("last", f001304), new SimpleImmutableEntry("length", f001308), new SimpleImmutableEntry("make", f001314), new SimpleImmutableEntry("map", f001320), new SimpleImmutableEntry("reverse", f001337), new SimpleImmutableEntry("shifted", f001359), new SimpleImmutableEntry("singleton", f001362), new SimpleImmutableEntry("toList", f001364), new SimpleImmutableEntry("unzip", f001375), new SimpleImmutableEntry("zip", f001390) }); private static final Expr f001392 = Expr.makeOperatorApplication(Operator.NOT_EQUALS, f000085, f000076); private static final Expr f001393 = Expr.makeLambda("n", f000001, f001392); private static final Expr f001394 = Expr.makeLambda("m", f000001, f001393); private static final Expr f001395 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f000085, f000076); private static final Expr f001396 = Expr.makeIdentifier("type", 0); private static final Expr f001397 = Expr.makeApplication(f000010, new Expr[] {f001396}); private static final Expr f001398 = Expr.makeLambda("n", f001397, f001395); private static final Expr f001399 = Expr.makeLambda("m", f001397, f001398); private static final Expr f001400 = Expr.makeLambda("type", f000019, f001399); private static final Expr f001401 = Expr.makeOperatorApplication(Operator.AND, f000085, f000076); private static final Expr f001402 = Expr.makeLambda("n", f000001, f001401); private static final Expr f001403 = Expr.makeLambda("m", f000001, f001402); private static final Expr f001404 = Expr.makeOperatorApplication(Operator.TIMES, f000085, f000076); private static final Expr f001405 = Expr.makeLambda("n", f000221, f001404); private static final Expr f001406 = Expr.makeLambda("m", f000221, f001405); private static final Expr f001407 = Expr.makeOperatorApplication(Operator.PLUS, f000085, f000076); private static final Expr f001408 = Expr.makeLambda("n", f000221, f001407); private static final Expr f001409 = Expr.makeLambda("m", f000221, f001408); private static final Expr f001410 = Expr.makeTextLiteral(new String[] {"", "", ""}, new Expr[] {f000085, f000076}); private static final Expr f001411 = Expr.makeLambda("n", f000226, f001410); private static final Expr f001412 = Expr.makeLambda("m", f000226, f001411); private static final Expr f001413 = Expr.makeOperatorApplication(Operator.EQUALS, f000085, f000076); private static final Expr f001414 = Expr.makeLambda("n", f000001, f001413); private static final Expr f001415 = Expr.makeLambda("m", f000001, f001414); private static final Expr f001416 = Expr.makeOperatorApplication(Operator.OR, f000085, f000076); private static final Expr f001417 = Expr.makeLambda("n", f000001, f001416); private static final Expr f001418 = Expr.makeLambda("m", f000001, f001417); private static final Expr f001419 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("!=", f001394), new SimpleImmutableEntry("#", f001400), new SimpleImmutableEntry("&&", f001403), new SimpleImmutableEntry("*", f001406), new SimpleImmutableEntry("+", f001409), new SimpleImmutableEntry("++", f001412), new SimpleImmutableEntry("==", f001415), new SimpleImmutableEntry("||", f001418) }); private static final Expr f001420 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000008), new SimpleImmutableEntry("Some", f000013) }); private static final Expr f001421 = Expr.makeMerge(f001420, f000002, null); private static final Expr f001422 = Expr.makeLambda("xs", f001002, f001421); private static final Expr f001423 = Expr.makeLambda("f", f000797, f001422); private static final Expr f001424 = Expr.makeLambda("a", f000019, f001423); private static final Expr f001425 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000014), new SimpleImmutableEntry("Some", f000013) }); private static final Expr f001426 = Expr.makeMerge(f001425, f000002, null); private static final Expr f001427 = Expr.makeLambda("xs", f001002, f001426); private static final Expr f001428 = Expr.makeLambda("f", f000797, f001427); private static final Expr f001429 = Expr.makeLambda("a", f000019, f001428); private static final Expr f001430 = Expr.makeIdentifier("build", 0); private static final Expr f001431 = Expr.makeApplication(f000218, new Expr[] {f000022}); private static final Expr f001432 = Expr.makeLambda("x", f000070, f001431); private static final Expr f001433 = Expr.makeApplication(f000220, new Expr[] {f000070}); private static final Expr f001434 = Expr.makeApplication(f001430, new Expr[] {f001002, f001432, f001433}); private static final Expr f001435 = Expr.makeIdentifier("optional", 0); private static final Expr f001436 = Expr.makePi("none", f001435, f001435); private static final Expr f001437 = Expr.makePi("_", f000070, f001435); private static final Expr f001438 = Expr.makePi("some", f001437, f001436); private static final Expr f001439 = Expr.makePi("optional", f000019, f001438); private static final Expr f001440 = Expr.makeLambda("build", f001439, f001434); private static final Expr f001441 = Expr.makeLambda("a", f000019, f001440); private static final Expr f001442 = Expr.makeLambda("y", f001002, f000023); private static final Expr f001443 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f001433), new SimpleImmutableEntry("Some", f001442) }); private static final Expr f001444 = Expr.makeMerge(f001443, f000022, null); private static final Expr f001445 = Expr.makeApplication(f000396, new Expr[] {f001002}); private static final Expr f001446 = Expr.makeLambda("x", f001445, f001444); private static final Expr f001447 = Expr.makeLambda("a", f000019, f001446); private static final Expr f001448 = Expr.makeApplication(f000220, new Expr[] {f000029}); private static final Expr f001449 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f001448), new SimpleImmutableEntry("Some", f000013) }); private static final Expr f001450 = Expr.makeMerge(f001449, f000836, null); private static final Expr f001451 = Expr.makeLambda("o", f001002, f001450); private static final Expr f001452 = Expr.makeApplication(f000396, new Expr[] {f000029}); private static final Expr f001453 = Expr.makePi("_", f000070, f001452); private static final Expr f001454 = Expr.makeLambda("f", f001453, f001451); private static final Expr f001455 = Expr.makeLambda("b", f000019, f001454); private static final Expr f001456 = Expr.makeLambda("a", f000019, f001455); private static final Expr f001457 = Expr.makeIdentifier("default", 0); private static final Expr f001458 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f001457), new SimpleImmutableEntry("Some", f000071) }); private static final Expr f001459 = Expr.makeMerge(f001458, f000836, null); private static final Expr f001460 = Expr.makeLambda("o", f001002, f001459); private static final Expr f001461 = Expr.makeLambda("default", f000070, f001460); private static final Expr f001462 = Expr.makeLambda("a", f000019, f001461); private static final Expr f001463 = Expr.makeIf(f000057, f001431, f001433); private static final Expr f001464 = Expr.makeLambda("x", f000070, f001463); private static final Expr f001465 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f001433), new SimpleImmutableEntry("Some", f001464) }); private static final Expr f001466 = Expr.makeMerge(f001465, f000002, null); private static final Expr f001467 = Expr.makeLambda("xs", f001002, f001466); private static final Expr f001468 = Expr.makeLambda("f", f000797, f001467); private static final Expr f001469 = Expr.makeLambda("a", f000019, f001468); private static final Expr f001470 = Expr.makeIdentifier("none", 0); private static final Expr f001471 = Expr.makeIdentifier("some", 0); private static final Expr f001472 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f001470), new SimpleImmutableEntry("Some", f001471) }); private static final Expr f001473 = Expr.makeMerge(f001472, f000836, null); private static final Expr f001474 = Expr.makeLambda("none", f001435, f001473); private static final Expr f001475 = Expr.makeLambda("some", f001437, f001474); private static final Expr f001476 = Expr.makeLambda("optional", f000019, f001475); private static final Expr f001477 = Expr.makeLambda("o", f001002, f001476); private static final Expr f001478 = Expr.makeLambda("a", f000019, f001477); private static final Expr f001479 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000004), new SimpleImmutableEntry("Some", f001432) }); private static final Expr f001480 = Expr.makeMerge(f001479, f000003, null); private static final Expr f001481 = Expr.makeLambda("r", f001002, f001480); private static final Expr f001482 = Expr.makeLambda("l", f001002, f001481); private static final Expr f001483 = Expr.makeApplication(f000000, new Expr[] {f001002, f000002, f001002, f001482, f001433}); private static final Expr f001484 = Expr.makeLambda("xs", f001009, f001483); private static final Expr f001485 = Expr.makeLambda("a", f000019, f001484); private static final Expr f001486 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000003), new SimpleImmutableEntry("Some", f001432) }); private static final Expr f001487 = Expr.makeMerge(f001486, f000004, null); private static final Expr f001488 = Expr.makeLambda("r", f001002, f001487); private static final Expr f001489 = Expr.makeLambda("l", f001002, f001488); private static final Expr f001490 = Expr.makeApplication(f000000, new Expr[] {f001002, f000002, f001002, f001489, f001433}); private static final Expr f001491 = Expr.makeLambda("xs", f001009, f001490); private static final Expr f001492 = Expr.makeLambda("a", f000019, f001491); private static final Expr f001493 = Expr.makeLambda("_", f000070, f001183); private static final Expr f001494 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000984), new SimpleImmutableEntry("Some", f001493) }); private static final Expr f001495 = Expr.makeMerge(f001494, f000002, null); private static final Expr f001496 = Expr.makeLambda("xs", f001002, f001495); private static final Expr f001497 = Expr.makeLambda("a", f000019, f001496); private static final Expr f001498 = Expr.makeApplication(f000218, new Expr[] {f000057}); private static final Expr f001499 = Expr.makeLambda("x", f000070, f001498); private static final Expr f001500 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f001448), new SimpleImmutableEntry("Some", f001499) }); private static final Expr f001501 = Expr.makeMerge(f001500, f000836, null); private static final Expr f001502 = Expr.makeLambda("o", f001002, f001501); private static final Expr f001503 = Expr.makeLambda("f", f000928, f001502); private static final Expr f001504 = Expr.makeLambda("b", f000019, f001503); private static final Expr f001505 = Expr.makeLambda("a", f000019, f001504); private static final Expr f001506 = Expr.makeLambda("_", f000070, f000014); private static final Expr f001507 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000008), new SimpleImmutableEntry("Some", f001506) }); private static final Expr f001508 = Expr.makeMerge(f001507, f000002, null); private static final Expr f001509 = Expr.makeLambda("xs", f001002, f001508); private static final Expr f001510 = Expr.makeLambda("a", f000019, f001509); private static final Expr f001511 = Expr.makeMerge(f001004, f000836, null); private static final Expr f001512 = Expr.makeLambda("o", f001002, f001511); private static final Expr f001513 = Expr.makeLambda("a", f000019, f001512); private static final Expr f001514 = Expr.makeApplication(f000218, new Expr[] {f001013}); private static final Expr f001515 = Expr.makeLambda("x", f001012, f001514); private static final Expr f001516 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f001433), new SimpleImmutableEntry("Some", f001515) }); private static final Expr f001517 = Expr.makeMerge(f001516, f000002, null); private static final Expr f001518 = Expr.makeApplication(f000218, new Expr[] {f001019}); private static final Expr f001519 = Expr.makeLambda("x", f001012, f001518); private static final Expr f001520 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f001448), new SimpleImmutableEntry("Some", f001519) }); private static final Expr f001521 = Expr.makeMerge(f001520, f000002, null); private static final Expr f001522 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("_1", f001517), new SimpleImmutableEntry("_2", f001521) }); private static final Expr f001523 = Expr.makeApplication(f000396, new Expr[] {f001012}); private static final Expr f001524 = Expr.makeLambda("xs", f001523, f001522); private static final Expr f001525 = Expr.makeLambda("b", f000019, f001524); private static final Expr f001526 = Expr.makeLambda("a", f000019, f001525); private static final Expr f001527 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("all", f001424), new SimpleImmutableEntry("any", f001429), new SimpleImmutableEntry("build", f001441), new SimpleImmutableEntry("concat", f001447), new SimpleImmutableEntry("concatMap", f001456), new SimpleImmutableEntry("default", f001462), new SimpleImmutableEntry("filter", f001469), new SimpleImmutableEntry("fold", f001478), new SimpleImmutableEntry("head", f001485), new SimpleImmutableEntry("last", f001492), new SimpleImmutableEntry("length", f001497), new SimpleImmutableEntry("map", f001505), new SimpleImmutableEntry("null", f001510), new SimpleImmutableEntry("toList", f001513), new SimpleImmutableEntry("unzip", f001526) }); private static final Expr f001528 = Expr.makeTextLiteral(new String[] {"", "", ""}, new Expr[] {f000022, f000023}); private static final Expr f001529 = Expr.makeLambda("y", f000226, f001528); private static final Expr f001530 = Expr.makeLambda("x", f000226, f001529); private static final Expr f001531 = Expr.makeApplication(f000000, new Expr[] {f000226, f000002, f000226, f001530, f000647}); private static final Expr f001532 = Expr.makeLambda("xs", f000357, f001531); private static final Expr f001533 = Expr.makeTextLiteral(new String[] {"", "", ""}, new Expr[] {f000057, f000023}); private static final Expr f001534 = Expr.makeLambda("y", f000226, f001533); private static final Expr f001535 = Expr.makeLambda("x", f000070, f001534); private static final Expr f001536 = Expr.makeApplication(f000000, new Expr[] {f000070, f000002, f000226, f001535, f000647}); private static final Expr f001537 = Expr.makeLambda("xs", f000795, f001536); private static final Expr f001538 = Expr.makePi("_", f000070, f000226); private static final Expr f001539 = Expr.makeLambda("f", f001538, f001537); private static final Expr f001540 = Expr.makeLambda("a", f000019, f001539); private static final Expr f001541 = Expr.makeIdentifier("elements", 0); private static final Expr f001542 = Expr.makeApplication(f000655, new Expr[] {f000057}); private static final Expr f001543 = Expr.makeIdentifier("separator", 0); private static final Expr f001544 = Expr.makeTextLiteral(new String[] {"", "", "", ""}, new Expr[] {f000057, f001543, f000651}); private static final Expr f001545 = Expr.makeApplication(f000655, new Expr[] {f001544}); private static final Expr f001546 = Expr.makeLambda("result", f000226, f001545); private static final Expr f001547 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("Empty", f001542), new SimpleImmutableEntry("NonEmpty", f001546) }); private static final Expr f001548 = Expr.makeMerge(f001547, f000662, null); private static final Expr f001549 = Expr.makeLambda("status", f000654, f001548); private static final Expr f001550 = Expr.makeLambda("x", f000070, f001549); private static final Expr f001551 = Expr.makeApplication(f000000, new Expr[] {f000070, f001541, f000654, f001550, f000666}); private static final Expr f001552 = Expr.makeMerge(f000653, f001551, null); private static final Expr f001553 = Expr.makeLambda("elements", f000795, f001552); private static final Expr f001554 = Expr.makeLambda("f", f001538, f001553); private static final Expr f001555 = Expr.makeLambda("a", f000019, f001554); private static final Expr f001556 = Expr.makeLambda("separator", f000226, f001555); private static final Expr f001557 = Expr.makeIdentifier("element", 0); private static final Expr f001558 = Expr.makeApplication(f000655, new Expr[] {f001557}); private static final Expr f001559 = Expr.makeTextLiteral(new String[] {"", "", "", ""}, new Expr[] {f001557, f001543, f000651}); private static final Expr f001560 = Expr.makeApplication(f000655, new Expr[] {f001559}); private static final Expr f001561 = Expr.makeLambda("result", f000226, f001560); private static final Expr f001562 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("Empty", f001558), new SimpleImmutableEntry("NonEmpty", f001561) }); private static final Expr f001563 = Expr.makeMerge(f001562, f000662, null); private static final Expr f001564 = Expr.makeLambda("status", f000654, f001563); private static final Expr f001565 = Expr.makeLambda("element", f000226, f001564); private static final Expr f001566 = Expr.makeApplication(f000000, new Expr[] {f000226, f001541, f000654, f001565, f000666}); private static final Expr f001567 = Expr.makeMerge(f000653, f001566, null); private static final Expr f001568 = Expr.makeLambda("elements", f000357, f001567); private static final Expr f001569 = Expr.makeLambda("separator", f000226, f001568); private static final Expr f001570 = Expr.makeIdentifier("t", 0); private static final Expr f001571 = Expr.makeLambda("t", f000226, f001570); private static final Expr f001572 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000647), new SimpleImmutableEntry("Some", f001571) }); private static final Expr f001573 = Expr.makeMerge(f001572, f000836, null); private static final Expr f001574 = Expr.makeApplication(f000396, new Expr[] {f000226}); private static final Expr f001575 = Expr.makeLambda("o", f001574, f001573); private static final Expr f001576 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("None", f000647), new SimpleImmutableEntry("Some", f000013) }); private static final Expr f001577 = Expr.makeMerge(f001576, f000836, null); private static final Expr f001578 = Expr.makeLambda("o", f001002, f001577); private static final Expr f001579 = Expr.makeLambda("f", f001538, f001578); private static final Expr f001580 = Expr.makeLambda("a", f000019, f001579); private static final Expr f001581 = Expr.makePi("_", f000226, f000226); private static final Expr f001582 = Expr.makeTextLiteral("A"); private static final Expr f001583 = Expr.makeTextLiteral("a"); private static final Expr f001584 = Expr.makeApplication(f000528, new Expr[] {f001582, f001583}); private static final Expr f001585 = Expr.makeTextLiteral("B"); private static final Expr f001586 = Expr.makeTextLiteral("b"); private static final Expr f001587 = Expr.makeApplication(f000528, new Expr[] {f001585, f001586}); private static final Expr f001588 = Expr.makeTextLiteral("C"); private static final Expr f001589 = Expr.makeTextLiteral("c"); private static final Expr f001590 = Expr.makeApplication(f000528, new Expr[] {f001588, f001589}); private static final Expr f001591 = Expr.makeTextLiteral("D"); private static final Expr f001592 = Expr.makeTextLiteral("d"); private static final Expr f001593 = Expr.makeApplication(f000528, new Expr[] {f001591, f001592}); private static final Expr f001594 = Expr.makeTextLiteral("E"); private static final Expr f001595 = Expr.makeTextLiteral("e"); private static final Expr f001596 = Expr.makeApplication(f000528, new Expr[] {f001594, f001595}); private static final Expr f001597 = Expr.makeTextLiteral("F"); private static final Expr f001598 = Expr.makeTextLiteral("f"); private static final Expr f001599 = Expr.makeApplication(f000528, new Expr[] {f001597, f001598}); private static final Expr f001600 = Expr.makeTextLiteral("G"); private static final Expr f001601 = Expr.makeTextLiteral("g"); private static final Expr f001602 = Expr.makeApplication(f000528, new Expr[] {f001600, f001601}); private static final Expr f001603 = Expr.makeTextLiteral("H"); private static final Expr f001604 = Expr.makeTextLiteral("h"); private static final Expr f001605 = Expr.makeApplication(f000528, new Expr[] {f001603, f001604}); private static final Expr f001606 = Expr.makeTextLiteral("I"); private static final Expr f001607 = Expr.makeTextLiteral("i"); private static final Expr f001608 = Expr.makeApplication(f000528, new Expr[] {f001606, f001607}); private static final Expr f001609 = Expr.makeTextLiteral("J"); private static final Expr f001610 = Expr.makeTextLiteral("j"); private static final Expr f001611 = Expr.makeApplication(f000528, new Expr[] {f001609, f001610}); private static final Expr f001612 = Expr.makeTextLiteral("K"); private static final Expr f001613 = Expr.makeTextLiteral("k"); private static final Expr f001614 = Expr.makeApplication(f000528, new Expr[] {f001612, f001613}); private static final Expr f001615 = Expr.makeTextLiteral("L"); private static final Expr f001616 = Expr.makeTextLiteral("l"); private static final Expr f001617 = Expr.makeApplication(f000528, new Expr[] {f001615, f001616}); private static final Expr f001618 = Expr.makeTextLiteral("M"); private static final Expr f001619 = Expr.makeTextLiteral("m"); private static final Expr f001620 = Expr.makeApplication(f000528, new Expr[] {f001618, f001619}); private static final Expr f001621 = Expr.makeTextLiteral("N"); private static final Expr f001622 = Expr.makeTextLiteral("n"); private static final Expr f001623 = Expr.makeApplication(f000528, new Expr[] {f001621, f001622}); private static final Expr f001624 = Expr.makeTextLiteral("O"); private static final Expr f001625 = Expr.makeTextLiteral("o"); private static final Expr f001626 = Expr.makeApplication(f000528, new Expr[] {f001624, f001625}); private static final Expr f001627 = Expr.makeTextLiteral("P"); private static final Expr f001628 = Expr.makeTextLiteral("p"); private static final Expr f001629 = Expr.makeApplication(f000528, new Expr[] {f001627, f001628}); private static final Expr f001630 = Expr.makeTextLiteral("Q"); private static final Expr f001631 = Expr.makeTextLiteral("q"); private static final Expr f001632 = Expr.makeApplication(f000528, new Expr[] {f001630, f001631}); private static final Expr f001633 = Expr.makeTextLiteral("R"); private static final Expr f001634 = Expr.makeTextLiteral("r"); private static final Expr f001635 = Expr.makeApplication(f000528, new Expr[] {f001633, f001634}); private static final Expr f001636 = Expr.makeTextLiteral("S"); private static final Expr f001637 = Expr.makeTextLiteral("s"); private static final Expr f001638 = Expr.makeApplication(f000528, new Expr[] {f001636, f001637}); private static final Expr f001639 = Expr.makeTextLiteral("T"); private static final Expr f001640 = Expr.makeTextLiteral("t"); private static final Expr f001641 = Expr.makeApplication(f000528, new Expr[] {f001639, f001640}); private static final Expr f001642 = Expr.makeTextLiteral("U"); private static final Expr f001643 = Expr.makeTextLiteral("u"); private static final Expr f001644 = Expr.makeApplication(f000528, new Expr[] {f001642, f001643}); private static final Expr f001645 = Expr.makeTextLiteral("V"); private static final Expr f001646 = Expr.makeTextLiteral("v"); private static final Expr f001647 = Expr.makeApplication(f000528, new Expr[] {f001645, f001646}); private static final Expr f001648 = Expr.makeTextLiteral("W"); private static final Expr f001649 = Expr.makeTextLiteral("w"); private static final Expr f001650 = Expr.makeApplication(f000528, new Expr[] {f001648, f001649}); private static final Expr f001651 = Expr.makeTextLiteral("X"); private static final Expr f001652 = Expr.makeTextLiteral("x"); private static final Expr f001653 = Expr.makeApplication(f000528, new Expr[] {f001651, f001652}); private static final Expr f001654 = Expr.makeTextLiteral("Y"); private static final Expr f001655 = Expr.makeTextLiteral("y"); private static final Expr f001656 = Expr.makeApplication(f000528, new Expr[] {f001654, f001655}); private static final Expr f001657 = Expr.makeTextLiteral("Z"); private static final Expr f001658 = Expr.makeTextLiteral("z"); private static final Expr f001659 = Expr.makeApplication(f000528, new Expr[] {f001657, f001658}); private static final Expr f001660 = Expr.makeNonEmptyListLiteral( new Expr[] { f001584, f001587, f001590, f001593, f001596, f001599, f001602, f001605, f001608, f001611, f001614, f001617, f001620, f001623, f001626, f001629, f001632, f001635, f001638, f001641, f001644, f001647, f001650, f001653, f001656, f001659 }); private static final Expr f001661 = Expr.makeIdentifier("replacement", 0); private static final Expr f001662 = Expr.makeLambda("replacement", f001581, f001661); private static final Expr f001663 = Expr.makeApplication(f000000, new Expr[] {f001581, f001660, f000226, f001662}); private static final Expr f001664 = Expr.makeIdentifier("num", 0); private static final Expr f001665 = Expr.makeIdentifier("text", 0); private static final Expr f001666 = Expr.makeNonEmptyListLiteral(new Expr[] {f001665}); private static final Expr f001667 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001666, f000248); private static final Expr f001668 = Expr.makeLambda("as", f000357, f001667); private static final Expr f001669 = Expr.makeApplication(f000884, new Expr[] {f001664, f000357, f001668, f000360}); private static final Expr f001670 = Expr.makeApplication(f000000, new Expr[] {f000226, f001669, f000226, f001530, f000647}); private static final Expr f001671 = Expr.makeLambda("text", f000226, f001670); private static final Expr f001672 = Expr.makeLambda("num", f000221, f001671); private static final Expr f001673 = Expr.makeTextLiteral(" "); private static final Expr f001674 = Expr.makeNonEmptyListLiteral(new Expr[] {f001673}); private static final Expr f001675 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001674, f000248); private static final Expr f001676 = Expr.makeLambda("as", f000357, f001675); private static final Expr f001677 = Expr.makeApplication(f000884, new Expr[] {f000070, f000357, f001676, f000360}); private static final Expr f001678 = Expr.makeApplication(f000000, new Expr[] {f000226, f001677, f000226, f001530, f000647}); private static final Expr f001679 = Expr.makeLambda("a", f000221, f001678); private static final Expr f001680 = Expr.makeApplication(f000528, new Expr[] {f001583, f001582}); private static final Expr f001681 = Expr.makeApplication(f000528, new Expr[] {f001586, f001585}); private static final Expr f001682 = Expr.makeApplication(f000528, new Expr[] {f001589, f001588}); private static final Expr f001683 = Expr.makeApplication(f000528, new Expr[] {f001592, f001591}); private static final Expr f001684 = Expr.makeApplication(f000528, new Expr[] {f001595, f001594}); private static final Expr f001685 = Expr.makeApplication(f000528, new Expr[] {f001598, f001597}); private static final Expr f001686 = Expr.makeApplication(f000528, new Expr[] {f001601, f001600}); private static final Expr f001687 = Expr.makeApplication(f000528, new Expr[] {f001604, f001603}); private static final Expr f001688 = Expr.makeApplication(f000528, new Expr[] {f001607, f001606}); private static final Expr f001689 = Expr.makeApplication(f000528, new Expr[] {f001610, f001609}); private static final Expr f001690 = Expr.makeApplication(f000528, new Expr[] {f001613, f001612}); private static final Expr f001691 = Expr.makeApplication(f000528, new Expr[] {f001616, f001615}); private static final Expr f001692 = Expr.makeApplication(f000528, new Expr[] {f001619, f001618}); private static final Expr f001693 = Expr.makeApplication(f000528, new Expr[] {f001622, f001621}); private static final Expr f001694 = Expr.makeApplication(f000528, new Expr[] {f001625, f001624}); private static final Expr f001695 = Expr.makeApplication(f000528, new Expr[] {f001628, f001627}); private static final Expr f001696 = Expr.makeApplication(f000528, new Expr[] {f001631, f001630}); private static final Expr f001697 = Expr.makeApplication(f000528, new Expr[] {f001634, f001633}); private static final Expr f001698 = Expr.makeApplication(f000528, new Expr[] {f001637, f001636}); private static final Expr f001699 = Expr.makeApplication(f000528, new Expr[] {f001640, f001639}); private static final Expr f001700 = Expr.makeApplication(f000528, new Expr[] {f001643, f001642}); private static final Expr f001701 = Expr.makeApplication(f000528, new Expr[] {f001646, f001645}); private static final Expr f001702 = Expr.makeApplication(f000528, new Expr[] {f001649, f001648}); private static final Expr f001703 = Expr.makeApplication(f000528, new Expr[] {f001652, f001651}); private static final Expr f001704 = Expr.makeApplication(f000528, new Expr[] {f001655, f001654}); private static final Expr f001705 = Expr.makeApplication(f000528, new Expr[] {f001658, f001657}); private static final Expr f001706 = Expr.makeNonEmptyListLiteral( new Expr[] { f001680, f001681, f001682, f001683, f001684, f001685, f001686, f001687, f001688, f001689, f001690, f001691, f001692, f001693, f001694, f001695, f001696, f001697, f001698, f001699, f001700, f001701, f001702, f001703, f001704, f001705 }); private static final Expr f001707 = Expr.makeApplication(f000000, new Expr[] {f001581, f001706, f000226, f001662}); private static final Expr f001708 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("concat", f001532), new SimpleImmutableEntry("concatMap", f001540), new SimpleImmutableEntry("concatMapSep", f001556), new SimpleImmutableEntry("concatSep", f001569), new SimpleImmutableEntry("default", f001575), new SimpleImmutableEntry("defaultMap", f001580), new SimpleImmutableEntry("lowerASCII", f001663), new SimpleImmutableEntry("replace", f000528), new SimpleImmutableEntry("replicate", f001672), new SimpleImmutableEntry("show", f000682), new SimpleImmutableEntry("spaces", f001679), new SimpleImmutableEntry("upperASCII", f001707) }); private static final Expr f001709 = Expr.makeIdentifier("XML", 0); private static final Expr f001710 = Expr.makeApplication(f000010, new Expr[] {f001709}); private static final Expr f001711 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("attributes", f000696), new SimpleImmutableEntry("content", f001710), new SimpleImmutableEntry("name", f000226) }); private static final Expr f001712 = Expr.makePi("_", f001711, f001709); private static final Expr f001713 = Expr.makePi("_", f000226, f001709); private static final Expr f001714 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("element", f001712), new SimpleImmutableEntry("text", f001713) }); private static final Expr f001715 = Expr.makePi("xml", f001714, f001709); private static final Expr f001716 = Expr.makePi("XML", f000019, f001715); private static final Expr f001717 = Expr.makeIdentifier("xml", 0); private static final Expr f001718 = Expr.makeFieldAccess(f001717, "element"); private static final Expr f001719 = Expr.makeIdentifier("elem", 0); private static final Expr f001720 = Expr.makeFieldAccess(f001719, "attributes"); private static final Expr f001721 = Expr.makeFieldAccess(f001719, "content"); private static final Expr f001722 = Expr.makeApplication(f000022, new Expr[] {f001709, f001717}); private static final Expr f001723 = Expr.makeNonEmptyListLiteral(new Expr[] {f001722}); private static final Expr f001724 = Expr.makeOperatorApplication(Operator.LIST_APPEND, f001723, f000248); private static final Expr f001725 = Expr.makeLambda("as", f001710, f001724); private static final Expr f001726 = Expr.makeLambda("x", f001716, f001725); private static final Expr f001727 = Expr.makeEmptyListLiteral(f001710); private static final Expr f001728 = Expr.makeApplication(f000000, new Expr[] {f001716, f001721, f001710, f001726, f001727}); private static final Expr f001729 = Expr.makeFieldAccess(f001719, "name"); private static final Expr f001730 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("attributes", f001720), new SimpleImmutableEntry("content", f001728), new SimpleImmutableEntry("name", f001729) }); private static final Expr f001731 = Expr.makeApplication(f001718, new Expr[] {f001730}); private static final Expr f001732 = Expr.makeLambda("xml", f001714, f001731); private static final Expr f001733 = Expr.makeLambda("XML", f000019, f001732); private static final Expr f001734 = Expr.makeApplication(f000010, new Expr[] {f001716}); private static final Expr f001735 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("attributes", f000696), new SimpleImmutableEntry("content", f001734), new SimpleImmutableEntry("name", f000226) }); private static final Expr f001736 = Expr.makeLambda("elem", f001735, f001733); private static final Expr f001737 = Expr.makeEmptyListLiteral(f000696); private static final Expr f001738 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("attributes", f001720), new SimpleImmutableEntry("content", f001727), new SimpleImmutableEntry("name", f001729) }); private static final Expr f001739 = Expr.makeApplication(f001718, new Expr[] {f001738}); private static final Expr f001740 = Expr.makeLambda("xml", f001714, f001739); private static final Expr f001741 = Expr.makeLambda("XML", f000019, f001740); private static final Expr f001742 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("attributes", f000696), new SimpleImmutableEntry("name", f000226) }); private static final Expr f001743 = Expr.makeLambda("elem", f001742, f001741); private static final Expr f001744 = Expr.makeTextLiteral("""); private static final Expr f001745 = Expr.makeTextLiteral("'"); private static final Expr f001746 = Expr.makeTextLiteral("'"); private static final Expr f001747 = Expr.makeTextLiteral("<"); private static final Expr f001748 = Expr.makeTextLiteral("<"); private static final Expr f001749 = Expr.makeTextLiteral("&"); private static final Expr f001750 = Expr.makeTextLiteral("&"); private static final Expr f001751 = Expr.makeApplication(f000528, new Expr[] {f001749, f001750, f000294}); private static final Expr f001752 = Expr.makeApplication(f000528, new Expr[] {f001747, f001748, f001751}); private static final Expr f001753 = Expr.makeApplication(f000528, new Expr[] {f001745, f001746, f001752}); private static final Expr f001754 = Expr.makeApplication(f000528, new Expr[] {f000529, f001744, f001753}); private static final Expr f001755 = Expr.makeTextLiteral( new String[] {" ", "=\"", "\"", ""}, new Expr[] {f000293, f001754, f000023}); private static final Expr f001756 = Expr.makeLambda("y", f000226, f001755); private static final Expr f001757 = Expr.makeLambda("x", f000681, f001756); private static final Expr f001758 = Expr.makeApplication(f000000, new Expr[] {f000681, f001720, f000226, f001757, f000647}); private static final Expr f001759 = Expr.makeApplication(f000921, new Expr[] {f000226, f001721}); private static final Expr f001760 = Expr.makeApplication(f000074, new Expr[] {f001759}); private static final Expr f001761 = Expr.makeTextLiteral("/>"); private static final Expr f001762 = Expr.makeApplication(f000000, new Expr[] {f000226, f001721, f000226, f001530, f000647}); private static final Expr f001763 = Expr.makeTextLiteral(new String[] {">", ""}, new Expr[] {f001762, f001729}); private static final Expr f001764 = Expr.makeIf(f001760, f001761, f001763); private static final Expr f001765 = Expr.makeTextLiteral(new String[] {"<", "", "", ""}, new Expr[] {f001729, f001758, f001764}); private static final Expr f001766 = Expr.makeRecordType( new Entry[] { new SimpleImmutableEntry("attributes", f000696), new SimpleImmutableEntry("content", f000357), new SimpleImmutableEntry("name", f000226) }); private static final Expr f001767 = Expr.makeLambda("elem", f001766, f001765); private static final Expr f001768 = Expr.makeTextLiteral(">"); private static final Expr f001769 = Expr.makeTextLiteral(">"); private static final Expr f001770 = Expr.makeApplication(f000528, new Expr[] {f001749, f001750, f001665}); private static final Expr f001771 = Expr.makeApplication(f000528, new Expr[] {f001747, f001748, f001770}); private static final Expr f001772 = Expr.makeApplication(f000528, new Expr[] {f001768, f001769, f001771}); private static final Expr f001773 = Expr.makeLambda("text", f000226, f001772); private static final Expr f001774 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("element", f001767), new SimpleImmutableEntry("text", f001773) }); private static final Expr f001775 = Expr.makeApplication(f000022, new Expr[] {f000226, f001774}); private static final Expr f001776 = Expr.makeLambda("x", f001716, f001775); private static final Expr f001777 = Expr.makeFieldAccess(f001717, "text"); private static final Expr f001778 = Expr.makeIdentifier("d", 0); private static final Expr f001779 = Expr.makeApplication(f001777, new Expr[] {f001778}); private static final Expr f001780 = Expr.makeLambda("xml", f001714, f001779); private static final Expr f001781 = Expr.makeLambda("XML", f000019, f001780); private static final Expr f001782 = Expr.makeLambda("d", f000226, f001781); private static final Expr f001783 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("Type", f001716), new SimpleImmutableEntry("attribute", f000278), new SimpleImmutableEntry("element", f001736), new SimpleImmutableEntry("emptyAttributes", f001737), new SimpleImmutableEntry("leaf", f001743), new SimpleImmutableEntry("render", f001776), new SimpleImmutableEntry("text", f001782) }); private static final Expr f001784 = Expr.makeRecordLiteral( new Entry[] { new SimpleImmutableEntry("Bool", f000053), new SimpleImmutableEntry("Double", f000055), new SimpleImmutableEntry("Function", f000073), new SimpleImmutableEntry("Integer", f000225), new SimpleImmutableEntry("JSON", f000790), new SimpleImmutableEntry("List", f001057), new SimpleImmutableEntry("Location", f001059), new SimpleImmutableEntry("Map", f001123), new SimpleImmutableEntry("Monoid", f001126), new SimpleImmutableEntry("Natural", f001226), new SimpleImmutableEntry("NonEmpty", f001391), new SimpleImmutableEntry("Operator", f001419), new SimpleImmutableEntry("Optional", f001527), new SimpleImmutableEntry("Text", f001708), new SimpleImmutableEntry("XML", f001783) }); public static final Expr instance = f001784; } ================================================ FILE: modules/scala/src/main/scala/org/dhallj/syntax/package.scala ================================================ package org.dhallj import org.dhallj.core.DhallException.{ParsingFailure, ResolutionFailure} import org.dhallj.core.Expr import org.dhallj.core.typechecking.TypeCheckFailure import org.dhallj.imports.mini.Resolver import org.dhallj.parser.DhallParser package object syntax { implicit final class DhallStringOps(val value: String) extends AnyVal { def parseExpr: Either[ParsingFailure, Expr] = try { Right(DhallParser.parse(value)) } catch { case e: ParsingFailure => Left(e) } } implicit final class DhallExprOps(val expr: Expr) extends AnyVal { def apply(args: Expr*): Expr = Expr.makeApplication(expr, args.toArray) def typeCheck: Either[TypeCheckFailure, Expr] = try { Right(Expr.Util.typeCheck(expr)) } catch { case e: TypeCheckFailure => Left(e) } def diff(other: Expr): Option[(Option[Expr], Option[Expr])] = Option(Expr.Util.getFirstDiff(expr, other)).map(entry => (Option(entry.getKey), Option(entry.getValue))) def resolve: Either[ResolutionFailure, Expr] = try { Right(Resolver.resolve(expr)) } catch { case e: ResolutionFailure => Left(e) } } } ================================================ FILE: modules/scala-codec/src/main/scala/org/dhallj/codec/Decoder.scala ================================================ package org.dhallj.codec import cats.Traverse import cats.instances.either._ import cats.instances.vector._ import org.dhallj.core.Expr import org.dhallj.core.typechecking.TypeCheckFailure import org.dhallj.ast._ trait Decoder[A] { self => def decode(expr: Expr): Decoder.Result[A] def isValidType(typeExpr: Expr): Boolean /** * Can any value of this Dhall type be decoded into this Scala type? */ def isExactType(typeExpr: Expr): Boolean def map[B](f: A => B): Decoder[B] = new Decoder[B] { def decode(expr: Expr): Decoder.Result[B] = self.decode(expr).map(f) def isValidType(typeExpr: Expr): Boolean = self.isValidType(typeExpr) def isExactType(typeExpr: Expr): Boolean = self.isExactType(typeExpr) } } object Decoder { type Result[A] = Either[DecodingFailure, A] def apply[A](implicit A: Decoder[A]): Decoder[A] = A implicit val decodeLong: Decoder[Long] = new Decoder[Long] { def decode(expr: Expr): Result[Long] = expr.normalize match { case NaturalLiteral(v) => if (v <= Long.MaxValue) { Right(v.toLong) } else { Left(new DecodingFailure("Long", expr)) } case IntegerLiteral(v) => if (v <= Long.MaxValue && v >= Long.MinValue) { Right(v.toLong) } else { Left(new DecodingFailure("Long", expr)) } case other => Left(new DecodingFailure("Long", other)) } def isValidType(typeExpr: Expr): Boolean = typeExpr == Expr.Constants.NATURAL || typeExpr == Expr.Constants.INTEGER def isExactType(typeExpr: Expr): Boolean = false } implicit val decodeInt: Decoder[Int] = new Decoder[Int] { def decode(expr: Expr): Result[Int] = expr.normalize match { case NaturalLiteral(v) => if (v <= Int.MaxValue) { Right(v.toInt) } else { Left(new DecodingFailure("Int", expr)) } case IntegerLiteral(v) => if (v <= Int.MaxValue && v >= Int.MinValue) { Right(v.toInt) } else { Left(new DecodingFailure("Int", expr)) } case other => Left(new DecodingFailure("Int", other)) } def isValidType(typeExpr: Expr): Boolean = typeExpr == Expr.Constants.NATURAL || typeExpr == Expr.Constants.INTEGER def isExactType(typeExpr: Expr): Boolean = false } implicit val decodeBigInt: Decoder[BigInt] = new Decoder[BigInt] { def decode(expr: Expr): Result[BigInt] = expr.normalize match { case NaturalLiteral(v) => Right(v) case IntegerLiteral(v) => Right(v) case other => Left(new DecodingFailure("BigInt", other)) } def isValidType(typeExpr: Expr): Boolean = typeExpr == Expr.Constants.NATURAL || typeExpr == Expr.Constants.INTEGER def isExactType(typeExpr: Expr): Boolean = typeExpr == Expr.Constants.INTEGER } implicit val decodeDouble: Decoder[Double] = new Decoder[Double] { def decode(expr: Expr): Result[Double] = expr.normalize match { case DoubleLiteral(v) => Right(v) case NaturalLiteral(v) => Right(v.toDouble) case IntegerLiteral(v) => Right(v.toDouble) case other => Left(new DecodingFailure("Double", other)) } def isValidType(typeExpr: Expr): Boolean = typeExpr == Expr.Constants.DOUBLE || typeExpr == Expr.Constants.NATURAL || typeExpr == Expr.Constants.INTEGER def isExactType(typeExpr: Expr): Boolean = isValidType(typeExpr) } implicit val decodeString: Decoder[String] = new Decoder[String] { def decode(expr: Expr): Result[String] = expr.normalize match { case TextLiteral((v, Vector())) => Right(v) case other => Left(new DecodingFailure("String", other)) } def isValidType(typeExpr: Expr): Boolean = typeExpr == Expr.Constants.TEXT def isExactType(typeExpr: Expr): Boolean = false } implicit val decodeBoolean: Decoder[Boolean] = new Decoder[Boolean] { def decode(expr: Expr): Result[Boolean] = expr.normalize match { case BoolLiteral(v) => Right(v) case other => Left(new DecodingFailure("Boolean", other)) } def isValidType(typeExpr: Expr): Boolean = typeExpr == Expr.Constants.BOOL def isExactType(typeExpr: Expr): Boolean = isValidType(typeExpr) } implicit def decodeOption[A: Decoder]: Decoder[Option[A]] = new Decoder[Option[A]] { def decode(expr: Expr): Result[Option[A]] = expr.normalize match { case Application(Expr.Constants.SOME, value) => Decoder[A].decode(value).map(Some(_)) case Application(Expr.Constants.NONE, elementType) if Decoder[A].isValidType(elementType) => Right(None) case other => Left(new DecodingFailure("Optional", other)) } def isValidType(typeExpr: Expr): Boolean = typeExpr match { case Application(Expr.Constants.OPTIONAL, elementType) => Decoder[A].isValidType(elementType) case _ => false } def isExactType(typeExpr: Expr): Boolean = typeExpr match { case Application(Expr.Constants.OPTIONAL, elementType) => Decoder[A].isExactType(elementType) case _ => false } } implicit def decodeVector[A: Decoder]: Decoder[Vector[A]] = new Decoder[Vector[A]] { def decode(expr: Expr): Result[Vector[A]] = expr.normalize match { case NonEmptyListLiteral(values) => Traverse[Vector].traverse(values)(Decoder[A].decode) case EmptyListLiteral(Application(Expr.Constants.LIST, elementType)) if Decoder[A].isValidType(elementType) => Right(Vector.empty) case other => Left(new DecodingFailure("Vector", other)) } def isValidType(typeExpr: Expr): Boolean = typeExpr match { case Application(Expr.Constants.LIST, elementType) => Decoder[A].isValidType(elementType) case _ => false } def isExactType(typeExpr: Expr): Boolean = typeExpr match { case Application(Expr.Constants.LIST, elementType) => Decoder[A].isExactType(elementType) case _ => false } } implicit def decodeList[A: Decoder]: Decoder[List[A]] = new Decoder[List[A]] { def decode(expr: Expr): Result[List[A]] = expr.normalize match { case NonEmptyListLiteral(values) => Traverse[Vector].traverse(values)(Decoder[A].decode).map(_.toList) case EmptyListLiteral(Application(Expr.Constants.LIST, elementType)) if Decoder[A].isValidType(elementType) => Right(Nil) case other => Left(new DecodingFailure("List", other)) } def isValidType(typeExpr: Expr): Boolean = typeExpr match { case Application(Expr.Constants.LIST, elementType) => Decoder[A].isValidType(elementType) case _ => false } def isExactType(typeExpr: Expr): Boolean = typeExpr match { case Application(Expr.Constants.LIST, elementType) => Decoder[A].isExactType(elementType) case _ => false } } implicit def decodeFunction1[A: Encoder, B: Decoder]: Decoder[A => B] = new Decoder[A => B] { def decode(expr: Expr): Result[A => B] = expr.normalize match { case f @ Lambda(_, inputType, result) if Encoder[A].isExactType(inputType) => val inferredType = try { Right(Expr.Util.typeCheck(f)) } catch { case e: TypeCheckFailure => Left(new DecodingFailure("Function1", f)) } inferredType.flatMap { case Pi(_, inputType, outputType) if Encoder[A].isExactType(inputType) && Decoder[B].isExactType(outputType) => // This is still a little hand-wavy. Right((a: A) => Decoder[B].decode(Application(f, Encoder[A].encode(a)).normalize).toOption.get) case _ => Left(new DecodingFailure("Function1", f)) } case other => Left(new DecodingFailure("Function1", other)) } def isValidType(typeExpr: Expr): Boolean = typeExpr match { case Pi(_, inputType, outputType) => Encoder[A].isExactType(inputType) && Decoder[B].isValidType(outputType) case _ => false } def isExactType(typeExpr: Expr): Boolean = typeExpr match { case Pi(_, inputType, outputType) => Encoder[A].isExactType(inputType) && Decoder[B].isExactType(outputType) case _ => false } } } ================================================ FILE: modules/scala-codec/src/main/scala/org/dhallj/codec/DecodingFailure.scala ================================================ package org.dhallj.codec import org.dhallj.core.{DhallException, Expr} class DecodingFailure(val target: String, val value: Expr) extends DhallException(s"Error decoding $target") { final override def fillInStackTrace(): Throwable = this } ================================================ FILE: modules/scala-codec/src/main/scala/org/dhallj/codec/Encoder.scala ================================================ package org.dhallj.codec import org.dhallj.core.Expr import org.dhallj.ast._ trait Encoder[A] { self => def encode(value: A, target: Option[Expr]): Expr def dhallType(value: Option[A], target: Option[Expr]): Expr final def encode(value: A): Expr = encode(value, None) final def isExactType(typeExpr: Expr): Boolean = typeExpr == dhallType(None, Some(typeExpr)) def contramap[B](f: B => A): Encoder[B] = new Encoder[B] { def encode(value: B, target: Option[Expr]): Expr = self.encode(f(value), target) def dhallType(value: Option[B], target: Option[Expr]): Expr = self.dhallType(value.map(f), target) } } object Encoder { def apply[A](implicit A: Encoder[A]): Encoder[A] = A implicit val encodeLong: Encoder[Long] = new Encoder[Long] { def encode(value: Long, target: Option[Expr]): Expr = { val asBigInt = BigInt(value) target match { case Some(Expr.Constants.INTEGER) => IntegerLiteral(asBigInt) case _ => NaturalLiteral(asBigInt).getOrElse(IntegerLiteral(asBigInt)) } } def dhallType(value: Option[Long], target: Option[Expr]): Expr = target match { case Some(Expr.Constants.INTEGER) => Expr.Constants.INTEGER case _ => value match { case Some(v) if v < 0 => Expr.Constants.INTEGER case _ => Expr.Constants.NATURAL } } } implicit val encodeInt: Encoder[Int] = encodeLong.contramap(_.toLong) implicit val encodeBigInt: Encoder[BigInt] = new Encoder[BigInt] { def encode(value: BigInt, target: Option[Expr]): Expr = target match { case Some(Expr.Constants.INTEGER) => IntegerLiteral(value) case _ => NaturalLiteral(value).getOrElse(IntegerLiteral(value)) } def dhallType(value: Option[BigInt], target: Option[Expr]): Expr = target match { case Some(Expr.Constants.INTEGER) => Expr.Constants.INTEGER case _ => value match { case Some(v) if v < 0 => Expr.Constants.INTEGER case _ => Expr.Constants.NATURAL } } } implicit val encodeDouble: Encoder[Double] = new Encoder[Double] { def encode(value: Double, target: Option[Expr]): Expr = DoubleLiteral(value) def dhallType(value: Option[Double], target: Option[Expr]): Expr = Expr.Constants.DOUBLE } implicit val encodeString: Encoder[String] = new Encoder[String] { def encode(value: String, target: Option[Expr]): Expr = TextLiteral(value) def dhallType(value: Option[String], target: Option[Expr]): Expr = Expr.Constants.TEXT } implicit val encodeBoolean: Encoder[Boolean] = new Encoder[Boolean] { def encode(value: Boolean, target: Option[Expr]): Expr = BoolLiteral(value) def dhallType(value: Option[Boolean], target: Option[Expr]): Expr = Expr.Constants.BOOL } implicit def encodeOption[A: Encoder]: Encoder[Option[A]] = new Encoder[Option[A]] { def encode(value: Option[A], target: Option[Expr]): Expr = target match { case Some(Application(Expr.Constants.OPTIONAL, elementType)) => value match { case Some(a) => Application(Expr.Constants.SOME, Encoder[A].encode(a, Some(elementType))) case None => Application(Expr.Constants.NONE, Encoder[A].dhallType(None, Some(elementType))) } case _ => value match { case Some(a) => Application(Expr.Constants.SOME, Encoder[A].encode(a)) case None => Application(Expr.Constants.NONE, Encoder[A].dhallType(None, None)) } } def dhallType(value: Option[Option[A]], target: Option[Expr]): Expr = { val targetElementType = target.flatMap { case Application(Expr.Constants.OPTIONAL, elementType) => Some(elementType) case _ => None } Application(Expr.Constants.OPTIONAL, Encoder[A].dhallType(value.flatten, targetElementType)) } } implicit def encodeVector[A: Encoder]: Encoder[Vector[A]] = new Encoder[Vector[A]] { def encode(value: Vector[A], target: Option[Expr]): Expr = { val tpe = dhallElementType(Some(value), target) value match { case Vector() => EmptyListLiteral(Application(Expr.Constants.LIST, tpe)) case h +: t => NonEmptyListLiteral( Encoder[A].encode(h, Some(tpe)), t.map(Encoder[A].encode(_, Some(tpe))) ) } } private def dhallElementType(value: Option[Vector[A]], target: Option[Expr]): Expr = { val targetElementType = target.flatMap { case Application(Expr.Constants.LIST, elementType) => Some(elementType) case _ => None } value match { case None => Encoder[A].dhallType(None, targetElementType) case Some(Vector()) => Encoder[A].dhallType(None, targetElementType) case Some(h +: t) => t.foldLeft(Encoder[A].dhallType(Some(h), targetElementType)) { case (acc, a) => Encoder[A].dhallType(Some(a), Some(acc)) } } } def dhallType(value: Option[Vector[A]], target: Option[Expr]): Expr = Application(Expr.Constants.LIST, dhallElementType(value, target)) } implicit def encodeList[A: Encoder]: Encoder[List[A]] = encodeVector[A].contramap(_.toVector) } ================================================ FILE: modules/scala-codec/src/main/scala/org/dhallj/codec/syntax/package.scala ================================================ package org.dhallj.codec import org.dhallj.core.Expr package object syntax { implicit final class DhallCodecAnyOps[A](val value: A) extends AnyVal { def asExpr(implicit A: Encoder[A]): Expr = A.encode(value) } implicit final class DhallCodecExprOps(val expr: Expr) extends AnyVal { def as[A: Decoder]: Decoder.Result[A] = Decoder[A].decode(expr) } } ================================================ FILE: modules/scala-codec/src/test/scala/org/dhallj/codec/DecoderSuite.scala ================================================ package org.dhallj.codec import munit.FunSuite import org.dhallj.codec.syntax._ import org.dhallj.syntax._ class DecoderSuite extends FunSuite() { test("Decoder[Vector[String]] decodes non-empty List Text") { val Right(parsed) = """[ "abc" , "def" ] : List Text""".parseExpr assertEquals(parsed.as[Vector[String]], Right(Vector("abc", "def"))) } test("Decoder[Vector[String]] decodes empty List Text") { val Right(parsed) = "[] : List Text".parseExpr assertEquals(parsed.as[Vector[String]], Right(Vector.empty[String])) } test("Decoder[List[String]] decodes non-empty List Text") { val Right(parsed) = """[ "abc" , "def" ] : List Text""".parseExpr assertEquals(parsed.as[List[String]], Right(List("abc", "def"))) } test("Decoder[List[String]] decodes empty List Text") { val Right(parsed) = "[] : List Text".parseExpr assertEquals(parsed.as[List[String]], Right(List.empty[String])) } test("Decoder[Option[String]] decodes non-empty Optional Text") { val Right(parsed) = """Some "abc" : Optional Text""".parseExpr assertEquals(parsed.as[Option[String]], Right(Some("abc"))) } test("Decoder[Option[String]] decodes empty Optional Text") { val Right(parsed) = "None Text : Optional Text".parseExpr assertEquals(parsed.as[Option[String]], Right(Option.empty[String])) } } ================================================ FILE: modules/testing/src/main/scala/org/dhallj/testing/ArbitraryInstances.scala ================================================ package org.dhallj.testing import org.dhallj.ast._ import org.dhallj.core.{Expr, Operator} import org.scalacheck.{Arbitrary, Gen, Shrink} trait ArbitraryInstances { def genNameString: Gen[String] def genTextString: Gen[String] def defaultMaxDepth: Int = 3 def defaultMaxInterpolated: Int = 3 def defaultMaxFields: Int = 5 def genValidNameString: Gen[String] = genNameString.map(_.replace("`", "")).map { case "" => "x" case other => other } def isValidName(name: String): Boolean = List('\u0000', '\u0001', '`').forall(c => name.indexOf(c.toInt) == -1) def genIdentifier: Gen[Expr] = for { name <- genValidNameString index <- Gen.oneOf(Gen.const(0L), Arbitrary.arbitrary[Long].map(_.abs)) } yield Identifier(name, index) val genOperator: Gen[Operator] = Gen.oneOf(Operator.values()) def genForType(tpe: Expr): Option[Gen[Expr]] = tpe match { case Expr.Constants.NATURAL => Some(Arbitrary.arbitrary[BigInt].map(value => NaturalLiteral(value.abs).get)) case Expr.Constants.INTEGER => Some(Arbitrary.arbitrary[BigInt].map(IntegerLiteral(_))) case Expr.Constants.DOUBLE => Some(Arbitrary.arbitrary[Double].map(DoubleLiteral(_))) case Expr.Constants.BOOL => Some( Arbitrary.arbitrary[Boolean].map(value => if (value) Expr.Constants.TRUE else Expr.Constants.FALSE) ) case Application(Expr.Constants.LIST, elementType) => genForType(elementType).map( Gen .buildableOf[Vector[Expr], Expr](_) .map(values => if (values.isEmpty) { EmptyListLiteral(Application(Expr.Constants.LIST, elementType)) } else { NonEmptyListLiteral(values.head, values.tail) } ) ) case Application(Expr.Constants.OPTIONAL, elementType) => genForType(elementType).map(genElementType => Gen.oneOf( genElementType.map(Application(Expr.Constants.SOME, _)), Gen.const(Application(Expr.Constants.NONE, elementType)) ) ) case Expr.Constants.TEXT => Some(genText(defaultMaxDepth)) case RecordType(fields) => Some( fields .foldLeft(Gen.const(Map.empty[String, Expr])) { case (acc, (name, tpe)) => genForType(tpe) match { case Some(genFieldType) => acc.flatMap(m => genFieldType.map(m.updated(name, _))) case None => acc } } .map(RecordLiteral(_)) ) case _ => None } def genText(maxDepth: Int): Gen[Expr] = if (maxDepth == 0) genTextString.map(TextLiteral(_)) else { Gen.oneOf( genTextString.map(TextLiteral(_)), for { first <- genTextString rest <- Gen.buildableOfN[Vector[(Expr, String)], (Expr, String)]( defaultMaxInterpolated, genText(maxDepth - 1).flatMap(interpolated => genTextString.map((interpolated, _))) ) } yield TextLiteral(first, rest) ) } val genType: Gen[Expr] = Gen.oneOf( List( Expr.Constants.NATURAL, Expr.Constants.INTEGER, Expr.Constants.DOUBLE, Expr.Constants.BOOL, Expr.Constants.TEXT ) ) def genType(maxDepth: Int): Gen[Expr] = if (maxDepth == 0) genType else { Gen.oneOf( genType, genType(maxDepth - 1).map(Application(Expr.Constants.LIST, _)), genType(maxDepth - 1).map(Application(Expr.Constants.OPTIONAL, _)), Gen .buildableOfN[Vector[(String, Expr)], (String, Expr)]( defaultMaxFields, genValidNameString.flatMap(name => genType(maxDepth - 1).map((name, _))) ) .map(fields => RecordType(fields.toMap)), Gen .buildableOfN[Vector[(String, Option[Expr])], (String, Option[Expr])]( defaultMaxFields, genValidNameString.flatMap(name => Gen.option(genType(maxDepth - 1)).map((name, _))) ) .map(fields => UnionType(fields.toMap)) ) } implicit val arbitraryWellTypedExpr: Arbitrary[WellTypedExpr] = Arbitrary( Gen .oneOf( genType(defaultMaxDepth), genType(defaultMaxDepth).flatMap(tpe => genForType(tpe).getOrElse(genType(defaultMaxDepth))) /* TODO: We should test type annotations. genType(defaultMaxDepth).flatMap(tpe => genForType(tpe).map(_.map(expr => Annotated(expr, tpe))).getOrElse(genType(defaultMaxDepth)) ) */ ) .map(WellTypedExpr(_)) ) def genExpr(maxDepth: Int): Gen[Expr] = if (maxDepth == 0) { arbitraryWellTypedExpr.arbitrary.map(_.value) } else { Gen.oneOf( arbitraryWellTypedExpr.arbitrary.map(_.value), for { operator <- genOperator lhs <- genExpr(maxDepth - 1) rhs <- genExpr(maxDepth - 1) } yield Expr.makeOperatorApplication(operator, lhs, rhs), for { f <- genExpr(maxDepth - 1) arg <- genExpr(maxDepth - 1) } yield Expr.makeApplication(f, arg) ) } implicit val arbitraryExpr: Arbitrary[Expr] = Arbitrary(genExpr(defaultMaxDepth)) implicit val shrinkExpr: Shrink[Expr] = Shrink { case NaturalLiteral(value) => Shrink.shrink(value).flatMap(value => NaturalLiteral(value.abs)) case IntegerLiteral(value) => Shrink.shrink(value).map(IntegerLiteral(_)) case DoubleLiteral(value) => Shrink.shrink(value).map(DoubleLiteral(_)) case RecordLiteral(fields) => safeFieldsShrink.shrink(fields).map(RecordLiteral(_)) case RecordType(fields) => safeFieldsShrink.shrink(fields).map(RecordType(_)) case UnionType(fields) => safeOptionFieldsShrink.shrink(fields).map(UnionType(_)) case TextLiteral(first, rest) => Shrink.shrink(first).zip(Shrink.shrink(rest)).map { case (shrunkFirst, shrunkRest) => TextLiteral(shrunkFirst, shrunkRest) } case Application(Expr.Constants.SOME, arg) => Shrink.shrink(arg).map(Application(Expr.Constants.SOME, _)) case NonEmptyListLiteral(values) => Shrink.shrink(values).filter(_.nonEmpty).map(values => NonEmptyListLiteral(values.head, values.tail)) // The following match doesn't preserve type, but is useful for isolating parsing issues for now. case OperatorApplication(_, lhs, rhs) => Stream(lhs, rhs) case other => Stream.empty } implicit val shrinkWellTypedExpr: Shrink[WellTypedExpr] = Shrink.xmap[Expr, WellTypedExpr](WellTypedExpr(_), _.value) val safeNameShrink: Shrink[String] = Shrink(name => name.inits.toStream.init) lazy val safeFieldsShrink: Shrink[Map[String, Expr]] = Shrink.shrinkContainer2[Map, String, Expr]( implicitly, Shrink.shrinkTuple2(safeNameShrink, shrinkExpr), implicitly ) lazy val safeOptionFieldsShrink: Shrink[Map[String, Option[Expr]]] = Shrink.shrinkContainer2[Map, String, Option[Expr]]( implicitly, Shrink.shrinkTuple2(safeNameShrink, Shrink.shrinkOption(shrinkExpr)), implicitly ) } ================================================ FILE: modules/testing/src/main/scala/org/dhallj/testing/WellTypedExpr.scala ================================================ package org.dhallj.testing import org.dhallj.core.Expr case class WellTypedExpr(value: Expr) ================================================ FILE: modules/testing/src/main/scala/org/dhallj/testing/package.scala ================================================ package org.dhallj.testing import org.scalacheck.Gen package object instances extends ArbitraryInstances { def genNameString: Gen[String] = Gen.alphaStr def genTextString: Gen[String] = Gen.alphaStr } ================================================ FILE: modules/yaml/src/main/java/org/dhallj/yaml/YamlContext.java ================================================ package org.dhallj.yaml; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; interface YamlContext { void add(String key); void add(Object value); Object getResult(); } final class RootContext implements YamlContext { private final Object result; RootContext(Object result) { this.result = result; } public void add(String key) {} public void add(Object value) {} public Object getResult() { return this.result; } } final class ObjectContext implements YamlContext { private String key = null; private final Map fields = new LinkedHashMap<>(); private final boolean skipNulls; public ObjectContext(boolean skipNulls) { this.skipNulls = skipNulls; } public void add(String key) { this.key = key; } public void add(Object value) { if (!skipNulls || value != null) { this.fields.put(this.key, value); } else { this.key = null; } } public Object getResult() { return this.fields; } } final class ArrayContext implements YamlContext { private final List values = new ArrayList<>(); public void add(String key) {} public void add(Object value) { this.values.add(value); } public Object getResult() { return this.values; } } ================================================ FILE: modules/yaml/src/main/java/org/dhallj/yaml/YamlConverter.java ================================================ package org.dhallj.yaml; import org.dhallj.core.Expr; import org.dhallj.core.converters.JsonConverter; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.DumperOptions; public class YamlConverter { private static final DumperOptions defaultOptions = new DumperOptions(); static { defaultOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); } public static final String toYamlString(Expr expr) { return toYamlString(expr, defaultOptions, true); } public static final String toYamlString(Expr expr, DumperOptions options) { return toYamlString(expr, options, true); } public static final String toYamlString(Expr expr, boolean skipNulls) { return toYamlString(expr, defaultOptions, skipNulls); } public static final String toYamlString(Expr expr, DumperOptions options, boolean skipNulls) { YamlHandler handler = new YamlHandler(skipNulls); boolean wasConverted = expr.accept(new JsonConverter(handler)); if (wasConverted) { Yaml yaml = new Yaml(options); return yaml.dump(handler.getResult()); } else { return null; } } } ================================================ FILE: modules/yaml/src/main/java/org/dhallj/yaml/YamlHandler.java ================================================ package org.dhallj.yaml; import java.math.BigInteger; import java.util.ArrayDeque; import java.util.Deque; import org.dhallj.core.converters.JsonHandler; public class YamlHandler implements JsonHandler { private final Deque stack = new ArrayDeque<>(); private final boolean skipNulls; public YamlHandler(boolean skipNulls) { this.skipNulls = skipNulls; } public YamlHandler() { this(true); } private final void addValue(Object value) { if (stack.isEmpty()) { stack.push(new RootContext(value)); } else { stack.peek().add(value); } } public Object getResult() { return this.stack.pop().getResult(); } public void onNull() { this.addValue(null); } public void onBoolean(boolean value) { this.addValue(value); } public void onNumber(BigInteger value) { this.addValue(value); } public void onDouble(double value) { this.addValue(value); } public void onString(String value) { this.addValue(value.replaceAll("\\\\n", "\n").replaceAll("\\\\\"", "\"")); } public void onArrayStart() { this.stack.push(new ArrayContext()); } public void onArrayEnd() { YamlContext current = this.stack.pop(); if (this.stack.isEmpty()) { this.stack.push(current); } else { this.stack.peek().add(current.getResult()); } } public void onArrayElementGap() {} public void onObjectStart() { this.stack.push(new ObjectContext(this.skipNulls)); } public void onObjectEnd() { YamlContext current = this.stack.pop(); if (this.stack.isEmpty()) { this.stack.push(current); } else { this.stack.peek().add(current.getResult()); } } public void onObjectField(String name) { this.stack.peek().add(name); } public void onObjectFieldGap() {} } ================================================ FILE: modules/yaml/src/test/scala/org/dhallj/yaml/YamlConverterSuite.scala ================================================ package org.dhallj.yaml import munit.ScalaCheckSuite import org.dhallj.ast._ import org.dhallj.core.Expr import org.dhallj.parser.DhallParser import org.scalacheck.Prop import org.yaml.snakeyaml.DumperOptions class YamlConverterSuite extends ScalaCheckSuite { property("convert integers") { Prop.forAll { (value: BigInt) => val asDhall = IntegerLiteral(value.underlying) Option(YamlConverter.toYamlString(asDhall)) == Some(value.toString + "\n") } } test("convert nested lists") { val expr = DhallParser.parse("[[]: List Bool]") assert(clue(Option(YamlConverter.toYamlString(expr))) == Some("- []\n")) } test("convert None") { val expr = DhallParser.parse("None Bool") assert(clue(Option(YamlConverter.toYamlString(expr))) == Some("null\n")) } test("convert Some") { val expr = DhallParser.parse("""Some "foo"""") assert(clue(Option(YamlConverter.toYamlString(expr))) == Some("foo\n")) } test("convert records") { val expr1 = DhallParser.parse("{foo = [{bar = [1]}, {bar = [1, 2, 3]}]}") val expected = """|foo: |- bar: | - 1 |- bar: | - 1 | - 2 | - 3 |""".stripMargin assert(clue(Option(YamlConverter.toYamlString(expr1))) == Some(expected)) } test("convert unions (nullary constructors)") { val expr1 = DhallParser.parse("[((\\(x: Natural) -> ) 1).bar]").normalize() assert(clue(Option(YamlConverter.toYamlString(expr1))) == Some("- bar\n")) } test("convert unions") { val expr1 = DhallParser.parse("[.foo True]").normalize() assert(clue(Option(YamlConverter.toYamlString(expr1))) == Some("- true\n")) } test("convert text containing newlines") { val expr1 = DhallParser.parse(""" { a = "foo\nbar" } """).normalize() assert(clue(Option(YamlConverter.toYamlString(expr1))) == Some("a: |-\n foo\n bar\n")) } test("convert test containing quotes") { val expr1 = DhallParser.parse(""" { a = "\"" } """).normalize() val expr2 = DhallParser.parse(""" { a = "\"\n" } """).normalize() assert(clue(Option(YamlConverter.toYamlString(expr1))) == Some("a: '\"'\n")) assert(clue(Option(YamlConverter.toYamlString(expr2))) == Some("a: |\n \"\n")) } test("convert text containing newlines and respect SnakeYAML configuration") { val expr1 = DhallParser.parse(""" { a = "foo\nbar" } """).normalize() val expected = "\"a\": \"foo\\nbar\"\n" val options = new DumperOptions() options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED) assert(clue(Option(YamlConverter.toYamlString(expr1, options))) == clue(Some(expected))) } test("fail safely on unconvertible expressions") { val expr1 = Lambda("x", Expr.Constants.NATURAL, Identifier("x")) assert(Option(YamlConverter.toYamlString(expr1)) == None) } } ================================================ FILE: project/build.properties ================================================ sbt.version=1.5.5 ================================================ FILE: project/plugins.sbt ================================================ addSbtPlugin("com.codecommit" % "sbt-github-actions" % "0.13.0") addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.1.0") addSbtPlugin("com.eed3si9n" % "sbt-unidoc" % "0.4.3") addSbtPlugin("com.github.sbt" % "sbt-release" % "1.1.0") addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") addSbtPlugin("com.lightbend.sbt" % "sbt-java-formatter" % "0.6.1") addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.0.1") addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.6.3") addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.2") addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.9.6") addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.4.1") addSbtPlugin("dev.travisbrown" % "sbt-javacc" % "0.1.0") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.3") addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.9.1") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3") ================================================ FILE: scalastyle-config.xml ================================================ Circe Configuration FOR IF true all .+ ================================================ FILE: tests/src/main/scala/org/dhallj/tests/HaskellDhall.scala ================================================ package org.dhallj.tests import java.io.ByteArrayInputStream import java.nio.charset.StandardCharsets.UTF_8 import scala.sys.process._ import scala.util.Try class HaskellDhall {} object HaskellDhall { def isAvailable(): Boolean = Try(Process("dhall --version").lineStream.head.split("\\.")).toOption.exists(_.length == 3) def hash(input: String): String = { val stream = new ByteArrayInputStream(input.getBytes(UTF_8)) Process("dhall hash").#<(stream).lineStream.head.substring(7) } def hashFromPath(path: String): String = { Process(s"dhall hash --file $path").lineStream.head.substring(7) } } ================================================ FILE: tests/src/main/scala/org/dhallj/tests/acceptance/AcceptanceFailureSuite.scala ================================================ package org.dhallj.tests.acceptance import org.dhallj.core.Expr import org.dhallj.core.binary.Decode.decode import org.dhallj.parser.DhallParser import scala.reflect.ClassTag abstract class AcceptanceFailureSuite[A, E <: Throwable: ClassTag] extends AcceptanceSuite { override def isInputFileName(fileName: String): Boolean = fileName.endsWith(".dhall") override def makeName(inputFileName: String): String = inputFileName.dropRight(6) def loadInput(input: Array[Byte]): A testInputs .map { case (name, path) => (name, readBytes(path)) } .foreach { case (name, input) => test(name) { intercept[E](loadInput(input)) } } } class ParsingFailureSuite(val base: String) extends AcceptanceFailureSuite[Expr, Exception] { def loadInput(input: Array[Byte]): Expr = DhallParser.parse(new String(input)) } class TypeCheckingFailureSuite(val base: String) extends AcceptanceFailureSuite[Expr, RuntimeException] { def loadInput(input: Array[Byte]): Expr = Expr.Util.typeCheck(DhallParser.parse(new String(input))) } class BinaryDecodingFailureSuite(val base: String) extends AcceptanceFailureSuite[Expr, RuntimeException] { override def isInputFileName(fileName: String): Boolean = fileName.endsWith(".dhallb") def loadInput(input: Array[Byte]): Expr = decode(input) } ================================================ FILE: tests/src/main/scala/org/dhallj/tests/acceptance/AcceptanceSuccessSuite.scala ================================================ package org.dhallj.tests.acceptance import cats.effect.IO import cats.effect.unsafe.IORuntime import java.nio.file.{Files, Path, Paths} import org.dhallj.core.Expr import org.dhallj.core.binary.Decode.decode import org.dhallj.parser.DhallParser import org.dhallj.imports.{ImportCache, Resolver} import org.http4s.client._ import org.http4s.blaze.client._ import scala.concurrent.ExecutionContext trait SuccessSuite[A, B] extends AcceptanceSuite { self: Input[A] => def makeExpectedFilename(input: String): String final private def makeExpectedPath(inputPath: Path): Path = inputPath.resolveSibling(makeExpectedFilename(inputPath.getFileName.toString)) def transform(input: A): B def loadExpected(input: Array[Byte]): B def compare(result: B, expected: B): Boolean def testPairs: List[(String, Path, (String, B))] = testInputs.map { case (name, path) => (name, path, (readString(path), loadExpected(readBytes(makeExpectedPath(path))))) } testPairs.foreach { case (name, path, (input, expected)) => test(name) { assert(compare(clue(transform(parseInput(path.toString, clue(input)))), clue(expected))) } } } trait Input[A] { def parseInput(path: String, input: String): A } trait ParsingInput extends Input[Expr] { override def parseInput(path: String, input: String): Expr = DhallParser.parse(input) } trait CachedResolvingInput extends Input[Expr] { override def parseInput(path: String, input: String): Expr = { val p = Paths.get(path) val parsed = DhallParser.parse(new String(Files.readAllBytes(p))) if (parsed.isResolved) parsed else { implicit val runtime: IORuntime = cats.effect.unsafe.IORuntime.global BlazeClientBuilder[IO](ExecutionContext.global).resource .use { client => implicit val c: Client[IO] = client Resolver.resolveRelativeTo[IO](p.getParent)(parsed) } .unsafeRunSync() } } } trait ResolvingInput extends Input[Expr] { def parseInput(path: String, input: String): Expr = { val p = Paths.get(path) val parsed = DhallParser.parse(new String(Files.readAllBytes(p))) if (parsed.isResolved) parsed else { implicit val runtime: IORuntime = cats.effect.unsafe.IORuntime.global BlazeClientBuilder[IO](ExecutionContext.global).resource .use { client => implicit val c: Client[IO] = client Resolver.resolveRelativeTo[IO](new ImportCache.NoopImportCache[IO], new ImportCache.NoopImportCache[IO])( p.getParent )(parsed) } .unsafeRunSync() } } } abstract class ExprOperationAcceptanceSuite(transformation: Expr => Expr) extends SuccessSuite[Expr, Expr] { self: Input[Expr] => def makeExpectedFilename(input: String): String = input.dropRight(7) + "B.dhall" def transform(input: Expr): Expr = transformation(input) def loadExpected(input: Array[Byte]): Expr = DhallParser.parse(new String(input)) def compare(result: Expr, expected: Expr): Boolean = result.sameStructure(expected) && result.equivalent(expected) } class ParsingTypeCheckingSuite(val base: String, override val recurse: Boolean = false) extends ExprOperationAcceptanceSuite(Expr.Util.typeCheck(_)) with ParsingInput class TypeCheckingSuite(val base: String, override val recurse: Boolean = false) extends ExprOperationAcceptanceSuite(Expr.Util.typeCheck(_)) with ResolvingInput class AlphaNormalizationSuite(val base: String) extends ExprOperationAcceptanceSuite(_.alphaNormalize) with ParsingInput class NormalizationSuite(val base: String, override val recurse: Boolean = false) extends ExprOperationAcceptanceSuite(_.normalize) with CachedResolvingInput class NormalizationUSuite(val base: String) extends ExprOperationAcceptanceSuite(_.normalize) with ParsingInput class HashingSuite(val base: String, override val recurse: Boolean = false) extends SuccessSuite[Expr, String] with ResolvingInput { def makeExpectedFilename(input: String): String = input.dropRight(7) + "B.hash" def transform(input: Expr): String = input.normalize.alphaNormalize.hash def loadExpected(input: Array[Byte]): String = new String(input).trim.drop(7) def compare(result: String, expected: String): Boolean = result == expected } class ParsingSuite(val base: String) extends SuccessSuite[Expr, Array[Byte]] with ParsingInput { def makeExpectedFilename(input: String): String = input.dropRight(7) + "B.dhallb" def transform(input: Expr): Array[Byte] = input.getEncodedBytes def loadExpected(input: Array[Byte]): Array[Byte] = input def compare(result: Array[Byte], expected: Array[Byte]): Boolean = result.sameElements(expected) } class BinaryDecodingSuite(val base: String) extends SuccessSuite[Expr, Expr] with ParsingInput { def makeExpectedFilename(input: String): String = input.dropRight(8) + "B.dhall" override def isInputFileName(fileName: String): Boolean = fileName.endsWith("A.dhallb") override def parseInput(path: String, input: String): Expr = decode(readBytes(Paths.get(path))) def transform(input: Expr): Expr = input def loadExpected(input: Array[Byte]): Expr = DhallParser.parse(new String(input)) def compare(result: Expr, expected: Expr): Boolean = result.sameStructure(expected) && result.equivalent(expected) } ================================================ FILE: tests/src/main/scala/org/dhallj/tests/acceptance/AcceptanceSuite.scala ================================================ package org.dhallj.tests.acceptance import java.nio.file.{Files, Path, Paths} import munit.{FunSuite, Ignore, Slow} import scala.collection.JavaConverters._ trait AcceptanceSuite extends FunSuite { def prefix: String = "./dhall-lang/tests" def base: String def recurse: Boolean = false def isInputFileName(fileName: String): Boolean = fileName.endsWith("A.dhall") def makeName(inputFileName: String): String = inputFileName.dropRight(7) /** * Test names to ignore. */ def ignored: String => Boolean = Set.empty /** * Names of tests that are slow. */ def slow: Set[String] = Set.empty private def basePath: Path = Paths.get(s"$prefix/$base") /** * Returns a list of name-path pairs. */ def testInputs: List[(String, Path)] = ( if (this.recurse) Files.walk(basePath) else Files.list(basePath) ).iterator.asScala .map(path => (basePath.relativize(path).toString, path)) .flatMap { case (name, path) => if (isInputFileName(name)) { Some((makeName(name), path)) } else None } .toList .sortBy(_._2) final override def munitTests(): Seq[Test] = super .munitTests() .map(test => if (ignored(test.name)) test.tag(Ignore) else if (slow(test.name)) test.tag(Slow) else test) final protected def readString(path: Path): String = new String(readBytes(path)) final protected def readBytes(path: Path): Array[Byte] = Files.readAllBytes(path) } ================================================ FILE: tests/src/main/scala/org/dhallj/tests/acceptance/ImportResolutionSuite.scala ================================================ package org.dhallj.tests.acceptance import java.nio.file.{Files, Paths} import cats.effect.IO import cats.effect.unsafe.IORuntime import org.dhallj.core.Expr import org.dhallj.imports.{ImportCache, Resolver} import org.dhallj.parser.DhallParser import org.http4s.client._ import org.http4s.blaze.client._ import scala.concurrent.ExecutionContext.global import scala.io.Source class ImportResolutionSuite(val base: String) extends ExprOperationAcceptanceSuite(identity) with CachedResolvingInput with ImportResolutionHelpers { setEnv("DHALL_TEST_VAR", "6 * 7") //Yes, this is SUPER hacky but the JVM doesn't really support setting env vars override def parseInput(path: String, input: String): Expr = { val p = Paths.get(path) val parsed = DhallParser.parse(readString(p)) if (parsed.isResolved) parsed else { implicit val runtime: IORuntime = cats.effect.unsafe.IORuntime.global val cache = initializeCache BlazeClientBuilder[IO](global).resource .use { client => implicit val c: Client[IO] = client Resolver.resolveRelativeTo[IO](cache, new ImportCache.NoopImportCache[IO])(p.getParent)(parsed) } .unsafeRunSync() } } } // TODO: revisit this. class ImportResolutionFailureSuite(val base: String) extends AcceptanceSuite with ImportResolutionHelpers { override def isInputFileName(fileName: String): Boolean = fileName.endsWith(".dhall") override def makeName(inputFileName: String): String = inputFileName.dropRight(6) testInputs .map { case (name, path) => (name, path, readBytes(path)) } .foreach { case (name, path, input) => test(name) { intercept[RuntimeException](parseInput(path.toString, new String(input))) } } setEnv("DHALL_TEST_VAR", "6 * 7") //Yes, this is SUPER hacky but the JVM doesn't really support setting env vars def parseInput(path: String, input: String): Expr = { val p = Paths.get(path) val parsed = DhallParser.parse(readString(p)) if (parsed.isResolved) parsed else { implicit val runtime: IORuntime = cats.effect.unsafe.IORuntime.global val cache = initializeCache BlazeClientBuilder[IO](global).resource .use { client => implicit val c: Client[IO] = client Resolver.resolveRelativeTo[IO](cache, new ImportCache.NoopImportCache[IO])(p.getParent)(parsed) } .unsafeRunSync() } } } trait ImportResolutionHelpers { protected def initializeCache: ImportCache[IO] = { implicit val runtime: IORuntime = cats.effect.unsafe.IORuntime.global val dir = Files.createTempDirectory("dhallj") val cache = ImportCache[IO](dir).unsafeRunSync().get Source .fromResource("tests/import/cache/dhall") .getLines() .foreach { p => val content = Files.readAllBytes(Paths.get(s"dhall-lang/tests/import/cache/dhall/$p")) Files.write(dir.resolve(p), content) } cache } protected def setEnv(key: String, value: String): Unit = { val field = System.getenv().getClass.getDeclaredField("m") field.setAccessible(true) val map = field.get(System.getenv()).asInstanceOf[java.util.Map[java.lang.String, java.lang.String]] map.put(key, value) } } ================================================ FILE: tests/src/test/resources/learndhall.dhall ================================================ -- Single-line comment {- Multi-line comment Unicode is fine 🙂 This file is a valid Dhall expression that evaluates to a large record collecting the results of each step. You can view the results by interpreting the file: $ dhall --file learndhall.dhall {- Comments can be nested -} -} let greeting = "Hello, world!" let fruits = "🍋🍓🍍🍉🍌" let interpolation = "Enjoy some delicious fruit: ${fruits}" let multilineText {- Inline comments work, too -} = '' Leading whitespace is stripped from multi-line text literals. That means you can freely indent or dedent a text literal without changing the result. Relative indentation within the literal is still preserved. Other than that, the text literal is preserved verbatim, similar to a "literal" YAML multiline string. '' let bool = True -- Type annotations on bindings are optional, but helpful, so we'll use them let annotation : Bool = True let renderedBool : Text = if bool then "True" else "False" -- Natural numbers are non-negative and are unsigned let naturalNumber : Natural = 42 -- Integers may be negative, but require an explicit sign, even if positive let positiveInteger : Integer = +1 let negativeInteger : Integer = -12 let pi : Double = 3.14159265359 {- You can use a wider character range for identifiers (such as quotation marks and whitespace) if you quote them using backticks -} let `Avogadro's Number` : Double = 6.0221409e+23 let origin : { x : Double, y : Double } = { x = 0.0, y = 0.0 } let somePrimes : List Natural = [ 2, 3, 5, 7, 11 ] {- A schema is the same thing as a type Types begin with an uppercase letter by convention, but this convention is not enforced -} let Profile : Type = { person : { name : Text , age : Natural } , address : { country : Text , state : Text , city : Text } } let john : Profile = { person = { name = "John Doe" , age = 67 } , address = { country = "United States" , state = "Pennsylvania" , city = "Philadelphia" } } let philadelphia : Text = john.address.city {- Enum alternatives also begin with an uppercase letter by convention. This convention is not enforced -} let DNA : Type = < Adenine | Cytosine | Guanine | Thymine > let dnaSequence : List DNA = [ DNA.Thymine, DNA.Guanine, DNA.Guanine ] let compactDNASequence : List DNA = let a = DNA.Adenine let c = DNA.Cytosine let g = DNA.Guanine let t = DNA.Thymine in [ c, t, t, a, t, c, g, g, c ] -- You can transform enums by providing a record with one field per alternative let theLetterG : Text = merge { Adenine = "A" , Cytosine = "C" , Guanine = "G" , Thymine = "T" } DNA.Guanine let presentOptionalValue : Optional Natural = Some 1 let absentOptionalValue : Optional Natural = None Natural let points : List { x : Double, y : Double } = [ { x = 1.1, y = -4.2 } , { x = 4.4, y = -3.0 } , { x = 8.2, y = -5.5 } ] {- `Natural -> List Natural` is the type of a function whose input type is a `Natural` and whose output type is a `List Natural` All functions in Dhall are anonymous functions (a.k.a. "lambdas"), which you can optionally give a name For example, the following function is equivalent to this Python code: lambda n : [ n, n + 1 ] ... and this JavaScript code: function (n) { return [ n, n + 1 ]; } -} let exampleFunction : Natural -> List Natural = \(n : Natural) -> [ n, n + 1 ] -- Dhall also supports Unicode syntax, but this tutorial will stick to ASCII let unicodeFunction : Natural → List Natural = λ(n : Natural) → [ n, n + 1 ] -- You don't need to parenthesize function arguments let exampleFunctionApplication : List Natural = exampleFunction 2 let functionOfMultipleArguments : Natural -> Natural -> List Natural = \(x : Natural) -> \(y : Natural) -> [ x, y ] let functionAppliedToMultipleArguments : List Natural = functionOfMultipleArguments 2 3 {- Same as `exampleFunction` except we gave the function's input type a name: "n" -} let namedArgumentType : forall (n : Natural) -> List Natural = \(n : Natural) -> [ n, n + 1 ] {- If you name a function's input type, you can use that name later within the same type This lets you write a function that works for more than one type of input (a.k.a. a "polymorphic" function) -} let duplicate : forall (a : Type) -> a -> List a = \(a : Type) -> \(x : a) -> [ x, x ] let duplicatedNumber : List Natural = duplicate Natural 2 let duplicatedBool : List Bool = duplicate Bool False {- The language also has some built-in polymorphic functions, such as: List/head : forall (a : Type) -> List a -> Optional a -} let firstPrime : Optional Natural = List/head Natural somePrimes let functionOfARecord : { x : Natural, y : Natural } -> List Natural = \(args : { x : Natural, y : Natural }) -> [ args.x, args.y ] let functionAppliedToARecord : List Natural = functionOfARecord { x = 2, y = 5 } {- All type conversions are explicit `Natural/show` is a built-in function of the following type: Natural/show : Natural -> Text ... that converts `Natural` numbers to their `Text` representation -} let typeConversion : Natural -> Text = \(age : Natural) -> "I am ${Natural/show age} years old!" -- A "template" is the same thing as a function whose output type is `Text` let mitLicense : { year : Natural, copyrightHolder : Text } -> Text = \(args : { year : Natural, copyrightHolder : Text }) -> '' Copyright ${Natural/show args.year} ${args.copyrightHolder} Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. '' -- Template instantiation is the same thing as function application let templatedLicense : Text = mitLicense { year = 2019, copyrightHolder = "Jane Smith" } {- You can import expressions by URL Also, like Bash, you can import code from your local filesystem (not shown) Security-conscious users can pin remotely-imported expressions by adding a semantic integrity check. The interpreter rejects any attempt to tamper with an expression pinned in this way. However, behavior-preserving refactors of imported content will not perturb the hash. Imported expressions pinned in this way are also locally cached in a content-addressable store (typically underneath `~/.cache/dhall`) -} {-let Natural/sum : List Natural -> Natural = https://prelude.dhall-lang.org/Natural/sum sha256:33f7f4c3aff62e5ecf4848f964363133452d420dcde045784518fb59fa970037-} let twentyEight : Natural = Natural/sum somePrimes -- A "package" is the same thing as a (possibly nested) record that you can import --let Prelude = https://prelude.dhall-lang.org/package.dhall let false : Bool = Prelude.Bool.not True -- You can import the raw contents of a file by adding `as Text` to an import --let sourceCode : Text = https://prelude.dhall-lang.org/Bool/not as Text -- You can import environment variables, too: --let presentWorkingDirectory = env:PWD as Text -- You can provide a fallback expression if an import fails --let home : Optional Text = Some env:HOME ? None Text -- Fallback expressions can contain alternative imports of their own {-let possiblyCustomPrelude = env:DHALL_PRELUDE ? https://prelude.dhall-lang.org/package.dhall-} {- Tie everything together by auto-generating configurations for 10 build users using the `generate` function: Prelude.List.generate : Natural -> forall (a : Type) -> (Natural -> a) -> List a -} let buildUsers = let makeUser = \(user : Text) -> let home = "/home/${user}" let privateKey = "${home}/.ssh/id_ed25519" let publicKey = "${privateKey}.pub" in { home = home , privateKey = privateKey , publicKey = publicKey } let buildUser = \(index : Natural) -> makeUser "build${Natural/show index}" let Config = { home : Text , privateKey : Text , publicKey : Text } in Prelude.List.generate 10 Config buildUser -- Present all of the results in a final record in { greeting = greeting , fruits = fruits , interpolation = interpolation , multilineText = multilineText , bool = bool , annotation = annotation , renderedBool = renderedBool , naturalNumber = naturalNumber , positiveInteger = positiveInteger , negativeInteger = negativeInteger , pi = pi , `Avogadro's Number` = `Avogadro's Number` , origin = origin , somePrimes = somePrimes , john = john , philadelphia = philadelphia , dnaSequence = dnaSequence , compactDNASequence = compactDNASequence , theLetterG = theLetterG , presentOptionalValue = presentOptionalValue , absentOptionalValue = absentOptionalValue , points = points , exampleFunction = exampleFunction , unicodeFunction = unicodeFunction , exampleFunctionApplication = exampleFunctionApplication , functionOfMultipleArguments = functionOfMultipleArguments , functionAppliedToMultipleArguments = functionAppliedToMultipleArguments , namedArgumentType = namedArgumentType , duplicate = duplicate , duplicatedNumber = duplicatedNumber , duplicatedBool = duplicatedBool , firstPrime = firstPrime , functionOfARecord = functionOfARecord , functionAppliedToARecord = functionAppliedToARecord , typeConversion = typeConversion , mitLicense = mitLicense , templatedLicense = templatedLicense , twentyEight = twentyEight , false = false , sourceCode = sourceCode , presentWorkingDirectory = presentWorkingDirectory , home = home , buildUsers = buildUsers } ================================================ FILE: tests/src/test/scala/org/dhallj/tests/BinaryDecodingTests.scala ================================================ package org.dhallj.tests import java.nio.file.{Files, Paths} import munit.FunSuite import org.dhallj.core.binary.Decode.decode import org.dhallj.parser.DhallParser.parse class BinaryDecodingTests extends FunSuite { test("Decode natural") { val bytes = load("nat.bin") val decoded = decode(bytes) val expected = parse("123") assert(decoded.equivalent(expected)) } test("Decode integer") { val bytes = load("int.bin") val decoded = decode(bytes) val expected = parse("-123") assert(decoded.equivalent(expected)) } test("Decode variable") { val bytes = load("var.bin") val decoded = decode(bytes) val expected = parse("x@1") assert(decoded.equivalent(expected)) } test("Decode anonymous variable") { val bytes = load("anon_var.bin") val decoded = decode(bytes) val expected = parse("_@1") assert(decoded.equivalent(expected)) } test("Decode builtin") { val bytes = load("builtin.bin") val decoded = decode(bytes) val expected = parse("Natural/even") assert(decoded.equivalent(expected)) } test("Decode True") { val bytes = load("true.bin") val decoded = decode(bytes) val expected = parse("True") assert(decoded.equivalent(expected)) } test("Decode False") { val bytes = load("false.bin") val decoded = decode(bytes) val expected = parse("False") assert(decoded.equivalent(expected)) } test("Decode empty list") { val bytes = load("empty_list.bin") val decoded = decode(bytes) val expected = parse("[] : List Natural") assert(decoded.equivalent(expected)) } test("Decode non-empty list") { val bytes = load("list.bin") val decoded = decode(bytes) val expected = parse("[1,2,3]") assert(decoded.equivalent(expected)) } test("Decode some") { val bytes = load("some.bin") val decoded = decode(bytes) val expected = parse("Some \"foo\"") assert(decoded.equivalent(expected)) } test("Decode lambda") { val bytes = load("lambda.bin") val decoded = decode(bytes) val expected = parse("\\(x: Text) -> x") assert(decoded.equivalent(expected)) } test("Decode anonymous lambda") { val bytes = load("anon_lambda.bin") val decoded = decode(bytes) val expected = parse("\\(_: Text) -> \"foo\"") assert(decoded.equivalent(expected)) } test("Decode pi") { val bytes = load("pi.bin") val decoded = decode(bytes) val expected = parse("forall (x: a) -> List x") assert(decoded.equivalent(expected)) } test("Decode anonymous pi") { val bytes = load("anon_pi.bin") val decoded = decode(bytes) val expected = parse("forall (_: a) -> List Text") assert(decoded.equivalent(expected)) } test("Decode function application") { val bytes = load("app.bin") val decoded = decode(bytes) val expected = parse("(\\(x: Text) -> x) \"foo\"") assert(decoded.equivalent(expected)) } test("Decode record type") { val bytes = load("record_type.bin") val decoded = decode(bytes) val expected = parse("{x: Text, y: Natural}") assert(decoded.equivalent(expected)) } test("Decode record literal") { val bytes = load("record_literal.bin") val decoded = decode(bytes) val expected = parse("{x = \"foo\", y = 3}") assert(decoded.equivalent(expected)) } test("Decode field access") { val bytes = load("field_access.bin") val decoded = decode(bytes) val expected = parse("{x: Text, y: Natural}.x") assert(decoded.equivalent(expected)) } test("Decode record projection") { val bytes = load("record_projection.bin") val decoded = decode(bytes) val expected = parse("{x: Text, y: Natural, z: Bool}.{x,y}") assert(decoded.equivalent(expected)) } test("Decode record projection by type") { val bytes = load("record_type_projection.bin") val decoded = decode(bytes) val expected = parse("{x = 1, y = 2, z = \"foo\"}.({x : Natural, y : Natural})") assert(decoded.equivalent(expected)) } test("Decode union") { val bytes = load("union.bin") val decoded = decode(bytes) val expected = parse("") assert(decoded.equivalent(expected)) } test("Decode merge") { val bytes = load("merge.bin") val decoded = decode(bytes) val expected = parse("merge { Foo = False, Bar = Natural/even } < Foo | Bar : Natural >") assert(decoded.equivalent(expected)) } test("Decode to map") { val bytes = load("to_map.bin") val decoded = decode(bytes) val expected = parse("toMap { bar = 2, foo = 1 }") assert(decoded.equivalent(expected)) } test("Decode assert") { val bytes = load("assert.bin") val decoded = decode(bytes) val expected = parse("assert : Natural/even 2 === True") assert(decoded.equivalent(expected)) } test("Decode let, 1 variable") { val bytes = load("let.bin") val decoded = decode(bytes) val expected = parse("let x = 1 in [x]") assert(decoded.equivalent(expected)) } test("Decode let, >1 variable") { val bytes = load("let.bin") val decoded = decode(bytes) val expected = parse("let x = 1 in [x]") assert(decoded.equivalent(expected)) } test("Decode type annotation") { val bytes = load("annotation.bin") val decoded = decode(bytes) val expected = parse("1 : Natural") assert(decoded.equivalent(expected)) } test("Decode env import") { val bytes = load("env_import.bin") val decoded = decode(bytes) val expected = parse("env:foo") assert(decoded.equivalent(expected)) } test("Decode local import") { val bytes = load("local_import.bin") val decoded = decode(bytes) val expected = parse("~/foo/bar") assert(decoded.equivalent(expected)) } test("Decode local import as text") { val bytes = load("local_import_text.bin") val decoded = decode(bytes) val expected = parse("../foo/bar as Text") assert(decoded.equivalent(expected)) } test("Decode local import as location") { val bytes = load("local_import_location.bin") val decoded = decode(bytes) val expected = parse("/foo/bar as Location") assert(decoded.equivalent(expected)) } test("Decode remote import") { val bytes = load("remote_import.bin") val decoded = decode(bytes) val expected = parse( "https://raw.githubusercontent.com/dhall-lang/dhall-lang/master/Prelude/package.dhall?foo=bar using { pwd = \"secret\"}" ) assert(decoded.equivalent(expected)) } test("Decode classpath import") { val bytes = parse("let x = classpath:/foo/bar.dhall in x").getEncodedBytes val decoded = decode(bytes) val expected = parse( "let x = classpath:/foo/bar.dhall in x" ) assert(decoded.equivalent(expected)) } test("Decode classpath import as text") { val bytes = parse("let x = classpath:/foo/bar.dhall as Text in x").getEncodedBytes val decoded = decode(bytes) val expected = parse( "let x = classpath:/foo/bar.dhall as Text in x" ) assert(decoded.equivalent(expected)) } private def load(resource: String): Array[Byte] = Files.readAllBytes(Paths.get(getClass.getResource(s"/binary/$resource").toURI)) } ================================================ FILE: tests/src/test/scala/org/dhallj/tests/ImportResolutionSuite.scala ================================================ package org.dhallj.tests import java.nio.file.{Path, Paths} import cats.effect.{IO, Resource} import cats.effect.unsafe.implicits.global import munit.FunSuite import org.dhallj.core.Expr import org.dhallj.imports.syntax._ import org.dhallj.parser.DhallParser.parse import org.http4s.client._ import org.http4s.blaze.client._ class ImportResolutionSuite extends FunSuite { implicit val client: Resource[IO, Client[IO]] = BlazeClientBuilder[IO](scala.concurrent.ExecutionContext.global).resource test("Resolve with different base directory") { //Path inside dhall-lang submodule val expr = parse("let x = ./success/prelude/Bool/and/0B.dhall in x") val expected = parse("Bool").normalize assert(resolveRelativeTo(Paths.get("./dhall-lang/tests/type-inference"))(expr) == expected) } private def resolveRelativeTo(relativeTo: Path)(e: Expr): Expr = client .use { c => implicit val http: Client[IO] = c e.resolveImportsRelativeTo[IO](relativeTo).map(_.normalize) } .unsafeRunSync() } ================================================ FILE: tests/src/test/scala/org/dhallj/tests/JsonConverterSuite.scala ================================================ package org.dhallj.tests import munit.FunSuite import org.dhallj.core.converters.JsonConverter import org.dhallj.parser.DhallParser class JsonConverterSuite extends FunSuite() { test("toCompactString correctly escapes text") { val expr = DhallParser.parse("""[{mapKey = " \n \$ \" ", mapValue = " \n \$ \" "}]""") assert(clue(JsonConverter.toCompactString(expr)) == clue("""{" \n $ \" ":" \n $ \" "}""")) } test("toCompactString correctly escapes text from Text/show") { val expr = DhallParser.parse("""Text/show "$100 #"""").normalize assert(clue(JsonConverter.toCompactString(expr)) == clue(""""\"\\u0024100 #\""""")) } test("toCompactString correctly escapes text from toMap") { val expr = DhallParser.parse("""toMap {` \n \$ \" ` = " \n \$ \" "}""").normalize assert(clue(JsonConverter.toCompactString(expr)) == clue("""{" \\n \\$ \\\" ":" \n $ \" "}""")) } test("toCompactString flattens toMap-formatted lists") { val expr = DhallParser.parse( """[{ mapKey = "foo", mapValue = 1}, {mapKey = "bar", mapValue = 2}]""" ) assert(clue(JsonConverter.toCompactString(expr)) == """{"foo":1,"bar":2}""") } test("toCompactString flattens toMap-typed empty lists") { val expr = DhallParser.parse( """[]: List { mapKey: Text, mapValue: Integer }""" ) assert(clue(JsonConverter.toCompactString(expr)) == """{}""") } test("toCompactString keeps last value in case of toMap-format duplicates") { val expr = DhallParser.parse( """[{ mapKey = "foo", mapValue = 100}, {mapKey = "foo", mapValue = 2 }]""" ) assert(clue(JsonConverter.toCompactString(expr)) == """{"foo":2}""") } test("toCompactString doesn't flatten near-toMap-format lists") { val expr = DhallParser.parse( """[{ mapKey = "foo", mapValue = 1}, {mapKey = "bar", other = 1, mapValue = 2}]""" ) val expected = """[{"mapKey":"foo","mapValue":1},{"mapKey":"bar","other":1,"mapValue":2}]""" assert(clue(JsonConverter.toCompactString(expr)) == clue(expected)) } } ================================================ FILE: tests/src/test/scala/org/dhallj/tests/MiscSuite.scala ================================================ package org.dhallj.tests import java.time.Instant import munit.ScalaCheckSuite import org.dhallj.ast._ import org.dhallj.core.binary.Decode import org.dhallj.core.Expr import org.dhallj.parser.DhallParser import org.scalacheck.{Arbitrary, Gen, Prop} class MiscSuite extends ScalaCheckSuite { case class AsciiPrintableString(value: String) implicit val arbitraryAsciiPrintableString: Arbitrary[AsciiPrintableString] = Arbitrary(Gen.alphaNumStr.map(AsciiPrintableString(_))) def checkParse(name: String, input: String, expected: Expr)(implicit loc: munit.Location): Unit = test(name)(assert(clue(DhallParser.parse(input)).equivalent(clue(expected)))) def checkBetaNormalization(name: String, input: String, expected: Expr)(implicit loc: munit.Location): Unit = test(name)(assert(clue(DhallParser.parse(input).normalize).equivalent(clue(expected)))) def parsesTo(input: String, expected: Expr): Boolean = DhallParser.parse(input).equivalent(expected) test("getFirstDiff should work for classpath imports") { assert( Option(Expr.Util.getFirstDiff(DhallParser.parse("classpath:/foo"), DhallParser.parse("classpath:/bar"))).nonEmpty ) } property("doubles")(Prop.forAll((value: Double) => parsesTo(value.toString, DoubleLiteral(value)))) property("naturals")(Prop.forAll((value: BigInt) => parsesTo(value.abs.toString, NaturalLiteral(value.abs).get))) property("strings")( Prop.forAll((value: AsciiPrintableString) => parsesTo(s""""${value.value}"""", TextLiteral(value.value))) ) property("round-trip instants")( Prop.forAll { (value: Instant) => val asString = value.toString val parts = asString.split("-") // We need a reasonable four-digit year. val cleaned = ( if (parts(0).isEmpty) { // If it's negative we just make something up. "2021" + "-" + parts.drop(2).mkString("-") } else if (parts(0).size != 4) { // If it's not four digits we just make something up. "1999" + "-" + parts.drop(1).mkString("-") } else { asString } // The ScalaCheck instance produces instants that can't be parsed. ).replaceAll("-02-29", "-02-28") val expected = Instant.parse(cleaned) val result = Decode.decode(DhallParser.parse(cleaned).getEncodedBytes).toString // Keep it low-tech. val resultString = result.substring(8, 18) + "T" + result.substring(46).takeWhile(_ != '}') + "Z" Instant.parse(resultString) == expected } ) } ================================================ FILE: tests/src/test/scala/org/dhallj/tests/PreludeSuite.scala ================================================ package org.dhallj.tests import java.nio.file.Paths import munit.{FunSuite, Ignore, Slow, TestOptions} import org.dhallj.imports.mini.Resolver import org.dhallj.parser.DhallParser import scala.io.Source class PreludeSuite extends FunSuite() { val haskellDhallIsAvailable = HaskellDhall.isAvailable() val preludeFiles = Source.fromResource(s"Prelude").getLines().toList.sorted.flatMap { case "Monoid" => Nil case "Monoid.dhall" => List("Prelude/Monoid.dhall") case "README.md" => Nil case "package.dhall" => List("Prelude/package.dhall") case other => Source.fromResource(s"Prelude/$other").getLines().toList.sorted.map { case file => s"Prelude/$other/$file" } } def slow: Set[String] = Set( "Prelude/JSON/render", "Prelude/JSON/renderAs", "Prelude/JSON/renderYAML", "Prelude/JSON/package.dhall", "Prelude/package.dhall" ) def checkHash(path: String)(implicit loc: munit.Location): Unit = { val content = Source.fromResource(path).getLines().mkString("\n") val parsed = DhallParser.parse(content) val resolved = Resolver.resolveFromResources(parsed, false, Paths.get(path), this.getClass.getClassLoader) val name = s"$path hash matches dhall hash" val testOptions: TestOptions = if (haskellDhallIsAvailable) { if (slow(path)) name.tag(Slow) else name } else name.tag(Ignore) lazy val expected = HaskellDhall.hashFromPath(s"dhall-lang/$path") test(testOptions)(assert(resolved.normalize.alphaNormalize.hash == clue(expected))) } preludeFiles.foreach(checkHash) } ================================================ FILE: tests/src/test/scala/org/dhallj/tests/ToStringSuite.scala ================================================ package org.dhallj.tests import munit.{Ignore, ScalaCheckSuite, Slow} import org.dhallj.core.{Expr, Operator} import org.dhallj.parser.DhallParser import org.dhallj.testing.WellTypedExpr import org.dhallj.testing.instances._ import org.scalacheck.Prop import scala.util.Try class ToStringSuite extends ScalaCheckSuite() { property("toString produces parseable code given well-typed values") { Prop.forAll((expr: WellTypedExpr) => DhallParser.parse(clue(expr.value.toString)) == expr.value) } property("toString produces parseable code") { Prop.forAll((expr: Expr) => DhallParser.parse(clue(expr.toString)) == expr) } property("toString produces parseable code for sequence of operators") { Prop.forAll { (ops: Vector[Operator]) => val x = Expr.makeIdentifier("x") val expr1 = ops.take(16).foldLeft(x) { case (acc, op) => Expr.makeOperatorApplication(op, x, acc) } val expr2 = ops.take(16).foldLeft(x) { case (acc, op) => Expr.makeOperatorApplication(op, acc, x) } DhallParser.parse(clue(expr1.toString)) == expr1 && DhallParser.parse(clue(expr2.toString)) == expr2 } } // TODO: make this pass (e.g. seed rBdMB22PVU38DRrcK3rrh1kEBDitFj8dL1tbCloBmgM=) property("toString doesn't introduce unnecessary parentheses".tag(Ignore)) { Prop.forAll { (expr: Expr) => val asString = expr.toString removeParentheses(asString).forall { smaller => !Try(DhallParser.parse(smaller)).toOption.exists(_ == expr) } } } test("Unnormalized Prelude should round-trip through toString".tag(Slow)) { import org.dhallj.syntax._ val Right(prelude) = "./dhall-lang/Prelude/package.dhall".parseExpr.flatMap(_.resolve) val Right(fromToString) = prelude.toString.parseExpr assert(prelude.hash.sameElements(fromToString.hash)) assert(prelude.diff(fromToString).isEmpty) } private def closingIndex(input: String, startIndex: Int): Int = { var i = startIndex + 1 var depth = 1 while (i < input.length && depth > 0) { if (input.charAt(i) == ')') { depth -= 1 } else if (input.charAt(i) == '(') { depth += 1 } i += 1 } i - 1 } // Assumes valid nesting private def removeParentheses(input: String, first: Int = 0): List[String] = { val nextIndex = input.indexOf("(", first) if (nextIndex < 0) { Nil } else { val end = closingIndex(input, nextIndex) val next = input.take(nextIndex) + input.substring(nextIndex + 1, end) + input.substring(end + 1) next :: removeParentheses(input, nextIndex + 1) } } } ================================================ FILE: tests/src/test/scala/org/dhallj/tests/acceptance/AcceptanceSuites.scala ================================================ package org.dhallj.tests.acceptance class NormalizationSimpleSuite extends NormalizationUSuite("normalization/success/simple") class NormalizationRegressionSuite extends NormalizationSuite("normalization/success/regression") class NormalizationUnitSuite extends NormalizationUSuite("normalization/success/unit") class NormalizationSimplificationsSuite extends NormalizationSuite("normalization/success/simplifications") class NormalizationOtherSuite extends NormalizationSuite("normalization/success") class NormalizationHaskellTutorialSuite extends NormalizationSuite("normalization/success/haskell-tutorial", true) class HashingSimpleSuite extends HashingSuite("semantic-hash/success/simple") class HashingSimplificationsSuite extends HashingSuite("semantic-hash/success/simplifications") class HashingOtherSuite extends HashingSuite("semantic-hash/success") class HashingHaskellTutorialSuite extends HashingSuite("semantic-hash/success/haskell-tutorial", true) class HashingPreludeSuite extends HashingSuite("semantic-hash/success/prelude", true) class AlphaNormalizationUnitSuite extends AlphaNormalizationSuite("alpha-normalization/success/unit") class AlphaNormalizationRegressionSuite extends AlphaNormalizationSuite("alpha-normalization/success/regression") class TypeCheckingSimpleSuite extends ParsingTypeCheckingSuite("type-inference/success/simple") class TypeCheckingUnitSuite extends ParsingTypeCheckingSuite("type-inference/success/unit") class TypeCheckingRegressionSuite extends TypeCheckingSuite("type-inference/success/regression") class TypeCheckingOtherSuite extends TypeCheckingSuite("type-inference/success") { override def slow = Set("prelude") // Depends on http://csrng.net/, which is rate-limited (and also currently entirely down). // Temporary workaround awaiting dhall-lang#1198 (prelude only). override def ignored = Set("CacheImports", "CacheImportsCanonicalize", "prelude") } class TypeCheckingFailureUnitSuite extends TypeCheckingFailureSuite("type-inference/failure/unit") class TypeCheckingPreludeSuite extends TypeCheckingSuite("type-inference/success/prelude", true) { // Temporary workaround awaiting dhall-lang#1198. override def ignored = _.startsWith("Monoid/") } class ParsingUnitSuite extends ParsingSuite("parser/success/unit") class ParsingTextSuite extends ParsingSuite("parser/success/text") class ParsingTimeSuite extends ParsingSuite("parser/success/time") class ParsingOtherSuite extends ParsingSuite("parser/success") class ParsingFailureUnitSuite extends ParsingFailureSuite("parser/failure/unit") { // TODO: Fix these WithPrecedenceN failures; these are known bugs. override def ignored = Set("WithPrecedence2", "WithPrecedence3") } class ParsingFailureSpacingSuite extends ParsingFailureSuite("parser/failure/spacing") class ParsingFailureTimeSuite extends ParsingFailureSuite("parser/failure/time") class ParsingFailureOtherSuite extends ParsingFailureSuite("parser/failure") { // We ignore "nonUtf8" because by the time we see a string in Java any non-UTF-8 characters have // been replaced. See `DhallParserSuite` for a non-ignored test that covers the same ground. override def ignored = Set("nonUtf8") } class BinaryDecodingUnitSuite extends BinaryDecodingSuite("binary-decode/success/unit") class BinaryDecodingImportsUnitSuite extends BinaryDecodingSuite("binary-decode/success/unit/imports") class BinaryDecodingFailureUnitSuite extends BinaryDecodingFailureSuite("binary-decode/failure/unit") class ImportResolutionSuccessSuite extends ImportResolutionSuite("import/success") class ImportResolutionSuccessUnitSuite extends ImportResolutionSuite("import/success/unit") { // TODO: fix this bug override def ignored = Set("DontCacheIfHash") } class ImportResolutionSuccessUnitAsLocationSuite extends ImportResolutionSuite("import/success/unit/asLocation") class ImportResolutionFailureUnitSuite extends ImportResolutionFailureSuite("import/failure/unit") class ImportResolutionFailureOtherSuite extends ImportResolutionFailureSuite("import/failure") ================================================ FILE: version.sbt ================================================ ThisBuild / version := "0.10.0-M2"