summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNadrieril2020-04-05 17:57:07 +0100
committerGitHub2020-04-05 17:57:07 +0100
commit7e977f282fb6a0eff0ef45738b9b5c98dc4c6fee (patch)
treead4249609707fd8720a44469152105c2f6a67c79
parent5a5aa49e64197899006751db72e404f4b2292d4e (diff)
parent820214615547101f8f2b5de209b5189968bddfee (diff)
Merge pull request #154 from Nadrieril/cleanup-api
Rewrite serde_dhall API
-rw-r--r--Cargo.lock155
-rw-r--r--README.md6
-rw-r--r--abnf_to_pest/Cargo.toml2
-rw-r--r--dhall/Cargo.toml3
-rw-r--r--dhall/README.md9
-rw-r--r--dhall/src/error/builder.rs2
-rw-r--r--dhall/src/error/mod.rs14
-rw-r--r--dhall/src/lib.rs120
-rw-r--r--dhall/src/semantics/builtins.rs105
-rw-r--r--dhall/src/semantics/mod.rs8
-rw-r--r--dhall/src/semantics/nze/env.rs6
-rw-r--r--dhall/src/semantics/nze/mod.rs8
-rw-r--r--dhall/src/semantics/nze/nir.rs140
-rw-r--r--dhall/src/semantics/nze/normalize.rs100
-rw-r--r--dhall/src/semantics/nze/var.rs4
-rw-r--r--dhall/src/semantics/parse.rs10
-rw-r--r--dhall/src/semantics/resolve/env.rs19
-rw-r--r--dhall/src/semantics/resolve/hir.rs28
-rw-r--r--dhall/src/semantics/resolve/mod.rs6
-rw-r--r--dhall/src/semantics/resolve/resolve.rs20
-rw-r--r--dhall/src/semantics/tck/env.rs8
-rw-r--r--dhall/src/semantics/tck/mod.rs6
-rw-r--r--dhall/src/semantics/tck/tir.rs13
-rw-r--r--dhall/src/semantics/tck/typecheck.rs55
-rw-r--r--dhall/src/syntax/ast/expr.rs16
-rw-r--r--dhall/src/syntax/ast/mod.rs2
-rw-r--r--dhall/src/syntax/ast/span.rs12
-rw-r--r--dhall/src/syntax/ast/visitor.rs4
-rw-r--r--dhall/src/syntax/binary/decode.rs20
-rw-r--r--dhall/src/syntax/binary/encode.rs12
-rw-r--r--dhall/src/syntax/binary/mod.rs4
-rw-r--r--dhall/src/syntax/text/parser.rs14
-rw-r--r--dhall/src/syntax/text/printer.rs6
-rw-r--r--dhall/src/tests.rs17
-rw-r--r--dhall/tests/version_numbers.rs4
-rw-r--r--dhall_proc_macros/Cargo.toml3
-rw-r--r--dhall_proc_macros/src/derive.rs19
-rw-r--r--dhall_proc_macros/tests/version_numbers.rs4
-rw-r--r--serde_dhall/Cargo.toml10
-rw-r--r--serde_dhall/src/deserialize.rs143
-rw-r--r--serde_dhall/src/error.rs40
-rw-r--r--serde_dhall/src/lib.rs316
-rw-r--r--serde_dhall/src/options.rs368
-rw-r--r--serde_dhall/src/serde.rs144
-rw-r--r--serde_dhall/src/static_type.rs119
-rw-r--r--serde_dhall/src/value.rs287
-rw-r--r--serde_dhall/tests/de.rs28
-rw-r--r--serde_dhall/tests/traits.rs6
-rw-r--r--serde_dhall/tests/version_numbers.rs17
-rw-r--r--test.dhall1
-rw-r--r--tests_buffer5
51 files changed, 1563 insertions, 905 deletions
diff --git a/Cargo.lock b/Cargo.lock
index b4230ff..e5a25e4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -14,11 +14,19 @@ version = "0.2.0"
dependencies = [
"abnf 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pretty 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
+name = "aho-corasick"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "annotate-snippets"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -147,6 +155,7 @@ dependencies = [
"serde_cbor 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "version-sync 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -158,6 +167,7 @@ dependencies = [
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "version-sync 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -174,6 +184,11 @@ dependencies = [
]
[[package]]
+name = "doc-comment"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "dtoa"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -384,6 +399,16 @@ dependencies = [
[[package]]
name = "idna"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "idna"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
@@ -636,6 +661,11 @@ dependencies = [
[[package]]
name = "percent-encoding"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -769,6 +799,14 @@ dependencies = [
[[package]]
name = "proc-macro2"
+version = "0.4.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "proc-macro2"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
@@ -776,6 +814,24 @@ dependencies = [
]
[[package]]
+name = "pulldown-cmark"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "quote"
+version = "0.6.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "quote"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -826,6 +882,22 @@ version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "regex"
+version = "1.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "remove_dir_all"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -931,6 +1003,11 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "semver-parser"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "serde"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -964,7 +1041,11 @@ version = "0.4.0"
dependencies = [
"dhall 0.4.0",
"dhall_proc_macros 0.4.0",
+ "doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "reqwest 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "version-sync 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1016,6 +1097,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
+version = "0.15.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "syn"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
@@ -1038,6 +1129,14 @@ dependencies = [
]
[[package]]
+name = "thread_local"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "time"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1086,6 +1185,14 @@ dependencies = [
]
[[package]]
+name = "toml"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "tower-service"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1136,11 +1243,26 @@ dependencies = [
[[package]]
name = "unicode-xid"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "url"
+version = "1.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "url"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
@@ -1155,6 +1277,21 @@ version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "version-sync"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pulldown-cmark 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "semver-parser 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "version_check"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1310,6 +1447,7 @@ dependencies = [
[metadata]
"checksum abnf 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "47feb9fbcef700639ef28e04ca2a87eab8161a01a075ee227b15c90143805462"
+"checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada"
"checksum annotate-snippets 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aba2d96b8c8b5e656ad7ffb0d09f57772f10a1db74c8d23fca0ec695b38a4047"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
@@ -1329,6 +1467,7 @@ dependencies = [
"checksum ctor 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "47c5e5ac752e18207b12e16b10631ae5f7f68f8805f335f9b817ead83d9ffce1"
"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
+"checksum doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
"checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
"checksum encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28"
@@ -1355,6 +1494,7 @@ dependencies = [
"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
"checksum hyper 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e7b15203263d1faa615f9337d79c1d37959439dc46c2b4faab33286fadc2a1c5"
"checksum hyper-tls 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3adcd308402b9553630734e9c36b77a7e48b3821251ca2493e8cd596763aafaa"
+"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
"checksum indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292"
"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
@@ -1385,6 +1525,7 @@ dependencies = [
"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
"checksum openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)" = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986"
"checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
+"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
"checksum pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
"checksum pest_consume 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3f016ccda869edc16802ba5feb6c4467038132067fbf914117f851ec6ac5e3a1"
@@ -1401,13 +1542,18 @@ dependencies = [
"checksum pretty 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f60c0d9f6fc88ecdd245d90c1920ff76a430ab34303fc778d33b1d0a4c3bf6d3"
"checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
"checksum proc-macro-hack 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)" = "f918f2b601f93baa836c1c2945faef682ba5b6d4828ecb45eeb7cc3c71b811b4"
+"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
"checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435"
+"checksum pulldown-cmark 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d1b74cc784b038a9921fd1a48310cc2e238101aa8ae0b94201e2d85121dd68b5"
+"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
+"checksum regex 1.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3"
+"checksum regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae"
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
"checksum reqwest 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b81e49ddec5109a9dcfc5f2a317ff53377c915e9ae9d4f2fb50914b85614e2"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
@@ -1418,6 +1564,7 @@ dependencies = [
"checksum security-framework-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "06fd2f23e31ef68dd2328cc383bd493142e46107a3a0e24f7d734e3f3b80fe4c"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+"checksum semver-parser 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b46e1121e8180c12ff69a742aabc4f310542b6ccb69f1691689ac17fdf8618aa"
"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
"checksum serde_cbor 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45cd6d95391b16cd57e88b68be41d504183b7faae22030c0cc3b3f73dd57b2fd"
"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
@@ -1427,12 +1574,15 @@ dependencies = [
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
"checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc"
"checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
+"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
"checksum syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859"
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
+"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
"checksum tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "0fa5e81d6bc4e67fe889d5783bd2a128ab2e0cfa487e0be16b6a8d177b101616"
"checksum tokio-tls 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7bde02a3a5291395f59b06ec6945a3077602fac2b07eeeaf0dee2122f3619828"
"checksum tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930"
+"checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
"checksum tower-service 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860"
"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382"
"checksum typed-arena 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d"
@@ -1441,9 +1591,12 @@ dependencies = [
"checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
"checksum unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4"
+"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
+"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
"checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
"checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
+"checksum version-sync 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "844f3d3a2467f15cb999f5af7775f6e108ac546d4f42365832ed4c755404f806"
"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
"checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
"checksum want 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
diff --git a/README.md b/README.md
index ffd55ef..1520b7d 100644
--- a/README.md
+++ b/README.md
@@ -58,7 +58,7 @@ use std::collections::BTreeMap;
let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }";
// Deserialize it to a Rust type.
-let deserialized_map: BTreeMap<String, usize> = serde_dhall::from_str(data)?;
+let deserialized_map: BTreeMap<String, usize> = serde_dhall::from_str(data).parse().unwrap();
let mut expected_map = BTreeMap::new();
expected_map.insert("x".to_string(), 1);
@@ -167,6 +167,10 @@ same name as the corresponding test.
## Changelog
+#### [???]
+
+- Breaking change: reworked most of the `serde_dhall` api
+
#### [0.4.0]
- `dhall` now uses the stable Rust toolchain !
diff --git a/abnf_to_pest/Cargo.toml b/abnf_to_pest/Cargo.toml
index 780010e..1567512 100644
--- a/abnf_to_pest/Cargo.toml
+++ b/abnf_to_pest/Cargo.toml
@@ -14,5 +14,5 @@ doctest = false
[dependencies]
abnf = "0.6.0"
indexmap = "1.0.2"
-itertools = "0.8.0"
+itertools = "0.9.0"
pretty = "0.5.2"
diff --git a/dhall/Cargo.toml b/dhall/Cargo.toml
index 0ffe78b..bb60d9e 100644
--- a/dhall/Cargo.toml
+++ b/dhall/Cargo.toml
@@ -22,10 +22,11 @@ reqwest = { version = "0.10", features = ["blocking"] }
serde = "1.0"
serde_cbor = "0.9.0"
smallvec = "1.0.0"
-url = "2.1.1"
+url = "2.1"
[dev-dependencies]
pretty_assertions = "0.6.1"
+version-sync = "0.8"
[build-dependencies]
walkdir = "2"
diff --git a/dhall/README.md b/dhall/README.md
index 82b3e6a..25f11f7 100644
--- a/dhall/README.md
+++ b/dhall/README.md
@@ -1,10 +1,11 @@
# `dhall`
Implementation of the Dhall configuration language.
-This is an internal crate used for [`serde_dhall`], you probably want to use
-that instead.
-The API is very unstable and does not respect semver;
-use at your own risk.
+WARNING: This is an internal crate used for [`serde_dhall`], you probably want
+to use that instead.
+
+WARNING: The API is very unstable and does not respect semver; use at your own
+risk.
[`serde_dhall`]: https://docs.rs/serde_dhall
diff --git a/dhall/src/error/builder.rs b/dhall/src/error/builder.rs
index c0bacb5..3ee65fb 100644
--- a/dhall/src/error/builder.rs
+++ b/dhall/src/error/builder.rs
@@ -6,7 +6,7 @@ use annotate_snippets::{
use crate::syntax::{ParsedSpan, Span};
#[derive(Debug, Clone, Default)]
-pub(crate) struct ErrorBuilder {
+pub struct ErrorBuilder {
title: FreeAnnotation,
annotations: Vec<SpannedAnnotation>,
footer: Vec<FreeAnnotation>,
diff --git a/dhall/src/error/mod.rs b/dhall/src/error/mod.rs
index e28b98b..ef4d41f 100644
--- a/dhall/src/error/mod.rs
+++ b/dhall/src/error/mod.rs
@@ -4,7 +4,7 @@ use crate::semantics::resolve::{ImportLocation, ImportStack};
use crate::syntax::{Import, ParseError};
mod builder;
-pub(crate) use builder::*;
+pub use builder::*;
pub type Result<T> = std::result::Result<T, Error>;
@@ -15,7 +15,7 @@ pub struct Error {
#[derive(Debug)]
#[non_exhaustive]
-pub(crate) enum ErrorKind {
+pub enum ErrorKind {
IO(IOError),
Parse(ParseError),
Decode(DecodeError),
@@ -25,7 +25,7 @@ pub(crate) enum ErrorKind {
}
#[derive(Debug)]
-pub(crate) enum ImportError {
+pub enum ImportError {
Missing,
MissingEnvVar,
SanityCheck,
@@ -53,21 +53,21 @@ pub struct TypeError {
/// The specific type error
#[derive(Debug)]
-pub(crate) enum TypeMessage {
+pub enum TypeMessage {
Custom(String),
}
impl Error {
- pub(crate) fn new(kind: ErrorKind) -> Self {
+ pub fn new(kind: ErrorKind) -> Self {
Error { kind }
}
- pub(crate) fn kind(&self) -> &ErrorKind {
+ pub fn kind(&self) -> &ErrorKind {
&self.kind
}
}
impl TypeError {
- pub(crate) fn new(message: TypeMessage) -> Self {
+ pub fn new(message: TypeMessage) -> Self {
TypeError { message }
}
}
diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs
index 24e4377..392c344 100644
--- a/dhall/src/lib.rs
+++ b/dhall/src/lib.rs
@@ -1,7 +1,10 @@
#![doc(html_root_url = "https://docs.rs/dhall/0.4.0")]
#![allow(
+ clippy::implicit_hasher,
clippy::module_inception,
clippy::needless_lifetimes,
+ clippy::new_ret_no_self,
+ clippy::new_without_default,
clippy::useless_format
)]
@@ -15,23 +18,15 @@ use std::fmt::Display;
use std::path::Path;
use url::Url;
-use crate::error::{EncodeError, Error, TypeError};
+use crate::error::{Error, TypeError};
use crate::semantics::parse;
use crate::semantics::resolve;
use crate::semantics::resolve::ImportLocation;
-use crate::semantics::{
- typecheck, typecheck_with, Hir, Nir, NirKind, Tir, Type,
-};
-use crate::syntax::binary;
-use crate::syntax::{Builtin, Expr};
-
-pub type ParsedExpr = Expr;
-pub type DecodedExpr = Expr;
-pub type ResolvedExpr = Expr;
-pub type NormalizedExpr = Expr;
+use crate::semantics::{typecheck, typecheck_with, Hir, Nir, Tir, Type};
+use crate::syntax::Expr;
#[derive(Debug, Clone)]
-pub struct Parsed(ParsedExpr, ImportLocation);
+pub struct Parsed(Expr, ImportLocation);
/// An expression where all imports have been resolved
///
@@ -48,15 +43,15 @@ pub struct Typed {
/// A normalized expression.
///
-/// Invariant: the contained expression must be in normal form,
+/// This is actually a lie, because the expression will only get normalized on demand.
#[derive(Debug, Clone)]
pub struct Normalized(Nir);
/// Controls conversion from `Nir` to `Expr`
-#[derive(Copy, Clone)]
-pub(crate) struct ToExprOptions {
+#[derive(Copy, Clone, Default)]
+pub struct ToExprOptions {
/// Whether to convert all variables to `_`
- pub(crate) alpha: bool,
+ pub alpha: bool,
}
impl Parsed {
@@ -72,6 +67,7 @@ impl Parsed {
pub fn parse_binary_file(f: &Path) -> Result<Parsed, Error> {
parse::parse_binary_file(f)
}
+ #[allow(dead_code)]
pub fn parse_binary(data: &[u8]) -> Result<Parsed, Error> {
parse::parse_binary(data)
}
@@ -80,15 +76,11 @@ impl Parsed {
resolve::resolve(self)
}
pub fn skip_resolve(self) -> Result<Resolved, Error> {
- Ok(Resolved(resolve::skip_resolve(&self.0)?))
- }
-
- pub fn encode(&self) -> Result<Vec<u8>, EncodeError> {
- binary::encode(&self.0)
+ resolve::skip_resolve(self)
}
/// Converts a value back to the corresponding AST expression.
- pub fn to_expr(&self) -> ParsedExpr {
+ pub fn to_expr(&self) -> Expr {
self.0.clone()
}
}
@@ -97,11 +89,11 @@ impl Resolved {
pub fn typecheck(&self) -> Result<Typed, TypeError> {
Ok(Typed::from_tir(typecheck(&self.0)?))
}
- pub fn typecheck_with(self, ty: &Normalized) -> Result<Typed, TypeError> {
- Ok(Typed::from_tir(typecheck_with(&self.0, ty.to_hir())?))
+ pub fn typecheck_with(self, ty: &Hir) -> Result<Typed, TypeError> {
+ Ok(Typed::from_tir(typecheck_with(&self.0, ty)?))
}
/// Converts a value back to the corresponding AST expression.
- pub fn to_expr(&self) -> ResolvedExpr {
+ pub fn to_expr(&self) -> Expr {
self.0.to_expr_noopts()
}
}
@@ -115,79 +107,38 @@ impl Typed {
}
/// Reduce an expression to its normal form, performing beta reduction
pub fn normalize(&self) -> Normalized {
- Normalized(self.hir.rec_eval_closed_expr())
+ Normalized(self.hir.eval_closed_expr())
}
/// Converts a value back to the corresponding AST expression.
- fn to_expr(&self) -> ResolvedExpr {
+ fn to_expr(&self) -> Expr {
self.hir.to_expr(ToExprOptions { alpha: false })
}
- pub(crate) fn ty(&self) -> &Type {
+ pub fn ty(&self) -> &Type {
&self.ty
}
- pub(crate) fn get_type(&self) -> Result<Normalized, TypeError> {
+ pub fn get_type(&self) -> Result<Normalized, TypeError> {
Ok(Normalized(self.ty.clone().into_nir()))
}
}
impl Normalized {
- pub fn encode(&self) -> Result<Vec<u8>, EncodeError> {
- binary::encode(&self.to_expr())
- }
-
/// Converts a value back to the corresponding AST expression.
- pub fn to_expr(&self) -> NormalizedExpr {
- self.0.to_expr(ToExprOptions { alpha: false })
+ pub fn to_expr(&self) -> Expr {
+ self.0.to_expr(ToExprOptions::default())
}
/// Converts a value back to the corresponding Hir expression.
- pub(crate) fn to_hir(&self) -> Hir {
+ pub fn to_hir(&self) -> Hir {
self.0.to_hir_noenv()
}
+ pub fn as_nir(&self) -> &Nir {
+ &self.0
+ }
/// Converts a value back to the corresponding AST expression, alpha-normalizing in the process.
- pub(crate) fn to_expr_alpha(&self) -> NormalizedExpr {
+ pub fn to_expr_alpha(&self) -> Expr {
self.0.to_expr(ToExprOptions { alpha: true })
}
- pub(crate) fn to_nir(&self) -> Nir {
- self.0.clone()
- }
- pub(crate) fn into_nir(self) -> Nir {
- self.0
- }
-
- pub(crate) fn from_kind(v: NirKind) -> Self {
- Normalized(Nir::from_kind(v))
- }
- pub(crate) fn from_nir(th: Nir) -> Self {
- Normalized(th)
- }
-
- pub fn make_builtin_type(b: Builtin) -> Self {
- Normalized::from_nir(Nir::from_builtin(b))
- }
- pub fn make_optional_type(t: Normalized) -> Self {
- Normalized::from_nir(
- Nir::from_builtin(Builtin::Optional).app(t.to_nir()),
- )
- }
- pub fn make_list_type(t: Normalized) -> Self {
- Normalized::from_nir(Nir::from_builtin(Builtin::List).app(t.to_nir()))
- }
- pub fn make_record_type(
- kts: impl Iterator<Item = (String, Normalized)>,
- ) -> Self {
- Normalized::from_kind(NirKind::RecordType(
- kts.map(|(k, t)| (k.into(), t.into_nir())).collect(),
- ))
- }
- pub fn make_union_type(
- kts: impl Iterator<Item = (String, Option<Normalized>)>,
- ) -> Self {
- Normalized::from_kind(NirKind::UnionType(
- kts.map(|(k, t)| (k.into(), t.map(|t| t.into_nir())))
- .collect(),
- ))
- }
}
macro_rules! derive_traits_for_wrapper_struct {
@@ -213,23 +164,12 @@ macro_rules! derive_traits_for_wrapper_struct {
derive_traits_for_wrapper_struct!(Parsed);
-impl std::hash::Hash for Normalized {
- fn hash<H>(&self, state: &mut H)
- where
- H: std::hash::Hasher,
- {
- if let Ok(vec) = self.encode() {
- vec.hash(state)
- }
- }
-}
-
-impl From<Parsed> for NormalizedExpr {
+impl From<Parsed> for Expr {
fn from(other: Parsed) -> Self {
other.to_expr()
}
}
-impl From<Normalized> for NormalizedExpr {
+impl From<Normalized> for Expr {
fn from(other: Normalized) -> Self {
other.to_expr()
}
diff --git a/dhall/src/semantics/builtins.rs b/dhall/src/semantics/builtins.rs
index 803630b..6007a92 100644
--- a/dhall/src/semantics/builtins.rs
+++ b/dhall/src/semantics/builtins.rs
@@ -1,48 +1,35 @@
use crate::semantics::{
- skip_resolve, typecheck, Hir, HirKind, Nir, NirKind, NzEnv, VarEnv,
+ skip_resolve_expr, typecheck, Hir, HirKind, Nir, NirKind, NzEnv, VarEnv,
};
use crate::syntax::map::DupTreeMap;
use crate::syntax::Const::Type;
use crate::syntax::{
BinOp, Builtin, Const, Expr, ExprKind, InterpolatedText,
- InterpolatedTextContents, Label, LitKind, NaiveDouble, Span, UnspannedExpr,
+ InterpolatedTextContents, Label, NaiveDouble, NumKind, Span, UnspannedExpr,
V,
};
-use crate::Normalized;
use std::collections::HashMap;
use std::convert::TryInto;
/// A partially applied builtin.
/// Invariant: the evaluation of the given args must not be able to progress further
#[derive(Debug, Clone)]
-pub(crate) struct BuiltinClosure<Nir> {
- pub env: NzEnv,
- pub b: Builtin,
+pub struct BuiltinClosure {
+ env: NzEnv,
+ b: Builtin,
/// Arguments applied to the closure so far.
- pub args: Vec<Nir>,
+ args: Vec<Nir>,
}
-impl BuiltinClosure<Nir> {
- pub fn new(b: Builtin, env: NzEnv) -> Self {
- BuiltinClosure {
- env,
- b,
- args: Vec::new(),
- }
+impl BuiltinClosure {
+ pub fn new(b: Builtin, env: NzEnv) -> NirKind {
+ apply_builtin(b, Vec::new(), env)
}
-
pub fn apply(&self, a: Nir) -> NirKind {
use std::iter::once;
let args = self.args.iter().cloned().chain(once(a)).collect();
apply_builtin(self.b, args, self.env.clone())
}
- /// This doesn't break the invariant because we already checked that the appropriate arguments
- /// did not normalize to something that allows evaluation to proceed.
- pub fn normalize(&self) {
- for x in self.args.iter() {
- x.normalize();
- }
- }
pub fn to_hirkind(&self, venv: VarEnv) -> HirKind {
HirKind::Expr(self.args.iter().fold(
ExprKind::Builtin(self.b),
@@ -56,7 +43,7 @@ impl BuiltinClosure<Nir> {
}
}
-pub(crate) fn rc(x: UnspannedExpr) -> Expr {
+pub fn rc(x: UnspannedExpr) -> Expr {
Expr::new(x, Span::Artificial)
}
@@ -116,7 +103,7 @@ macro_rules! make_type {
};
}
-pub(crate) fn type_of_builtin(b: Builtin) -> Hir {
+pub fn type_of_builtin(b: Builtin) -> Hir {
use Builtin::*;
let expr = match b {
Bool | Natural | Integer | Double | Text => make_type!(Type),
@@ -202,7 +189,7 @@ pub(crate) fn type_of_builtin(b: Builtin) -> Hir {
forall (A: Type) -> Optional A
),
};
- skip_resolve(&expr).unwrap()
+ skip_resolve_expr(&expr).unwrap()
}
// Ad-hoc macro to help construct closures
@@ -241,7 +228,7 @@ macro_rules! make_closure {
rc(ExprKind::BinOp(
BinOp::NaturalPlus,
make_closure!($($v)*),
- rc(ExprKind::Lit(LitKind::Natural(1)))
+ rc(ExprKind::Num(NumKind::Natural(1)))
))
};
([ $($head:tt)* ] # $($tail:tt)*) => {{
@@ -257,8 +244,8 @@ macro_rules! make_closure {
#[allow(clippy::cognitive_complexity)]
fn apply_builtin(b: Builtin, args: Vec<Nir>, env: NzEnv) -> NirKind {
- use LitKind::{Bool, Double, Integer, Natural};
use NirKind::*;
+ use NumKind::{Bool, Double, Integer, Natural};
// Small helper enum
enum Ret {
@@ -267,46 +254,54 @@ fn apply_builtin(b: Builtin, args: Vec<Nir>, env: NzEnv) -> NirKind {
DoneAsIs,
}
let make_closure = |e| {
- typecheck(&skip_resolve(&e).unwrap())
+ typecheck(&skip_resolve_expr(&e).unwrap())
.unwrap()
.eval(env.clone())
};
let ret = match (b, args.as_slice()) {
+ (Builtin::Bool, [])
+ | (Builtin::Natural, [])
+ | (Builtin::Integer, [])
+ | (Builtin::Double, [])
+ | (Builtin::Text, []) => Ret::NirKind(BuiltinType(b)),
+ (Builtin::Optional, [t]) => Ret::NirKind(OptionalType(t.clone())),
+ (Builtin::List, [t]) => Ret::NirKind(ListType(t.clone())),
+
(Builtin::OptionalNone, [t]) => {
Ret::NirKind(EmptyOptionalLit(t.clone()))
}
(Builtin::NaturalIsZero, [n]) => match &*n.kind() {
- Lit(Natural(n)) => Ret::NirKind(Lit(Bool(*n == 0))),
+ Num(Natural(n)) => Ret::NirKind(Num(Bool(*n == 0))),
_ => Ret::DoneAsIs,
},
(Builtin::NaturalEven, [n]) => match &*n.kind() {
- Lit(Natural(n)) => Ret::NirKind(Lit(Bool(*n % 2 == 0))),
+ Num(Natural(n)) => Ret::NirKind(Num(Bool(*n % 2 == 0))),
_ => Ret::DoneAsIs,
},
(Builtin::NaturalOdd, [n]) => match &*n.kind() {
- Lit(Natural(n)) => Ret::NirKind(Lit(Bool(*n % 2 != 0))),
+ Num(Natural(n)) => Ret::NirKind(Num(Bool(*n % 2 != 0))),
_ => Ret::DoneAsIs,
},
(Builtin::NaturalToInteger, [n]) => match &*n.kind() {
- Lit(Natural(n)) => Ret::NirKind(Lit(Integer(*n as isize))),
+ Num(Natural(n)) => Ret::NirKind(Num(Integer(*n as isize))),
_ => Ret::DoneAsIs,
},
(Builtin::NaturalShow, [n]) => match &*n.kind() {
- Lit(Natural(n)) => Ret::Nir(Nir::from_text(n)),
+ Num(Natural(n)) => Ret::Nir(Nir::from_text(n)),
_ => Ret::DoneAsIs,
},
(Builtin::NaturalSubtract, [a, b]) => match (&*a.kind(), &*b.kind()) {
- (Lit(Natural(a)), Lit(Natural(b))) => {
- Ret::NirKind(Lit(Natural(if b > a { b - a } else { 0 })))
+ (Num(Natural(a)), Num(Natural(b))) => {
+ Ret::NirKind(Num(Natural(if b > a { b - a } else { 0 })))
}
- (Lit(Natural(0)), _) => Ret::Nir(b.clone()),
- (_, Lit(Natural(0))) => Ret::NirKind(Lit(Natural(0))),
- _ if a == b => Ret::NirKind(Lit(Natural(0))),
+ (Num(Natural(0)), _) => Ret::Nir(b.clone()),
+ (_, Num(Natural(0))) => Ret::NirKind(Num(Natural(0))),
+ _ if a == b => Ret::NirKind(Num(Natural(0))),
_ => Ret::DoneAsIs,
},
(Builtin::IntegerShow, [n]) => match &*n.kind() {
- Lit(Integer(n)) => {
+ Num(Integer(n)) => {
let s = if *n < 0 {
n.to_string()
} else {
@@ -317,30 +312,30 @@ fn apply_builtin(b: Builtin, args: Vec<Nir>, env: NzEnv) -> NirKind {
_ => Ret::DoneAsIs,
},
(Builtin::IntegerToDouble, [n]) => match &*n.kind() {
- Lit(Integer(n)) => {
- Ret::NirKind(Lit(Double(NaiveDouble::from(*n as f64))))
+ Num(Integer(n)) => {
+ Ret::NirKind(Num(Double(NaiveDouble::from(*n as f64))))
}
_ => Ret::DoneAsIs,
},
(Builtin::IntegerNegate, [n]) => match &*n.kind() {
- Lit(Integer(n)) => Ret::NirKind(Lit(Integer(-n))),
+ Num(Integer(n)) => Ret::NirKind(Num(Integer(-n))),
_ => Ret::DoneAsIs,
},
(Builtin::IntegerClamp, [n]) => match &*n.kind() {
- Lit(Integer(n)) => {
- Ret::NirKind(Lit(Natural((*n).try_into().unwrap_or(0))))
+ Num(Integer(n)) => {
+ Ret::NirKind(Num(Natural((*n).try_into().unwrap_or(0))))
}
_ => Ret::DoneAsIs,
},
(Builtin::DoubleShow, [n]) => match &*n.kind() {
- Lit(Double(n)) => Ret::Nir(Nir::from_text(n)),
+ Num(Double(n)) => Ret::Nir(Nir::from_text(n)),
_ => Ret::DoneAsIs,
},
(Builtin::TextShow, [v]) => match &*v.kind() {
TextLit(tlit) => {
if let Some(s) = tlit.as_text() {
// Printing InterpolatedText takes care of all the escaping
- let txt: InterpolatedText<Normalized> =
+ let txt: InterpolatedText<Expr> =
std::iter::once(InterpolatedTextContents::Text(s))
.collect();
Ret::Nir(Nir::from_text(txt))
@@ -351,8 +346,8 @@ fn apply_builtin(b: Builtin, args: Vec<Nir>, env: NzEnv) -> NirKind {
_ => Ret::DoneAsIs,
},
(Builtin::ListLength, [_, l]) => match &*l.kind() {
- EmptyListLit(_) => Ret::NirKind(Lit(Natural(0))),
- NEListLit(xs) => Ret::NirKind(Lit(Natural(xs.len()))),
+ EmptyListLit(_) => Ret::NirKind(Num(Natural(0))),
+ NEListLit(xs) => Ret::NirKind(Num(Natural(xs.len()))),
_ => Ret::DoneAsIs,
},
(Builtin::ListHead, [_, l]) => match &*l.kind() {
@@ -398,7 +393,7 @@ fn apply_builtin(b: Builtin, args: Vec<Nir>, env: NzEnv) -> NirKind {
let mut kvs = HashMap::new();
kvs.insert(
"index".into(),
- Nir::from_kind(Lit(Natural(i))),
+ Nir::from_kind(Num(Natural(i))),
);
kvs.insert("value".into(), e.clone());
Nir::from_kind(RecordLit(kvs))
@@ -466,14 +461,14 @@ fn apply_builtin(b: Builtin, args: Vec<Nir>, env: NzEnv) -> NirKind {
λ(x : Natural) ->
1 + var(x)
)))
- .app(Lit(Natural(0)).into_nir()),
+ .app(Num(Natural(0)).into_nir()),
),
(Builtin::NaturalFold, [n, t, succ, zero]) => match &*n.kind() {
- Lit(Natural(0)) => Ret::Nir(zero.clone()),
- Lit(Natural(n)) => {
+ Num(Natural(0)) => Ret::Nir(zero.clone()),
+ Num(Natural(n)) => {
let fold = Nir::from_builtin(Builtin::NaturalFold)
- .app(Lit(Natural(n - 1)).into_nir())
+ .app(Num(Natural(n - 1)).into_nir())
.app(t.clone())
.app(succ.clone())
.app(zero.clone());
@@ -490,9 +485,9 @@ fn apply_builtin(b: Builtin, args: Vec<Nir>, env: NzEnv) -> NirKind {
}
}
-impl<Nir: std::cmp::PartialEq> std::cmp::PartialEq for BuiltinClosure<Nir> {
+impl std::cmp::PartialEq for BuiltinClosure {
fn eq(&self, other: &Self) -> bool {
self.b == other.b && self.args == other.args
}
}
-impl<Nir: std::cmp::Eq> std::cmp::Eq for BuiltinClosure<Nir> {}
+impl std::cmp::Eq for BuiltinClosure {}
diff --git a/dhall/src/semantics/mod.rs b/dhall/src/semantics/mod.rs
index 87033c9..468d8b1 100644
--- a/dhall/src/semantics/mod.rs
+++ b/dhall/src/semantics/mod.rs
@@ -3,7 +3,7 @@ pub mod nze;
pub mod parse;
pub mod resolve;
pub mod tck;
-pub(crate) use self::builtins::*;
-pub(crate) use self::nze::*;
-pub(crate) use self::resolve::*;
-pub(crate) use self::tck::*;
+pub use self::builtins::*;
+pub use self::nze::*;
+pub use self::resolve::*;
+pub use self::tck::*;
diff --git a/dhall/src/semantics/nze/env.rs b/dhall/src/semantics/nze/env.rs
index ef2bee6..ec99dbe 100644
--- a/dhall/src/semantics/nze/env.rs
+++ b/dhall/src/semantics/nze/env.rs
@@ -1,7 +1,7 @@
use crate::semantics::{AlphaVar, Nir, NirKind};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub(crate) enum NzVar {
+pub enum NzVar {
/// Reverse-debruijn index: counts number of binders from the bottom of the stack.
Bound(usize),
/// Fake fresh variable generated for expression equality checking.
@@ -17,11 +17,11 @@ enum EnvItem<Type> {
}
#[derive(Debug, Clone)]
-pub(crate) struct ValEnv<Type> {
+pub struct ValEnv<Type> {
items: Vec<EnvItem<Type>>,
}
-pub(crate) type NzEnv = ValEnv<()>;
+pub type NzEnv = ValEnv<()>;
impl NzVar {
pub fn new(idx: usize) -> Self {
diff --git a/dhall/src/semantics/nze/mod.rs b/dhall/src/semantics/nze/mod.rs
index 2648339..23022e0 100644
--- a/dhall/src/semantics/nze/mod.rs
+++ b/dhall/src/semantics/nze/mod.rs
@@ -3,7 +3,7 @@ pub mod lazy;
pub mod nir;
pub mod normalize;
pub mod var;
-pub(crate) use env::*;
-pub(crate) use nir::*;
-pub(crate) use normalize::*;
-pub(crate) use var::*;
+pub use env::*;
+pub use nir::*;
+pub use normalize::*;
+pub use var::*;
diff --git a/dhall/src/semantics/nze/nir.rs b/dhall/src/semantics/nze/nir.rs
index 32ef590..e0d227e 100644
--- a/dhall/src/semantics/nze/nir.rs
+++ b/dhall/src/semantics/nze/nir.rs
@@ -7,10 +7,10 @@ use crate::semantics::{
BuiltinClosure, Hir, HirKind, NzEnv, NzVar, TyEnv, Type, Universe, VarEnv,
};
use crate::syntax::{
- BinOp, Builtin, Const, ExprKind, InterpolatedTextContents, Label, LitKind,
- Span,
+ BinOp, Builtin, Const, Expr, ExprKind, InterpolatedTextContents, Label,
+ NumKind, Span,
};
-use crate::{NormalizedExpr, ToExprOptions};
+use crate::ToExprOptions;
/// Stores a possibly unevaluated value. Gets (partially) normalized on-demand, sharing computation
/// automatically. Uses a Rc<RefCell> to share computation.
@@ -19,7 +19,7 @@ use crate::{NormalizedExpr, ToExprOptions};
/// normalize as needed.
/// Stands for "Normalized intermediate representation"
#[derive(Clone)]
-pub(crate) struct Nir(Rc<NirInternal>);
+pub struct Nir(Rc<NirInternal>);
#[derive(Debug)]
struct NirInternal {
@@ -28,7 +28,7 @@ struct NirInternal {
/// An unevaluated subexpression
#[derive(Debug, Clone)]
-pub(crate) enum Thunk {
+pub enum Thunk {
/// A completely unnormalized expression.
Thunk { env: NzEnv, body: Hir },
/// A partially normalized expression that may need to go through `normalize_one_layer`.
@@ -37,7 +37,7 @@ pub(crate) enum Thunk {
/// An unevaluated subexpression that takes an argument.
#[derive(Debug, Clone)]
-pub(crate) enum Closure {
+pub enum Closure {
/// Normal closure
Closure { env: NzEnv, body: Hir },
/// Closure that ignores the argument passed
@@ -48,7 +48,7 @@ pub(crate) enum Closure {
// Invariant: this must not contain interpolations that are themselves TextLits, and contiguous
// text values must be merged.
#[derive(Debug, Clone, PartialEq, Eq)]
-pub(crate) struct TextLit(Vec<InterpolatedTextContents<Nir>>);
+pub struct TextLit(Vec<InterpolatedTextContents<Nir>>);
/// This represents a value in Weak Head Normal Form (WHNF). This means that the value is
/// normalized up to the first constructor, but subexpressions may not be fully normalized.
@@ -58,7 +58,7 @@ pub(crate) struct TextLit(Vec<InterpolatedTextContents<Nir>>);
/// In particular, this means that once we get a `NirKind`, it can be considered immutable, and
/// we only need to recursively normalize its sub-`Nir`s to get to the NF.
#[derive(Debug, Clone, PartialEq, Eq)]
-pub(crate) enum NirKind {
+pub enum NirKind {
/// Closures
LamClosure {
binder: Binder,
@@ -70,13 +70,17 @@ pub(crate) enum NirKind {
annot: Nir,
closure: Closure,
},
- AppliedBuiltin(BuiltinClosure<Nir>),
+ AppliedBuiltin(BuiltinClosure),
Var(NzVar),
Const(Const),
- Lit(LitKind),
+ // Must be a number type, Bool or Text
+ BuiltinType(Builtin),
+ Num(NumKind),
+ OptionalType(Nir),
EmptyOptionalLit(Nir),
NEOptionalLit(Nir),
+ ListType(Nir),
// EmptyListLit(t) means `[] : List t`, not `[] : t`
EmptyListLit(Nir),
NEListLit(Vec<Nir>),
@@ -93,34 +97,34 @@ pub(crate) enum NirKind {
impl Nir {
/// Construct a Nir from a completely unnormalized expression.
- pub(crate) fn new_thunk(env: NzEnv, hir: Hir) -> Nir {
+ pub fn new_thunk(env: NzEnv, hir: Hir) -> Nir {
NirInternal::from_thunk(Thunk::new(env, hir)).into_nir()
}
/// Construct a Nir from a partially normalized expression that's not in WHNF.
- pub(crate) fn from_partial_expr(e: ExprKind<Nir>) -> Nir {
+ pub fn from_partial_expr(e: ExprKind<Nir>) -> Nir {
// TODO: env
let env = NzEnv::new();
NirInternal::from_thunk(Thunk::from_partial_expr(env, e)).into_nir()
}
/// Make a Nir from a NirKind
- pub(crate) fn from_kind(v: NirKind) -> Nir {
+ pub fn from_kind(v: NirKind) -> Nir {
NirInternal::from_whnf(v).into_nir()
}
- pub(crate) fn from_const(c: Const) -> Self {
+ pub fn from_const(c: Const) -> Self {
let v = NirKind::Const(c);
NirInternal::from_whnf(v).into_nir()
}
- pub(crate) fn from_builtin(b: Builtin) -> Self {
+ pub fn from_builtin(b: Builtin) -> Self {
Self::from_builtin_env(b, &NzEnv::new())
}
- pub(crate) fn from_builtin_env(b: Builtin, env: &NzEnv) -> Self {
+ pub fn from_builtin_env(b: Builtin, env: &NzEnv) -> Self {
Nir::from_kind(NirKind::from_builtin_env(b, env.clone()))
}
- pub(crate) fn from_text(txt: impl ToString) -> Self {
+ pub fn from_text(txt: impl ToString) -> Self {
Nir::from_kind(NirKind::TextLit(TextLit::from_text(txt.to_string())))
}
- pub(crate) fn as_const(&self) -> Option<Const> {
+ pub fn as_const(&self) -> Option<Const> {
match &*self.kind() {
NirKind::Const(c) => Some(*c),
_ => None,
@@ -128,26 +132,22 @@ impl Nir {
}
/// This is what you want if you want to pattern-match on the value.
- pub(crate) fn kind(&self) -> &NirKind {
+ pub fn kind(&self) -> &NirKind {
self.0.kind()
}
- pub(crate) fn to_type(&self, u: impl Into<Universe>) -> Type {
+ pub fn to_type(&self, u: impl Into<Universe>) -> Type {
Type::new(self.clone(), u.into())
}
/// Converts a value back to the corresponding AST expression.
- pub(crate) fn to_expr(&self, opts: ToExprOptions) -> NormalizedExpr {
+ pub fn to_expr(&self, opts: ToExprOptions) -> Expr {
self.to_hir_noenv().to_expr(opts)
}
- pub(crate) fn to_expr_tyenv(&self, tyenv: &TyEnv) -> NormalizedExpr {
+ pub fn to_expr_tyenv(&self, tyenv: &TyEnv) -> Expr {
self.to_hir(tyenv.as_varenv()).to_expr_tyenv(tyenv)
}
- pub(crate) fn normalize(&self) {
- self.0.normalize()
- }
-
- pub(crate) fn app(&self, v: Nir) -> Nir {
+ pub fn app(&self, v: Nir) -> Nir {
Nir::from_kind(apply_any(self.clone(), v))
}
@@ -188,12 +188,21 @@ impl Nir {
closure.to_hir(venv),
),
NirKind::Const(c) => ExprKind::Const(*c),
- NirKind::Lit(l) => ExprKind::Lit(l.clone()),
+ NirKind::BuiltinType(b) => ExprKind::Builtin(*b),
+ NirKind::Num(l) => ExprKind::Num(l.clone()),
+ NirKind::OptionalType(t) => ExprKind::App(
+ Nir::from_builtin(Builtin::Optional).to_hir(venv),
+ t.to_hir(venv),
+ ),
NirKind::EmptyOptionalLit(n) => ExprKind::App(
Nir::from_builtin(Builtin::OptionalNone).to_hir(venv),
n.to_hir(venv),
),
NirKind::NEOptionalLit(n) => ExprKind::SomeLit(n.to_hir(venv)),
+ NirKind::ListType(t) => ExprKind::App(
+ Nir::from_builtin(Builtin::List).to_hir(venv),
+ t.to_hir(venv),
+ ),
NirKind::EmptyListLit(n) => ExprKind::EmptyListLit(Hir::new(
HirKind::Expr(ExprKind::App(
Nir::from_builtin(Builtin::List).to_hir(venv),
@@ -274,75 +283,18 @@ impl NirInternal {
fn kind(&self) -> &NirKind {
&self.kind
}
- fn normalize(&self) {
- self.kind().normalize();
- }
}
impl NirKind {
- pub(crate) fn into_nir(self) -> Nir {
+ pub fn into_nir(self) -> Nir {
Nir::from_kind(self)
}
- pub(crate) fn normalize(&self) {
- match self {
- NirKind::Var(..) | NirKind::Const(_) | NirKind::Lit(_) => {}
-
- NirKind::EmptyOptionalLit(tth) | NirKind::EmptyListLit(tth) => {
- tth.normalize();
- }
-
- NirKind::NEOptionalLit(th) => {
- th.normalize();
- }
- NirKind::LamClosure { annot, closure, .. }
- | NirKind::PiClosure { annot, closure, .. } => {
- annot.normalize();
- closure.normalize();
- }
- NirKind::AppliedBuiltin(closure) => closure.normalize(),
- NirKind::NEListLit(elts) => {
- for x in elts.iter() {
- x.normalize();
- }
- }
- NirKind::RecordLit(kvs) => {
- for x in kvs.values() {
- x.normalize();
- }
- }
- NirKind::RecordType(kvs) => {
- for x in kvs.values() {
- x.normalize();
- }
- }
- NirKind::UnionType(kts) | NirKind::UnionConstructor(_, kts) => {
- for x in kts.values().flatten() {
- x.normalize();
- }
- }
- NirKind::UnionLit(_, v, kts) => {
- v.normalize();
- for x in kts.values().flatten() {
- x.normalize();
- }
- }
- NirKind::TextLit(tlit) => tlit.normalize(),
- NirKind::Equivalence(x, y) => {
- x.normalize();
- y.normalize();
- }
- NirKind::PartialExpr(e) => {
- e.map_ref(Nir::normalize);
- }
- }
- }
-
- pub(crate) fn from_builtin(b: Builtin) -> NirKind {
+ pub fn from_builtin(b: Builtin) -> NirKind {
NirKind::from_builtin_env(b, NzEnv::new())
}
- pub(crate) fn from_builtin_env(b: Builtin, env: NzEnv) -> NirKind {
- NirKind::AppliedBuiltin(BuiltinClosure::new(b, env))
+ pub fn from_builtin_env(b: Builtin, env: NzEnv) -> NirKind {
+ BuiltinClosure::new(b, env)
}
}
@@ -390,9 +342,6 @@ impl Closure {
}
}
- // TODO: somehow normalize the body. Might require to pass an env.
- pub fn normalize(&self) {}
-
/// Convert this closure to a Hir expression
pub fn to_hir(&self, venv: VarEnv) -> Hir {
self.apply_var(NzVar::new(venv.size()))
@@ -456,13 +405,6 @@ impl TextLit {
pub fn iter(&self) -> impl Iterator<Item = &InterpolatedTextContents<Nir>> {
self.0.iter()
}
- /// Normalize the contained values. This does not break the invariant because we have already
- /// ensured that no contained values normalize to a TextLit.
- pub fn normalize(&self) {
- for x in self.0.iter() {
- x.map_ref(Nir::normalize);
- }
- }
}
impl lazy::Eval<NirKind> for Thunk {
diff --git a/dhall/src/semantics/nze/normalize.rs b/dhall/src/semantics/nze/normalize.rs
index 79d55e8..570e106 100644
--- a/dhall/src/semantics/nze/normalize.rs
+++ b/dhall/src/semantics/nze/normalize.rs
@@ -2,14 +2,10 @@ use itertools::Itertools;
use std::collections::HashMap;
use crate::semantics::NzEnv;
-use crate::semantics::{
- Binder, BuiltinClosure, Closure, Hir, HirKind, Nir, NirKind, TextLit,
-};
-use crate::syntax::{
- BinOp, Builtin, ExprKind, InterpolatedTextContents, LitKind,
-};
+use crate::semantics::{Binder, Closure, Hir, HirKind, Nir, NirKind, TextLit};
+use crate::syntax::{BinOp, ExprKind, InterpolatedTextContents, NumKind};
-pub(crate) fn apply_any(f: Nir, a: Nir) -> NirKind {
+pub fn apply_any(f: Nir, a: Nir) -> NirKind {
match f.kind() {
NirKind::LamClosure { closure, .. } => closure.apply(a).kind().clone(),
NirKind::AppliedBuiltin(closure) => closure.apply(a),
@@ -20,7 +16,7 @@ pub(crate) fn apply_any(f: Nir, a: Nir) -> NirKind {
}
}
-pub(crate) fn squash_textlit(
+pub fn squash_textlit(
elts: impl Iterator<Item = InterpolatedTextContents<Nir>>,
) -> Vec<InterpolatedTextContents<Nir>> {
use std::mem::replace;
@@ -58,7 +54,7 @@ pub(crate) fn squash_textlit(
ret
}
-pub(crate) fn merge_maps<K, V, F>(
+pub fn merge_maps<K, V, F>(
map1: &HashMap<K, V>,
map2: &HashMap<K, V>,
mut f: F,
@@ -99,40 +95,40 @@ fn apply_binop<'a>(o: BinOp, x: &'a Nir, y: &'a Nir) -> Option<Ret<'a>> {
NaturalTimes, RecursiveRecordMerge, RecursiveRecordTypeMerge,
RightBiasedRecordMerge, TextAppend,
};
- use LitKind::{Bool, Natural};
- use NirKind::{EmptyListLit, Lit, NEListLit, RecordLit, RecordType};
+ use NirKind::{EmptyListLit, NEListLit, Num, RecordLit, RecordType};
+ use NumKind::{Bool, Natural};
Some(match (o, x.kind(), y.kind()) {
- (BoolAnd, Lit(Bool(true)), _) => Ret::NirRef(y),
- (BoolAnd, _, Lit(Bool(true))) => Ret::NirRef(x),
- (BoolAnd, Lit(Bool(false)), _) => Ret::NirKind(Lit(Bool(false))),
- (BoolAnd, _, Lit(Bool(false))) => Ret::NirKind(Lit(Bool(false))),
+ (BoolAnd, Num(Bool(true)), _) => Ret::NirRef(y),
+ (BoolAnd, _, Num(Bool(true))) => Ret::NirRef(x),
+ (BoolAnd, Num(Bool(false)), _) => Ret::NirKind(Num(Bool(false))),
+ (BoolAnd, _, Num(Bool(false))) => Ret::NirKind(Num(Bool(false))),
(BoolAnd, _, _) if x == y => Ret::NirRef(x),
- (BoolOr, Lit(Bool(true)), _) => Ret::NirKind(Lit(Bool(true))),
- (BoolOr, _, Lit(Bool(true))) => Ret::NirKind(Lit(Bool(true))),
- (BoolOr, Lit(Bool(false)), _) => Ret::NirRef(y),
- (BoolOr, _, Lit(Bool(false))) => Ret::NirRef(x),
+ (BoolOr, Num(Bool(true)), _) => Ret::NirKind(Num(Bool(true))),
+ (BoolOr, _, Num(Bool(true))) => Ret::NirKind(Num(Bool(true))),
+ (BoolOr, Num(Bool(false)), _) => Ret::NirRef(y),
+ (BoolOr, _, Num(Bool(false))) => Ret::NirRef(x),
(BoolOr, _, _) if x == y => Ret::NirRef(x),
- (BoolEQ, Lit(Bool(true)), _) => Ret::NirRef(y),
- (BoolEQ, _, Lit(Bool(true))) => Ret::NirRef(x),
- (BoolEQ, Lit(Bool(x)), Lit(Bool(y))) => Ret::NirKind(Lit(Bool(x == y))),
- (BoolEQ, _, _) if x == y => Ret::NirKind(Lit(Bool(true))),
- (BoolNE, Lit(Bool(false)), _) => Ret::NirRef(y),
- (BoolNE, _, Lit(Bool(false))) => Ret::NirRef(x),
- (BoolNE, Lit(Bool(x)), Lit(Bool(y))) => Ret::NirKind(Lit(Bool(x != y))),
- (BoolNE, _, _) if x == y => Ret::NirKind(Lit(Bool(false))),
+ (BoolEQ, Num(Bool(true)), _) => Ret::NirRef(y),
+ (BoolEQ, _, Num(Bool(true))) => Ret::NirRef(x),
+ (BoolEQ, Num(Bool(x)), Num(Bool(y))) => Ret::NirKind(Num(Bool(x == y))),
+ (BoolEQ, _, _) if x == y => Ret::NirKind(Num(Bool(true))),
+ (BoolNE, Num(Bool(false)), _) => Ret::NirRef(y),
+ (BoolNE, _, Num(Bool(false))) => Ret::NirRef(x),
+ (BoolNE, Num(Bool(x)), Num(Bool(y))) => Ret::NirKind(Num(Bool(x != y))),
+ (BoolNE, _, _) if x == y => Ret::NirKind(Num(Bool(false))),
- (NaturalPlus, Lit(Natural(0)), _) => Ret::NirRef(y),
- (NaturalPlus, _, Lit(Natural(0))) => Ret::NirRef(x),
- (NaturalPlus, Lit(Natural(x)), Lit(Natural(y))) => {
- Ret::NirKind(Lit(Natural(x + y)))
+ (NaturalPlus, Num(Natural(0)), _) => Ret::NirRef(y),
+ (NaturalPlus, _, Num(Natural(0))) => Ret::NirRef(x),
+ (NaturalPlus, Num(Natural(x)), Num(Natural(y))) => {
+ Ret::NirKind(Num(Natural(x + y)))
}
- (NaturalTimes, Lit(Natural(0)), _) => Ret::NirKind(Lit(Natural(0))),
- (NaturalTimes, _, Lit(Natural(0))) => Ret::NirKind(Lit(Natural(0))),
- (NaturalTimes, Lit(Natural(1)), _) => Ret::NirRef(y),
- (NaturalTimes, _, Lit(Natural(1))) => Ret::NirRef(x),
- (NaturalTimes, Lit(Natural(x)), Lit(Natural(y))) => {
- Ret::NirKind(Lit(Natural(x * y)))
+ (NaturalTimes, Num(Natural(0)), _) => Ret::NirKind(Num(Natural(0))),
+ (NaturalTimes, _, Num(Natural(0))) => Ret::NirKind(Num(Natural(0))),
+ (NaturalTimes, Num(Natural(1)), _) => Ret::NirRef(y),
+ (NaturalTimes, _, Num(Natural(1))) => Ret::NirRef(x),
+ (NaturalTimes, Num(Natural(x)), Num(Natural(y))) => {
+ Ret::NirKind(Num(Natural(x * y)))
}
(ListAppend, EmptyListLit(_), _) => Ret::NirRef(y),
@@ -211,13 +207,13 @@ fn apply_binop<'a>(o: BinOp, x: &'a Nir, y: &'a Nir) -> Option<Ret<'a>> {
}
#[allow(clippy::cognitive_complexity)]
-pub(crate) fn normalize_one_layer(expr: ExprKind<Nir>, env: &NzEnv) -> NirKind {
- use LitKind::Bool;
+pub fn normalize_one_layer(expr: ExprKind<Nir>, env: &NzEnv) -> NirKind {
use NirKind::{
- EmptyListLit, EmptyOptionalLit, Lit, NEListLit, NEOptionalLit,
+ EmptyListLit, EmptyOptionalLit, NEListLit, NEOptionalLit, Num,
PartialExpr, RecordLit, RecordType, UnionConstructor, UnionLit,
UnionType,
};
+ use NumKind::Bool;
let ret = match expr {
ExprKind::Import(..) | ExprKind::Completion(..) => {
@@ -235,15 +231,11 @@ pub(crate) fn normalize_one_layer(expr: ExprKind<Nir>, env: &NzEnv) -> NirKind {
ExprKind::Builtin(b) => Ret::Nir(Nir::from_builtin_env(b, env)),
ExprKind::Assert(_) => Ret::Expr(expr),
ExprKind::App(v, a) => Ret::Nir(v.app(a)),
- ExprKind::Lit(l) => Ret::NirKind(Lit(l)),
+ ExprKind::Num(l) => Ret::NirKind(Num(l)),
ExprKind::SomeLit(e) => Ret::NirKind(NEOptionalLit(e)),
ExprKind::EmptyListLit(t) => {
let arg = match t.kind() {
- NirKind::AppliedBuiltin(BuiltinClosure {
- b: Builtin::List,
- args,
- ..
- }) if args.len() == 1 => args[0].clone(),
+ NirKind::ListType(t) => t.clone(),
_ => panic!("internal type error"),
};
Ret::NirKind(NirKind::EmptyListLit(arg))
@@ -271,12 +263,12 @@ pub(crate) fn normalize_one_layer(expr: ExprKind<Nir>, env: &NzEnv) -> NirKind {
}
ExprKind::BoolIf(ref b, ref e1, ref e2) => {
match b.kind() {
- Lit(Bool(true)) => Ret::NirRef(e1),
- Lit(Bool(false)) => Ret::NirRef(e2),
+ Num(Bool(true)) => Ret::NirRef(e1),
+ Num(Bool(false)) => Ret::NirRef(e2),
_ => {
match (e1.kind(), e2.kind()) {
// Simplify `if b then True else False`
- (Lit(Bool(true)), Lit(Bool(false))) => Ret::NirRef(b),
+ (Num(Bool(true)), Num(Bool(false))) => Ret::NirRef(b),
_ if e1 == e2 => Ret::NirRef(e1),
_ => Ret::Expr(expr),
}
@@ -442,12 +434,8 @@ pub(crate) fn normalize_one_layer(expr: ExprKind<Nir>, env: &NzEnv) -> NirKind {
ExprKind::ToMap(ref v, ref annot) => match v.kind() {
RecordLit(kvs) if kvs.is_empty() => {
match annot.as_ref().map(|v| v.kind()) {
- Some(NirKind::AppliedBuiltin(BuiltinClosure {
- b: Builtin::List,
- args,
- ..
- })) if args.len() == 1 => {
- Ret::NirKind(EmptyListLit(args[0].clone()))
+ Some(NirKind::ListType(t)) => {
+ Ret::NirKind(EmptyListLit(t.clone()))
}
_ => Ret::Expr(expr),
}
@@ -476,7 +464,7 @@ pub(crate) fn normalize_one_layer(expr: ExprKind<Nir>, env: &NzEnv) -> NirKind {
}
/// Normalize Hir into WHNF
-pub(crate) fn normalize_hir_whnf(env: &NzEnv, hir: &Hir) -> NirKind {
+pub fn normalize_hir_whnf(env: &NzEnv, hir: &Hir) -> NirKind {
match hir.kind() {
HirKind::Var(var) => env.lookup_val(*var),
HirKind::Import(hir, _) => normalize_hir_whnf(env, hir),
diff --git a/dhall/src/semantics/nze/var.rs b/dhall/src/semantics/nze/var.rs
index 413c759..302dbb7 100644
--- a/dhall/src/semantics/nze/var.rs
+++ b/dhall/src/semantics/nze/var.rs
@@ -8,10 +8,10 @@ pub struct Binder {
}
impl Binder {
- pub(crate) fn new(name: Label) -> Self {
+ pub fn new(name: Label) -> Self {
Binder { name }
}
- pub(crate) fn to_label(&self) -> Label {
+ pub fn to_label(&self) -> Label {
self.clone().into()
}
}
diff --git a/dhall/src/semantics/parse.rs b/dhall/src/semantics/parse.rs
index 45860d0..2326471 100644
--- a/dhall/src/semantics/parse.rs
+++ b/dhall/src/semantics/parse.rs
@@ -9,33 +9,33 @@ use crate::syntax::binary;
use crate::syntax::parse_expr;
use crate::Parsed;
-pub(crate) fn parse_file(f: &Path) -> Result<Parsed, Error> {
+pub fn parse_file(f: &Path) -> Result<Parsed, Error> {
let text = std::fs::read_to_string(f)?;
let expr = parse_expr(&text)?;
let root = ImportLocation::Local(f.to_owned());
Ok(Parsed(expr, root))
}
-pub(crate) fn parse_remote(url: Url) -> Result<Parsed, Error> {
+pub fn parse_remote(url: Url) -> Result<Parsed, Error> {
let body = reqwest::blocking::get(url.clone()).unwrap().text().unwrap();
let expr = parse_expr(&body)?;
let root = ImportLocation::Remote(url);
Ok(Parsed(expr, root))
}
-pub(crate) fn parse_str(s: &str) -> Result<Parsed, Error> {
+pub fn parse_str(s: &str) -> Result<Parsed, Error> {
let expr = parse_expr(s)?;
let root = ImportLocation::Missing;
Ok(Parsed(expr, root))
}
-pub(crate) fn parse_binary(data: &[u8]) -> Result<Parsed, Error> {
+pub fn parse_binary(data: &[u8]) -> Result<Parsed, Error> {
let expr = binary::decode(data)?;
let root = ImportLocation::Missing;
Ok(Parsed(expr, root))
}
-pub(crate) fn parse_binary_file(f: &Path) -> Result<Parsed, Error> {
+pub fn parse_binary_file(f: &Path) -> Result<Parsed, Error> {
let mut buffer = Vec::new();
File::open(f)?.read_to_end(&mut buffer)?;
let expr = binary::decode(&buffer)?;
diff --git a/dhall/src/semantics/resolve/env.rs b/dhall/src/semantics/resolve/env.rs
index fe8c178..d7ff0ae 100644
--- a/dhall/src/semantics/resolve/env.rs
+++ b/dhall/src/semantics/resolve/env.rs
@@ -5,24 +5,24 @@ use crate::semantics::{AlphaVar, ImportLocation, TypedHir, VarEnv};
use crate::syntax::{Label, V};
/// Environment for resolving names.
-#[derive(Debug, Clone)]
-pub(crate) struct NameEnv {
+#[derive(Debug, Clone, Default)]
+pub struct NameEnv {
names: Vec<Label>,
}
-pub(crate) type ImportCache = HashMap<ImportLocation, TypedHir>;
-pub(crate) type ImportStack = Vec<ImportLocation>;
+pub type ImportCache = HashMap<ImportLocation, TypedHir>;
+pub type ImportStack = Vec<ImportLocation>;
/// Environment for resolving imports
-#[derive(Debug, Clone)]
-pub(crate) struct ImportEnv {
+#[derive(Debug, Clone, Default)]
+pub struct ImportEnv {
cache: ImportCache,
stack: ImportStack,
}
impl NameEnv {
pub fn new() -> Self {
- NameEnv { names: Vec::new() }
+ NameEnv::default()
}
pub fn as_varenv(&self) -> VarEnv {
VarEnv::from_size(self.names.len())
@@ -66,10 +66,7 @@ impl NameEnv {
impl ImportEnv {
pub fn new() -> Self {
- ImportEnv {
- cache: HashMap::new(),
- stack: Vec::new(),
- }
+ ImportEnv::default()
}
pub fn handle_import(
diff --git a/dhall/src/semantics/resolve/hir.rs b/dhall/src/semantics/resolve/hir.rs
index fa2989f..9256425 100644
--- a/dhall/src/semantics/resolve/hir.rs
+++ b/dhall/src/semantics/resolve/hir.rs
@@ -1,7 +1,7 @@
use crate::error::TypeError;
use crate::semantics::{type_with, NameEnv, Nir, NzEnv, Tir, TyEnv, Type};
use crate::syntax::{Expr, ExprKind, Span, V};
-use crate::{NormalizedExpr, ToExprOptions};
+use crate::ToExprOptions;
/// Stores an alpha-normalized variable.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -10,7 +10,7 @@ pub struct AlphaVar {
}
#[derive(Debug, Clone, PartialEq, Eq)]
-pub(crate) enum HirKind {
+pub enum HirKind {
/// A resolved variable (i.e. a DeBruijn index)
Var(AlphaVar),
/// Result of resolving an import.
@@ -21,16 +21,16 @@ pub(crate) enum HirKind {
// An expression with resolved variables and imports.
#[derive(Debug, Clone)]
-pub(crate) struct Hir {
+pub struct Hir {
kind: Box<HirKind>,
span: Span,
}
impl AlphaVar {
- pub(crate) fn new(idx: usize) -> Self {
+ pub fn new(idx: usize) -> Self {
AlphaVar { idx }
}
- pub(crate) fn idx(self) -> usize {
+ pub fn idx(self) -> usize {
self.idx
}
}
@@ -51,15 +51,15 @@ impl Hir {
}
/// Converts a closed Hir expr back to the corresponding AST expression.
- pub fn to_expr(&self, opts: ToExprOptions) -> NormalizedExpr {
+ pub fn to_expr(&self, opts: ToExprOptions) -> Expr {
hir_to_expr(self, opts, &mut NameEnv::new())
}
/// Converts a closed Hir expr back to the corresponding AST expression.
- pub fn to_expr_noopts(&self) -> NormalizedExpr {
+ pub fn to_expr_noopts(&self) -> Expr {
let opts = ToExprOptions { alpha: false };
self.to_expr(opts)
}
- pub fn to_expr_tyenv(&self, env: &TyEnv) -> NormalizedExpr {
+ pub fn to_expr_tyenv(&self, env: &TyEnv) -> Expr {
let opts = ToExprOptions { alpha: false };
let mut env = env.as_nameenv().clone();
hir_to_expr(self, opts, &mut env)
@@ -85,19 +85,9 @@ impl Hir {
pub fn eval_closed_expr(&self) -> Nir {
self.eval(NzEnv::new())
}
- /// Eval a closed Hir fully and recursively;
- pub fn rec_eval_closed_expr(&self) -> Nir {
- let val = self.eval_closed_expr();
- val.normalize();
- val
- }
}
-fn hir_to_expr(
- hir: &Hir,
- opts: ToExprOptions,
- env: &mut NameEnv,
-) -> NormalizedExpr {
+fn hir_to_expr(hir: &Hir, opts: ToExprOptions, env: &mut NameEnv) -> Expr {
let kind = match hir.kind() {
HirKind::Var(v) if opts.alpha => ExprKind::Var(V("_".into(), v.idx())),
HirKind::Var(v) => ExprKind::Var(env.label_var(*v)),
diff --git a/dhall/src/semantics/resolve/mod.rs b/dhall/src/semantics/resolve/mod.rs
index 517907b..33b477e 100644
--- a/dhall/src/semantics/resolve/mod.rs
+++ b/dhall/src/semantics/resolve/mod.rs
@@ -1,6 +1,6 @@
pub mod env;
pub mod hir;
pub mod resolve;
-pub(crate) use env::*;
-pub(crate) use hir::*;
-pub(crate) use resolve::*;
+pub use env::*;
+pub use hir::*;
+pub use resolve::*;
diff --git a/dhall/src/semantics/resolve/resolve.rs b/dhall/src/semantics/resolve/resolve.rs
index f3fda4b..7745e0b 100644
--- a/dhall/src/semantics/resolve/resolve.rs
+++ b/dhall/src/semantics/resolve/resolve.rs
@@ -13,17 +13,17 @@ use crate::syntax::{
BinOp, Builtin, Expr, ExprKind, FilePath, FilePrefix, ImportMode,
ImportTarget, Span, UnspannedExpr, URL,
};
-use crate::{Parsed, ParsedExpr, Resolved};
+use crate::{Parsed, Resolved};
// TODO: evaluate import headers
-pub(crate) type Import = syntax::Import<()>;
+pub type Import = syntax::Import<()>;
/// Owned Hir with a type. Different from Tir because the Hir is owned.
-pub(crate) type TypedHir = (Hir, Type);
+pub type TypedHir = (Hir, Type);
/// The location of some data, usually some dhall code.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub(crate) enum ImportLocation {
+pub enum ImportLocation {
/// Local file
Local(PathBuf),
/// Remote file
@@ -227,7 +227,7 @@ fn resolve_one_import(
}
ImportMode::Location => {
let expr = location.into_location();
- let hir = skip_resolve(&expr)?;
+ let hir = skip_resolve_expr(&expr)?;
let ty = hir.typecheck_noenv()?.ty().clone();
Ok((hir, ty))
}
@@ -329,16 +329,22 @@ fn resolve_with_env(
Ok(Resolved(resolved))
}
-pub(crate) fn resolve(parsed: Parsed) -> Result<Resolved, Error> {
+pub fn resolve(parsed: Parsed) -> Result<Resolved, Error> {
resolve_with_env(&mut ImportEnv::new(), parsed)
}
-pub(crate) fn skip_resolve(expr: &ParsedExpr) -> Result<Hir, Error> {
+pub fn skip_resolve_expr(expr: &Expr) -> Result<Hir, Error> {
traverse_resolve_expr(&mut NameEnv::new(), expr, &mut |import| {
Err(ImportError::UnexpectedImport(import).into())
})
}
+pub fn skip_resolve(parsed: Parsed) -> Result<Resolved, Error> {
+ let Parsed(expr, _) = parsed;
+ let resolved = skip_resolve_expr(&expr)?;
+ Ok(Resolved(resolved))
+}
+
pub trait Canonicalize {
fn canonicalize(&self) -> Self;
}
diff --git a/dhall/src/semantics/tck/env.rs b/dhall/src/semantics/tck/env.rs
index 6dd5076..1fa66f0 100644
--- a/dhall/src/semantics/tck/env.rs
+++ b/dhall/src/semantics/tck/env.rs
@@ -2,21 +2,21 @@ use crate::semantics::{AlphaVar, NameEnv, Nir, NzEnv, NzVar, Type, ValEnv};
use crate::syntax::Label;
/// Environment for indexing variables.
-#[derive(Debug, Clone, Copy)]
-pub(crate) struct VarEnv {
+#[derive(Debug, Clone, Copy, Default)]
+pub struct VarEnv {
size: usize,
}
/// Environment for typing expressions.
#[derive(Debug, Clone)]
-pub(crate) struct TyEnv {
+pub struct TyEnv {
names: NameEnv,
items: ValEnv<Type>,
}
impl VarEnv {
pub fn new() -> Self {
- VarEnv { size: 0 }
+ VarEnv::default()
}
pub fn from_size(size: usize) -> Self {
VarEnv { size }
diff --git a/dhall/src/semantics/tck/mod.rs b/dhall/src/semantics/tck/mod.rs
index 93c8f48..6dddfc5 100644
--- a/dhall/src/semantics/tck/mod.rs
+++ b/dhall/src/semantics/tck/mod.rs
@@ -1,6 +1,6 @@
pub mod env;
pub mod tir;
pub mod typecheck;
-pub(crate) use env::*;
-pub(crate) use tir::*;
-pub(crate) use typecheck::*;
+pub use env::*;
+pub use tir::*;
+pub use typecheck::*;
diff --git a/dhall/src/semantics/tck/tir.rs b/dhall/src/semantics/tck/tir.rs
index aeb7bf9..89a8027 100644
--- a/dhall/src/semantics/tck/tir.rs
+++ b/dhall/src/semantics/tck/tir.rs
@@ -1,15 +1,14 @@
use crate::error::{ErrorBuilder, TypeError};
use crate::semantics::{mkerr, Hir, Nir, NirKind, NzEnv, TyEnv, VarEnv};
-use crate::syntax::{Builtin, Const, Span};
-use crate::NormalizedExpr;
+use crate::syntax::{Builtin, Const, Expr, Span};
/// The type of a type. 0 is `Type`, 1 is `Kind`, etc...
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
-pub(crate) struct Universe(u8);
+pub struct Universe(u8);
/// An expression representing a type
#[derive(Debug, Clone, PartialEq, Eq)]
-pub(crate) struct Type {
+pub struct Type {
val: Nir,
univ: Universe,
}
@@ -17,7 +16,7 @@ pub(crate) struct Type {
/// A hir expression plus its inferred type.
/// Stands for "Typed intermediate representation"
#[derive(Debug, Clone)]
-pub(crate) struct Tir<'hir> {
+pub struct Tir<'hir> {
hir: &'hir Hir,
ty: Type,
}
@@ -101,7 +100,7 @@ impl Type {
pub fn to_hir(&self, venv: VarEnv) -> Hir {
self.val.to_hir(venv)
}
- pub fn to_expr_tyenv(&self, tyenv: &TyEnv) -> NormalizedExpr {
+ pub fn to_expr_tyenv(&self, tyenv: &TyEnv) -> Expr {
self.val.to_hir(tyenv.as_varenv()).to_expr_tyenv(tyenv)
}
}
@@ -124,7 +123,7 @@ impl<'hir> Tir<'hir> {
pub fn as_hir(&self) -> &Hir {
&self.hir
}
- pub fn to_expr_tyenv(&self, env: &TyEnv) -> NormalizedExpr {
+ pub fn to_expr_tyenv(&self, env: &TyEnv) -> Expr {
self.as_hir().to_expr_tyenv(env)
}
diff --git a/dhall/src/semantics/tck/typecheck.rs b/dhall/src/semantics/tck/typecheck.rs
index 173b76d..c3334b5 100644
--- a/dhall/src/semantics/tck/typecheck.rs
+++ b/dhall/src/semantics/tck/typecheck.rs
@@ -5,11 +5,11 @@ use std::collections::HashMap;
use crate::error::{ErrorBuilder, TypeError, TypeMessage};
use crate::semantics::merge_maps;
use crate::semantics::{
- type_of_builtin, Binder, BuiltinClosure, Closure, Hir, HirKind, Nir,
- NirKind, Tir, TyEnv, Type,
+ type_of_builtin, Binder, Closure, Hir, HirKind, Nir, NirKind, Tir, TyEnv,
+ Type,
};
use crate::syntax::{
- BinOp, Builtin, Const, ExprKind, InterpolatedTextContents, LitKind, Span,
+ BinOp, Builtin, Const, ExprKind, InterpolatedTextContents, NumKind, Span,
};
fn check_rectymerge(
@@ -53,14 +53,11 @@ fn function_check(a: Const, b: Const) -> Const {
}
}
-pub(crate) fn mkerr<T, S: ToString>(msg: S) -> Result<T, TypeError> {
+pub fn mkerr<T, S: ToString>(msg: S) -> Result<T, TypeError> {
Err(TypeError::new(TypeMessage::Custom(msg.to_string())))
}
-pub(crate) fn mk_span_err<T, S: ToString>(
- span: Span,
- msg: S,
-) -> Result<T, TypeError> {
+pub fn mk_span_err<T, S: ToString>(span: Span, msg: S) -> Result<T, TypeError> {
mkerr(
ErrorBuilder::new(msg.to_string())
.span_err(span, msg.to_string())
@@ -96,14 +93,14 @@ fn type_one_layer(
let t_hir = type_of_builtin(*b);
typecheck(&t_hir)?.eval_to_type(env)?
}
- ExprKind::Lit(LitKind::Bool(_)) => Type::from_builtin(Builtin::Bool),
- ExprKind::Lit(LitKind::Natural(_)) => {
+ ExprKind::Num(NumKind::Bool(_)) => Type::from_builtin(Builtin::Bool),
+ ExprKind::Num(NumKind::Natural(_)) => {
Type::from_builtin(Builtin::Natural)
}
- ExprKind::Lit(LitKind::Integer(_)) => {
+ ExprKind::Num(NumKind::Integer(_)) => {
Type::from_builtin(Builtin::Integer)
}
- ExprKind::Lit(LitKind::Double(_)) => {
+ ExprKind::Num(NumKind::Double(_)) => {
Type::from_builtin(Builtin::Double)
}
ExprKind::TextLit(interpolated) => {
@@ -121,11 +118,7 @@ fn type_one_layer(
ExprKind::EmptyListLit(t) => {
let t = t.eval_to_type(env)?;
match t.kind() {
- NirKind::AppliedBuiltin(BuiltinClosure {
- b: Builtin::List,
- args,
- ..
- }) if args.len() == 1 => {}
+ NirKind::ListType(..) => {}
_ => return span_err("InvalidListType"),
};
t
@@ -376,10 +369,7 @@ fn type_one_layer(
}
ExprKind::BinOp(BinOp::ListAppend, l, r) => {
match l.ty().kind() {
- NirKind::AppliedBuiltin(BuiltinClosure {
- b: Builtin::List,
- ..
- }) => {}
+ NirKind::ListType(..) => {}
_ => return span_err("BinOpTypeMismatch"),
}
@@ -435,12 +425,7 @@ fn type_one_layer(
let union_type = union.ty();
let variants = match union_type.kind() {
NirKind::UnionType(kts) => Cow::Borrowed(kts),
- NirKind::AppliedBuiltin(BuiltinClosure {
- b: Builtin::Optional,
- args,
- ..
- }) if args.len() == 1 => {
- let ty = &args[0];
+ NirKind::OptionalType(ty) => {
let mut kts = HashMap::new();
kts.insert("None".into(), None);
kts.insert("Some".into(), Some(ty.clone()));
@@ -595,11 +580,7 @@ fn type_one_layer(
let err_msg = "The type of `toMap x` must be of the form \
`List { mapKey : Text, mapValue : T }`";
let arg = match annot_val.kind() {
- NirKind::AppliedBuiltin(BuiltinClosure {
- b: Builtin::List,
- args,
- ..
- }) if args.len() == 1 => &args[0],
+ NirKind::ListType(t) => t,
_ => return span_err(err_msg),
};
let kts = match arg.kind() {
@@ -704,7 +685,7 @@ fn type_one_layer(
/// `type_with` typechecks an expression in the provided environment. Optionally pass an annotation
/// to compare with.
-pub(crate) fn type_with<'hir>(
+pub fn type_with<'hir>(
env: &TyEnv,
hir: &'hir Hir,
annot: Option<Type>,
@@ -801,15 +782,15 @@ pub(crate) fn type_with<'hir>(
/// Typecheck an expression and return the expression annotated with types if type-checking
/// succeeded, or an error if type-checking failed.
-pub(crate) fn typecheck<'hir>(hir: &'hir Hir) -> Result<Tir<'hir>, TypeError> {
+pub fn typecheck<'hir>(hir: &'hir Hir) -> Result<Tir<'hir>, TypeError> {
type_with(&TyEnv::new(), hir, None)
}
/// Like `typecheck`, but additionally checks that the expression's type matches the provided type.
-pub(crate) fn typecheck_with<'hir>(
+pub fn typecheck_with<'hir>(
hir: &'hir Hir,
- ty: Hir,
+ ty: &Hir,
) -> Result<Tir<'hir>, TypeError> {
- let ty = typecheck(&ty)?.eval_to_type(&TyEnv::new())?;
+ let ty = typecheck(ty)?.eval_to_type(&TyEnv::new())?;
type_with(&TyEnv::new(), hir, Some(ty))
}
diff --git a/dhall/src/syntax/ast/expr.rs b/dhall/src/syntax/ast/expr.rs
index b53e6cb..6ba6649 100644
--- a/dhall/src/syntax/ast/expr.rs
+++ b/dhall/src/syntax/ast/expr.rs
@@ -22,7 +22,7 @@ pub enum Const {
}
impl Const {
- pub(crate) fn to_universe(self) -> Universe {
+ pub fn to_universe(self) -> Universe {
Universe::from_const(self)
}
}
@@ -112,9 +112,9 @@ pub struct Expr {
pub type UnspannedExpr = ExprKind<Expr>;
-/// Simple literals
+/// Numeric literals
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum LitKind {
+pub enum NumKind {
/// `True`
Bool(bool),
/// `1`
@@ -132,7 +132,7 @@ pub enum LitKind {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ExprKind<SubExpr> {
Const(Const),
- Lit(LitKind),
+ Num(NumKind),
/// `x`
/// `x@n`
Var(V),
@@ -205,7 +205,7 @@ impl<SE> ExprKind<SE> {
})
}
- pub(crate) fn traverse_ref<'a, SE2, Err>(
+ pub fn traverse_ref<'a, SE2, Err>(
&'a self,
mut visit_subexpr: impl FnMut(&'a SE) -> Result<SE2, Err>,
) -> Result<ExprKind<SE2>, Err> {
@@ -239,17 +239,17 @@ impl<SE> ExprKind<SE> {
}
impl Expr {
- pub(crate) fn as_ref(&self) -> &UnspannedExpr {
+ pub fn as_ref(&self) -> &UnspannedExpr {
&self.kind
}
pub fn kind(&self) -> &UnspannedExpr {
&self.kind
}
- pub(crate) fn span(&self) -> Span {
+ pub fn span(&self) -> Span {
self.span.clone()
}
- pub(crate) fn new(kind: UnspannedExpr, span: Span) -> Self {
+ pub fn new(kind: UnspannedExpr, span: Span) -> Self {
Expr {
kind: Box::new(kind),
span,
diff --git a/dhall/src/syntax/ast/mod.rs b/dhall/src/syntax/ast/mod.rs
index 5e20c5d..1950154 100644
--- a/dhall/src/syntax/ast/mod.rs
+++ b/dhall/src/syntax/ast/mod.rs
@@ -5,7 +5,7 @@ pub use import::*;
mod label;
pub use label::*;
mod span;
-pub(crate) use span::*;
+pub use span::*;
mod text;
pub use text::*;
pub mod map;
diff --git a/dhall/src/syntax/ast/span.rs b/dhall/src/syntax/ast/span.rs
index 2e09863..e250602 100644
--- a/dhall/src/syntax/ast/span.rs
+++ b/dhall/src/syntax/ast/span.rs
@@ -2,7 +2,7 @@ use std::rc::Rc;
/// A location in the source text
#[derive(Debug, Clone)]
-pub(crate) struct ParsedSpan {
+pub struct ParsedSpan {
input: Rc<str>,
/// # Safety
///
@@ -15,7 +15,7 @@ pub(crate) struct ParsedSpan {
}
#[derive(Debug, Clone)]
-pub(crate) enum Span {
+pub enum Span {
/// A location in the source text
Parsed(ParsedSpan),
/// Desugarings
@@ -30,12 +30,12 @@ pub(crate) enum Span {
}
impl ParsedSpan {
- pub(crate) fn to_input(&self) -> String {
+ pub fn to_input(&self) -> String {
self.input.to_string()
}
/// Convert to a char range for consumption by annotate_snippets.
/// This compensates for https://github.com/rust-lang/annotate-snippets-rs/issues/24
- pub(crate) fn as_char_range(&self) -> (usize, usize) {
+ pub fn as_char_range(&self) -> (usize, usize) {
(
char_idx_from_byte_idx(&self.input, self.start),
char_idx_from_byte_idx(&self.input, self.end),
@@ -44,7 +44,7 @@ impl ParsedSpan {
}
impl Span {
- pub(crate) fn make(input: Rc<str>, sp: pest::Span) -> Self {
+ pub fn make(input: Rc<str>, sp: pest::Span) -> Self {
Span::Parsed(ParsedSpan {
input,
start: sp.start(),
@@ -55,7 +55,7 @@ impl Span {
/// Takes the union of the two spans, i.e. the range of input covered by the two spans plus any
/// input between them. Assumes that the spans come from the same input. Fails if one of the
/// spans does not point to an input location.
- pub(crate) fn union(&self, other: &Span) -> Self {
+ pub fn union(&self, other: &Span) -> Self {
use std::cmp::{max, min};
use Span::*;
match (self, other) {
diff --git a/dhall/src/syntax/ast/visitor.rs b/dhall/src/syntax/ast/visitor.rs
index c361bc1..0a0c5ef 100644
--- a/dhall/src/syntax/ast/visitor.rs
+++ b/dhall/src/syntax/ast/visitor.rs
@@ -51,7 +51,7 @@ where
.collect()
}
-pub(crate) fn visit_ref<'a, F, SE1, SE2, Err>(
+pub fn visit_ref<'a, F, SE1, SE2, Err>(
input: &'a ExprKind<SE1>,
mut f: F,
) -> Result<ExprKind<SE2>, Err>
@@ -91,7 +91,7 @@ where
Annot(x, t) => Annot(expr!(x)?, expr!(t)?),
Const(k) => Const(*k),
Builtin(v) => Builtin(*v),
- Lit(l) => Lit(l.clone()),
+ Num(n) => Num(n.clone()),
TextLit(t) => TextLit(t.traverse_ref(|e| expr!(e))?),
BinOp(o, x, y) => BinOp(*o, expr!(x)?, expr!(y)?),
BoolIf(b, t, f) => BoolIf(expr!(b)?, expr!(t)?, expr!(f)?),
diff --git a/dhall/src/syntax/binary/decode.rs b/dhall/src/syntax/binary/decode.rs
index 2ecd7e0..3c93419 100644
--- a/dhall/src/syntax/binary/decode.rs
+++ b/dhall/src/syntax/binary/decode.rs
@@ -6,12 +6,12 @@ use crate::error::DecodeError;
use crate::syntax;
use crate::syntax::{
Expr, ExprKind, FilePath, FilePrefix, Hash, ImportMode, ImportTarget,
- Integer, InterpolatedText, Label, LitKind, Natural, Scheme, Span,
+ Integer, InterpolatedText, Label, Natural, NumKind, Scheme, Span,
UnspannedExpr, URL, V,
};
-use crate::DecodedExpr;
+type DecodedExpr = Expr;
-pub(crate) fn decode(data: &[u8]) -> Result<DecodedExpr, DecodeError> {
+pub fn decode(data: &[u8]) -> Result<DecodedExpr, DecodeError> {
match serde_cbor::de::from_slice(data) {
Ok(v) => cbor_value_to_dhall(&v),
Err(e) => Err(DecodeError::CBORError(e)),
@@ -31,8 +31,8 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result<DecodedExpr, DecodeError> {
String(s) => match Builtin::parse(s) {
Some(b) => ExprKind::Builtin(b),
None => match s.as_str() {
- "True" => Lit(LitKind::Bool(true)),
- "False" => Lit(LitKind::Bool(false)),
+ "True" => Num(NumKind::Bool(true)),
+ "False" => Num(NumKind::Bool(false)),
"Type" => Const(Const::Type),
"Kind" => Const(Const::Kind),
"Sort" => Const(Const::Sort),
@@ -44,8 +44,8 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result<DecodedExpr, DecodeError> {
},
},
U64(n) => Var(V(Label::from("_"), *n as usize)),
- F64(x) => Lit(LitKind::Double((*x).into())),
- Bool(b) => Lit(LitKind::Bool(*b)),
+ F64(x) => Num(NumKind::Double((*x).into())),
+ Bool(b) => Num(NumKind::Bool(*b)),
Array(vec) => match vec.as_slice() {
[String(l), U64(n)] => {
if l.as_str() == "_" {
@@ -224,9 +224,9 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result<DecodedExpr, DecodeError> {
let z = cbor_value_to_dhall(&z)?;
BoolIf(x, y, z)
}
- [U64(15), U64(x)] => Lit(LitKind::Natural(*x as Natural)),
- [U64(16), U64(x)] => Lit(LitKind::Integer(*x as Integer)),
- [U64(16), I64(x)] => Lit(LitKind::Integer(*x as Integer)),
+ [U64(15), U64(x)] => Num(NumKind::Natural(*x as Natural)),
+ [U64(16), U64(x)] => Num(NumKind::Integer(*x as Integer)),
+ [U64(16), I64(x)] => Num(NumKind::Integer(*x as Integer)),
[U64(18), String(first), rest @ ..] => {
TextLit(InterpolatedText::from((
first.clone(),
diff --git a/dhall/src/syntax/binary/encode.rs b/dhall/src/syntax/binary/encode.rs
index 9e6948e..8d22a9b 100644
--- a/dhall/src/syntax/binary/encode.rs
+++ b/dhall/src/syntax/binary/encode.rs
@@ -10,7 +10,7 @@ use crate::syntax::{
Scheme, V,
};
-pub(crate) fn encode(expr: &Expr) -> Result<Vec<u8>, EncodeError> {
+pub fn encode(expr: &Expr) -> Result<Vec<u8>, EncodeError> {
serde_cbor::ser::to_vec(&Serialize::Expr(expr))
.map_err(EncodeError::CBORError)
}
@@ -48,7 +48,7 @@ where
use std::iter::once;
use syntax::Builtin;
use syntax::ExprKind::*;
- use syntax::LitKind::*;
+ use syntax::NumKind::*;
use self::Serialize::{RecordDupMap, RecordMap, UnionMap};
fn expr(x: &Expr) -> self::Serialize<'_> {
@@ -63,10 +63,10 @@ where
match e.as_ref() {
Const(c) => ser.serialize_str(&c.to_string()),
Builtin(b) => ser.serialize_str(&b.to_string()),
- Lit(Bool(b)) => ser.serialize_bool(*b),
- Lit(Natural(n)) => ser_seq!(ser; tag(15), U64(*n as u64)),
- Lit(Integer(n)) => ser_seq!(ser; tag(16), I64(*n as i64)),
- Lit(Double(n)) => {
+ Num(Bool(b)) => ser.serialize_bool(*b),
+ Num(Natural(n)) => ser_seq!(ser; tag(15), U64(*n as u64)),
+ Num(Integer(n)) => ser_seq!(ser; tag(16), I64(*n as i64)),
+ Num(Double(n)) => {
let n: f64 = (*n).into();
ser.serialize_f64(n)
}
diff --git a/dhall/src/syntax/binary/mod.rs b/dhall/src/syntax/binary/mod.rs
index 7ed1f6e..98e0520 100644
--- a/dhall/src/syntax/binary/mod.rs
+++ b/dhall/src/syntax/binary/mod.rs
@@ -1,4 +1,4 @@
mod decode;
mod encode;
-pub(crate) use decode::decode;
-pub(crate) use encode::encode;
+pub use decode::decode;
+pub use encode::encode;
diff --git a/dhall/src/syntax/text/parser.rs b/dhall/src/syntax/text/parser.rs
index 03211c7..6f5949f 100644
--- a/dhall/src/syntax/text/parser.rs
+++ b/dhall/src/syntax/text/parser.rs
@@ -9,7 +9,7 @@ use pest_consume::{match_nodes, Parser};
use crate::syntax::map::{DupTreeMap, DupTreeSet};
use crate::syntax::ExprKind::*;
-use crate::syntax::LitKind::*;
+use crate::syntax::NumKind::*;
use crate::syntax::{
Double, Expr, FilePath, FilePrefix, Hash, ImportMode, ImportTarget,
Integer, InterpolatedText, InterpolatedTextContents, Label, NaiveDouble,
@@ -135,7 +135,7 @@ fn insert_recordlit_entry(map: &mut BTreeMap<Label, Expr>, l: Label, e: Expr) {
entry.insert(e);
}
Entry::Occupied(mut entry) => {
- let dummy = Expr::new(Lit(Bool(false)), Span::Artificial);
+ let dummy = Expr::new(Num(Bool(false)), Span::Artificial);
let other = entry.insert(dummy);
entry.insert(Expr::new(
BinOp(RecursiveRecordMerge, other, e),
@@ -390,8 +390,8 @@ impl DhallParser {
let e = match crate::syntax::Builtin::parse(s) {
Some(b) => Builtin(b),
None => match s {
- "True" => Lit(Bool(true)),
- "False" => Lit(Bool(false)),
+ "True" => Num(Bool(true)),
+ "False" => Num(Bool(false)),
"Type" => Const(crate::syntax::Const::Type),
"Kind" => Const(crate::syntax::Const::Kind),
"Sort" => Const(crate::syntax::Const::Sort),
@@ -924,9 +924,9 @@ impl DhallParser {
#[alias(expression, shortcut = true)]
fn primitive_expression(input: ParseInput) -> ParseResult<Expr> {
Ok(match_nodes!(input.children();
- [double_literal(n)] => spanned(input, Lit(Double(n))),
- [natural_literal(n)] => spanned(input, Lit(Natural(n))),
- [integer_literal(n)] => spanned(input, Lit(Integer(n))),
+ [double_literal(n)] => spanned(input, Num(Double(n))),
+ [natural_literal(n)] => spanned(input, Num(Natural(n))),
+ [integer_literal(n)] => spanned(input, Num(Integer(n))),
[double_quote_literal(s)] => spanned(input, TextLit(s)),
[single_quote_literal(s)] => spanned(input, TextLit(s)),
[record_type_or_literal(e)] => spanned(input, e),
diff --git a/dhall/src/syntax/text/printer.rs b/dhall/src/syntax/text/printer.rs
index 2b7bc2e..378f408 100644
--- a/dhall/src/syntax/text/printer.rs
+++ b/dhall/src/syntax/text/printer.rs
@@ -201,7 +201,7 @@ impl<SE: Display + Clone> Display for ExprKind<SE> {
Var(a) => a.fmt(f)?,
Const(k) => k.fmt(f)?,
Builtin(v) => v.fmt(f)?,
- Lit(a) => a.fmt(f)?,
+ Num(a) => a.fmt(f)?,
TextLit(a) => a.fmt(f)?,
RecordType(a) if a.is_empty() => f.write_str("{}")?,
RecordType(a) => fmt_list("{ ", ", ", " }", a, f, |(k, t), f| {
@@ -240,9 +240,9 @@ impl Display for Expr {
}
}
-impl Display for LitKind {
+impl Display for NumKind {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
- use LitKind::*;
+ use NumKind::*;
match self {
Bool(true) => f.write_str("True")?,
Bool(false) => f.write_str("False")?,
diff --git a/dhall/src/tests.rs b/dhall/src/tests.rs
index a5a278c..2cd354f 100644
--- a/dhall/src/tests.rs
+++ b/dhall/src/tests.rs
@@ -10,8 +10,8 @@ use std::io::{Read, Write};
use std::path::PathBuf;
use crate::error::{ErrorKind, Result};
-use crate::syntax::binary;
-use crate::{Normalized, NormalizedExpr, Parsed, Resolved, Typed};
+use crate::syntax::{binary, Expr};
+use crate::{Normalized, Parsed, Resolved, Typed};
macro_rules! assert_eq_display {
($left:expr, $right:expr) => {{
@@ -111,7 +111,7 @@ impl TestFile {
env::var("UPDATE_TEST_FILES") == Ok("1".to_string())
}
/// Write the provided expression to the pointed file.
- fn write_expr(&self, expr: impl Into<NormalizedExpr>) -> Result<()> {
+ fn write_expr(&self, expr: impl Into<Expr>) -> Result<()> {
let expr = expr.into();
let path = self.path();
create_dir_all(path.parent().unwrap())?;
@@ -142,7 +142,7 @@ impl TestFile {
}
/// Check that the provided expression matches the file contents.
- pub fn compare(&self, expr: impl Into<NormalizedExpr>) -> Result<()> {
+ pub fn compare(&self, expr: impl Into<Expr>) -> Result<()> {
let expr = expr.into();
if !self.path().is_file() {
return self.write_expr(expr);
@@ -159,7 +159,7 @@ impl TestFile {
Ok(())
}
/// Check that the provided expression matches the file contents.
- pub fn compare_debug(&self, expr: impl Into<NormalizedExpr>) -> Result<()> {
+ pub fn compare_debug(&self, expr: impl Into<Expr>) -> Result<()> {
let expr = expr.into();
if !self.path().is_file() {
return self.write_expr(expr);
@@ -176,10 +176,7 @@ impl TestFile {
Ok(())
}
/// Check that the provided expression matches the file contents.
- pub fn compare_binary(
- &self,
- expr: impl Into<NormalizedExpr>,
- ) -> Result<()> {
+ pub fn compare_binary(&self, expr: impl Into<Expr>) -> Result<()> {
let expr = expr.into();
match self {
TestFile::Binary(_) => {}
@@ -302,7 +299,7 @@ fn run_test(test: Test) -> Result<()> {
expected.compare(expr)?;
}
ImportFailure(expr, expected) => {
- let err = expr.parse()?.resolve().unwrap_err();
+ let err = expr.resolve().unwrap_err();
expected.compare_ui(err)?;
}
TypeInferenceSuccess(expr, expected) => {
diff --git a/dhall/tests/version_numbers.rs b/dhall/tests/version_numbers.rs
new file mode 100644
index 0000000..9f1d04a
--- /dev/null
+++ b/dhall/tests/version_numbers.rs
@@ -0,0 +1,4 @@
+#[test]
+fn test_html_root_url() {
+ version_sync::assert_html_root_url_updated!("src/lib.rs");
+}
diff --git a/dhall_proc_macros/Cargo.toml b/dhall_proc_macros/Cargo.toml
index 6de2850..48b55e8 100644
--- a/dhall_proc_macros/Cargo.toml
+++ b/dhall_proc_macros/Cargo.toml
@@ -17,3 +17,6 @@ itertools = "0.9.0"
quote = "1.0"
proc-macro2 = "1.0"
syn = "1.0"
+
+[dev-dependencies]
+version-sync = "0.8"
diff --git a/dhall_proc_macros/src/derive.rs b/dhall_proc_macros/src/derive.rs
index 48626a0..e484ec6 100644
--- a/dhall_proc_macros/src/derive.rs
+++ b/dhall_proc_macros/src/derive.rs
@@ -52,9 +52,11 @@ fn derive_for_struct(
let ty = static_type(ty);
quote!( (#name.to_owned(), #ty) )
});
- Ok(quote! { ::serde_dhall::value::Value::make_record_type(
- vec![ #(#entries),* ].into_iter()
- ) })
+ Ok(quote! {
+ ::serde_dhall::SimpleType::Record(
+ vec![ #(#entries),* ].into_iter().collect()
+ )
+ })
}
fn derive_for_enum(
@@ -89,9 +91,11 @@ fn derive_for_enum(
})
.collect::<Result<_, Error>>()?;
- Ok(quote! { ::serde_dhall::value::Value::make_union_type(
- vec![ #(#entries),* ].into_iter()
- ) })
+ Ok(quote! {
+ ::serde_dhall::SimpleType::Union(
+ vec![ #(#entries),* ].into_iter().collect()
+ )
+ })
}
pub fn derive_static_type_inner(
@@ -164,8 +168,7 @@ pub fn derive_static_type_inner(
impl #impl_generics ::serde_dhall::StaticType
for #ident #ty_generics
#where_clause {
- fn static_type() ->
- ::serde_dhall::value::Value {
+ fn static_type() -> ::serde_dhall::SimpleType {
#(#assertions)*
#get_type
}
diff --git a/dhall_proc_macros/tests/version_numbers.rs b/dhall_proc_macros/tests/version_numbers.rs
new file mode 100644
index 0000000..9f1d04a
--- /dev/null
+++ b/dhall_proc_macros/tests/version_numbers.rs
@@ -0,0 +1,4 @@
+#[test]
+fn test_html_root_url() {
+ version_sync::assert_html_root_url_updated!("src/lib.rs");
+}
diff --git a/serde_dhall/Cargo.toml b/serde_dhall/Cargo.toml
index b0030e9..6af883a 100644
--- a/serde_dhall/Cargo.toml
+++ b/serde_dhall/Cargo.toml
@@ -11,5 +11,11 @@ edition = "2018"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
-dhall = { version = "0.4.0", path = "../dhall" }
-dhall_proc_macros = { version = "0.4.0", path = "../dhall_proc_macros" }
+dhall = { version = "=0.4.0", path = "../dhall" }
+dhall_proc_macros = { version = "=0.4.0", path = "../dhall_proc_macros" }
+doc-comment = "0.3"
+reqwest = { version = "0.10", features = ["blocking"] }
+url = "2.1"
+
+[dev-dependencies]
+version-sync = "0.8"
diff --git a/serde_dhall/src/deserialize.rs b/serde_dhall/src/deserialize.rs
new file mode 100644
index 0000000..92be2e9
--- /dev/null
+++ b/serde_dhall/src/deserialize.rs
@@ -0,0 +1,143 @@
+use serde::de::value::{
+ MapAccessDeserializer, MapDeserializer, SeqDeserializer,
+};
+use std::borrow::Cow;
+
+use dhall::syntax::NumKind;
+
+use crate::value::SimpleValue;
+use crate::{Error, ErrorKind, Result, Value};
+
+pub trait Sealed {}
+
+/// A data structure that can be deserialized from a Dhall expression.
+///
+/// This is automatically implemented for any type that [serde] can deserialize.
+/// In fact, this trait cannot be implemented manually. To implement it for your type,
+/// use serde's derive mechanism.
+///
+/// # Example
+///
+/// ```rust
+/// # fn main() -> serde_dhall::Result<()> {
+/// use serde::Deserialize;
+///
+/// // Use serde's derive
+/// #[derive(Deserialize)]
+/// struct Point {
+/// x: u64,
+/// y: u64,
+/// }
+///
+/// // Convert a Dhall string to a Point.
+/// let point: Point = serde_dhall::from_str("{ x = 1, y = 1 + 1 }").parse()?;
+/// # Ok(())
+/// # }
+/// ```
+///
+/// [serde]: https://serde.rs
+pub trait FromDhall: Sealed + Sized {
+ #[doc(hidden)]
+ fn from_dhall(v: &Value) -> Result<Self>;
+}
+
+impl<T> Sealed for T where T: serde::de::DeserializeOwned {}
+
+struct Deserializer<'a>(Cow<'a, SimpleValue>);
+
+impl<T> FromDhall for T
+where
+ T: serde::de::DeserializeOwned,
+{
+ fn from_dhall(v: &Value) -> Result<Self> {
+ let sval = v.to_simple_value().ok_or_else(|| {
+ Error(ErrorKind::Deserialize(format!(
+ "this cannot be deserialized into the serde data model: {}",
+ v
+ )))
+ })?;
+ T::deserialize(Deserializer(Cow::Owned(sval)))
+ }
+}
+
+impl<'de: 'a, 'a> serde::de::IntoDeserializer<'de, Error> for Deserializer<'a> {
+ type Deserializer = Deserializer<'a>;
+ fn into_deserializer(self) -> Self::Deserializer {
+ self
+ }
+}
+
+impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> {
+ type Error = Error;
+
+ fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: serde::de::Visitor<'de>,
+ {
+ use std::convert::TryInto;
+ use NumKind::*;
+ use SimpleValue::*;
+
+ let val = |x| Deserializer(Cow::Borrowed(x));
+ match self.0.as_ref() {
+ Num(Bool(x)) => visitor.visit_bool(*x),
+ Num(Natural(x)) => {
+ if let Ok(x64) = (*x).try_into() {
+ visitor.visit_u64(x64)
+ } else if let Ok(x32) = (*x).try_into() {
+ visitor.visit_u32(x32)
+ } else {
+ unimplemented!()
+ }
+ }
+ Num(Integer(x)) => {
+ if let Ok(x64) = (*x).try_into() {
+ visitor.visit_i64(x64)
+ } else if let Ok(x32) = (*x).try_into() {
+ visitor.visit_i32(x32)
+ } else {
+ unimplemented!()
+ }
+ }
+ Num(Double(x)) => visitor.visit_f64((*x).into()),
+ Text(x) => visitor.visit_str(x),
+ List(xs) => {
+ visitor.visit_seq(SeqDeserializer::new(xs.iter().map(val)))
+ }
+ Optional(None) => visitor.visit_none(),
+ Optional(Some(x)) => visitor.visit_some(val(x)),
+ Record(m) => visitor.visit_map(MapDeserializer::new(
+ m.iter().map(|(k, v)| (k.as_ref(), val(v))),
+ )),
+ Union(field_name, Some(x)) => visitor.visit_enum(
+ MapAccessDeserializer::new(MapDeserializer::new(
+ Some((field_name.as_str(), val(x))).into_iter(),
+ )),
+ ),
+ Union(field_name, None) => visitor.visit_enum(
+ MapAccessDeserializer::new(MapDeserializer::new(
+ Some((field_name.as_str(), ())).into_iter(),
+ )),
+ ),
+ }
+ }
+
+ fn deserialize_tuple<V>(self, _: usize, visitor: V) -> Result<V::Value>
+ where
+ V: serde::de::Visitor<'de>,
+ {
+ let val = |x| Deserializer(Cow::Borrowed(x));
+ match self.0.as_ref() {
+ // Blindly takes keys in sorted order.
+ SimpleValue::Record(m) => visitor
+ .visit_seq(SeqDeserializer::new(m.iter().map(|(_, v)| val(v)))),
+ _ => self.deserialize_any(visitor),
+ }
+ }
+
+ serde::forward_to_deserialize_any! {
+ bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
+ bytes byte_buf option unit unit_struct newtype_struct seq
+ tuple_struct map struct enum identifier ignored_any
+ }
+}
diff --git a/serde_dhall/src/error.rs b/serde_dhall/src/error.rs
new file mode 100644
index 0000000..896e8b9
--- /dev/null
+++ b/serde_dhall/src/error.rs
@@ -0,0 +1,40 @@
+use dhall::error::Error as DhallError;
+
+/// Alias for a `Result` with the error type `serde_dhall::Error`.
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// Errors that can occur when deserializing Dhall data.
+#[derive(Debug)]
+pub struct Error(pub(crate) ErrorKind);
+
+#[derive(Debug)]
+pub(crate) enum ErrorKind {
+ Dhall(DhallError),
+ Deserialize(String),
+}
+
+impl From<ErrorKind> for Error {
+ fn from(kind: ErrorKind) -> Error {
+ Error(kind)
+ }
+}
+
+impl std::fmt::Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ match &self.0 {
+ ErrorKind::Dhall(err) => write!(f, "{}", err),
+ ErrorKind::Deserialize(err) => write!(f, "{}", err),
+ }
+ }
+}
+
+impl std::error::Error for Error {}
+
+impl serde::de::Error for Error {
+ fn custom<T>(msg: T) -> Self
+ where
+ T: std::fmt::Display,
+ {
+ ErrorKind::Deserialize(msg.to_string()).into()
+ }
+}
diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs
index 0a53420..c478b2a 100644
--- a/serde_dhall/src/lib.rs
+++ b/serde_dhall/src/lib.rs
@@ -1,4 +1,5 @@
#![doc(html_root_url = "https://docs.rs/serde_dhall/0.4.0")]
+#![warn(missing_docs, missing_doc_code_examples)]
//! [Dhall][dhall] is a programmable configuration language that provides a non-repetitive
//! alternative to JSON and YAML.
//!
@@ -16,20 +17,20 @@
//!
//! # Basic usage
//!
-//! The main entrypoint of this library is the [`from_str`][from_str] function. It reads a string
+//! The main entrypoint of this library is the [`from_str`](fn.from_str.html) function. It reads a string
//! containing a Dhall expression and deserializes it into any serde-compatible type.
//!
//! This could mean a common Rust type like `HashMap`:
//!
//! ```rust
-//! # fn main() -> serde_dhall::de::Result<()> {
+//! # fn main() -> serde_dhall::Result<()> {
//! use std::collections::HashMap;
//!
//! // Some Dhall data
//! let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }";
//!
//! // Deserialize it to a Rust type.
-//! let deserialized_map: HashMap<String, usize> = serde_dhall::from_str(data)?;
+//! let deserialized_map: HashMap<String, usize> = serde_dhall::from_str(data).parse()?;
//!
//! let mut expected_map = HashMap::new();
//! expected_map.insert("x".to_string(), 1);
@@ -43,10 +44,10 @@
//! or a custom datatype, using serde's `derive` mechanism:
//!
//! ```rust
-//! # fn main() -> serde_dhall::de::Result<()> {
+//! # fn main() -> serde_dhall::Result<()> {
//! use serde::Deserialize;
//!
-//! #[derive(Debug, Deserialize)]
+//! #[derive(Deserialize)]
//! struct Point {
//! x: u64,
//! y: u64,
@@ -56,7 +57,7 @@
//! let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }";
//!
//! // Convert the Dhall string to a Point.
-//! let point: Point = serde_dhall::from_str(data)?;
+//! let point: Point = serde_dhall::from_str(data).parse()?;
//! assert_eq!(point.x, 1);
//! assert_eq!(point.y, 2);
//!
@@ -64,67 +65,82 @@
//! # }
//! ```
//!
-//! # Type correspondence
-//!
-//! The following Dhall types correspond to the following Rust types:
-//!
-//! Dhall | Rust
-//! -------|------
-//! `Bool` | `bool`
-//! `Natural` | `u64`, `u32`, ...
-//! `Integer` | `i64`, `i32`, ...
-//! `Double` | `f64`, `f32`, ...
-//! `Text` | `String`
-//! `List T` | `Vec<T>`
-//! `Optional T` | `Option<T>`
-//! `{ x: T, y: U }` | structs
-//! `{ _1: T, _2: U }` | `(T, U)`, structs
-//! `{ x: T, y: T }` | `HashMap<String, T>`, structs
-//! `< x: T \| y: U >` | enums
-//! `T -> U` | unsupported
-//! `Prelude.JSON.Type` | unsupported
-//! `Prelude.Map.Type T U` | unsupported
-//!
-//!
//! # Replacing `serde_json` or `serde_yaml`
//!
-//! If you used to consume JSON or YAML, you only need to replace [serde_json::from_str] or
-//! [serde_yaml::from_str] with [serde_dhall::from_str][from_str].
+//! If you used to consume JSON or YAML, you only need to replace [`serde_json::from_str`] or
+//! [`serde_yaml::from_str`] with [`serde_dhall::from_str(…).parse()`](fn.from_str.html).
//!
-//! [serde_json::from_str]: https://docs.serde.rs/serde_json/de/fn.from_str.html
-//! [serde_yaml::from_str]: https://docs.serde.rs/serde_yaml/fn.from_str.html
+//! [`serde_json::from_str`]: https://docs.serde.rs/serde_json/fn.from_str.html
+//! [`serde_yaml::from_str`]: https://docs.serde.rs/serde_yaml/fn.from_str.html
//!
//!
//! # Additional Dhall typechecking
//!
//! When deserializing, normal type checking is done to ensure that the returned value is a valid
-//! Dhall value, and that it can be deserialized into the required Rust type. However types are
-//! first-class in Dhall, and this library allows you to additionally check that some input data
+//! Dhall value. However types are
+//! first-class in Dhall, and this library allows you to additionally check that the input data
//! matches a given Dhall type. That way, a type error will be caught on the Dhall side, and have
//! pretty and explicit errors that point to the source file.
//!
-//! There are two ways to typecheck a Dhall value: you can provide the type as Dhall text or you
-//! can let Rust infer it for you.
+//! There are two ways to typecheck a Dhall value in this way: you can provide the type manually or
+//! you can let Rust infer it for you.
//!
-//! To provide a type written in Dhall, first parse it into a [`serde_dhall::Value`][Value], then
-//! pass it to [`from_str_check_type`][from_str_check_type].
+//! To let Rust infer the appropriate Dhall type, use the [StaticType](trait.StaticType.html)
+//! trait.
//!
//! ```rust
-//! # fn main() -> serde_dhall::de::Result<()> {
-//! use serde_dhall::Value;
+//! # fn main() -> serde_dhall::Result<()> {
+//! use serde::Deserialize;
+//! use serde_dhall::StaticType;
+//!
+//! #[derive(Deserialize, StaticType)]
+//! struct Point {
+//! x: u64,
+//! y: u64,
+//! }
+//!
+//! // Some Dhall data
+//! let data = "{ x = 1, y = 1 + 1 }";
+//!
+//! // Convert the Dhall string to a Point.
+//! let point = serde_dhall::from_str(data)
+//! .static_type_annotation()
+//! .parse::<Point>()?;
+//! assert_eq!(point.x, 1);
+//! assert_eq!(point.y, 2);
+//!
+//! // Invalid data fails the type validation
+//! let invalid_data = "{ x = 1, z = 0.3 }";
+//! assert!(
+//! serde_dhall::from_str(invalid_data)
+//! .static_type_annotation()
+//! .parse::<Point>()
+//! .is_err()
+//! );
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! To provide a type manually, you need a [`SimpleType`](enum.SimpleType.html) value. You
+//! can parse it from some Dhall text like you would parse any other value.
+//!
+//! ```rust
+//! # fn main() -> serde_dhall::Result<()> {
+//! use serde_dhall::SimpleType;
//! use std::collections::HashMap;
//!
//! // Parse a Dhall type
//! let point_type_str = "{ x: Natural, y: Natural }";
-//! let point_type: Value = serde_dhall::from_str(point_type_str)?;
+//! let point_type = serde_dhall::from_str(point_type_str).parse::<SimpleType>()?;
//!
//! // Some Dhall data
//! let point_data = "{ x = 1, y = 1 + 1 }";
//!
//! // Deserialize the data to a Rust type. This checks that
//! // the data matches the provided type.
-//! let deserialized_map: HashMap<String, usize> =
-//! serde_dhall::from_str_check_type(point_data, &point_type)?;
+//! let deserialized_map = serde_dhall::from_str(point_data)
+//! .type_annotation(&point_type)
+//! .parse::<HashMap<String, usize>>()?;
//!
//! let mut expected_map = HashMap::new();
//! expected_map.insert("x".to_string(), 1);
@@ -135,207 +151,35 @@
//! # }
//! ```
//!
-//! You can also let Rust infer the appropriate Dhall type, using the [StaticType] trait.
-//!
-//! ```rust
-//! # fn main() -> serde_dhall::de::Result<()> {
-//! use serde::Deserialize;
-//! use serde_dhall::StaticType;
+//! # Controlling deserialization
//!
-//! #[derive(Debug, Deserialize, StaticType)]
-//! struct Point {
-//! x: u64,
-//! y: u64,
-//! }
-//!
-//! // Some Dhall data
-//! let data = "{ x = 1, y = 1 + 1 }";
-//!
-//! // Convert the Dhall string to a Point.
-//! let point: Point = serde_dhall::from_str_auto_type(data)?;
-//! assert_eq!(point.x, 1);
-//! assert_eq!(point.y, 2);
-//!
-//! // Invalid data fails the type validation
-//! let invalid_data = "{ x = 1, z = 0.3 }";
-//! assert!(serde_dhall::from_str_auto_type::<Point>(invalid_data).is_err());
-//! # Ok(())
-//! # }
-//! ```
+//! If you need more control over the process of reading Dhall values, e.g. disabling
+//! imports, see the [`Deserializer`] methods.
//!
+//! [`Deserializer`]: struct.Deserializer.html
//! [dhall]: https://dhall-lang.org/
//! [serde]: https://docs.serde.rs/serde/
//! [serde::Deserialize]: https://docs.serde.rs/serde/trait.Deserialize.html
-mod serde;
+#[cfg(doctest)]
+mod test_readme {
+ doc_comment::doctest!("../../README.md");
+}
+
+mod deserialize;
+mod error;
+mod options;
mod static_type;
+/// Dhall values
+mod value;
-#[doc(inline)]
-pub use de::{from_str, from_str_auto_type, from_str_check_type};
#[doc(hidden)]
pub use dhall_proc_macros::StaticType;
-pub use static_type::StaticType;
-#[doc(inline)]
-pub use value::Value;
-
-// A Dhall value.
-#[doc(hidden)]
-pub mod value {
- use dhall::syntax::Builtin;
- use dhall::{Normalized, NormalizedExpr, Parsed};
-
- use super::de::{Error, Result};
-
- /// A Dhall value
- #[derive(Debug, Clone, PartialEq, Eq)]
- pub struct Value(Normalized);
- impl Value {
- pub fn from_str(s: &str, ty: Option<&Value>) -> Result<Self> {
- Value::from_str_using_dhall_error_type(s, ty).map_err(Error::Dhall)
- }
- fn from_str_using_dhall_error_type(
- s: &str,
- ty: Option<&Value>,
- ) -> dhall::error::Result<Self> {
- let resolved = Parsed::parse_str(s)?.resolve()?;
- let typed = match ty {
- None => resolved.typecheck()?,
- Some(t) => resolved.typecheck_with(t.as_normalized())?,
- };
- Ok(Value(typed.normalize()))
- }
- pub(crate) fn to_expr(&self) -> NormalizedExpr {
- self.0.to_expr()
- }
- pub(crate) fn as_normalized(&self) -> &Normalized {
- &self.0
- }
-
- pub(crate) fn make_builtin_type(b: Builtin) -> Self {
- Value(Normalized::make_builtin_type(b))
- }
- pub(crate) fn make_optional_type(t: Value) -> Self {
- Value(Normalized::make_optional_type(t.0))
- }
- pub(crate) fn make_list_type(t: Value) -> Self {
- Value(Normalized::make_list_type(t.0))
- }
- // Made public for the StaticType derive macro
- #[doc(hidden)]
- pub fn make_record_type(
- kts: impl Iterator<Item = (String, Value)>,
- ) -> Self {
- Value(Normalized::make_record_type(kts.map(|(k, t)| (k, t.0))))
- }
- #[doc(hidden)]
- pub fn make_union_type(
- kts: impl Iterator<Item = (String, Option<Value>)>,
- ) -> Self {
- Value(Normalized::make_union_type(
- kts.map(|(k, t)| (k, t.map(|t| t.0))),
- ))
- }
- }
-
- impl super::de::sealed::Sealed for Value {}
-
- impl super::de::Deserialize for Value {
- fn from_dhall(v: &Value) -> Result<Self> {
- Ok(v.clone())
- }
- }
-}
-
-/// Deserialize Dhall data to a Rust data structure.
-pub mod de {
- use super::StaticType;
- use super::Value;
- pub use error::{Error, Result};
-
- mod error {
- use dhall::error::Error as DhallError;
-
- pub type Result<T> = std::result::Result<T, Error>;
-
- #[derive(Debug)]
- #[non_exhaustive]
- pub enum Error {
- Dhall(DhallError),
- Deserialize(String),
- }
-
- impl std::fmt::Display for Error {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- match self {
- Error::Dhall(err) => write!(f, "{}", err),
- Error::Deserialize(err) => write!(f, "{}", err),
- }
- }
- }
-
- impl std::error::Error for Error {}
-
- impl serde::de::Error for Error {
- fn custom<T>(msg: T) -> Self
- where
- T: std::fmt::Display,
- {
- Error::Deserialize(msg.to_string())
- }
- }
- }
-
- pub(crate) mod sealed {
- pub trait Sealed {}
- }
-
- /// A data structure that can be deserialized from a Dhall expression
- ///
- /// This is automatically implemented for any type that [serde][serde]
- /// can deserialize.
- ///
- /// This trait cannot be implemented manually.
- pub trait Deserialize: sealed::Sealed + Sized {
- /// See [serde_dhall::from_str][crate::from_str]
- fn from_dhall(v: &Value) -> Result<Self>;
- }
-
- /// Deserialize an instance of type `T` from a string of Dhall text.
- ///
- /// This will recursively resolve all imports in the expression, and
- /// typecheck it before deserialization. Relative imports will be resolved relative to the
- /// provided file. More control over this process is not yet available
- /// but will be in a coming version of this crate.
- pub fn from_str<T>(s: &str) -> Result<T>
- where
- T: Deserialize,
- {
- T::from_dhall(&Value::from_str(s, None)?)
- }
-
- /// Deserialize an instance of type `T` from a string of Dhall text,
- /// additionally checking that it matches the supplied type.
- ///
- /// Like [from_str], but this additionally checks that
- /// the type of the provided expression matches the supplied type.
- pub fn from_str_check_type<T>(s: &str, ty: &Value) -> Result<T>
- where
- T: Deserialize,
- {
- T::from_dhall(&Value::from_str(s, Some(ty))?)
- }
-
- /// Deserialize an instance of type `T` from a string of Dhall text,
- /// additionally checking that it matches the type of `T`.
- ///
- /// Like [from_str], but this additionally checks that
- /// the type of the provided expression matches the output type `T`. The [StaticType] trait
- /// captures Rust types that are valid Dhall types.
- pub fn from_str_auto_type<T>(s: &str) -> Result<T>
- where
- T: Deserialize + StaticType,
- {
- from_str_check_type(s, &<T as StaticType>::static_type())
- }
-}
+pub use deserialize::FromDhall;
+pub(crate) use deserialize::Sealed;
+pub(crate) use error::ErrorKind;
+pub use error::{Error, Result};
+pub use options::{from_file, from_str, Deserializer};
+pub use static_type::StaticType;
+pub use value::{SimpleType, Value};
diff --git a/serde_dhall/src/options.rs b/serde_dhall/src/options.rs
new file mode 100644
index 0000000..06a4368
--- /dev/null
+++ b/serde_dhall/src/options.rs
@@ -0,0 +1,368 @@
+use std::path::{Path, PathBuf};
+
+use dhall::Parsed;
+
+use crate::SimpleType;
+use crate::{Error, ErrorKind, FromDhall, Result, StaticType, Value};
+
+#[derive(Debug, Clone)]
+enum Source<'a> {
+ Str(&'a str),
+ File(PathBuf),
+ // Url(&'a str),
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct NoAnnot;
+#[derive(Debug, Clone, Copy)]
+pub struct ManualAnnot<'ty>(&'ty SimpleType);
+#[derive(Debug, Clone, Copy)]
+pub struct StaticAnnot;
+
+pub trait HasAnnot<A> {
+ fn get_annot(a: &A) -> Option<SimpleType>;
+}
+impl<T> HasAnnot<NoAnnot> for T {
+ fn get_annot(_: &NoAnnot) -> Option<SimpleType> {
+ None
+ }
+}
+impl<'ty, T> HasAnnot<ManualAnnot<'ty>> for T {
+ fn get_annot(a: &ManualAnnot<'ty>) -> Option<SimpleType> {
+ Some(a.0.clone())
+ }
+}
+impl<T: StaticType> HasAnnot<StaticAnnot> for T {
+ fn get_annot(_: &StaticAnnot) -> Option<SimpleType> {
+ Some(T::static_type())
+ }
+}
+
+/// Controls how a Dhall value is read.
+///
+/// This builder exposes the ability to configure how a value is deserialized and what operations
+/// are permitted during evaluation.
+///
+/// Generally speaking, when using [`Deserializer`], you'll create it with [`from_str`] or [`from_file`], then
+/// chain calls to methods to set each option, then call [`parse`]. This will give you a
+/// [`Result<T>`] where `T` is a deserializable type of your choice.
+///
+/// [`Deserializer`]: struct.Deserializer.html
+/// [`from_str`]: fn.from_str.html
+/// [`from_file`]: fn.from_file.html
+/// [`parse`]: struct.Deserializer.html#method.parse
+/// [`Result<T>`]: type.Result.html
+///
+/// # Examples
+///
+/// Reading from a file:
+///
+/// ```no_run
+/// # fn main() -> serde_dhall::Result<()> {
+/// use serde_dhall::from_file;
+///
+/// let data = from_file("foo.dhall").parse::<u64>()?;
+/// # Ok(())
+/// # }
+/// ```
+///
+/// Reading from a file and checking the value against a provided type:
+///
+/// ```no_run
+/// # fn main() -> serde_dhall::Result<()> {
+/// use std::collections::HashMap;
+/// use serde_dhall::{from_file, from_str};
+///
+/// let ty = from_str("{ x: Natural, y: Natural }").parse()?;
+/// let data = from_file("foo.dhall")
+/// .type_annotation(&ty)
+/// .parse::<HashMap<String, usize>>()?;
+/// # Ok(())
+/// # }
+/// ```
+#[derive(Debug, Clone)]
+pub struct Deserializer<'a, A> {
+ source: Source<'a>,
+ annot: A,
+ allow_imports: bool,
+ // allow_remote_imports: bool,
+ // use_cache: bool,
+}
+
+impl<'a> Deserializer<'a, NoAnnot> {
+ fn default_with_source(source: Source<'a>) -> Self {
+ Deserializer {
+ source,
+ annot: NoAnnot,
+ allow_imports: true,
+ // allow_remote_imports: true,
+ // use_cache: true,
+ }
+ }
+ fn from_str(s: &'a str) -> Self {
+ Self::default_with_source(Source::Str(s))
+ }
+ fn from_file<P: AsRef<Path>>(path: P) -> Self {
+ Self::default_with_source(Source::File(path.as_ref().to_owned()))
+ }
+ // fn from_url(url: &'a str) -> Self {
+ // Self::default_with_source(Source::Url(url))
+ // }
+
+ /// Ensures that the parsed value matches the provided type.
+ ///
+ /// In many cases the Dhall type that corresponds to a Rust type can be inferred automatically.
+ /// See the [`StaticType`] trait and the [`static_type_annotation`] method for that.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # fn main() -> serde_dhall::Result<()> {
+ /// use std::collections::HashMap;
+ /// use serde::Deserialize;
+ /// use serde_dhall::{from_str, SimpleType};
+ ///
+ /// // Parse a Dhall type
+ /// let type_str = "{ x: Natural, y: Natural }";
+ /// let ty = from_str(type_str).parse::<SimpleType>()?;
+ ///
+ /// // Parse some Dhall data.
+ /// let data = "{ x = 1, y = 1 + 1 }";
+ /// let point = from_str(data)
+ /// .type_annotation(&ty)
+ /// .parse::<HashMap<String, usize>>()?;
+ /// assert_eq!(point.get("y"), Some(&2));
+ ///
+ /// // Invalid data fails the type validation; deserialization would have succeeded otherwise.
+ /// let invalid_data = "{ x = 1, z = 3 }";
+ /// assert!(
+ /// from_str(invalid_data)
+ /// .type_annotation(&ty)
+ /// .parse::<HashMap<String, usize>>()
+ /// .is_err()
+ /// );
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`static_type_annotation`]: struct.Deserializer.html#method.static_type_annotation
+ /// [`StaticType`]: trait.StaticType.html
+ pub fn type_annotation<'ty>(
+ self,
+ ty: &'ty SimpleType,
+ ) -> Deserializer<'a, ManualAnnot<'ty>> {
+ Deserializer {
+ annot: ManualAnnot(ty),
+ source: self.source,
+ allow_imports: self.allow_imports,
+ }
+ }
+
+ /// Ensures that the parsed value matches the type of `T`.
+ ///
+ /// `T` must implement the [`StaticType`] trait. If it doesn't, you can use [`type_annotation`]
+ /// to provide a type manually.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # fn main() -> serde_dhall::Result<()> {
+ /// use serde::Deserialize;
+ /// use serde_dhall::StaticType;
+ ///
+ /// #[derive(Deserialize, StaticType)]
+ /// struct Point {
+ /// x: u64,
+ /// y: Option<u64>,
+ /// }
+ ///
+ /// // Some Dhall data
+ /// let data = "{ x = 1, y = Some (1 + 1) }";
+ ///
+ /// // Convert the Dhall string to a Point.
+ /// let point = serde_dhall::from_str(data)
+ /// .static_type_annotation()
+ /// .parse::<Point>()?;
+ /// assert_eq!(point.x, 1);
+ /// assert_eq!(point.y, Some(2));
+ ///
+ /// // Invalid data fails the type validation; deserialization would have succeeded otherwise.
+ /// let invalid_data = "{ x = 1 }";
+ /// assert!(
+ /// serde_dhall::from_str(invalid_data)
+ /// .static_type_annotation()
+ /// .parse::<Point>()
+ /// .is_err()
+ /// );
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`type_annotation`]: struct.Deserializer.html#method.type_annotation
+ /// [`StaticType`]: trait.StaticType.html
+ pub fn static_type_annotation(self) -> Deserializer<'a, StaticAnnot> {
+ Deserializer {
+ annot: StaticAnnot,
+ source: self.source,
+ allow_imports: self.allow_imports,
+ }
+ }
+}
+
+impl<'a, A> Deserializer<'a, A> {
+ /// Sets whether to enable imports.
+ ///
+ /// By default, imports are enabled.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # fn main() -> serde_dhall::Result<()> {
+ /// use serde::Deserialize;
+ /// use serde_dhall::SimpleType;
+ ///
+ /// let data = "12 + ./other_file.dhall : Natural";
+ /// assert!(
+ /// serde_dhall::from_str(data)
+ /// .imports(false)
+ /// .parse::<u64>()
+ /// .is_err()
+ /// );
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`static_type_annotation`]: struct.Deserializer.html#method.static_type_annotation
+ /// [`StaticType`]: trait.StaticType.html
+ pub fn imports(self, imports: bool) -> Self {
+ Deserializer {
+ allow_imports: imports,
+ ..self
+ }
+ }
+
+ // /// TODO
+ // pub fn remote_imports(&mut self, imports: bool) -> &mut Self {
+ // self.allow_remote_imports = imports;
+ // if imports {
+ // self.allow_imports = true;
+ // }
+ // self
+ // }
+
+ fn _parse<T>(&self) -> dhall::error::Result<Value>
+ where
+ T: HasAnnot<A>,
+ {
+ let parsed = match &self.source {
+ Source::Str(s) => Parsed::parse_str(s)?,
+ Source::File(p) => Parsed::parse_file(p.as_ref())?,
+ };
+ let resolved = if self.allow_imports {
+ parsed.resolve()?
+ } else {
+ parsed.skip_resolve()?
+ };
+ let typed = match &T::get_annot(&self.annot) {
+ None => resolved.typecheck()?,
+ Some(ty) => resolved.typecheck_with(ty.to_value().as_hir())?,
+ };
+ Ok(Value::from_nir(typed.normalize().as_nir()))
+ }
+
+ /// Parses the chosen dhall value with the options provided.
+ ///
+ /// If you enabled static annotations, `T` is required to implement [`StaticType`].
+ ///
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # fn main() -> serde_dhall::Result<()> {
+ /// let data = serde_dhall::from_str("6 * 7").parse::<u64>()?;
+ /// assert_eq!(data, 42);
+ /// # Ok(())
+ /// # }
+ /// ```
+ /// [`StaticType`]: trait.StaticType.html
+ pub fn parse<T>(&self) -> Result<T>
+ where
+ T: FromDhall + HasAnnot<A>,
+ {
+ let val = self
+ ._parse::<T>()
+ .map_err(ErrorKind::Dhall)
+ .map_err(Error)?;
+ T::from_dhall(&val)
+ }
+}
+
+/// Deserialize a value from a string of Dhall text.
+///
+/// This returns a [`Deserializer`] object. Call the [`parse`] method to get the deserialized
+/// value, or use other [`Deserializer`] methods to control the deserialization process.
+///
+/// # Example
+///
+/// ```rust
+/// # fn main() -> serde_dhall::Result<()> {
+/// use serde::Deserialize;
+///
+/// // We use serde's derive feature
+/// #[derive(Deserialize)]
+/// struct Point {
+/// x: u64,
+/// y: u64,
+/// }
+///
+/// // Some Dhall data
+/// let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }";
+///
+/// // Parse the Dhall string as a Point.
+/// let point: Point = serde_dhall::from_str(data).parse()?;
+///
+/// assert_eq!(point.x, 1);
+/// assert_eq!(point.y, 2);
+/// # Ok(())
+/// # }
+/// ```
+///
+/// [`Deserializer`]: struct.Deserializer.html
+/// [`parse`]: struct.Deserializer.html#method.parse
+pub fn from_str(s: &str) -> Deserializer<'_, NoAnnot> {
+ Deserializer::from_str(s)
+}
+
+/// Deserialize a value from a Dhall file.
+///
+/// This returns a [`Deserializer`] object. Call the [`parse`] method to get the deserialized
+/// value, or use other [`Deserializer`] methods to control the deserialization process.
+///
+/// # Example
+///
+/// ```no_run
+/// # fn main() -> serde_dhall::Result<()> {
+/// use serde::Deserialize;
+///
+/// // We use serde's derive feature
+/// #[derive(Deserialize)]
+/// struct Point {
+/// x: u64,
+/// y: u64,
+/// }
+///
+/// // Parse the Dhall file as a Point.
+/// let point: Point = serde_dhall::from_file("foo.dhall").parse()?;
+/// # Ok(())
+/// # }
+/// ```
+///
+/// [`Deserializer`]: struct.Deserializer.html
+/// [`parse`]: struct.Deserializer.html#method.parse
+pub fn from_file<'a, P: AsRef<Path>>(path: P) -> Deserializer<'a, NoAnnot> {
+ Deserializer::from_file(path)
+}
+
+// pub fn from_url(url: &str) -> Deserializer<'_, NoAnnot> {
+// Deserializer::from_url(url)
+// }
diff --git a/serde_dhall/src/serde.rs b/serde_dhall/src/serde.rs
deleted file mode 100644
index 4fd7815..0000000
--- a/serde_dhall/src/serde.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-use std::borrow::Cow;
-
-use serde::de::value::{
- MapAccessDeserializer, MapDeserializer, SeqDeserializer,
-};
-
-use dhall::syntax::{ExprKind, LitKind};
-use dhall::NormalizedExpr;
-
-use crate::de::{Deserialize, Error, Result};
-use crate::Value;
-
-impl<'a, T> crate::de::sealed::Sealed for T where T: serde::Deserialize<'a> {}
-
-impl<'a, T> Deserialize for T
-where
- T: serde::Deserialize<'a>,
-{
- fn from_dhall(v: &Value) -> Result<Self> {
- T::deserialize(Deserializer(Cow::Owned(v.to_expr())))
- }
-}
-
-struct Deserializer<'a>(Cow<'a, NormalizedExpr>);
-
-impl<'de: 'a, 'a> serde::de::IntoDeserializer<'de, Error> for Deserializer<'a> {
- type Deserializer = Deserializer<'a>;
- fn into_deserializer(self) -> Self::Deserializer {
- self
- }
-}
-
-impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> {
- type Error = Error;
-
- fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
- where
- V: serde::de::Visitor<'de>,
- {
- use std::convert::TryInto;
- use ExprKind::*;
- use LitKind::*;
- let expr = self.0.as_ref();
- let not_serde_compatible = || {
- Err(Error::Deserialize(format!(
- "this cannot be deserialized into the serde data model: {}",
- expr
- )))
- };
-
- match expr.kind() {
- Lit(Bool(x)) => visitor.visit_bool(*x),
- Lit(Natural(x)) => {
- if let Ok(x64) = (*x).try_into() {
- visitor.visit_u64(x64)
- } else if let Ok(x32) = (*x).try_into() {
- visitor.visit_u32(x32)
- } else {
- unimplemented!()
- }
- }
- Lit(Integer(x)) => {
- if let Ok(x64) = (*x).try_into() {
- visitor.visit_i64(x64)
- } else if let Ok(x32) = (*x).try_into() {
- visitor.visit_i32(x32)
- } else {
- unimplemented!()
- }
- }
- Lit(Double(x)) => visitor.visit_f64((*x).into()),
- TextLit(x) => {
- // Normal form ensures that the tail is empty.
- assert!(x.tail().is_empty());
- visitor.visit_str(x.head())
- }
- EmptyListLit(..) => {
- visitor.visit_seq(SeqDeserializer::new(None::<()>.into_iter()))
- }
- NEListLit(xs) => visitor.visit_seq(SeqDeserializer::new(
- xs.iter().map(|x| Deserializer(Cow::Borrowed(x))),
- )),
- SomeLit(x) => visitor.visit_some(Deserializer(Cow::Borrowed(x))),
- App(f, x) => match f.kind() {
- Builtin(dhall::syntax::Builtin::OptionalNone) => {
- visitor.visit_none()
- }
- Field(y, name) => match y.kind() {
- UnionType(..) => {
- let name: String = name.into();
- visitor.visit_enum(MapAccessDeserializer::new(
- MapDeserializer::new(
- Some((name, Deserializer(Cow::Borrowed(x))))
- .into_iter(),
- ),
- ))
- }
- _ => not_serde_compatible(),
- },
- _ => not_serde_compatible(),
- },
- RecordLit(m) => visitor
- .visit_map(MapDeserializer::new(m.iter().map(|(k, v)| {
- (k.as_ref(), Deserializer(Cow::Borrowed(v)))
- }))),
- Field(y, name) => match y.kind() {
- UnionType(..) => {
- let name: String = name.into();
- visitor.visit_enum(MapAccessDeserializer::new(
- MapDeserializer::new(Some((name, ())).into_iter()),
- ))
- }
- _ => not_serde_compatible(),
- },
- Const(..) | Var(..) | Lam(..) | Pi(..) | Let(..) | Annot(..)
- | Assert(..) | Builtin(..) | BinOp(..) | BoolIf(..)
- | RecordType(..) | UnionType(..) | Merge(..) | ToMap(..)
- | Projection(..) | ProjectionByExpr(..) | Completion(..)
- | Import(..) => not_serde_compatible(),
- }
- }
-
- fn deserialize_tuple<V>(self, _: usize, visitor: V) -> Result<V::Value>
- where
- V: serde::de::Visitor<'de>,
- {
- use ExprKind::*;
- let expr = self.0.as_ref();
-
- match expr.kind() {
- // Blindly takes keys in sorted order.
- RecordLit(m) => visitor.visit_seq(SeqDeserializer::new(
- m.iter().map(|(_, v)| Deserializer(Cow::Borrowed(v))),
- )),
- _ => self.deserialize_any(visitor),
- }
- }
-
- serde::forward_to_deserialize_any! {
- bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
- bytes byte_buf option unit unit_struct newtype_struct seq
- tuple_struct map struct enum identifier ignored_any
- }
-}
diff --git a/serde_dhall/src/static_type.rs b/serde_dhall/src/static_type.rs
index 1eb9150..26c70cd 100644
--- a/serde_dhall/src/static_type.rs
+++ b/serde_dhall/src/static_type.rs
@@ -1,27 +1,92 @@
-use dhall::syntax::Builtin;
-
-use crate::Value;
+use crate::SimpleType;
/// A Rust type that can be represented as a Dhall type.
///
-/// A typical example is `Option<bool>`,
-/// represented by the dhall expression `Optional Bool`.
+/// A typical example is `Option<bool>`, represented by the Dhall expression `Optional Bool`.
+///
+/// This trait can be automatically derived, and this is the recommended way of implementing it.
+///
+/// Some Rust types cannot implement this trait, because there isn't a single Dhall type that
+/// corresponds to them. For example, `HashMap<String, u64>` could correspond to multiple different
+/// Dhall types, e.g. `{ foo: Natural, bar: Natural }` and `{ baz: Natural }`.
+///
+/// # Example
+///
+/// ```rust
+/// # fn main() -> serde_dhall::Result<()> {
+/// use serde_dhall::{SimpleType, StaticType};
+///
+/// #[derive(StaticType)]
+/// struct Foo {
+/// x: bool,
+/// y: Vec<u64>,
+/// }
+///
+/// let ty: SimpleType =
+/// serde_dhall::from_str("{ x: Bool, y: List Natural }").parse()?;
+///
+/// assert_eq!(Foo::static_type(), ty);
+/// # Ok(())
+/// # }
+/// ```
+///
+/// # Type correspondence
///
-/// This trait can and should be automatically derived.
+/// The following Dhall types correspond to the following Rust types:
///
-/// The representation needs to be independent of the value.
-/// For this reason, something like `HashMap<String, bool>` cannot implement
-/// [StaticType] because each different value would
-/// have a different Dhall record type.
+/// Dhall | Rust
+/// -------|------
+/// `Bool` | `bool`
+/// `Natural` | `u64`, `u32`, ...
+/// `Integer` | `i64`, `i32`, ...
+/// `Double` | `f64`, `f32`, ...
+/// `Text` | `String`
+/// `List T` | `Vec<T>`
+/// `Optional T` | `Option<T>`
+/// `{ x: T, y: U }` | structs
+/// `{ _1: T, _2: U }` | `(T, U)`, structs
+/// `{ x: T, y: T }` | `HashMap<String, T>`, structs
+/// `< x: T \| y: U >` | enums
+/// `T -> U` | unsupported
+/// `Prelude.JSON.Type` | unsupported
+/// `Prelude.Map.Type T U` | unsupported
pub trait StaticType {
- fn static_type() -> Value;
+ /// Return the Dhall type that represents this type.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # fn main() -> serde_dhall::Result<()> {
+ /// use serde::Deserialize;
+ /// use serde_dhall::{SimpleType, StaticType};
+ ///
+ /// // Using `derive(StaticType)` here would give it the type `{ _1: List Natural }`.
+ /// #[derive(Deserialize)]
+ /// #[serde(transparent)]
+ /// struct Foo(Vec<u64>);
+ ///
+ /// impl StaticType for Foo {
+ /// fn static_type() -> SimpleType {
+ /// SimpleType::List(Box::new(SimpleType::Natural))
+ /// }
+ /// }
+ ///
+ /// let foo = serde_dhall::from_str("[ 1, 2 ]")
+ /// .static_type_annotation()
+ /// .parse::<Foo>()?;
+ ///
+ /// assert_eq!(foo.0, vec![1, 2]);
+ /// # Ok(())
+ /// # }
+ /// ```
+ fn static_type() -> SimpleType;
}
macro_rules! derive_builtin {
- ($ty:ty, $builtin:ident) => {
- impl StaticType for $ty {
- fn static_type() -> Value {
- Value::make_builtin_type(Builtin::$builtin)
+ ($rust_ty:ty, $dhall_ty:ident) => {
+ impl StaticType for $rust_ty {
+ fn static_type() -> SimpleType {
+ SimpleType::$dhall_ty
}
}
};
@@ -43,13 +108,14 @@ where
A: StaticType,
B: StaticType,
{
- fn static_type() -> Value {
- Value::make_record_type(
+ fn static_type() -> SimpleType {
+ SimpleType::Record(
vec![
("_1".to_owned(), A::static_type()),
("_2".to_owned(), B::static_type()),
]
- .into_iter(),
+ .into_iter()
+ .collect(),
)
}
}
@@ -59,13 +125,14 @@ where
T: StaticType,
E: StaticType,
{
- fn static_type() -> Value {
- Value::make_union_type(
+ fn static_type() -> SimpleType {
+ SimpleType::Union(
vec![
("Ok".to_owned(), Some(T::static_type())),
("Err".to_owned(), Some(E::static_type())),
]
- .into_iter(),
+ .into_iter()
+ .collect(),
)
}
}
@@ -74,8 +141,8 @@ impl<T> StaticType for Option<T>
where
T: StaticType,
{
- fn static_type() -> Value {
- Value::make_optional_type(T::static_type())
+ fn static_type() -> SimpleType {
+ SimpleType::Optional(Box::new(T::static_type()))
}
}
@@ -83,8 +150,8 @@ impl<T> StaticType for Vec<T>
where
T: StaticType,
{
- fn static_type() -> Value {
- Value::make_list_type(T::static_type())
+ fn static_type() -> SimpleType {
+ SimpleType::List(Box::new(T::static_type()))
}
}
@@ -92,7 +159,7 @@ impl<'a, T> StaticType for &'a T
where
T: StaticType,
{
- fn static_type() -> Value {
+ fn static_type() -> SimpleType {
T::static_type()
}
}
diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs
new file mode 100644
index 0000000..d6631da
--- /dev/null
+++ b/serde_dhall/src/value.rs
@@ -0,0 +1,287 @@
+use std::collections::{BTreeMap, HashMap};
+
+use dhall::semantics::{Hir, HirKind, Nir, NirKind};
+use dhall::syntax::{Builtin, Expr, ExprKind, NumKind, Span};
+
+use crate::{Error, ErrorKind, FromDhall, Result, Sealed};
+
+#[doc(hidden)]
+/// An arbitrary Dhall value.
+#[derive(Debug, Clone)]
+pub struct Value {
+ /// Invariant: in normal form
+ hir: Hir,
+ /// Cached conversions because they are annoying to construct from Hir.
+ /// At most one of them will be `Some`.
+ as_simple_val: Option<SimpleValue>,
+ as_simple_ty: Option<SimpleType>,
+}
+
+/// A simple value of the kind that can be decoded with serde
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) enum SimpleValue {
+ Num(NumKind),
+ Text(String),
+ Optional(Option<Box<SimpleValue>>),
+ List(Vec<SimpleValue>),
+ Record(BTreeMap<String, SimpleValue>),
+ Union(String, Option<Box<SimpleValue>>),
+}
+
+/// The type of a value that can be decoded by `serde_dhall`, e.g. `{ x: Bool, y: List Natural }`.
+///
+/// A `SimpleType` is used when deserializing values to ensure they are of the expected type.
+/// Rather than letting `serde` handle potential type mismatches, this uses the type-checking
+/// capabilities of Dhall to catch errors early and cleanly indicate in the user's code where the
+/// mismatch happened.
+///
+/// You would typically not manipulate `SimpleType`s by hand but rather let Rust infer it for your
+/// datatype by deriving the [`StaticType`] trait, and using
+/// [`Deserializer::static_type_annotation`]. If you need to supply a `SimpleType` manually, you
+/// can either deserialize it like any other Dhall value, or construct it manually.
+///
+/// [`StaticType`]: trait.StaticType.html
+/// [`Deserializer::static_type_annotation`]: options/struct.Deserializer.html#method.static_type_annotation
+///
+/// # Examples
+///
+/// ```rust
+/// # fn main() -> serde_dhall::Result<()> {
+/// use serde_dhall::{SimpleType, StaticType};
+///
+/// #[derive(StaticType)]
+/// struct Foo {
+/// x: bool,
+/// y: Vec<u64>,
+/// }
+///
+/// let ty: SimpleType =
+/// serde_dhall::from_str("{ x: Bool, y: List Natural }").parse()?;
+///
+/// assert_eq!(Foo::static_type(), ty);
+/// # Ok(())
+/// # }
+/// ```
+///
+/// ```rust
+/// # fn main() -> serde_dhall::Result<()> {
+/// use std::collections::HashMap;
+/// use serde_dhall::SimpleType;
+///
+/// let ty: SimpleType =
+/// serde_dhall::from_str("{ x: Natural, y: Natural }").parse()?;
+///
+/// let mut map = HashMap::new();
+/// map.insert("x".to_string(), SimpleType::Natural);
+/// map.insert("y".to_string(), SimpleType::Natural);
+/// assert_eq!(ty, SimpleType::Record(map));
+/// # Ok(())
+/// # }
+/// ```
+///
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum SimpleType {
+ /// Corresponds to the Dhall type `Bool`
+ Bool,
+ /// Corresponds to the Dhall type `Natural`
+ Natural,
+ /// Corresponds to the Dhall type `Integer`
+ Integer,
+ /// Corresponds to the Dhall type `Double`
+ Double,
+ /// Corresponds to the Dhall type `Text`
+ Text,
+ /// Corresponds to the Dhall type `Optional T`
+ Optional(Box<SimpleType>),
+ /// Corresponds to the Dhall type `List T`
+ List(Box<SimpleType>),
+ /// Corresponds to the Dhall type `{ x : T, y : U }`
+ Record(HashMap<String, SimpleType>),
+ /// Corresponds to the Dhall type `< x : T | y : U >`
+ Union(HashMap<String, Option<SimpleType>>),
+}
+
+impl Value {
+ pub(crate) fn from_nir(x: &Nir) -> Self {
+ Value {
+ hir: x.to_hir_noenv(),
+ as_simple_val: SimpleValue::from_nir(x),
+ as_simple_ty: SimpleType::from_nir(x),
+ }
+ }
+
+ pub(crate) fn as_hir(&self) -> &Hir {
+ &self.hir
+ }
+
+ /// Converts a Value into a SimpleValue.
+ pub(crate) fn to_simple_value(&self) -> Option<SimpleValue> {
+ self.as_simple_val.clone()
+ }
+
+ /// Converts a Value into a SimpleType.
+ pub(crate) fn to_simple_type(&self) -> Option<SimpleType> {
+ self.as_simple_ty.clone()
+ }
+
+ /// Converts a value back to the corresponding AST expression.
+ pub(crate) fn to_expr(&self) -> Expr {
+ self.hir.to_expr(Default::default())
+ }
+}
+
+impl SimpleValue {
+ pub(crate) fn from_nir(nir: &Nir) -> Option<Self> {
+ Some(match nir.kind() {
+ NirKind::Num(lit) => SimpleValue::Num(lit.clone()),
+ NirKind::TextLit(x) => SimpleValue::Text(
+ x.as_text()
+ .expect("Normal form should ensure the text is a string"),
+ ),
+ NirKind::EmptyOptionalLit(_) => SimpleValue::Optional(None),
+ NirKind::NEOptionalLit(x) => {
+ SimpleValue::Optional(Some(Box::new(Self::from_nir(x)?)))
+ }
+ NirKind::EmptyListLit(_) => SimpleValue::List(vec![]),
+ NirKind::NEListLit(xs) => SimpleValue::List(
+ xs.iter().map(Self::from_nir).collect::<Option<_>>()?,
+ ),
+ NirKind::RecordLit(kvs) => SimpleValue::Record(
+ kvs.iter()
+ .map(|(k, v)| Some((k.into(), Self::from_nir(v)?)))
+ .collect::<Option<_>>()?,
+ ),
+ NirKind::UnionLit(field, x, _) => SimpleValue::Union(
+ field.into(),
+ Some(Box::new(Self::from_nir(x)?)),
+ ),
+ NirKind::UnionConstructor(field, ty)
+ if ty.get(field).map(|f| f.is_some()) == Some(false) =>
+ {
+ SimpleValue::Union(field.into(), None)
+ }
+ _ => return None,
+ })
+ }
+}
+
+impl SimpleType {
+ pub(crate) fn from_nir(nir: &Nir) -> Option<Self> {
+ Some(match nir.kind() {
+ NirKind::BuiltinType(b) => match b {
+ Builtin::Bool => SimpleType::Bool,
+ Builtin::Natural => SimpleType::Natural,
+ Builtin::Integer => SimpleType::Integer,
+ Builtin::Double => SimpleType::Double,
+ Builtin::Text => SimpleType::Text,
+ _ => unreachable!(),
+ },
+ NirKind::OptionalType(t) => {
+ SimpleType::Optional(Box::new(Self::from_nir(t)?))
+ }
+ NirKind::ListType(t) => {
+ SimpleType::List(Box::new(Self::from_nir(t)?))
+ }
+ NirKind::RecordType(kts) => SimpleType::Record(
+ kts.iter()
+ .map(|(k, v)| Some((k.into(), Self::from_nir(v)?)))
+ .collect::<Option<_>>()?,
+ ),
+ NirKind::UnionType(kts) => SimpleType::Union(
+ kts.iter()
+ .map(|(k, v)| {
+ Some((
+ k.into(),
+ v.as_ref()
+ .map(|v| Ok(Self::from_nir(v)?))
+ .transpose()?,
+ ))
+ })
+ .collect::<Option<_>>()?,
+ ),
+ _ => return None,
+ })
+ }
+
+ pub(crate) fn to_value(&self) -> Value {
+ Value {
+ hir: self.to_hir(),
+ as_simple_val: None,
+ as_simple_ty: Some(self.clone()),
+ }
+ }
+ pub(crate) fn to_hir(&self) -> Hir {
+ let hir = |k| Hir::new(HirKind::Expr(k), Span::Artificial);
+ hir(match self {
+ SimpleType::Bool => ExprKind::Builtin(Builtin::Bool),
+ SimpleType::Natural => ExprKind::Builtin(Builtin::Natural),
+ SimpleType::Integer => ExprKind::Builtin(Builtin::Integer),
+ SimpleType::Double => ExprKind::Builtin(Builtin::Double),
+ SimpleType::Text => ExprKind::Builtin(Builtin::Text),
+ SimpleType::Optional(t) => ExprKind::App(
+ hir(ExprKind::Builtin(Builtin::Optional)),
+ t.to_hir(),
+ ),
+ SimpleType::List(t) => {
+ ExprKind::App(hir(ExprKind::Builtin(Builtin::List)), t.to_hir())
+ }
+ SimpleType::Record(kts) => ExprKind::RecordType(
+ kts.iter()
+ .map(|(k, t)| (k.as_str().into(), t.to_hir()))
+ .collect(),
+ ),
+ SimpleType::Union(kts) => ExprKind::UnionType(
+ kts.iter()
+ .map(|(k, t)| {
+ (k.as_str().into(), t.as_ref().map(|t| t.to_hir()))
+ })
+ .collect(),
+ ),
+ })
+ }
+}
+
+impl Sealed for Value {}
+impl Sealed for SimpleValue {}
+impl Sealed for SimpleType {}
+
+impl FromDhall for Value {
+ fn from_dhall(v: &Value) -> Result<Self> {
+ Ok(v.clone())
+ }
+}
+impl FromDhall for SimpleValue {
+ fn from_dhall(v: &Value) -> Result<Self> {
+ v.to_simple_value().ok_or_else(|| {
+ Error(ErrorKind::Deserialize(format!(
+ "this cannot be deserialized into a simple type: {}",
+ v
+ )))
+ })
+ }
+}
+impl FromDhall for SimpleType {
+ fn from_dhall(v: &Value) -> Result<Self> {
+ v.to_simple_type().ok_or_else(|| {
+ Error(ErrorKind::Deserialize(format!(
+ "this cannot be deserialized into a simple type: {}",
+ v
+ )))
+ })
+ }
+}
+
+impl Eq for Value {}
+impl PartialEq for Value {
+ fn eq(&self, other: &Self) -> bool {
+ self.hir == other.hir
+ }
+}
+impl std::fmt::Display for Value {
+ fn fmt(
+ &self,
+ f: &mut std::fmt::Formatter,
+ ) -> std::result::Result<(), std::fmt::Error> {
+ self.to_expr().fmt(f)
+ }
+}
diff --git a/serde_dhall/tests/de.rs b/serde_dhall/tests/de.rs
index 74912dd..a5c42fd 100644
--- a/serde_dhall/tests/de.rs
+++ b/serde_dhall/tests/de.rs
@@ -1,10 +1,10 @@
use serde::Deserialize;
-use serde_dhall::{from_str, from_str_auto_type, StaticType};
+use serde_dhall::{from_str, FromDhall, StaticType};
#[test]
fn test_de_typed() {
- fn parse<T: serde_dhall::de::Deserialize + StaticType>(s: &str) -> T {
- from_str_auto_type(s).unwrap()
+ fn parse<T: FromDhall + StaticType>(s: &str) -> T {
+ from_str(s).static_type_annotation().parse().unwrap()
}
assert_eq!(parse::<bool>("True"), true);
@@ -51,12 +51,17 @@ fn test_de_typed() {
Y(i64),
}
assert_eq!(parse::<Baz>("< X | Y: Integer >.X"), Baz::X);
+
+ assert!(from_str("< X | Y: Integer >.Y")
+ .static_type_annotation()
+ .parse::<Baz>()
+ .is_err());
}
#[test]
fn test_de_untyped() {
- fn parse<T: serde_dhall::de::Deserialize>(s: &str) -> T {
- from_str(s).unwrap()
+ fn parse<T: FromDhall>(s: &str) -> T {
+ from_str(s).parse().unwrap()
}
// Test tuples on record of wrong type
@@ -83,6 +88,17 @@ fn test_de_untyped() {
expected_map
);
+ #[derive(Debug, PartialEq, Eq, Deserialize)]
+ struct Foo {
+ x: u64,
+ y: Option<u64>,
+ }
+ // Omit optional field
+ assert_eq!(parse::<Foo>("{ x = 1 }"), Foo { x: 1, y: None });
+
// https://github.com/Nadrieril/dhall-rust/issues/155
- assert!(from_str::<bool>("List/length [True, 42]").is_err());
+ assert!(from_str("List/length [True, 42]").parse::<bool>().is_err());
}
+
+// TODO: test various builder configurations
+// In particular test cloning and reusing builder
diff --git a/serde_dhall/tests/traits.rs b/serde_dhall/tests/traits.rs
index 15a91ed..3c6fbfe 100644
--- a/serde_dhall/tests/traits.rs
+++ b/serde_dhall/tests/traits.rs
@@ -1,9 +1,9 @@
-use serde_dhall::{from_str, StaticType, Value};
+use serde_dhall::{from_str, SimpleType, StaticType};
#[test]
fn test_static_type() {
- fn parse(s: &str) -> Value {
- from_str(s).unwrap()
+ fn parse(s: &str) -> SimpleType {
+ from_str(s).parse().unwrap()
}
assert_eq!(bool::static_type(), parse("Bool"));
diff --git a/serde_dhall/tests/version_numbers.rs b/serde_dhall/tests/version_numbers.rs
new file mode 100644
index 0000000..8307e47
--- /dev/null
+++ b/serde_dhall/tests/version_numbers.rs
@@ -0,0 +1,17 @@
+#[test]
+fn test_readme_deps() {
+ version_sync::assert_markdown_deps_updated!("../README.md");
+}
+
+#[test]
+fn test_html_root_url() {
+ version_sync::assert_html_root_url_updated!("src/lib.rs");
+}
+
+#[test]
+fn test_readme_mentions_version() {
+ version_sync::assert_contains_regex!(
+ "../README.md",
+ "^#### \\[{version}\\]"
+ );
+}
diff --git a/test.dhall b/test.dhall
new file mode 100644
index 0000000..c91ac54
--- /dev/null
+++ b/test.dhall
@@ -0,0 +1 @@
+{ x = 0 } with x = (1 + 1)
diff --git a/tests_buffer b/tests_buffer
index 74e76bc..6f5aae5 100644
--- a/tests_buffer
+++ b/tests_buffer
@@ -7,6 +7,11 @@ x.({ a : Bool, b })
x.({ a })
x.{ a : Bool }
s/QuotedVariable/VariableQuoted/
+From https://github.com/dhall-lang/dhall-lang/issues/280 :
+ "${ not_really_an_expression ;-) }"
+ ''${ not_an_expression ;-) }''
+ {- {- -} 1
+{ x = 0 } with x = 1 + 1
import:
failure/