diff --git a/conformance/README.md b/conformance/README.md index 8784bfee..ef94a6e3 100644 --- a/conformance/README.md +++ b/conformance/README.md @@ -1,9 +1,10 @@ # `conformance/` — machine-readable Candid conformance suite -**Status: subtyping, coercion and decoding.** Vectors for the field-id hash, type-table -well-formedness, subtyping, coercion and decoding, generated by the Lean model, and a -runner that checks them against `candid` 0.10. The decoding assertions of -[test/](../test/) are vectors too. See [REWRITE.md](../REWRITE.md) for why this exists. +**Status: subtyping, coercion, decoding and textual values.** Vectors for the field-id +hash, type-table well-formedness, subtyping, coercion, decoding, and reading textual +values, generated by the Lean model, and a runner that checks them against `candid` +0.10. Every assertion of [test/](../test/) is a vector too. See +[REWRITE.md](../REWRITE.md) for why this exists. ``` vectors/ the suite: JSON, one vector per line @@ -65,12 +66,12 @@ where each expectation is stated independently of the model and checked against | File | Origin | | |---|---|---| -| `handwritten.json` | written by hand | named for what each shows; can catch the model being wrong | +| `handwritten.json` | written by hand | named for what each shows; can catch the model being wrong. The `parse` vectors follow [lean/Spec/TextValues.md](../lean/Spec/TextValues.md) | | `subtype-primitives.json` | generated | every primitive against every primitive | | `subtype-small.json` | generated | every pair from a universe of small types, including recursive ones | | `coerce-small.json` | generated | sample values of each type in that universe, coerced at every type in it | | `decode-small.json` | generated | each of those samples encoded at its own type, and decoded at it | -| `test-did.json` | from `test/*.test.did` | each message an assertion there names, at its types | +| `test-did.json` | from `test/*.test.did` | each message an assertion there names, bytes or text, at its types | The ways a message can be malformed are all in `handwritten.json`, with their bytes written out by hand: an encoder produces only well-formed messages, and the model's @@ -98,7 +99,9 @@ the source: [tools/test-did/](tools/test-did/) translates them into Lean with {"id":"...","kind":"coerce","value":,"from":,"to":,"expect":"reject"}, {"id":"...","kind":"decode","bytes":"4449444c...","types":,"expect":{"values":[, ...]}}, {"id":"...","kind":"decode","bytes":"4449444c...","types":,"expect":"reject"}, -{"id":"...","kind":"decode","bytes":"4449444c...","types":,"expect":"costly"} +{"id":"...","kind":"decode","bytes":"4449444c...","types":,"expect":"costly"}, +{"id":"...","kind":"parse","text":"(opt 5)","types":,"expect":{"values":[, ...]}}, +{"id":"...","kind":"parse","text":"(5)","types":,"expect":"reject"} ]} ``` @@ -128,6 +131,10 @@ the format, so it changes in place. (`spec/Candid.md`, "Resource limits"). A runner passes if the implementation decodes the message or rejects it, and fails if it panics or aborts. How long it takes, and what it allocates, is for a runner to bound as its environment would. +- **`parse`**: `text`, read as an argument sequence at `types` + ([lean/Spec/TextValues.md](../lean/Spec/TextValues.md)), is the `values`, one per + type, or is rejected. `types` is as for `decode`, with no environment of type + definitions, so a type name in an annotation in the text is rejected. A `` is `{"table": [, ...], "root": }`. A `` is a primitive's name (`"nat"`, `"principal"`, ...) or a table index (`3`). An `` is a composite, @@ -197,7 +204,10 @@ compares the values while leaving their representation in `IDLValue` to 0.10. It workspace, not a member of the root one. A `decode` vector is asked through `IDLArgs::from_bytes_with_types`, and the result -compared by encoding, as for `coerce`. Where 0.10 cannot represent an expected value, +compared by encoding, as for `coerce`. A `parse` vector is asked the way `candid_parser`'s +own test runner reads text: `candid_parser::parse_idl_args`, then +`IDLArgs::annotate_types` with the parser's rules (`from_parser`). A panic is a +disagreement. Where 0.10 cannot represent an expected value, that is reported as a disagreement rather than a broken vector: the question translated, and the answer is what 0.10 cannot hold. @@ -244,6 +254,18 @@ What the runners have found, recorded so that they outlive the run that found th rules give: every type is a subtype of `reserved` and of every option. The model adds only that a future type is a subtype of itself, which no decoder can be asked. +- **The model and 0.10 read every text in `test/` alike**, on both sides of `==`. The + model reads text by [lean/Spec/TextValues.md](../lean/Spec/TextValues.md), which + differs from 0.10 elsewhere, in its Appendix B.2; the `parse` vectors there are + 0.10's known divergences. +- **`candid_parser` 0.10 panics on some text.** `0X10` passes its lexer, which then + strips only `0x` and unwraps the failed parse. `IDLArgs::annotate_types` slices + `types[args.len()..]`, so `(1, 2)` at `(nat)` panics, in the path its own test runner + takes. And a positional field after id 4294967295 overflows `id + 1`, a panic in a + debug build and id 0 in a release one. Vectors: `text: the hexadecimal prefix is + lowercase (3.4)`, `text: an argument beyond the types is ignored (5.9)`, `text: a + positional field after the last id (4.2)`. + ### Still to come - **Encoding**, against the relation rather than fixed bytes. The binary format is @@ -266,11 +288,10 @@ What the runners have found, recorded so that they outlive the run that found th ## Migration All 471 existing assertions must exist as vectors and pass before -[test/](../test/) is deleted. 464 do, in `test-did.json`: the 17 in -`spacebomb.test.did` as `costly` vectors: they assert that a decoder with -self-metering rejects valid messages, and the spec leaves which ones to the -implementation. The other 7 have text on the left, which is a question for -the textual syntax, and wait on it. +[test/](../test/) is deleted. All 471 do, in `test-did.json`, and the model and 0.10 +agree on every one. The 17 in `spacebomb.test.did` are `costly` vectors: they assert +that a decoder with self-metering rejects valid messages, and the spec leaves which +ones to the implementation. The 7 with text on the left are `parse` vectors. For now the `.test.did` files stay the source, translated by [tools/test-did/](tools/test-did/), so `candid_parser`'s own test suite and these diff --git a/conformance/runners/candid-0.10/Cargo.lock b/conformance/runners/candid-0.10/Cargo.lock index 0e1b4d74..f3fa4f75 100644 --- a/conformance/runners/candid-0.10/Cargo.lock +++ b/conformance/runners/candid-0.10/Cargo.lock @@ -2,12 +2,27 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +[[package]] +name = "arbitrary" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bc62ac97cc33321f50863d514c3bc38a453947a8f9e781137e47c7401020aed" + [[package]] name = "array-init" version = "2.1.0" @@ -20,12 +35,27 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "ascii-canvas" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term", +] + [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" + [[package]] name = "binrw" version = "0.15.2" @@ -46,9 +76,30 @@ dependencies = [ "either", "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" + [[package]] name = "block-buffer" version = "0.10.4" @@ -88,7 +139,7 @@ dependencies = [ "serde", "serde_bytes", "stacker", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -98,7 +149,27 @@ dependencies = [ "lazy_static", "proc-macro2", "quote", - "syn", + "syn 2.0.87", +] + +[[package]] +name = "candid_parser" +version = "0.4.1" +dependencies = [ + "anyhow", + "candid", + "codespan-reporting", + "convert_case", + "handlebars", + "hex", + "lalrpop", + "lalrpop-util", + "logos", + "num-bigint", + "pretty", + "serde", + "thiserror 1.0.63", + "toml", ] [[package]] @@ -113,14 +184,34 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "conformance-candid-0-10" version = "0.0.0" dependencies = [ "candid", + "candid_parser", "serde_json", ] +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cpufeatures" version = "0.2.12" @@ -139,6 +230,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-common" version = "0.1.6" @@ -149,12 +246,78 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.87", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.87", +] + [[package]] name = "data-encoding" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.87", +] + [[package]] name = "digest" version = "0.10.7" @@ -165,12 +328,60 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "ena" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1" +dependencies = [ + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "generic-array" version = "0.14.7" @@ -181,6 +392,39 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "handlebars" +version = "6.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75c54236f9045c8004a77942bebc52145b4844639db934a5c70fe08617fbe61a" +dependencies = [ + "derive_builder", + "log", + "num-order", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror 2.0.21", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + [[package]] name = "hex" version = "0.4.3" @@ -191,11 +435,37 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" name = "ic_principal" version = "0.1.5" dependencies = [ + "arbitrary", "crc32fast", "data-encoding", "serde", "sha2", - "thiserror", + "thiserror 1.0.63", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", ] [[package]] @@ -204,6 +474,37 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "lalrpop" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" +dependencies = [ + "ascii-canvas", + "bit-set", + "ena", + "itertools", + "lalrpop-util", + "petgraph", + "pico-args", + "regex", + "regex-syntax", + "string_cache", + "term", + "tiny-keccak", + "unicode-xid", + "walkdir", +] + +[[package]] +name = "lalrpop-util" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" +dependencies = [ + "regex-automata", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -222,12 +523,75 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "libredox" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ff90caf6077a803a240f62fdbe88645a890bbca49ef8174c3cb0404362171d" +dependencies = [ + "libc", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d15f86d12da44cd8d4ec3c73984537eb7f4cadcb9776a575a0a8a05ca11005c" + +[[package]] +name = "logos" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7251356ef8cb7aec833ddf598c6cb24d17b689d20b993f9d11a3d764e34e6458" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-codegen" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f80069600c0d66734f5ff52cc42f2dabd6b29d205f333d61fd7832e9e9963f" +dependencies = [ + "beef", + "fnv", + "lazy_static", + "proc-macro2", + "quote", + "regex-syntax", + "syn 2.0.87", +] + +[[package]] +name = "logos-derive" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24fb722b06a9dc12adb0963ed585f19fc61dc5413e6a9be9422ef92c091e731d" +dependencies = [ + "logos-codegen", +] + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "num-bigint" version = "0.4.6" @@ -248,6 +612,21 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-modular" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd8e500409e6cd603b03e477c26a6caecdc27ac58979a53e881c75eafc079f44" + +[[package]] +name = "num-order" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6" +dependencies = [ + "num-modular", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -257,12 +636,108 @@ dependencies = [ "autocfg", ] +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pest" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45d3aca230fad2e6f6317ca0a72724338c4960cb97168a85cdee66df4a9a21a8" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284b60557f2c4a2e72ad3f2d34d42685a2fa4a6a61d0d2a10c0ae2a5e916c2cf" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d9d1f08a115309ee99268cf85e5228e0e56aa9caf8841ec12866b6be07c3109" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "pest_meta" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed93ba1a9ffcca32130a5188701c81c0c49cf00d4b7c5007d5148951d743adcb" +dependencies = [ + "pest", +] + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "pretty" version = "0.12.3" @@ -276,9 +751,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] @@ -301,6 +776,76 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror 1.0.63", +] + +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.224" @@ -337,7 +882,7 @@ checksum = "87ff78ab5e8561c9a675bfc1785cb07ae721f0ee53329a595cefd8c04c2ac4e0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -353,6 +898,15 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "sha2" version = "0.10.8" @@ -364,6 +918,18 @@ dependencies = [ "digest", ] +[[package]] +name = "siphasher" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33f4fe9184a62d842c9ef383018f3306d8ba224fd9d836f56d7288308847c256" + +[[package]] +name = "smallvec" +version = "1.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "149309498a5f7334b12ddae0cad8aba81ce785fe91755068867e017cf98fee46" + [[package]] name = "stacker" version = "0.1.15" @@ -377,6 +943,24 @@ dependencies = [ "winapi", ] +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.87" @@ -388,13 +972,53 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae9ccbea3a7b0b09443036490c8f34a8b1b760fd345e5c0dae0aac718faea79" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.63", +] + +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl 2.0.21", ] [[package]] @@ -405,7 +1029,61 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.10", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -420,24 +1098,58 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + [[package]] name = "unicode-width" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "winapi" version = "0.3.9" @@ -454,12 +1166,45 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + [[package]] name = "zmij" version = "1.0.21" diff --git a/conformance/runners/candid-0.10/Cargo.toml b/conformance/runners/candid-0.10/Cargo.toml index 6ac56583..13ce78f5 100644 --- a/conformance/runners/candid-0.10/Cargo.toml +++ b/conformance/runners/candid-0.10/Cargo.toml @@ -12,4 +12,5 @@ description = "Runs the conformance vectors against candid 0.10" [dependencies] candid = { path = "../../../rust/candid", features = ["value"] } +candid_parser = { path = "../../../rust/candid_parser" } serde_json = "1.0" diff --git a/conformance/runners/candid-0.10/src/main.rs b/conformance/runners/candid-0.10/src/main.rs index 380e4339..d03f00f0 100644 --- a/conformance/runners/candid-0.10/src/main.rs +++ b/conformance/runners/candid-0.10/src/main.rs @@ -48,6 +48,73 @@ const KNOWN: &[(&str, &str)] = &[ "decode: a principal of 30 bytes", "0.10 bounds a principal at 29 bytes, the Internet Computer's limit, in both its \ decoder and `Principal`; the spec bounds neither", + ), ( + "text: an underscore cannot end a number (3.4)", + "0.10's lexer takes `[0-9][_0-9]*`, so `1_` is a number", + ), + ( + "text: an underscore cannot follow another (3.4)", + "0.10's lexer takes `[0-9][_0-9]*`, so `1__0` is a number", + ), + ( + "text: an underscore in a float literal (3.4)", + "0.10's float token has no underscores, so `1_000.5` is two tokens", + ), + ( + "text: an underscore cannot end a code point (3.5)", + "0.10's `\\u{...}` takes `[0-9a-fA-F][_0-9a-fA-F]*`, so `\\u{41_}` is `A`", + ), + ( + "text: the hexadecimal prefix is lowercase (3.4)", + "0.10's lexer takes `0[xX]` but strips only `0x`, then unwraps the failed parse of `0X10`: a panic", + ), + ( + "text: a sign is part of its number (3.4)", + "0.10 lexes a sign as its own token, so `- 5` is `-5`", + ), + ( + "text: a float literal begins with a digit (3.4)", + "0.10's float token is `[0-9]*\\.[0-9]*`, so `.5` is a float", + ), + ( + "text: hexadecimal float literals (3.4)", + "0.10 has no hexadecimal float literals", + ), + ( + "text: minus zero at nat (5.3)", + "0.10 reads a literal at `nat` with `BigUint`'s parser, which rejects a sign", + ), + ( + "text: rounded once, not first to float64 (5.3)", + "0.10 rounds a literal to `f64`, then to `f32`; this one lands on a tie in between", + ), + ( + "text: a raw tab in a text literal (3.5)", + "0.10 accepts any character but `\"` and `\\` raw in a text literal", + ), + ( + "text: a raw line feed in a text literal (3.5)", + "0.10 accepts any character but `\"` and `\\` raw in a text literal", + ), + ( + "text: a positional field after the last id (4.2)", + "0.10 numbers the field after 4294967295 with `id + 1`: a panic in a debug build, and id 0 in a release build", + ), + ( + "text: an annotation does not convert (5.1)", + "0.10 re-annotates an annotated value at the outer type, which turns a `nat` into an `int`", + ), + ( + "text: an annotation does not narrow (5.1)", + "0.10 re-annotates an annotated value at the outer type, which turns a `float64` into a `float32`", + ), + ( + "text: an annotation at reserved (5.1)", + "0.10 accepts anything at `reserved`, annotated or not", + ), + ( + "text: an argument beyond the types is ignored (5.9)", + "`IDLArgs::annotate_types` slices `types[args.len()..]`, which panics when there are more arguments than types", ), ]; @@ -174,6 +241,7 @@ fn run(v: &Value) -> Result { "coerce" => coerce(v), "decode" if v["expect"] == "costly" => costly(v), "decode" => decode(v), + "parse" => parse(v), k => Err(format!("unknown vector kind {k:?}")), } } @@ -238,11 +306,46 @@ fn decode(v: &Value) -> Result { let bytes = hex(&v["bytes"])?; let mut env = TypeEnv::new(); let types = load_seq(&v["types"], &mut env, "B")?; - let got = IDLArgs::from_bytes_with_types(&bytes, &env, &types).map(|args| args.args); + let got = IDLArgs::from_bytes_with_types(&bytes, &env, &types) + .map(|args| args.args) + .map_err(|e| e.to_string()); + compare_values(got, &v["expect"], &env, &types) +} + +/// Read the text as `candid_parser`'s own test runner does (`Input::parse` in +/// `rust/candid_parser/src/test.rs`): parse an argument sequence, then annotate it with +/// the types. The values are compared as for `decode`. A panic is a disagreement: +/// 0.10 panics on some text. +fn parse(v: &Value) -> Result { + let text = string(&v["text"])?; + let mut env = TypeEnv::new(); + let types = load_seq(&v["types"], &mut env, "B")?; + let read = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + candid_parser::parse_idl_args(text) + .map_err(|e| e.to_string()) + .and_then(|args| { + args.annotate_types(true, &env, &types) + .map_err(|e| e.to_string()) + }) + .map(|args| args.args) + })); + match read { + Ok(got) => compare_values(got, &v["expect"], &env, &types), + Err(_) => Ok(Outcome::Disagree("panicked".to_string())), + } +} + +/// Compare values that 0.10 produced, or its error, with a vector's `expect`. +fn compare_values( + got: std::result::Result, String>, + expect: &Value, + env: &TypeEnv, + types: &[Type], +) -> Result { // An expected value that 0.10 cannot represent is a disagreement, not a broken // vector: the question -- bytes and types -- translated, and what 0.10 cannot hold // is the answer the model gives. - let want = match &v["expect"] { + let want = match expect { Value::String(s) if s == "reject" => None, e => match array(&e["values"])? .iter() @@ -260,7 +363,7 @@ fn decode(v: &Value) -> Result { } }, }; - let encode = |vals: Vec| IDLArgs::new(&vals).to_bytes_with_types(&env, &types); + let encode = |vals: Vec| IDLArgs::new(&vals).to_bytes_with_types(env, types); Ok(match (got, want) { (Err(_), None) => Outcome::Agree, (Err(e), Some(w)) => Outcome::Disagree(format!("rejected ({e}), want {w:?}")), diff --git a/conformance/tools/test-did/src/main.rs b/conformance/tools/test-did/src/main.rs index 06bd8eeb..d513a544 100644 --- a/conformance/tools/test-did/src/main.rs +++ b/conformance/tools/test-did/src/main.rs @@ -6,9 +6,9 @@ //! //! Each assertion becomes an `Oracle.TestDid.Assert` (`lean/Oracle/TestDid/Assert.lean`): //! its message, its types as a type table, and its claim. Nothing is decided here -- -//! whether the model agrees with a claim is `lean/Oracle/TestDid.lean`'s question -- with -//! one exception: the model has no textual syntax yet, so text in an assertion is -//! carried with the values `candid_parser` reads it as. +//! whether the model agrees with a claim is `lean/Oracle/TestDid.lean`'s question. Text in +//! an assertion is carried with the values `candid_parser` reads it as, which the model's +//! own reading is checked against. //! //! This exists while `test/` does. CI runs it and fails if the output differs from the //! committed file, so the `.test.did` files stay the source of these assertions. diff --git a/conformance/vectors/handwritten.json b/conformance/vectors/handwritten.json index 561a2c09..b6f1a10d 100644 --- a/conformance/vectors/handwritten.json +++ b/conformance/vectors/handwritten.json @@ -225,5 +225,94 @@ {"id":"decode: a future value's reference count is ignored","kind":"decode","bytes":"4449444c01670001000003","types":{"roots":["reserved"],"table":[]},"expect":{"values":[null]}}, {"id":"decode: a future value runs past the end","kind":"decode","bytes":"4449444c0167000100050068","types":{"roots":["reserved"],"table":[]},"expect":"reject"}, {"id":"decode: a future type runs past the end","kind":"decode","bytes":"4449444c0167054142","types":{"roots":[],"table":[]},"expect":"reject"}, -{"id":"decode: a future type is not a primitive, so it is not an argument type","kind":"decode","bytes":"4449444c0001670000","types":{"roots":[],"table":[]},"expect":"reject"} +{"id":"decode: a future type is not a primitive, so it is not an argument type","kind":"decode","bytes":"4449444c0001670000","types":{"roots":[],"table":[]},"expect":"reject"}, +{"id":"text: comments nest, and a line comment ends at the line feed (3.2)","kind":"parse","text":"( /* a /* nested */ b */ 1 // to the end of the line\n )","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"1"}]}}, +{"id":"text: an unclosed block comment (3.2)","kind":"parse","text":"(1 /* unclosed)","types":{"roots":["nat"],"table":[]},"expect":"reject"}, +{"id":"text: a keyword is not a field name (3.3)","kind":"parse","text":"(record { opt = 1 })","types":{"roots":[0],"table":[{"record":[[5545011,"nat"]]}]},"expect":"reject"}, +{"id":"text: a field named like a keyword is written as text (3.3)","kind":"parse","text":"(record { \"opt\" = 1 })","types":{"roots":[0],"table":[{"record":[[5545011,"nat"]]}]},"expect":{"values":[{"record":[[5545011,{"nat":"1"}]]}]}}, +{"id":"text: a type name is an identifier, not a keyword (3.3)","kind":"parse","text":"(record { nat = 1 })","types":{"roots":[0],"table":[{"record":[[5491937,"nat"]]}]},"expect":{"values":[{"record":[[5491937,{"nat":"1"}]]}]}}, +{"id":"text: an underscore separates digits (3.4)","kind":"parse","text":"(1_000)","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"1000"}]}}, +{"id":"text: an underscore cannot end a number (3.4)","kind":"parse","text":"(1_)","types":{"roots":["nat"],"table":[]},"expect":"reject"}, +{"id":"text: an underscore cannot follow another (3.4)","kind":"parse","text":"(1__0)","types":{"roots":["nat"],"table":[]},"expect":"reject"}, +{"id":"text: an underscore cannot begin the digits (3.4)","kind":"parse","text":"(0x_ff)","types":{"roots":["nat"],"table":[]},"expect":"reject"}, +{"id":"text: hexadecimal digits in either case (3.4)","kind":"parse","text":"(0xFF)","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"255"}]}}, +{"id":"text: the hexadecimal prefix is lowercase (3.4)","kind":"parse","text":"(0X10)","types":{"roots":["nat"],"table":[]},"expect":"reject"}, +{"id":"text: a signed literal at nat (5.3)","kind":"parse","text":"(+5)","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"5"}]}}, +{"id":"text: minus zero at nat (5.3)","kind":"parse","text":"(-0)","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"0"}]}}, +{"id":"text: a negative literal at nat (5.3)","kind":"parse","text":"(-1)","types":{"roots":["nat"],"table":[]},"expect":"reject"}, +{"id":"text: a sign is part of its number (3.4)","kind":"parse","text":"(- 5)","types":{"roots":["int"],"table":[]},"expect":"reject"}, +{"id":"text: a signed hexadecimal literal (3.4)","kind":"parse","text":"(-0x10)","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"-16"}]}}, +{"id":"text: the ends of the fixed-width ranges (5.3)","kind":"parse","text":"(255, -128, 18446744073709551615, -9223372036854775808)","types":{"roots":["nat8","int8","nat64","int64"],"table":[]},"expect":{"values":[{"nat8":"255"},{"int8":"-128"},{"nat64":"18446744073709551615"},{"int64":"-9223372036854775808"}]}}, +{"id":"text: past the top of nat8 (5.3)","kind":"parse","text":"(256)","types":{"roots":["nat8"],"table":[]},"expect":"reject"}, +{"id":"text: past the bottom of int8 (5.3)","kind":"parse","text":"(-129)","types":{"roots":["int8"],"table":[]},"expect":"reject"}, +{"id":"text: an integer literal at a float type (5.3)","kind":"parse","text":"(1)","types":{"roots":["float64"],"table":[]},"expect":"reject"}, +{"id":"text: a float literal at an integer type (5.3)","kind":"parse","text":"(1.0)","types":{"roots":["nat"],"table":[]},"expect":"reject"}, +{"id":"text: 1.5 at float32 (5.3)","kind":"parse","text":"(1.5)","types":{"roots":["float32"],"table":[]},"expect":{"values":[{"float32":"3fc00000"}]}}, +{"id":"text: 0.1 rounded to each format (5.3)","kind":"parse","text":"(0.1, 0.1)","types":{"roots":["float32","float64"],"table":[]},"expect":{"values":[{"float32":"3dcccccd"},{"float64":"3fb999999999999a"}]}}, +{"id":"text: rounded once, not first to float64 (5.3)","kind":"parse","text":"(1.0000000596046448)","types":{"roots":["float32"],"table":[]},"expect":{"values":[{"float32":"3f800001"}]}}, +{"id":"text: overflow rounds to infinity (5.3)","kind":"parse","text":"(1e400, 1e999999999)","types":{"roots":["float64","float64"],"table":[]},"expect":{"values":[{"float64":"7ff0000000000000"},{"float64":"7ff0000000000000"}]}}, +{"id":"text: a zero keeps the literal's sign (5.3)","kind":"parse","text":"(-1e-400, -0.0)","types":{"roots":["float64","float32"],"table":[]},"expect":{"values":[{"float64":"8000000000000000"},{"float32":"80000000"}]}}, +{"id":"text: an underscore in a float literal (3.4)","kind":"parse","text":"(1_000.5)","types":{"roots":["float64"],"table":[]},"expect":{"values":[{"float64":"408f440000000000"}]}}, +{"id":"text: a float literal begins with a digit (3.4)","kind":"parse","text":"(.5)","types":{"roots":["float64"],"table":[]},"expect":"reject"}, +{"id":"text: a point with no fraction (3.4)","kind":"parse","text":"(5.)","types":{"roots":["float64"],"table":[]},"expect":{"values":[{"float64":"4014000000000000"}]}}, +{"id":"text: hexadecimal float literals (3.4)","kind":"parse","text":"(0x1p4, 0x1.8p1)","types":{"roots":["float64","float32"],"table":[]},"expect":{"values":[{"float64":"4030000000000000"},{"float32":"40400000"}]}}, +{"id":"text: a raw tab in a text literal (3.5)","kind":"parse","text":"(\"a\u0009b\")","types":{"roots":["text"],"table":[]},"expect":"reject"}, +{"id":"text: a raw line feed in a text literal (3.5)","kind":"parse","text":"(\"a\nb\")","types":{"roots":["text"],"table":[]},"expect":"reject"}, +{"id":"text: the simple escapes (3.5)","kind":"parse","text":"(\"\\t\\n\\r\\\\\\\"\\'\")","types":{"roots":["text"],"table":[]},"expect":{"values":[{"text":"\u0009\n\r\\\"'"}]}}, +{"id":"text: a raw C1 control character (3.5)","kind":"parse","text":"(\"…\")","types":{"roots":["text"],"table":[]},"expect":{"values":[{"text":"…"}]}}, +{"id":"text: byte escapes that make UTF-8 (3.5)","kind":"parse","text":"(\"\\e2\\98\\83\")","types":{"roots":["text"],"table":[]},"expect":{"values":[{"text":"☃"}]}}, +{"id":"text: byte escapes that do not make UTF-8, as text (5.4)","kind":"parse","text":"(\"\\ff\")","types":{"roots":["text"],"table":[]},"expect":"reject"}, +{"id":"text: byte escapes that do not make UTF-8, as a blob (4.2)","kind":"parse","text":"(blob \"\\ff\")","types":{"roots":[0],"table":[{"vec":"nat8"}]},"expect":{"values":[{"vec":[{"nat8":"255"}]}]}}, +{"id":"text: a blob of a character is its UTF-8 (3.5)","kind":"parse","text":"(blob \"é\")","types":{"roots":[0],"table":[{"vec":"nat8"}]},"expect":{"values":[{"vec":[{"nat8":"195"},{"nat8":"169"}]}]}}, +{"id":"text: leading zeros in a code point (3.5)","kind":"parse","text":"(\"\\u{0000000041}\")","types":{"roots":["text"],"table":[]},"expect":{"values":[{"text":"A"}]}}, +{"id":"text: a surrogate is not a scalar value (3.5)","kind":"parse","text":"(\"\\u{d800}\")","types":{"roots":["text"],"table":[]},"expect":"reject"}, +{"id":"text: past the last code point (3.5)","kind":"parse","text":"(\"\\u{110000}\")","types":{"roots":["text"],"table":[]},"expect":"reject"}, +{"id":"text: an empty code point (3.5)","kind":"parse","text":"(\"\\u{}\")","types":{"roots":["text"],"table":[]},"expect":"reject"}, +{"id":"text: an underscore cannot end a code point (3.5)","kind":"parse","text":"(\"\\u{41_}\")","types":{"roots":["text"],"table":[]},"expect":"reject"}, +{"id":"text: an unknown escape (3.5)","kind":"parse","text":"(\"\\q\")","types":{"roots":["text"],"table":[]},"expect":"reject"}, +{"id":"text: a value is not made optional (5.5)","kind":"parse","text":"(5)","types":{"roots":[0],"table":[{"opt":"nat"}]},"expect":"reject"}, +{"id":"text: opt (5.5)","kind":"parse","text":"(opt 5)","types":{"roots":[0],"table":[{"opt":"nat"}]},"expect":{"values":[{"opt":{"nat":"5"}}]}}, +{"id":"text: null is absent at opt null, and opt null is present (5.5)","kind":"parse","text":"(null, opt null)","types":{"roots":[0,0],"table":[{"opt":"null"}]},"expect":{"values":[null,{"opt":null}]}}, +{"id":"text: a trailing separator in a vector (4.1)","kind":"parse","text":"(vec { 1; 2; })","types":{"roots":[0],"table":[{"vec":"nat"}]},"expect":{"values":[{"vec":[{"nat":"1"},{"nat":"2"}]}]}}, +{"id":"text: an empty vector at blob (5.5)","kind":"parse","text":"(vec {})","types":{"roots":[0],"table":[{"vec":"nat8"}]},"expect":{"values":[{"vec":[]}]}}, +{"id":"text: a blob at a vector of something else (5.5)","kind":"parse","text":"(blob \"a\")","types":{"roots":[0],"table":[{"vec":"nat16"}]},"expect":"reject"}, +{"id":"text: a positional field follows the field before it (4.2)","kind":"parse","text":"(record { a = 1; 2 })","types":{"roots":[0],"table":[{"record":[[97,"nat"],[98,"nat"]]}]},"expect":{"values":[{"record":[[97,{"nat":"1"}],[98,{"nat":"2"}]]}]}}, +{"id":"text: a field the type does not have is ignored (5.6)","kind":"parse","text":"(record { whatever = 0 })","types":{"roots":[0],"table":[{"record":[]}]},"expect":{"values":[{"record":[]}]}}, +{"id":"text: an ignored field is not elaborated (5.6)","kind":"parse","text":"(record { a = 1; b = vec { 1; -1 } })","types":{"roots":[0],"table":[{"record":[[97,"nat"]]}]},"expect":{"values":[{"record":[[97,{"nat":"1"}]]}]}}, +{"id":"text: a missing field of type opt, null or reserved is null (5.6)","kind":"parse","text":"(record {})","types":{"roots":[1],"table":[{"opt":"nat"},{"record":[[97,0],[98,"null"],[99,"reserved"]]}]},"expect":{"values":[{"record":[[97,null],[98,null],[99,null]]}]}}, +{"id":"text: a missing field of type nat (5.6)","kind":"parse","text":"(record {})","types":{"roots":[0],"table":[{"record":[[97,"nat"]]}]},"expect":"reject"}, +{"id":"text: a field id given twice (4.2)","kind":"parse","text":"(record { a = 1; 97 = 2 })","types":{"roots":["reserved"],"table":[]},"expect":"reject"}, +{"id":"text: a positional field after the last id (4.2)","kind":"parse","text":"(record { 4294967295 = 1; 2 })","types":{"roots":["reserved"],"table":[]},"expect":"reject"}, +{"id":"text: a field id past 32 bits (4.2)","kind":"parse","text":"(record { 4294967296 = 1 })","types":{"roots":["reserved"],"table":[]},"expect":"reject"}, +{"id":"text: a hexadecimal field id (4.2)","kind":"parse","text":"(record { 0x61 = 1 })","types":{"roots":[0],"table":[{"record":[[97,"nat"]]}]},"expect":{"values":[{"record":[[97,{"nat":"1"}]]}]}}, +{"id":"text: a variant label alone is null (4.2)","kind":"parse","text":"(variant { a })","types":{"roots":[0],"table":[{"variant":[[97,"null"],[98,"nat"]]}]},"expect":{"values":[{"variant":[97,null]}]}}, +{"id":"text: a variant has one field (4.1)","kind":"parse","text":"(variant {})","types":{"roots":[0],"table":[{"variant":[]}]},"expect":"reject"}, +{"id":"text: no separator after a variant's field (4.1)","kind":"parse","text":"(variant { a; })","types":{"roots":[0],"table":[{"variant":[[97,"null"]]}]},"expect":"reject"}, +{"id":"text: a variant field the type does not have (5.6)","kind":"parse","text":"(variant { c = 1 })","types":{"roots":[0],"table":[{"variant":[[97,"null"],[98,"nat"]]}]},"expect":"reject"}, +{"id":"text: anything is null at reserved (5.2)","kind":"parse","text":"(vec { 1; -1 })","types":{"roots":["reserved"],"table":[]},"expect":{"values":[null]}}, +{"id":"text: nothing elaborates at empty (5.8)","kind":"parse","text":"(null)","types":{"roots":["empty"],"table":[]},"expect":"reject"}, +{"id":"text: a principal's text, in either case (Appendix A)","kind":"parse","text":"(principal \"aaaaa-aa\", principal \"AAAAA-AA\")","types":{"roots":["principal","principal"],"table":[]},"expect":{"values":[{"principal":""},{"principal":""}]}}, +{"id":"text: a principal's dashes are checked (Appendix A)","kind":"parse","text":"(principal \"aaaaaaa\")","types":{"roots":["principal"],"table":[]},"expect":"reject"}, +{"id":"text: a principal's checksum is checked (Appendix A)","kind":"parse","text":"(principal \"aaaaa-ab\")","types":{"roots":["principal"],"table":[]},"expect":"reject"}, +{"id":"text: a principal is not a service (5.7)","kind":"parse","text":"(principal \"w7x7r-cok77-xa\")","types":{"roots":[0],"table":[{"service":[]}]},"expect":"reject"}, +{"id":"text: a service reference (5.7)","kind":"parse","text":"(service \"aaaaa-aa\")","types":{"roots":[0],"table":[{"service":[]}]},"expect":{"values":[{"service":""}]}}, +{"id":"text: a function reference, its method named by text (5.7)","kind":"parse","text":"(func \"aaaaa-aa\".\"mé\")","types":{"roots":[0],"table":[{"func":{"annots":["query"],"args":["nat"],"rets":[]}}]},"expect":{"values":[{"func":["","mé"]}]}}, +{"id":"text: an annotation at its own type (5.1)","kind":"parse","text":"((5 : nat), 6 : nat8)","types":{"roots":["nat","nat8"],"table":[]},"expect":{"values":[{"nat":"5"},{"nat8":"6"}]}}, +{"id":"text: an annotation does not convert (5.1)","kind":"parse","text":"((5 : nat))","types":{"roots":["int"],"table":[]},"expect":"reject"}, +{"id":"text: an annotation does not narrow (5.1)","kind":"parse","text":"((1.5 : float64))","types":{"roots":["float32"],"table":[]},"expect":"reject"}, +{"id":"text: an annotation at reserved (5.1)","kind":"parse","text":"((1 : nat))","types":{"roots":["reserved"],"table":[]},"expect":"reject"}, +{"id":"text: an annotation that does not hold (5.1)","kind":"parse","text":"((1 : text))","types":{"roots":["reserved"],"table":[]},"expect":"reject"}, +{"id":"text: the same type, its fields in another order (5.1)","kind":"parse","text":"((record { a = 1; b = 2 } : record { b : nat; a : nat }))","types":{"roots":[0],"table":[{"record":[[97,"nat"],[98,"nat"]]}]},"expect":{"values":[{"record":[[97,{"nat":"1"}],[98,{"nat":"2"}]]}]}}, +{"id":"text: an annotation with a function type (5.1)","kind":"parse","text":"((func \"aaaaa-aa\".m : func (x : nat) -> () query))","types":{"roots":[0],"table":[{"func":{"annots":["query"],"args":["nat"],"rets":[]}}]},"expect":{"values":[{"func":["","m"]}]}}, +{"id":"text: a parameter name repeated in an annotation (5.1)","kind":"parse","text":"((func \"aaaaa-aa\".m : func (x : nat, x : nat) -> ()))","types":{"roots":[0],"table":[{"func":{"annots":[],"args":["nat","nat"],"rets":[]}}]},"expect":"reject"}, +{"id":"text: a parameter name that is not UTF-8 in an annotation (5.1)","kind":"parse","text":"((func \"aaaaa-aa\".m : func (\"\\ff\" : nat) -> ()))","types":{"roots":[0],"table":[{"func":{"annots":[],"args":["nat"],"rets":[]}}]},"expect":"reject"}, +{"id":"text: a parameter and a result may share a name (5.1)","kind":"parse","text":"((func \"aaaaa-aa\".m : func (x : nat) -> (x : nat)))","types":{"roots":[0],"table":[{"func":{"annots":[],"args":["nat"],"rets":["nat"]}}]},"expect":{"values":[{"func":["","m"]}]}}, +{"id":"text: a type name with no definition (5.1)","kind":"parse","text":"((1 : t))","types":{"roots":["nat"],"table":[]},"expect":"reject"}, +{"id":"text: an ignored field must be well-annotated (5.1)","kind":"parse","text":"(record { a = 1; b = (1 : text) })","types":{"roots":[0],"table":[{"record":[[97,"nat"]]}]},"expect":"reject"}, +{"id":"text: an argument beyond the types is ignored (5.9)","kind":"parse","text":"(1, 2)","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"1"}]}}, +{"id":"text: a trailing comma (4.1)","kind":"parse","text":"(1,)","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"1"}]}}, +{"id":"text: missing arguments of type opt and null (5.9)","kind":"parse","text":"()","types":{"roots":[0,"null"],"table":[{"opt":"nat"}]},"expect":{"values":[null,null]}}, +{"id":"text: a missing argument of type nat (5.9)","kind":"parse","text":"()","types":{"roots":["nat"],"table":[]},"expect":"reject"}, +{"id":"text: an ignored argument must be well-annotated (5.9)","kind":"parse","text":"(1, (2 : text))","types":{"roots":["nat"],"table":[]},"expect":"reject"}, +{"id":"text: input after the arguments","kind":"parse","text":"(1) 2","types":{"roots":["nat"],"table":[]},"expect":"reject"} ]} diff --git a/conformance/vectors/test-did.json b/conformance/vectors/test-did.json index b45ad7e6..17530480 100644 --- a/conformance/vectors/test-did.json +++ b/conformance/vectors/test-did.json @@ -17,49 +17,79 @@ {"id":"construct.test.did: table entry in the table (self-reference)","kind":"decode","bytes":"4449444c010000","types":{"roots":[],"table":[]},"expect":"reject"}, {"id":"construct.test.did: table too long","kind":"decode","bytes":"4449444c016e6f6e6f00","types":{"roots":[],"table":[]},"expect":"reject"}, {"id":"construct.test.did: opt: null","kind":"decode","bytes":"4449444c016e7c010000","types":{"roots":[0],"table":[{"opt":"int"}]},"expect":{"values":[null]}}, +{"id":"construct.test.did: opt: null (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":"int"}]},"expect":{"values":[null]}}, {"id":"construct.test.did: opt: type out of range","kind":"decode","bytes":"4449444c016e02010000","types":{"roots":[0],"table":[{"opt":"int"}]},"expect":"reject"}, {"id":"construct.test.did: opt: 42","kind":"decode","bytes":"4449444c016e7c0100012a","types":{"roots":[0],"table":[{"opt":"int"}]},"expect":{"values":[{"opt":{"int":"42"}}]}}, +{"id":"construct.test.did: opt: 42 (right)","kind":"parse","text":"(opt 42)","types":{"roots":[0],"table":[{"opt":"int"}]},"expect":{"values":[{"opt":{"int":"42"}}]}}, {"id":"construct.test.did: opt: out of range #21","kind":"decode","bytes":"4449444c016e7c0100022a","types":{"roots":[0],"table":[{"opt":"int"}]},"expect":"reject"}, {"id":"construct.test.did: opt: out of range #22","kind":"decode","bytes":"4449444c016e7c0100ff","types":{"roots":[0],"table":[{"opt":"int"}]},"expect":"reject"}, {"id":"construct.test.did: opt: too short","kind":"decode","bytes":"4449444c016e7c010001","types":{"roots":[0],"table":[{"opt":"int"}]},"expect":"reject"}, {"id":"construct.test.did: opt: too long","kind":"decode","bytes":"4449444c016e7c0100002a","types":{"roots":[0],"table":[{"opt":"int"}]},"expect":"reject"}, {"id":"construct.test.did: opt: nested","kind":"decode","bytes":"4449444c026e016e7c010001012a","types":{"roots":[0],"table":[{"opt":1},{"opt":"int"}]},"expect":{"values":[{"opt":{"opt":{"int":"42"}}}]}}, +{"id":"construct.test.did: opt: nested (right)","kind":"parse","text":"(opt opt 42)","types":{"roots":[0],"table":[{"opt":1},{"opt":"int"}]},"expect":{"values":[{"opt":{"opt":{"int":"42"}}}]}}, {"id":"construct.test.did: opt: recursion","kind":"decode","bytes":"4449444c016e000100010100","types":{"roots":[0],"table":[{"opt":0}]},"expect":{"values":[{"opt":{"opt":null}}]}}, +{"id":"construct.test.did: opt: recursion (right)","kind":"parse","text":"(opt opt null)","types":{"roots":[0],"table":[{"opt":0}]},"expect":{"values":[{"opt":{"opt":null}}]}}, {"id":"construct.test.did: opt: non-recursive type","kind":"decode","bytes":"4449444c016e000100010100","types":{"roots":[0],"table":[{"opt":1},{"opt":2},{"opt":"empty"}]},"expect":{"values":[{"opt":{"opt":null}}]}}, +{"id":"construct.test.did: opt: non-recursive type (right)","kind":"parse","text":"(opt opt null)","types":{"roots":[0],"table":[{"opt":1},{"opt":2},{"opt":"empty"}]},"expect":{"values":[{"opt":{"opt":null}}]}}, {"id":"construct.test.did: opt: mutual recursion","kind":"decode","bytes":"4449444c026e016e000100010100","types":{"roots":[0],"table":[{"opt":0}]},"expect":{"values":[{"opt":{"opt":null}}]}}, +{"id":"construct.test.did: opt: mutual recursion (right)","kind":"parse","text":"(opt opt null)","types":{"roots":[0],"table":[{"opt":0}]},"expect":{"values":[{"opt":{"opt":null}}]}}, {"id":"construct.test.did: opt: extra arg","kind":"decode","bytes":"4449444c0000","types":{"roots":[0],"table":[{"opt":0}]},"expect":{"values":[null]}}, +{"id":"construct.test.did: opt: extra arg (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":0}]},"expect":{"values":[null]}}, {"id":"construct.test.did: vec: empty","kind":"decode","bytes":"4449444c016d7c010000","types":{"roots":[0],"table":[{"vec":"int"}]},"expect":{"values":[{"vec":[]}]}}, +{"id":"construct.test.did: vec: empty (right)","kind":"parse","text":"(vec {})","types":{"roots":[0],"table":[{"vec":"int"}]},"expect":{"values":[{"vec":[]}]}}, {"id":"construct.test.did: vec: non subtype empty","kind":"decode","bytes":"4449444c016d7c010000","types":{"roots":[0],"table":[{"vec":"int8"}]},"expect":{"values":[{"vec":[]}]}}, {"id":"construct.test.did: vec","kind":"decode","bytes":"4449444c016d7c0100020102","types":{"roots":[0],"table":[{"vec":"int"}]},"expect":{"values":[{"vec":[{"int":"1"},{"int":"2"}]}]}}, +{"id":"construct.test.did: vec (right)","kind":"parse","text":"(vec { 1; 2 })","types":{"roots":[0],"table":[{"vec":"int"}]},"expect":{"values":[{"vec":[{"int":"1"},{"int":"2"}]}]}}, {"id":"construct.test.did: vec: blob","kind":"decode","bytes":"4449444c016d7b0100020102","types":{"roots":[0],"table":[{"vec":"nat8"}]},"expect":{"values":[{"vec":[{"nat8":"1"},{"nat8":"2"}]}]}}, +{"id":"construct.test.did: vec: blob (right)","kind":"parse","text":"(blob \"\\01\\02\")","types":{"roots":[0],"table":[{"vec":"nat8"}]},"expect":{"values":[{"vec":[{"nat8":"1"},{"nat8":"2"}]}]}}, {"id":"construct.test.did: vec: recursive vector","kind":"decode","bytes":"4449444c016d00010000","types":{"roots":[0],"table":[{"vec":0}]},"expect":{"values":[{"vec":[]}]}}, +{"id":"construct.test.did: vec: recursive vector (right)","kind":"parse","text":"(vec {})","types":{"roots":[0],"table":[{"vec":0}]},"expect":{"values":[{"vec":[]}]}}, {"id":"construct.test.did: vec: tree","kind":"decode","bytes":"4449444c016d000100020000","types":{"roots":[0],"table":[{"vec":0}]},"expect":{"values":[{"vec":[{"vec":[]},{"vec":[]}]}]}}, +{"id":"construct.test.did: vec: tree (right)","kind":"parse","text":"(vec { vec {}; vec {} })","types":{"roots":[0],"table":[{"vec":0}]},"expect":{"values":[{"vec":[{"vec":[]},{"vec":[]}]}]}}, {"id":"construct.test.did: vec: non-recursive tree","kind":"decode","bytes":"4449444c016d000100020000","types":{"roots":[0],"table":[{"vec":1},{"vec":2},{"opt":"empty"}]},"expect":{"values":[{"vec":[{"vec":[]},{"vec":[]}]}]}}, +{"id":"construct.test.did: vec: non-recursive tree (right)","kind":"parse","text":"(vec { vec {}; vec {} })","types":{"roots":[0],"table":[{"vec":1},{"vec":2},{"opt":"empty"}]},"expect":{"values":[{"vec":[{"vec":[]},{"vec":[]}]}]}}, {"id":"construct.test.did: vec null","kind":"decode","bytes":"4449444c016d7f0100e807","types":{"roots":[0],"table":[{"vec":"null"}]},"expect":{"values":[{"vec":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]}]}}, {"id":"construct.test.did: vec null <: vec opt nat","kind":"decode","bytes":"4449444c016d7f0100e807","types":{"roots":[0],"table":[{"vec":1},{"opt":"nat"}]},"expect":{"values":[{"vec":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]}]}}, +{"id":"construct.test.did: vec: type mismatch","kind":"parse","text":"(vec { 1; -1 })","types":{"roots":[0],"table":[{"vec":"nat"}]},"expect":"reject"}, {"id":"construct.test.did: vec: too short #40","kind":"decode","bytes":"4449444c016d7c0100","types":{"roots":[0],"table":[{"vec":"int"}]},"expect":"reject"}, {"id":"construct.test.did: vec: too short #41","kind":"decode","bytes":"4449444c016d7c01000201","types":{"roots":[0],"table":[{"vec":"int"}]},"expect":"reject"}, {"id":"construct.test.did: vec: too long","kind":"decode","bytes":"4449444c016d7c0100010102","types":{"roots":[0],"table":[{"vec":"int"}]},"expect":"reject"}, {"id":"construct.test.did: vec of records","kind":"decode","bytes":"4449444c026d016c00010005","types":{"roots":[0],"table":[{"vec":1},{"record":[]}]},"expect":{"values":[{"vec":[{"record":[]},{"record":[]},{"record":[]},{"record":[]},{"record":[]}]}]}}, {"id":"construct.test.did: record: empty","kind":"decode","bytes":"4449444c016c000100","types":{"roots":[0],"table":[{"record":[]}]},"expect":{"values":[{"record":[]}]}}, +{"id":"construct.test.did: record: empty (right)","kind":"parse","text":"(record {})","types":{"roots":[0],"table":[{"record":[]}]},"expect":{"values":[{"record":[]}]}}, {"id":"construct.test.did: record: multiple records","kind":"decode","bytes":"4449444c016c0003000000","types":{"roots":[0,1,2],"table":[{"record":[]},{"record":[]},{"record":[]}]},"expect":{"values":[{"record":[]},{"record":[]},{"record":[]}]}}, +{"id":"construct.test.did: record: multiple records (right)","kind":"parse","text":"(record {}, record {}, record {})","types":{"roots":[0,1,2],"table":[{"record":[]},{"record":[]},{"record":[]}]},"expect":{"values":[{"record":[]},{"record":[]},{"record":[]}]}}, {"id":"construct.test.did: record","kind":"decode","bytes":"4449444c016c01017c01002a","types":{"roots":[0],"table":[{"record":[[1,"int"]]}]},"expect":{"values":[{"record":[[1,{"int":"42"}]]}]}}, +{"id":"construct.test.did: record (right)","kind":"parse","text":"(record { 1 = 42 })","types":{"roots":[0],"table":[{"record":[[1,"int"]]}]},"expect":{"values":[{"record":[[1,{"int":"42"}]]}]}}, {"id":"construct.test.did: record: opt","kind":"decode","bytes":"4449444c016c01017c01002a","types":{"roots":[0],"table":[{"record":[[1,1]]},{"opt":"int"}]},"expect":{"values":[{"record":[[1,{"opt":{"int":"42"}}]]}]}}, +{"id":"construct.test.did: record: opt (right)","kind":"parse","text":"(record { 1 = opt 42 })","types":{"roots":[0],"table":[{"record":[[1,1]]},{"opt":"int"}]},"expect":{"values":[{"record":[[1,{"opt":{"int":"42"}}]]}]}}, {"id":"construct.test.did: record: reserved","kind":"decode","bytes":"4449444c016c01017c01002a","types":{"roots":[0],"table":[{"record":[[1,"reserved"]]}]},"expect":{"values":[{"record":[[1,null]]}]}}, +{"id":"construct.test.did: record: reserved (right)","kind":"parse","text":"(record { 1 = null })","types":{"roots":[0],"table":[{"record":[[1,"reserved"]]}]},"expect":{"values":[{"record":[[1,null]]}]}}, {"id":"construct.test.did: record: ignore fields #49","kind":"decode","bytes":"4449444c016c01017c01002a","types":{"roots":[0],"table":[{"record":[]}]},"expect":{"values":[{"record":[]}]}}, +{"id":"construct.test.did: record: ignore fields #49 (right)","kind":"parse","text":"(record {})","types":{"roots":[0],"table":[{"record":[]}]},"expect":{"values":[{"record":[]}]}}, +{"id":"construct.test.did: record: ignore fields (textual)","kind":"parse","text":"(record { whatever = 0 })","types":{"roots":[0],"table":[{"record":[]}]},"expect":{"values":[{"record":[]}]}}, +{"id":"construct.test.did: record: ignore fields (textual) (right)","kind":"parse","text":"(record {})","types":{"roots":[0],"table":[{"record":[]}]},"expect":{"values":[{"record":[]}]}}, {"id":"construct.test.did: record: missing field","kind":"decode","bytes":"4449444c016c01017c01002a","types":{"roots":[0],"table":[{"record":[[2,"int"]]}]},"expect":"reject"}, {"id":"construct.test.did: record: missing opt field","kind":"decode","bytes":"4449444c016c01017c01002a","types":{"roots":[0],"table":[{"record":[[2,1]]},{"opt":"int"}]},"expect":{"values":[{"record":[[2,null]]}]}}, +{"id":"construct.test.did: record: missing opt field (right)","kind":"parse","text":"(record { 2 = null })","types":{"roots":[0],"table":[{"record":[[2,1]]},{"opt":"int"}]},"expect":{"values":[{"record":[[2,null]]}]}}, {"id":"construct.test.did: record: missing null field","kind":"decode","bytes":"4449444c016c000100","types":{"roots":[0],"table":[{"record":[[2,"null"]]}]},"expect":{"values":[{"record":[[2,null]]}]}}, +{"id":"construct.test.did: record: missing null field (right)","kind":"parse","text":"(record { 2 = null })","types":{"roots":[0],"table":[{"record":[[2,"null"]]}]},"expect":{"values":[{"record":[[2,null]]}]}}, {"id":"construct.test.did: record: missing null field also in textual format","kind":"decode","bytes":"4449444c016c000100","types":{"roots":[0],"table":[{"record":[[2,"null"]]}]},"expect":{"values":[{"record":[[2,null]]}]}}, +{"id":"construct.test.did: record: missing null field also in textual format (right)","kind":"parse","text":"(record { })","types":{"roots":[0],"table":[{"record":[[2,"null"]]}]},"expect":{"values":[{"record":[[2,null]]}]}}, {"id":"construct.test.did: record: tuple","kind":"decode","bytes":"4449444c016c02007c017e01002a01","types":{"roots":[0],"table":[{"record":[[0,"int"],[1,"bool"]]}]},"expect":{"values":[{"record":[[0,{"int":"42"}],[1,true]]}]}}, +{"id":"construct.test.did: record: tuple (right)","kind":"parse","text":"(record {42; true})","types":{"roots":[0],"table":[{"record":[[0,"int"],[1,"bool"]]}]},"expect":{"values":[{"record":[[0,{"int":"42"}],[1,true]]}]}}, {"id":"construct.test.did: record: ignore fields #56","kind":"decode","bytes":"4449444c016c02007c017e01002a01","types":{"roots":[0],"table":[{"record":[[1,"bool"]]}]},"expect":{"values":[{"record":[[1,true]]}]}}, +{"id":"construct.test.did: record: ignore fields #56 (right)","kind":"parse","text":"(record {1 = true})","types":{"roots":[0],"table":[{"record":[[1,"bool"]]}]},"expect":{"values":[{"record":[[1,true]]}]}}, {"id":"construct.test.did: record: type mismatch","kind":"decode","bytes":"4449444c016c02007c017e01002a01","types":{"roots":[0],"table":[{"record":[[0,"bool"],[1,"int"]]}]},"expect":"reject"}, {"id":"construct.test.did: record: duplicate fields","kind":"decode","bytes":"4449444c016c02007c007e01002a01","types":{"roots":[0],"table":[{"record":[[0,"int"],[1,"bool"]]}]},"expect":"reject"}, {"id":"construct.test.did: record: unsorted","kind":"decode","bytes":"4449444c016c02017c007e01002a01","types":{"roots":[0],"table":[{"record":[[0,"bool"],[1,"int"]]}]},"expect":"reject"}, {"id":"construct.test.did: record: named fields","kind":"decode","bytes":"4449444c016c02d3e3aa027e868eb7027c0100012a","types":{"roots":[0],"table":[{"record":[[4895187,"bool"],[5097222,"int"]]}]},"expect":{"values":[{"record":[[4895187,true],[5097222,{"int":"42"}]]}]}}, +{"id":"construct.test.did: record: named fields (right)","kind":"parse","text":"(record { foo = 42; bar = true})","types":{"roots":[0],"table":[{"record":[[4895187,"bool"],[5097222,"int"]]}]},"expect":{"values":[{"record":[[4895187,true],[5097222,{"int":"42"}]]}]}}, {"id":"construct.test.did: record: unicode field","kind":"decode","bytes":"4449444c016c01cd84b0057f0100","types":{"roots":[0],"table":[{"record":[[11272781,"null"]]}]},"expect":{"values":[{"record":[[11272781,null]]}]}}, +{"id":"construct.test.did: record: unicode field (right)","kind":"parse","text":"(record { \"☃\" = null })","types":{"roots":[0],"table":[{"record":[[11272781,"null"]]}]},"expect":{"values":[{"record":[[11272781,null]]}]}}, {"id":"construct.test.did: record: field hash larger than u32","kind":"decode","bytes":"4449444c016c0180e497d0127c01002a","types":{"roots":[0],"table":[{"record":[]}]},"expect":"reject"}, {"id":"construct.test.did: record: nested","kind":"decode","bytes":"4449444c046c0100016c0100026c0100036c000100","types":{"roots":[0],"table":[{"record":[[0,1]]},{"record":[[0,2]]},{"record":[[0,3]]},{"record":[]}]},"expect":{"values":[{"record":[[0,{"record":[[0,{"record":[[0,{"record":[]}]]}]]}]]}]}}, +{"id":"construct.test.did: record: nested (right)","kind":"parse","text":"(record { record { record { record {} } } })","types":{"roots":[0],"table":[{"record":[[0,1]]},{"record":[[0,2]]},{"record":[[0,3]]},{"record":[]}]},"expect":{"values":[{"record":[[0,{"record":[[0,{"record":[[0,{"record":[]}]]}]]}]]}]}}, {"id":"construct.test.did: record: empty recursion","kind":"decode","bytes":"4449444c016c0100000100","types":{"roots":[0],"table":[{"record":[[0,0]]}]},"expect":"reject"}, {"id":"construct.test.did: vec of empty records","kind":"decode","bytes":"4449444c026d016c010001010001","types":{"roots":[0],"table":[{"vec":1},{"record":[[0,1]]}]},"expect":"reject"}, {"id":"construct.test.did: record: value too short","kind":"decode","bytes":"4449444c016c02007c017e01002a","types":{"roots":[0],"table":[{"record":[[0,"int"]]}]},"expect":"reject"}, @@ -70,48 +100,79 @@ {"id":"construct.test.did: record: type out of range #71","kind":"decode","bytes":"4449444c016c020001017e01000000","types":{"roots":[0],"table":[{"record":[]}]},"expect":"reject"}, {"id":"construct.test.did: record: type out of range #72","kind":"decode","bytes":"4449444c016c020001017e017c2a","types":{"roots":["reserved"],"table":[]},"expect":"reject"}, {"id":"construct.test.did: opt: parsing null : null","kind":"decode","bytes":"4449444c00017f","types":{"roots":[0],"table":[{"opt":"empty"}]},"expect":{"values":[null]}}, +{"id":"construct.test.did: opt: parsing null : null (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":"empty"}]},"expect":{"values":[null]}}, {"id":"construct.test.did: opt: parsing null : opt empty","kind":"decode","bytes":"4449444c016e6f010000","types":{"roots":[0],"table":[{"opt":"empty"}]},"expect":{"values":[null]}}, +{"id":"construct.test.did: opt: parsing null : opt empty (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":"empty"}]},"expect":{"values":[null]}}, {"id":"construct.test.did: opt: parsing null : opt bool ","kind":"decode","bytes":"4449444c016e7e010000","types":{"roots":[0],"table":[{"opt":"bool"}]},"expect":{"values":[null]}}, +{"id":"construct.test.did: opt: parsing null : opt bool (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":"bool"}]},"expect":{"values":[null]}}, {"id":"construct.test.did: opt: parsing opt false : opt bool","kind":"decode","bytes":"4449444c016e7e01000100","types":{"roots":[0],"table":[{"opt":"bool"}]},"expect":{"values":[{"opt":false}]}}, +{"id":"construct.test.did: opt: parsing opt false : opt bool (right)","kind":"parse","text":"(opt false)","types":{"roots":[0],"table":[{"opt":"bool"}]},"expect":{"values":[{"opt":false}]}}, {"id":"construct.test.did: opt: parsing opt true : opt bool","kind":"decode","bytes":"4449444c016e7e01000101","types":{"roots":[0],"table":[{"opt":"bool"}]},"expect":{"values":[{"opt":true}]}}, +{"id":"construct.test.did: opt: parsing opt true : opt bool (right)","kind":"parse","text":"(opt true)","types":{"roots":[0],"table":[{"opt":"bool"}]},"expect":{"values":[{"opt":true}]}}, {"id":"construct.test.did: opt: parsing invalid bool at opt bool #78","kind":"decode","bytes":"4449444c016e7e01000102","types":{"roots":[0],"table":[{"opt":"bool"}]},"expect":"reject"}, {"id":"construct.test.did: opt: extra value","kind":"decode","bytes":"4449444c0000","types":{"roots":[0],"table":[{"opt":"bool"}]},"expect":{"values":[null]}}, +{"id":"construct.test.did: opt: extra value (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":"bool"}]},"expect":{"values":[null]}}, {"id":"construct.test.did: opt: parsing null : opt opt bool","kind":"decode","bytes":"4449444c026e016e7e010000","types":{"roots":[0],"table":[{"opt":1},{"opt":"bool"}]},"expect":{"values":[null]}}, +{"id":"construct.test.did: opt: parsing null : opt opt bool (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":1},{"opt":"bool"}]},"expect":{"values":[null]}}, {"id":"construct.test.did: opt: parsing opt null : opt opt bool","kind":"decode","bytes":"4449444c026e016e7e01000100","types":{"roots":[0],"table":[{"opt":1},{"opt":"bool"}]},"expect":{"values":[{"opt":null}]}}, +{"id":"construct.test.did: opt: parsing opt null : opt opt bool (right)","kind":"parse","text":"(opt null)","types":{"roots":[0],"table":[{"opt":1},{"opt":"bool"}]},"expect":{"values":[{"opt":null}]}}, {"id":"construct.test.did: opt: parsing opt opt false : opt opt bool","kind":"decode","bytes":"4449444c026e016e7e0100010100","types":{"roots":[0],"table":[{"opt":1},{"opt":"bool"}]},"expect":{"values":[{"opt":{"opt":false}}]}}, +{"id":"construct.test.did: opt: parsing opt opt false : opt opt bool (right)","kind":"parse","text":"(opt opt false)","types":{"roots":[0],"table":[{"opt":1},{"opt":"bool"}]},"expect":{"values":[{"opt":{"opt":false}}]}}, {"id":"construct.test.did: opt: parsing false : opt bool","kind":"decode","bytes":"4449444c00017e00","types":{"roots":[0],"table":[{"opt":"bool"}]},"expect":{"values":[{"opt":false}]}}, +{"id":"construct.test.did: opt: parsing false : opt bool (right)","kind":"parse","text":"(opt false)","types":{"roots":[0],"table":[{"opt":"bool"}]},"expect":{"values":[{"opt":false}]}}, {"id":"construct.test.did: opt: parsing true : opt bool","kind":"decode","bytes":"4449444c00017e01","types":{"roots":[0],"table":[{"opt":"bool"}]},"expect":{"values":[{"opt":true}]}}, +{"id":"construct.test.did: opt: parsing true : opt bool (right)","kind":"parse","text":"(opt true)","types":{"roots":[0],"table":[{"opt":"bool"}]},"expect":{"values":[{"opt":true}]}}, {"id":"construct.test.did: opt: parsing invalid bool at opt bool #85","kind":"decode","bytes":"4449444c00017e02","types":{"roots":[0],"table":[{"opt":"bool"}]},"expect":"reject"}, {"id":"construct.test.did: opt: parsing (null : opt null) at opt opt null gives null, not opt null","kind":"decode","bytes":"4449444c016e7f010000","types":{"roots":[0],"table":[{"opt":1},{"opt":"null"}]},"expect":{"values":[null]}}, +{"id":"construct.test.did: opt: parsing (null : opt null) at opt opt null gives null, not opt null (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":1},{"opt":"null"}]},"expect":{"values":[null]}}, {"id":"construct.test.did: opt: parsing true : opt opt bool gives null","kind":"decode","bytes":"4449444c00017e01","types":{"roots":[0],"table":[{"opt":1},{"opt":"bool"}]},"expect":{"values":[{"opt":{"opt":true}}]}}, +{"id":"construct.test.did: opt: parsing true : opt opt bool gives null (right)","kind":"parse","text":"(opt opt true)","types":{"roots":[0],"table":[{"opt":1},{"opt":"bool"}]},"expect":{"values":[{"opt":{"opt":true}}]}}, {"id":"construct.test.did: opt: parsing (true : bool) at \"fix opt\" fails","kind":"decode","bytes":"4449444c00017e01","types":{"roots":[0],"table":[{"opt":0}]},"expect":"reject"}, {"id":"construct.test.did: opt: parsing null : Opt","kind":"decode","bytes":"4449444c016e00010000","types":{"roots":[0],"table":[{"opt":0}]},"expect":{"values":[null]}}, +{"id":"construct.test.did: opt: parsing null : Opt (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":0}]},"expect":{"values":[null]}}, {"id":"construct.test.did: opt: parsing opt null : Opt","kind":"decode","bytes":"4449444c016e0001000100","types":{"roots":[0],"table":[{"opt":0}]},"expect":{"values":[{"opt":null}]}}, +{"id":"construct.test.did: opt: parsing opt null : Opt (right)","kind":"parse","text":"(opt null)","types":{"roots":[0],"table":[{"opt":0}]},"expect":{"values":[{"opt":null}]}}, {"id":"construct.test.did: opt: parsing opt opt null : Opt","kind":"decode","bytes":"4449444c016e000100010100","types":{"roots":[0],"table":[{"opt":0}]},"expect":{"values":[{"opt":{"opt":null}}]}}, +{"id":"construct.test.did: opt: parsing opt opt null : Opt (right)","kind":"parse","text":"(opt opt null)","types":{"roots":[0],"table":[{"opt":0}]},"expect":{"values":[{"opt":{"opt":null}}]}}, {"id":"construct.test.did: opt: parsing opt opt opt null : Opt","kind":"decode","bytes":"4449444c016e00010001010100","types":{"roots":[0],"table":[{"opt":0}]},"expect":{"values":[{"opt":{"opt":{"opt":null}}}]}}, +{"id":"construct.test.did: opt: parsing opt opt opt null : Opt (right)","kind":"parse","text":"(opt opt opt null)","types":{"roots":[0],"table":[{"opt":0}]},"expect":{"values":[{"opt":{"opt":{"opt":null}}}]}}, {"id":"construct.test.did: opt: parsing (true : bool) at a mutually recursive \"fix opt\" fails","kind":"decode","bytes":"4449444c00017e01","types":{"roots":[0],"table":[{"opt":1},{"opt":0}]},"expect":"reject"}, {"id":"construct.test.did: opt: parsing (opt true : opt bool) at \"fix opt\" fails, not null","kind":"decode","bytes":"4449444c016e7e01000101","types":{"roots":[0],"table":[{"opt":0}]},"expect":"reject"}, {"id":"construct.test.did: opt: a record field at \"fix opt\" fails the record","kind":"decode","bytes":"4449444c016c01007e010001","types":{"roots":[0],"table":[{"record":[[0,1]]},{"opt":1}]},"expect":"reject"}, {"id":"construct.test.did: opt: a vector element at \"fix opt\" fails the vector","kind":"decode","bytes":"4449444c016d7e01000101","types":{"roots":[0],"table":[{"vec":1},{"opt":1}]},"expect":"reject"}, {"id":"construct.test.did: opt: an empty vector at vec \"fix opt\" has nothing to coerce","kind":"decode","bytes":"4449444c016d7e010000","types":{"roots":[0],"table":[{"vec":1},{"opt":1}]},"expect":{"values":[{"vec":[]}]}}, +{"id":"construct.test.did: opt: an empty vector at vec \"fix opt\" has nothing to coerce (right)","kind":"parse","text":"(vec {})","types":{"roots":[0],"table":[{"vec":1},{"opt":1}]},"expect":{"values":[{"vec":[]}]}}, {"id":"construct.test.did: opt: parsing \"fix record\" at \"fix record opt\" fails","kind":"decode","bytes":"4449444c016c0100000100","types":{"roots":[0],"table":[{"opt":1},{"record":[[0,0]]}]},"expect":"reject"}, {"id":"construct.test.did: reserved <: opt nat","kind":"decode","bytes":"4449444c000170","types":{"roots":[0],"table":[{"opt":"nat"}]},"expect":{"values":[null]}}, +{"id":"construct.test.did: reserved <: opt nat (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":"nat"}]},"expect":{"values":[null]}}, {"id":"construct.test.did: null : opt bool <: opt nat","kind":"decode","bytes":"4449444c016e7e010000","types":{"roots":[0],"table":[{"opt":"nat"}]},"expect":{"values":[null]}}, +{"id":"construct.test.did: null : opt bool <: opt nat (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":"nat"}]},"expect":{"values":[null]}}, {"id":"construct.test.did: opt true : opt bool <: opt nat","kind":"decode","bytes":"4449444c016e7e01000101","types":{"roots":[0],"table":[{"opt":"nat"}]},"expect":{"values":[null]}}, +{"id":"construct.test.did: opt true : opt bool <: opt nat (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":"nat"}]},"expect":{"values":[null]}}, {"id":"construct.test.did: opt bool <: opt nat with invalid boolean value","kind":"decode","bytes":"4449444c016e7e01000102","types":{"roots":[0],"table":[{"opt":"nat"}]},"expect":"reject"}, {"id":"construct.test.did: opt opt true : opt opt bool <: opt nat","kind":"decode","bytes":"4449444c026e016e7e0100010101","types":{"roots":[0],"table":[{"opt":"nat"}]},"expect":{"values":[null]}}, +{"id":"construct.test.did: opt opt true : opt opt bool <: opt nat (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":"nat"}]},"expect":{"values":[null]}}, {"id":"construct.test.did: opt opt true : opt opt bool <: opt opt nat","kind":"decode","bytes":"4449444c026e016e7e0100010101","types":{"roots":[0],"table":[{"opt":1},{"opt":"nat"}]},"expect":{"values":[{"opt":null}]}}, +{"id":"construct.test.did: opt opt true : opt opt bool <: opt opt nat (right)","kind":"parse","text":"(opt null)","types":{"roots":[0],"table":[{"opt":1},{"opt":"nat"}]},"expect":{"values":[{"opt":null}]}}, {"id":"construct.test.did: opt: recovered coercion error under variant","kind":"decode","bytes":"4449444c026e016b01007e0100010001","types":{"roots":[0],"table":[{"opt":1},{"variant":[[0,"int"]]}]},"expect":{"values":[null]}}, +{"id":"construct.test.did: opt: recovered coercion error under variant (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":1},{"variant":[[0,"int"]]}]},"expect":{"values":[null]}}, {"id":"construct.test.did: missing optional field","kind":"decode","bytes":"4449444c016c000100","types":{"roots":[0],"table":[{"record":[[5097222,1]]},{"opt":"bool"}]},"expect":{"values":[{"record":[[5097222,null]]}]}}, +{"id":"construct.test.did: missing optional field (right)","kind":"parse","text":"(record { foo = null })","types":{"roots":[0],"table":[{"record":[[5097222,1]]},{"opt":"bool"}]},"expect":{"values":[{"record":[[5097222,null]]}]}}, {"id":"construct.test.did: missing reserved field","kind":"decode","bytes":"4449444c016c000100","types":{"roots":[0],"table":[{"record":[[5097222,"reserved"]]}]},"expect":{"values":[{"record":[[5097222,null]]}]}}, +{"id":"construct.test.did: missing reserved field (right)","kind":"parse","text":"(record { foo = \"☃\" })","types":{"roots":[0],"table":[{"record":[[5097222,"reserved"]]}]},"expect":{"values":[{"record":[[5097222,null]]}]}}, +{"id":"construct.test.did: variant: no empty value #108","kind":"parse","text":"(variant {})","types":{"roots":[0],"table":[{"variant":[]}]},"expect":"reject"}, {"id":"construct.test.did: variant: no empty value #109","kind":"decode","bytes":"4449444c016b000100","types":{"roots":[0],"table":[{"variant":[]}]},"expect":"reject"}, {"id":"construct.test.did: variant: numbered field","kind":"decode","bytes":"4449444c016b01007f010000","types":{"roots":[0],"table":[{"variant":[[0,"null"]]}]},"expect":{"values":[{"variant":[0,null]}]}}, +{"id":"construct.test.did: variant: numbered field (right)","kind":"parse","text":"(variant {0})","types":{"roots":[0],"table":[{"variant":[[0,"null"]]}]},"expect":{"values":[{"variant":[0,null]}]}}, {"id":"construct.test.did: variant: type mismatch","kind":"decode","bytes":"4449444c016b01007f0100002a","types":{"roots":[0],"table":[{"variant":[[0,"int"]]}]},"expect":"reject"}, {"id":"construct.test.did: variant: type mismatch in unused tag","kind":"decode","bytes":"4449444c016b02007f017c0100012a","types":{"roots":[0],"table":[{"variant":[[0,"int"],[1,"int"]]}]},"expect":{"values":[{"variant":[1,{"int":"42"}]}]}}, {"id":"construct.test.did: variant: ignore field","kind":"decode","bytes":"4449444c016b01007f010000","types":{"roots":[0],"table":[{"variant":[[0,"null"],[1,"null"]]}]},"expect":{"values":[{"variant":[0,null]}]}}, +{"id":"construct.test.did: variant: ignore field (right)","kind":"parse","text":"(variant {0})","types":{"roots":[0],"table":[{"variant":[[0,"null"],[1,"null"]]}]},"expect":{"values":[{"variant":[0,null]}]}}, {"id":"construct.test.did: variant {0;1} <: variant {0}","kind":"decode","bytes":"4449444c016b02007f017f010000","types":{"roots":[0],"table":[{"variant":[[0,"null"]]}]},"expect":{"values":[{"variant":[0,null]}]}}, {"id":"construct.test.did: variant {0;1} <: opt variant {0}","kind":"decode","bytes":"4449444c016b02007f017f010000","types":{"roots":[0],"table":[{"opt":1},{"variant":[[0,"null"]]}]},"expect":{"values":[{"opt":{"variant":[0,null]}}]}}, +{"id":"construct.test.did: variant {0;1} <: opt variant {0} (right)","kind":"parse","text":"(opt variant {0})","types":{"roots":[0],"table":[{"opt":1},{"variant":[[0,"null"]]}]},"expect":{"values":[{"opt":{"variant":[0,null]}}]}}, {"id":"construct.test.did: variant: change index","kind":"decode","bytes":"4449444c016b02007f017f010001","types":{"roots":[0],"table":[{"variant":[[0,"null"],[1,"null"],[2,"null"]]}]},"expect":{"values":[{"variant":[1,null]}]}}, +{"id":"construct.test.did: variant: change index (right)","kind":"parse","text":"(variant {1})","types":{"roots":[0],"table":[{"variant":[[0,"null"],[1,"null"],[2,"null"]]}]},"expect":{"values":[{"variant":[1,null]}]}}, {"id":"construct.test.did: variant: missing field","kind":"decode","bytes":"4449444c016b01007f010000","types":{"roots":[0],"table":[{"variant":[[1,"null"]]}]},"expect":"reject"}, {"id":"construct.test.did: variant: index out of range","kind":"decode","bytes":"4449444c016b01007f010001","types":{"roots":[0],"table":[{"variant":[[0,"null"]]}]},"expect":"reject"}, {"id":"construct.test.did: variant: duplicate fields #119","kind":"decode","bytes":"4449444c016b02007f007f010000","types":{"roots":[0],"table":[{"variant":[[0,"null"]]}]},"expect":"reject"}, @@ -120,11 +181,17 @@ {"id":"construct.test.did: variant: ok with maxInt","kind":"decode","bytes":"4449444c016b03007f017fffffffff0f7f010000","types":{"roots":[0],"table":[{"variant":[[0,"null"],[1,"null"],[4294967295,"null"]]}]},"expect":{"values":[{"variant":[0,null]}]}}, {"id":"construct.test.did: variant: unsorted with maxInt","kind":"decode","bytes":"4449444c016b03007fffffffff0f7f017f010000","types":{"roots":[0],"table":[{"variant":[[0,"null"],[1,"null"],[4294967295,"null"]]}]},"expect":"reject"}, {"id":"construct.test.did: variant: enum","kind":"decode","bytes":"4449444c016b02b3d3c9017fe6fdd5017f010000","types":{"roots":[0],"table":[{"variant":[[3303859,"null"],[3505894,"null"]]}]},"expect":{"values":[{"variant":[3303859,null]}]}}, +{"id":"construct.test.did: variant: enum (right)","kind":"parse","text":"(variant { Bar })","types":{"roots":[0],"table":[{"variant":[[3303859,"null"],[3505894,"null"]]}]},"expect":{"values":[{"variant":[3303859,null]}]}}, {"id":"construct.test.did: variant: unicode field","kind":"decode","bytes":"4449444c016b01cd84b0057f010000","types":{"roots":[0],"table":[{"variant":[[11272781,"null"]]}]},"expect":{"values":[{"variant":[11272781,null]}]}}, +{"id":"construct.test.did: variant: unicode field (right)","kind":"parse","text":"(variant { \"☃\" })","types":{"roots":[0],"table":[{"variant":[[11272781,"null"]]}]},"expect":{"values":[{"variant":[11272781,null]}]}}, {"id":"construct.test.did: variant: result","kind":"decode","bytes":"4449444c016b02bc8a0171c5fed2017101000004676f6f64","types":{"roots":[0],"table":[{"variant":[[17724,"text"],[3456837,"text"]]}]},"expect":{"values":[{"variant":[17724,{"text":"good"}]}]}}, +{"id":"construct.test.did: variant: result (right)","kind":"parse","text":"(variant { Ok = \"good\" })","types":{"roots":[0],"table":[{"variant":[[17724,"text"],[3456837,"text"]]}]},"expect":{"values":[{"variant":[17724,{"text":"good"}]}]}}, {"id":"construct.test.did: variant: with empty","kind":"decode","bytes":"4449444c016b029cc20171e58eb4026f01000004676f6f64","types":{"roots":[0],"table":[{"variant":[[24860,"text"],[5048165,"empty"]]}]},"expect":{"values":[{"variant":[24860,{"text":"good"}]}]}}, +{"id":"construct.test.did: variant: with empty (right)","kind":"parse","text":"(variant { ok = \"good\" })","types":{"roots":[0],"table":[{"variant":[[24860,"text"],[5048165,"empty"]]}]},"expect":{"values":[{"variant":[24860,{"text":"good"}]}]}}, {"id":"construct.test.did: variant: with EmptyRecord","kind":"decode","bytes":"4449444c026b029cc20171e58eb402016c01000101000004676f6f64","types":{"roots":[0],"table":[{"variant":[[24860,"text"],[5048165,1]]},{"record":[[0,1]]}]},"expect":{"values":[{"variant":[24860,{"text":"good"}]}]}}, +{"id":"construct.test.did: variant: with EmptyRecord (right)","kind":"parse","text":"(variant { ok = \"good\" })","types":{"roots":[0],"table":[{"variant":[[24860,"text"],[5048165,1]]},{"record":[[0,1]]}]},"expect":{"values":[{"variant":[24860,{"text":"good"}]}]}}, {"id":"construct.test.did: variant #129","kind":"decode","bytes":"4449444c036b03b3d3c90101bbd3c90102e6fdd5017f6c02007e017c6c02617c627d010000012a","types":{"roots":[0],"table":[{"variant":[[3303859,1],[3303867,2],[3505894,"null"]]},{"record":[[0,"bool"],[1,"int"]]},{"record":[[97,"int"],[98,"nat"]]}]},"expect":{"values":[{"variant":[3303859,{"record":[[0,true],[1,{"int":"42"}]]}]}]}}, +{"id":"construct.test.did: variant #129 (right)","kind":"parse","text":"(variant { Bar = record { true; 42 } })","types":{"roots":[0],"table":[{"variant":[[3303859,1],[3303867,2],[3505894,"null"]]},{"record":[[0,"bool"],[1,"int"]]},{"record":[[97,"int"],[98,"nat"]]}]},"expect":{"values":[{"variant":[3303859,{"record":[[0,true],[1,{"int":"42"}]]}]}]}}, {"id":"construct.test.did: variant: empty recursion","kind":"decode","bytes":"4449444c016b0100000100000000000000000000000000","types":{"roots":[0],"table":[{"variant":[[0,0]]}]},"expect":"reject"}, {"id":"construct.test.did: variant: field hash larger than u32","kind":"decode","bytes":"4449444c016b0180e497d0127c0100002a","types":{"roots":["reserved"],"table":[]},"expect":"reject"}, {"id":"construct.test.did: variant #132","kind":"decode","bytes":"4449444c016b02007f017c0100012a","types":{"roots":[0],"table":[{"variant":[[0,"null"],[1,"int"]]}]},"expect":{"values":[{"variant":[1,{"int":"42"}]}]}}, @@ -134,38 +201,60 @@ {"id":"construct.test.did: variant: type too short","kind":"decode","bytes":"4449444c016b02007f010000","types":{"roots":[0],"table":[{"variant":[[0,"null"],[1,"null"]]}]},"expect":"reject"}, {"id":"construct.test.did: variant: type too long","kind":"decode","bytes":"4449444c016b03007f017f010000","types":{"roots":[0],"table":[{"variant":[[0,"null"],[1,"null"]]}]},"expect":"reject"}, {"id":"construct.test.did: record: empty list","kind":"decode","bytes":"4449444c026e016c02a0d2aca8047c90eddae70400010000","types":{"roots":[0],"table":[{"opt":1},{"record":[[1158359328,"int"],[1291237008,0]]}]},"expect":{"values":[null]}}, +{"id":"construct.test.did: record: empty list (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":1},{"record":[[1158359328,"int"],[1291237008,0]]}]},"expect":{"values":[null]}}, {"id":"construct.test.did: record: list","kind":"decode","bytes":"4449444c026e016c02a0d2aca8047c90eddae704000100010101020103010400","types":{"roots":[0],"table":[{"opt":1},{"record":[[1158359328,"int"],[1291237008,0]]}]},"expect":{"values":[{"opt":{"record":[[1158359328,{"int":"1"}],[1291237008,{"opt":{"record":[[1158359328,{"int":"2"}],[1291237008,{"opt":{"record":[[1158359328,{"int":"3"}],[1291237008,{"opt":{"record":[[1158359328,{"int":"4"}],[1291237008,null]]}}]]}}]]}}]]}}]}}, +{"id":"construct.test.did: record: list (right)","kind":"parse","text":"(opt record { head = 1; tail = opt record { head = 2; tail = opt record { head = 3; tail = opt record { head = 4; tail = null }}}})","types":{"roots":[0],"table":[{"opt":1},{"record":[[1158359328,"int"],[1291237008,0]]}]},"expect":{"values":[{"opt":{"record":[[1158359328,{"int":"1"}],[1291237008,{"opt":{"record":[[1158359328,{"int":"2"}],[1291237008,{"opt":{"record":[[1158359328,{"int":"3"}],[1291237008,{"opt":{"record":[[1158359328,{"int":"4"}],[1291237008,null]]}}]]}}]]}}]]}}]}}, {"id":"construct.test.did: record: reorder type table","kind":"decode","bytes":"4449444c026e016c02a0d2aca8047c90eddae7040001000101010200","types":{"roots":[0],"table":[{"opt":1},{"record":[[1158359328,"int"],[1291237008,0]]}]},"expect":{"values":[{"opt":{"record":[[1158359328,{"int":"1"}],[1291237008,{"opt":{"record":[[1158359328,{"int":"2"}],[1291237008,null]]}}]]}}]}}, {"id":"construct.test.did: record: reorder type table (right)","kind":"decode","bytes":"4449444c026c02a0d2aca8047c90eddae704016e0001010101010200","types":{"roots":[0],"table":[{"opt":1},{"record":[[1158359328,"int"],[1291237008,0]]}]},"expect":{"values":[{"opt":{"record":[[1158359328,{"int":"1"}],[1291237008,{"opt":{"record":[[1158359328,{"int":"2"}],[1291237008,null]]}}]]}}]}}, {"id":"construct.test.did: record: mutual recursive list","kind":"decode","bytes":"4449444c026e016c02a0d2aca8047c90eddae7040001000101010200","types":{"roots":[0],"table":[{"opt":1},{"record":[[1158359328,"int"],[1291237008,0]]}]},"expect":{"values":[{"opt":{"record":[[1158359328,{"int":"1"}],[1291237008,{"opt":{"record":[[1158359328,{"int":"2"}],[1291237008,null]]}}]]}}]}}, +{"id":"construct.test.did: record: mutual recursive list (right)","kind":"parse","text":"(opt record { head = 1; tail = opt record { head = 2; tail = null } })","types":{"roots":[0],"table":[{"opt":1},{"record":[[1158359328,"int"],[1291237008,0]]}]},"expect":{"values":[{"opt":{"record":[[1158359328,{"int":"1"}],[1291237008,{"opt":{"record":[[1158359328,{"int":"2"}],[1291237008,null]]}}]]}}]}}, {"id":"construct.test.did: variant: empty list","kind":"decode","bytes":"4449444c026b02d1a7cf027ff1f3928e04016c02a0d2aca8047c90eddae70400010000","types":{"roots":[0],"table":[{"variant":[[5493713,"null"],[1103411697,1]]},{"record":[[1158359328,"int"],[1291237008,0]]}]},"expect":{"values":[{"variant":[5493713,null]}]}}, +{"id":"construct.test.did: variant: empty list (right)","kind":"parse","text":"(variant {nil})","types":{"roots":[0],"table":[{"variant":[[5493713,"null"],[1103411697,1]]},{"record":[[1158359328,"int"],[1291237008,0]]}]},"expect":{"values":[{"variant":[5493713,null]}]}}, {"id":"construct.test.did: variant: list","kind":"decode","bytes":"4449444c026b02d1a7cf027ff1f3928e04016c02a0d2aca8047c90eddae7040001000101010200","types":{"roots":[0],"table":[{"variant":[[5493713,"null"],[1103411697,1]]},{"record":[[1158359328,"int"],[1291237008,0]]}]},"expect":{"values":[{"variant":[1103411697,{"record":[[1158359328,{"int":"1"}],[1291237008,{"variant":[1103411697,{"record":[[1158359328,{"int":"2"}],[1291237008,{"variant":[5493713,null]}]]}]}]]}]}]}}, +{"id":"construct.test.did: variant: list (right)","kind":"parse","text":"(variant { cons = record { head = 1; tail = variant { cons = record { head = 2; tail = variant { nil } } } } })","types":{"roots":[0],"table":[{"variant":[[5493713,"null"],[1103411697,1]]},{"record":[[1158359328,"int"],[1291237008,0]]}]},"expect":{"values":[{"variant":[1103411697,{"record":[[1158359328,{"int":"1"}],[1291237008,{"variant":[1103411697,{"record":[[1158359328,{"int":"2"}],[1291237008,{"variant":[5493713,null]}]]}]}]]}]}]}}, {"id":"construct.test.did: variant: extra args","kind":"decode","bytes":"4449444c026b02d1a7cf027ff1f3928e04016c02a0d2aca8047c90eddae70400010000","types":{"roots":[0,2,"null","reserved",3],"table":[{"variant":[[5493713,"null"],[1103411697,1]]},{"record":[[1158359328,"int"],[1291237008,0]]},{"opt":0},{"opt":"int"}]},"expect":{"values":[{"variant":[5493713,null]},null,null,null,null]}}, +{"id":"construct.test.did: variant: extra args (right)","kind":"parse","text":"(variant {nil}, null, null, null, null)","types":{"roots":[0,2,"null","reserved",3],"table":[{"variant":[[5493713,"null"],[1103411697,1]]},{"record":[[1158359328,"int"],[1291237008,0]]},{"opt":0},{"opt":"int"}]},"expect":{"values":[{"variant":[5493713,null]},null,null,null,null]}}, {"id":"construct.test.did: non-null extra args","kind":"decode","bytes":"4449444c026b02d1a7cf027ff1f3928e04016c02a0d2aca8047c90eddae70400010000","types":{"roots":[0,2,3],"table":[{"variant":[[5493713,"null"],[1103411697,1]]},{"record":[[1158359328,"int"],[1291237008,0]]},{"opt":"int"},{"vec":"int"}]},"expect":"reject"}, {"id":"construct.test.did: #146","kind":"decode","bytes":"4449444c076c07c3e3aa0201d3e3aa027ed5e3aa0202dbe3aa0201a2e5aa0204bbf1aa0206868eb702756c02d3e3aa027e868eb702756b02d1a7cf027ff1f3928e04036c02a0d2aca8047c90eddae704026e056c02a0d2aca8047c90eddae704046b02d3e3aa027f868eb7027f0100010b0000000100000a000000011400002a000000","types":{"roots":[0],"table":[{"record":[[4895171,1],[4895187,"bool"],[4895189,2],[4895195,1],[4895394,4],[4896955,6],[5097222,"int32"]]},{"record":[[4895187,"bool"],[5097222,"int32"]]},{"variant":[[5493713,"null"],[1103411697,3]]},{"record":[[1158359328,"int"],[1291237008,2]]},{"opt":5},{"record":[[1158359328,"int"],[1291237008,4]]},{"variant":[[4895187,"null"],[5097222,"null"]]}]},"expect":{"values":[{"record":[[4895171,{"record":[[4895187,true],[5097222,{"int32":"11"}]]}],[4895187,true],[4895189,{"variant":[5493713,null]}],[4895195,{"record":[[4895187,false],[5097222,{"int32":"10"}]]}],[4895394,{"opt":{"record":[[1158359328,{"int":"20"}],[1291237008,null]]}}],[4896955,{"variant":[4895187,null]}],[5097222,{"int32":"42"}]]}]}}, +{"id":"construct.test.did: #146 (right)","kind":"parse","text":"(record { foo = 42; bar = true; bat = variant { nil }; baz = record { foo = 10; bar = false }; bbb = opt record { head = 20; tail = null }; bib = variant { bar }; bab = record { foo = 11; bar = true}})","types":{"roots":[0],"table":[{"record":[[4895171,1],[4895187,"bool"],[4895189,2],[4895195,1],[4895394,4],[4896955,6],[5097222,"int32"]]},{"record":[[4895187,"bool"],[5097222,"int32"]]},{"variant":[[5493713,"null"],[1103411697,3]]},{"record":[[1158359328,"int"],[1291237008,2]]},{"opt":5},{"record":[[1158359328,"int"],[1291237008,4]]},{"variant":[[4895187,"null"],[5097222,"null"]]}]},"expect":{"values":[{"record":[[4895171,{"record":[[4895187,true],[5097222,{"int32":"11"}]]}],[4895187,true],[4895189,{"variant":[5493713,null]}],[4895195,{"record":[[4895187,false],[5097222,{"int32":"10"}]]}],[4895394,{"opt":{"record":[[1158359328,{"int":"20"}],[1291237008,null]]}}],[4896955,{"variant":[4895187,null]}],[5097222,{"int32":"42"}]]}]}}, {"id":"construct.test.did: skip fields #147","kind":"decode","bytes":"4449444c076c07c3e3aa0201d3e3aa027ed5e3aa0202dbe3aa0201a2e5aa0204bbf1aa0206868eb702756c02d3e3aa027e868eb702756b02d1a7cf027ff1f3928e04036c02a0d2aca8047c90eddae704026e056c02a0d2aca8047c90eddae704046b02d3e3aa027f868eb7027f0100010b0000000100000a000000011400002a000000","types":{"roots":[0],"table":[{"record":[[4895187,"bool"],[5097222,"int32"]]}]},"expect":{"values":[{"record":[[4895187,true],[5097222,{"int32":"42"}]]}]}}, +{"id":"construct.test.did: skip fields #147 (right)","kind":"parse","text":"(record { foo = 42; bar = true })","types":{"roots":[0],"table":[{"record":[[4895187,"bool"],[5097222,"int32"]]}]},"expect":{"values":[{"record":[[4895187,true],[5097222,{"int32":"42"}]]}]}}, {"id":"construct.test.did: skip fields #148","kind":"decode","bytes":"4449444c076c07c3e3aa0201d3e3aa027ed5e3aa0202dbe3aa0201a2e5aa0204bbf1aa0206868eb702756c02d3e3aa027e868eb702756b02d1a7cf027ff1f3928e04036c02a0d2aca8047c90eddae704026e056c02a0d2aca8047c90eddae704046b02d3e3aa027f868eb7027f0100010b0000000100000a000000011400002a000000","types":{"roots":[0],"table":[{"record":[[4895187,"bool"],[4895195,1],[4895394,2]]},{"record":[[4895187,"bool"]]},{"opt":3},{"record":[[1158359328,"int"]]}]},"expect":{"values":[{"record":[[4895187,true],[4895195,{"record":[[4895187,false]]}],[4895394,{"opt":{"record":[[1158359328,{"int":"20"}]]}}]]}]}}, +{"id":"construct.test.did: skip fields #148 (right)","kind":"parse","text":"(record { bar = true; baz = record { bar = false }; bbb = opt record { head = 20 }})","types":{"roots":[0],"table":[{"record":[[4895187,"bool"],[4895195,1],[4895394,2]]},{"record":[[4895187,"bool"]]},{"opt":3},{"record":[[1158359328,"int"]]}]},"expect":{"values":[{"record":[[4895187,true],[4895195,{"record":[[4895187,false]]}],[4895394,{"opt":{"record":[[1158359328,{"int":"20"}]]}}]]}]}}, {"id":"construct.test.did: new variant field","kind":"decode","bytes":"4449444c076c07c3e3aa0201d3e3aa027ed5e3aa0202dbe3aa0201a2e5aa0204bbf1aa0206868eb702756c02d3e3aa027e868eb702756b02d1a7cf027ff1f3928e04036c02a0d2aca8047c90eddae704026e056c02a0d2aca8047c90eddae704046b02d3e3aa027f868eb7027f0100010b0000000100000a000000011400002a000000","types":{"roots":[0],"table":[{"record":[[4895189,1]]},{"variant":[[5493713,"null"],[1103411697,2],[2241279067,"empty"]]},{"record":[[1158359328,"int"],[1291237008,3]]},{"variant":[[5493713,"null"],[1103411697,4]]},{"record":[[1158359328,"int"],[1291237008,3]]}]},"expect":{"values":[{"record":[[4895189,{"variant":[5493713,null]}]]}]}}, +{"id":"construct.test.did: new variant field (right)","kind":"parse","text":"(record { bat = variant { nil } })","types":{"roots":[0],"table":[{"record":[[4895189,1]]},{"variant":[[5493713,"null"],[1103411697,2],[2241279067,"empty"]]},{"record":[[1158359328,"int"],[1291237008,3]]},{"variant":[[5493713,"null"],[1103411697,4]]},{"record":[[1158359328,"int"],[1291237008,3]]}]},"expect":{"values":[{"record":[[4895189,{"variant":[5493713,null]}]]}]}}, {"id":"construct.test.did: new record field","kind":"decode","bytes":"4449444c076c07c3e3aa0201d3e3aa027ed5e3aa0202dbe3aa0201a2e5aa0204bbf1aa0206868eb702756c02d3e3aa027e868eb702756b02d1a7cf027ff1f3928e04036c02a0d2aca8047c90eddae704026e056c02a0d2aca8047c90eddae704046b02d3e3aa027f868eb7027f0100010b0000000100000a000000011400002a000000","types":{"roots":[0],"table":[{"record":[[5097222,"int"],[2241279067,"bool"]]}]},"expect":"reject"}, {"id":"construct.test.did: reserved","kind":"decode","bytes":"4449444c000170","types":{"roots":["reserved"],"table":[]},"expect":{"values":[null]}}, +{"id":"construct.test.did: reserved (right)","kind":"parse","text":"(null)","types":{"roots":["reserved"],"table":[]},"expect":{"values":[null]}}, {"id":"construct.test.did: parsing reserved as null","kind":"decode","bytes":"4449444c000170","types":{"roots":["null"],"table":[]},"expect":"reject"}, {"id":"construct.test.did: parsing (reserved, reserved) as (null, null)","kind":"decode","bytes":"4449444c00027070","types":{"roots":["null","null"],"table":[]},"expect":"reject"}, {"id":"construct.test.did: parsing (reserved, 5) as (null, nat)","kind":"decode","bytes":"4449444c0002707d05","types":{"roots":["null","nat"],"table":[]},"expect":"reject"}, {"id":"construct.test.did: record with reserved field","kind":"decode","bytes":"4449444c016c0100700100","types":{"roots":[0],"table":[{"record":[[0,"reserved"]]}]},"expect":{"values":[{"record":[[0,null]]}]}}, +{"id":"construct.test.did: record with reserved field (right)","kind":"parse","text":"(record { 0 = null })","types":{"roots":[0],"table":[{"record":[[0,"reserved"]]}]},"expect":{"values":[{"record":[[0,null]]}]}}, {"id":"construct.test.did: parsing record with reserved field as record with null field","kind":"decode","bytes":"4449444c016c0100700100","types":{"roots":[0],"table":[{"record":[[0,"null"]]}]},"expect":"reject"}, {"id":"construct.test.did: variant with reserved field","kind":"decode","bytes":"4449444c016b010070010000","types":{"roots":[0],"table":[{"variant":[[0,"reserved"]]}]},"expect":{"values":[{"variant":[0,null]}]}}, +{"id":"construct.test.did: variant with reserved field (right)","kind":"parse","text":"(variant { 0 = null })","types":{"roots":[0],"table":[{"variant":[[0,"reserved"]]}]},"expect":{"values":[{"variant":[0,null]}]}}, {"id":"construct.test.did: parsing variant with reserved field as variant with null field","kind":"decode","bytes":"4449444c016b010070010000","types":{"roots":[0],"table":[{"variant":[[0,"null"]]}]},"expect":"reject"}, {"id":"construct.test.did: vector with reserved elements","kind":"decode","bytes":"4449444c016d70010001","types":{"roots":[0],"table":[{"vec":"reserved"}]},"expect":{"values":[{"vec":[null]}]}}, +{"id":"construct.test.did: vector with reserved elements (right)","kind":"parse","text":"(vec { null })","types":{"roots":[0],"table":[{"vec":"reserved"}]},"expect":{"values":[{"vec":[null]}]}}, {"id":"construct.test.did: parsing vector with reserved elements as vector with null elements","kind":"decode","bytes":"4449444c016d70010001","types":{"roots":[0],"table":[{"vec":"null"}]},"expect":"reject"}, {"id":"construct.test.did: optional reserved element","kind":"decode","bytes":"4449444c016e70010001","types":{"roots":[0],"table":[{"opt":"reserved"}]},"expect":{"values":[{"opt":null}]}}, +{"id":"construct.test.did: optional reserved element (right)","kind":"parse","text":"(opt null)","types":{"roots":[0],"table":[{"opt":"reserved"}]},"expect":{"values":[{"opt":null}]}}, {"id":"construct.test.did: parsing optional reserved element as optional null element","kind":"decode","bytes":"4449444c016e70010001","types":{"roots":[0],"table":[{"opt":"null"}]},"expect":{"values":[null]}}, +{"id":"construct.test.did: parsing optional reserved element as optional null element (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":"null"}]},"expect":{"values":[null]}}, {"id":"construct.test.did: parsing 5 as null","kind":"decode","bytes":"4449444c00017d05","types":{"roots":["null"],"table":[]},"expect":"reject"}, {"id":"construct.test.did: parsing an empty top-level tuple into a longer top-level tuple","kind":"decode","bytes":"4449444c0000","types":{"roots":["null"],"table":[]},"expect":{"values":[null]}}, +{"id":"construct.test.did: parsing an empty top-level tuple into a longer top-level tuple (right)","kind":"parse","text":"()","types":{"roots":["null"],"table":[]},"expect":{"values":[null]}}, {"id":"construct.test.did: parsing a top-level tuple into a longer top-level tuple","kind":"decode","bytes":"4449444c00027d7d0506","types":{"roots":["nat","nat","null"],"table":[]},"expect":{"values":[{"nat":"5"},{"nat":"6"},null]}}, +{"id":"construct.test.did: parsing a top-level tuple into a longer top-level tuple (right)","kind":"parse","text":"(5, 6)","types":{"roots":["nat","nat","null"],"table":[]},"expect":{"values":[{"nat":"5"},{"nat":"6"},null]}}, {"id":"construct.test.did: parsing into record with expected field that is greater than extra field on the wire","kind":"decode","bytes":"4449444c016c01007d010005","types":{"roots":[0],"table":[{"record":[[1,"null"]]}]},"expect":{"values":[{"record":[[1,null]]}]}}, +{"id":"construct.test.did: parsing into record with expected field that is greater than extra field on the wire (right)","kind":"parse","text":"(record { 0 = 5 })","types":{"roots":[0],"table":[{"record":[[1,"null"]]}]},"expect":{"values":[{"record":[[1,null]]}]}}, {"id":"construct.test.did: parsing into record with expected field that is less than extra field on the wire","kind":"decode","bytes":"4449444c016c01017d010005","types":{"roots":[0],"table":[{"record":[[0,"null"]]}]},"expect":{"values":[{"record":[[0,null]]}]}}, +{"id":"construct.test.did: parsing into record with expected field that is less than extra field on the wire (right)","kind":"parse","text":"(record { 1 = 5 })","types":{"roots":[0],"table":[{"record":[[0,"null"]]}]},"expect":{"values":[{"record":[[0,null]]}]}}, {"id":"construct.test.did: skipping minimal future type","kind":"decode","bytes":"4449444c01670002007e000001","types":{"roots":[0,"bool"],"table":[{"opt":"empty"}]},"expect":{"values":[null,true]}}, +{"id":"construct.test.did: skipping minimal future type (right)","kind":"parse","text":"(null, true)","types":{"roots":[0,"bool"],"table":[{"opt":"empty"}]},"expect":{"values":[null,true]}}, {"id":"construct.test.did: skipping future type with data","kind":"decode","bytes":"4449444c01670341424302007e050068656c6c6f01","types":{"roots":[0,"bool"],"table":[{"opt":"empty"}]},"expect":{"values":[null,true]}}, +{"id":"construct.test.did: skipping future type with data (right)","kind":"parse","text":"(null,true)","types":{"roots":[0,"bool"],"table":[{"opt":"empty"}]},"expect":{"values":[null,true]}}, {"id":"overshoot.test.did: type table length","kind":"decode","bytes":"4449444c8094ebdc0300","types":{"roots":[],"table":[]},"expect":"reject"}, {"id":"overshoot.test.did: argument sequence length","kind":"decode","bytes":"4449444c008094ebdc03","types":{"roots":[],"table":[]},"expect":"reject"}, {"id":"overshoot.test.did: text length","kind":"decode","bytes":"4449444c0001718094ebdc034d6f746f6b6f","types":{"roots":["text"],"table":[]},"expect":"reject"}, @@ -190,76 +279,127 @@ {"id":"prim.test.did: missing argument: nat fails","kind":"decode","bytes":"4449444c0000","types":{"roots":["nat"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: missing argument: empty fails","kind":"decode","bytes":"4449444c0000","types":{"roots":["empty"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: missing argument: null","kind":"decode","bytes":"4449444c0000","types":{"roots":["null"],"table":[]},"expect":{"values":[null]}}, +{"id":"prim.test.did: missing argument: null (right)","kind":"parse","text":"(null)","types":{"roots":["null"],"table":[]},"expect":{"values":[null]}}, {"id":"prim.test.did: missing argument: opt empty","kind":"decode","bytes":"4449444c0000","types":{"roots":[0],"table":[{"opt":"empty"}]},"expect":{"values":[null]}}, +{"id":"prim.test.did: missing argument: opt empty (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":"empty"}]},"expect":{"values":[null]}}, {"id":"prim.test.did: missing argument: opt null","kind":"decode","bytes":"4449444c0000","types":{"roots":[0],"table":[{"opt":"null"}]},"expect":{"values":[null]}}, +{"id":"prim.test.did: missing argument: opt null (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":"null"}]},"expect":{"values":[null]}}, {"id":"prim.test.did: missing argument: opt nat","kind":"decode","bytes":"4449444c0000","types":{"roots":[0],"table":[{"opt":"nat"}]},"expect":{"values":[null]}}, +{"id":"prim.test.did: missing argument: opt nat (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":"nat"}]},"expect":{"values":[null]}}, {"id":"prim.test.did: missing argument: reserved","kind":"decode","bytes":"4449444c0000","types":{"roots":["reserved"],"table":[]},"expect":{"values":[null]}}, {"id":"prim.test.did: missing argument: reserved (right)","kind":"decode","bytes":"4449444c000170","types":{"roots":["reserved"],"table":[]},"expect":{"values":[null]}}, {"id":"prim.test.did: #19","kind":"decode","bytes":"4449444c00017f","types":{"roots":["null"],"table":[]},"expect":{"values":[null]}}, {"id":"prim.test.did: wrong type","kind":"decode","bytes":"4449444c00017e","types":{"roots":["null"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: null: too long","kind":"decode","bytes":"4449444c00017f00","types":{"roots":["null"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: bool: false","kind":"decode","bytes":"4449444c00017e00","types":{"roots":["bool"],"table":[]},"expect":{"values":[false]}}, +{"id":"prim.test.did: bool: false (right)","kind":"parse","text":"(false)","types":{"roots":["bool"],"table":[]},"expect":{"values":[false]}}, {"id":"prim.test.did: bool: true","kind":"decode","bytes":"4449444c00017e01","types":{"roots":["bool"],"table":[]},"expect":{"values":[true]}}, +{"id":"prim.test.did: bool: true (right)","kind":"parse","text":"(true)","types":{"roots":["bool"],"table":[]},"expect":{"values":[true]}}, {"id":"prim.test.did: bool: missing","kind":"decode","bytes":"4449444c00017e","types":{"roots":["bool"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: bool: out of range #25","kind":"decode","bytes":"4449444c00017e02","types":{"roots":["bool"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: bool: out of range #26","kind":"decode","bytes":"4449444c00017eff","types":{"roots":["bool"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: nat: 0","kind":"decode","bytes":"4449444c00017d00","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"0"}]}}, +{"id":"prim.test.did: nat: 0 (right)","kind":"parse","text":"(0)","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"0"}]}}, {"id":"prim.test.did: nat: 1","kind":"decode","bytes":"4449444c00017d01","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"1"}]}}, +{"id":"prim.test.did: nat: 1 (right)","kind":"parse","text":"(1)","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"1"}]}}, {"id":"prim.test.did: nat: 0x7f","kind":"decode","bytes":"4449444c00017d7f","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"127"}]}}, +{"id":"prim.test.did: nat: 0x7f (right)","kind":"parse","text":"(127)","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"127"}]}}, {"id":"prim.test.did: nat: leb (two bytes)","kind":"decode","bytes":"4449444c00017d8001","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"128"}]}}, +{"id":"prim.test.did: nat: leb (two bytes) (right)","kind":"parse","text":"(128)","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"128"}]}}, {"id":"prim.test.did: nat: leb (two bytes, all bits)","kind":"decode","bytes":"4449444c00017dff7f","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"16383"}]}}, +{"id":"prim.test.did: nat: leb (two bytes, all bits) (right)","kind":"parse","text":"(16383)","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"16383"}]}}, {"id":"prim.test.did: nat: leb too short","kind":"decode","bytes":"4449444c00017d80","types":{"roots":["nat"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: nat: leb overlong #33","kind":"decode","bytes":"4449444c00017d8000","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"0"}]}}, +{"id":"prim.test.did: nat: leb overlong #33 (right)","kind":"parse","text":"(0)","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"0"}]}}, {"id":"prim.test.did: nat: leb overlong #34","kind":"decode","bytes":"4449444c00017dff00","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"127"}]}}, +{"id":"prim.test.did: nat: leb overlong #34 (right)","kind":"parse","text":"(127)","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"127"}]}}, {"id":"prim.test.did: nat: big number","kind":"decode","bytes":"4449444c00017d808098f4e9b5ca6a","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"60000000000000000"}]}}, +{"id":"prim.test.did: nat: big number (right)","kind":"parse","text":"(60000000000000000)","types":{"roots":["nat"],"table":[]},"expect":{"values":[{"nat":"60000000000000000"}]}}, {"id":"prim.test.did: int: 0","kind":"decode","bytes":"4449444c00017c00","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"0"}]}}, +{"id":"prim.test.did: int: 0 (right)","kind":"parse","text":"(0)","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"0"}]}}, {"id":"prim.test.did: int: 1","kind":"decode","bytes":"4449444c00017c01","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"1"}]}}, +{"id":"prim.test.did: int: 1 (right)","kind":"parse","text":"(1)","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"1"}]}}, {"id":"prim.test.did: int: -1","kind":"decode","bytes":"4449444c00017c7f","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"-1"}]}}, +{"id":"prim.test.did: int: -1 (right)","kind":"parse","text":"(-1)","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"-1"}]}}, {"id":"prim.test.did: int: -64","kind":"decode","bytes":"4449444c00017c40","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"-64"}]}}, +{"id":"prim.test.did: int: -64 (right)","kind":"parse","text":"(-64)","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"-64"}]}}, {"id":"prim.test.did: int: leb (two bytes)","kind":"decode","bytes":"4449444c00017c8001","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"128"}]}}, +{"id":"prim.test.did: int: leb (two bytes) (right)","kind":"parse","text":"(128)","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"128"}]}}, {"id":"prim.test.did: int: leb too short","kind":"decode","bytes":"4449444c00017c80","types":{"roots":["int"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: int: leb overlong (0s)","kind":"decode","bytes":"4449444c00017c8000","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"0"}]}}, +{"id":"prim.test.did: int: leb overlong (0s) (right)","kind":"parse","text":"(0)","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"0"}]}}, {"id":"prim.test.did: int: leb overlong (1s)","kind":"decode","bytes":"4449444c00017cff7f","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"-1"}]}}, +{"id":"prim.test.did: int: leb overlong (1s) (right)","kind":"parse","text":"(-1)","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"-1"}]}}, {"id":"prim.test.did: int: leb not overlong when signed #44","kind":"decode","bytes":"4449444c00017cff00","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"127"}]}}, +{"id":"prim.test.did: int: leb not overlong when signed #44 (right)","kind":"parse","text":"(127)","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"127"}]}}, {"id":"prim.test.did: int: leb not overlong when signed #45","kind":"decode","bytes":"4449444c00017c807f","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"-128"}]}}, +{"id":"prim.test.did: int: leb not overlong when signed #45 (right)","kind":"parse","text":"(-128)","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"-128"}]}}, {"id":"prim.test.did: int: big number","kind":"decode","bytes":"4449444c00017c808098f4e9b5caea00","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"60000000000000000"}]}}, +{"id":"prim.test.did: int: big number (right)","kind":"parse","text":"(60000000000000000)","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"60000000000000000"}]}}, {"id":"prim.test.did: int: negative big number","kind":"decode","bytes":"4449444c00017c8080e88b96cab5957f","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"-60000000000000000"}]}}, +{"id":"prim.test.did: int: negative big number (right)","kind":"parse","text":"(-60000000000000000)","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"-60000000000000000"}]}}, {"id":"prim.test.did: nat <: int: 0","kind":"decode","bytes":"4449444c00017d00","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"0"}]}}, +{"id":"prim.test.did: nat <: int: 0 (right)","kind":"parse","text":"(0)","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"0"}]}}, {"id":"prim.test.did: nat <: int: 1","kind":"decode","bytes":"4449444c00017d01","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"1"}]}}, +{"id":"prim.test.did: nat <: int: 1 (right)","kind":"parse","text":"(1)","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"1"}]}}, {"id":"prim.test.did: nat <: int: leb (two bytes) #50","kind":"decode","bytes":"4449444c00017d7f","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"127"}]}}, +{"id":"prim.test.did: nat <: int: leb (two bytes) #50 (right)","kind":"parse","text":"(127)","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"127"}]}}, {"id":"prim.test.did: nat <: int: leb (two bytes) #51","kind":"decode","bytes":"4449444c00017d8001","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"128"}]}}, +{"id":"prim.test.did: nat <: int: leb (two bytes) #51 (right)","kind":"parse","text":"(128)","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"128"}]}}, {"id":"prim.test.did: nat <: int: leb (two bytes, all bits)","kind":"decode","bytes":"4449444c00017dff7f","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"16383"}]}}, +{"id":"prim.test.did: nat <: int: leb (two bytes, all bits) (right)","kind":"parse","text":"(16383)","types":{"roots":["int"],"table":[]},"expect":{"values":[{"int":"16383"}]}}, {"id":"prim.test.did: nat8: 0","kind":"decode","bytes":"4449444c00017b00","types":{"roots":["nat8"],"table":[]},"expect":{"values":[{"nat8":"0"}]}}, +{"id":"prim.test.did: nat8: 0 (right)","kind":"parse","text":"(0)","types":{"roots":["nat8"],"table":[]},"expect":{"values":[{"nat8":"0"}]}}, {"id":"prim.test.did: nat8: 1","kind":"decode","bytes":"4449444c00017b01","types":{"roots":["nat8"],"table":[]},"expect":{"values":[{"nat8":"1"}]}}, +{"id":"prim.test.did: nat8: 1 (right)","kind":"parse","text":"(1)","types":{"roots":["nat8"],"table":[]},"expect":{"values":[{"nat8":"1"}]}}, {"id":"prim.test.did: nat8: 255","kind":"decode","bytes":"4449444c00017bff","types":{"roots":["nat8"],"table":[]},"expect":{"values":[{"nat8":"255"}]}}, +{"id":"prim.test.did: nat8: 255 (right)","kind":"parse","text":"(255)","types":{"roots":["nat8"],"table":[]},"expect":{"values":[{"nat8":"255"}]}}, {"id":"prim.test.did: nat8: too short","kind":"decode","bytes":"4449444c00017b","types":{"roots":["nat8"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: nat8: too long","kind":"decode","bytes":"4449444c00017b0000","types":{"roots":["nat8"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: nat16: 0","kind":"decode","bytes":"4449444c00017a0000","types":{"roots":["nat16"],"table":[]},"expect":{"values":[{"nat16":"0"}]}}, +{"id":"prim.test.did: nat16: 0 (right)","kind":"parse","text":"(0)","types":{"roots":["nat16"],"table":[]},"expect":{"values":[{"nat16":"0"}]}}, {"id":"prim.test.did: nat16: 1","kind":"decode","bytes":"4449444c00017a0100","types":{"roots":["nat16"],"table":[]},"expect":{"values":[{"nat16":"1"}]}}, +{"id":"prim.test.did: nat16: 1 (right)","kind":"parse","text":"(1)","types":{"roots":["nat16"],"table":[]},"expect":{"values":[{"nat16":"1"}]}}, {"id":"prim.test.did: nat16: 255","kind":"decode","bytes":"4449444c00017aff00","types":{"roots":["nat16"],"table":[]},"expect":{"values":[{"nat16":"255"}]}}, +{"id":"prim.test.did: nat16: 255 (right)","kind":"parse","text":"(255)","types":{"roots":["nat16"],"table":[]},"expect":{"values":[{"nat16":"255"}]}}, {"id":"prim.test.did: nat16: 256","kind":"decode","bytes":"4449444c00017a0001","types":{"roots":["nat16"],"table":[]},"expect":{"values":[{"nat16":"256"}]}}, +{"id":"prim.test.did: nat16: 256 (right)","kind":"parse","text":"(256)","types":{"roots":["nat16"],"table":[]},"expect":{"values":[{"nat16":"256"}]}}, {"id":"prim.test.did: nat16: 65535","kind":"decode","bytes":"4449444c00017affff","types":{"roots":["nat16"],"table":[]},"expect":{"values":[{"nat16":"65535"}]}}, +{"id":"prim.test.did: nat16: 65535 (right)","kind":"parse","text":"(65535)","types":{"roots":["nat16"],"table":[]},"expect":{"values":[{"nat16":"65535"}]}}, {"id":"prim.test.did: nat16: too short #63","kind":"decode","bytes":"4449444c00017a","types":{"roots":["nat16"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: nat16: too short #64","kind":"decode","bytes":"4449444c00017a00","types":{"roots":["nat16"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: nat16: too long","kind":"decode","bytes":"4449444c00017a000000","types":{"roots":["nat16"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: nat32: 0","kind":"decode","bytes":"4449444c00017900000000","types":{"roots":["nat32"],"table":[]},"expect":{"values":[{"nat32":"0"}]}}, +{"id":"prim.test.did: nat32: 0 (right)","kind":"parse","text":"(0)","types":{"roots":["nat32"],"table":[]},"expect":{"values":[{"nat32":"0"}]}}, {"id":"prim.test.did: nat32: 1","kind":"decode","bytes":"4449444c00017901000000","types":{"roots":["nat32"],"table":[]},"expect":{"values":[{"nat32":"1"}]}}, +{"id":"prim.test.did: nat32: 1 (right)","kind":"parse","text":"(1)","types":{"roots":["nat32"],"table":[]},"expect":{"values":[{"nat32":"1"}]}}, {"id":"prim.test.did: nat32: 255","kind":"decode","bytes":"4449444c000179ff000000","types":{"roots":["nat32"],"table":[]},"expect":{"values":[{"nat32":"255"}]}}, +{"id":"prim.test.did: nat32: 255 (right)","kind":"parse","text":"(255)","types":{"roots":["nat32"],"table":[]},"expect":{"values":[{"nat32":"255"}]}}, {"id":"prim.test.did: nat32: 256","kind":"decode","bytes":"4449444c00017900010000","types":{"roots":["nat32"],"table":[]},"expect":{"values":[{"nat32":"256"}]}}, +{"id":"prim.test.did: nat32: 256 (right)","kind":"parse","text":"(256)","types":{"roots":["nat32"],"table":[]},"expect":{"values":[{"nat32":"256"}]}}, {"id":"prim.test.did: nat32: 65535","kind":"decode","bytes":"4449444c000179ffff0000","types":{"roots":["nat32"],"table":[]},"expect":{"values":[{"nat32":"65535"}]}}, +{"id":"prim.test.did: nat32: 65535 (right)","kind":"parse","text":"(65535)","types":{"roots":["nat32"],"table":[]},"expect":{"values":[{"nat32":"65535"}]}}, {"id":"prim.test.did: nat32: 4294967295","kind":"decode","bytes":"4449444c000179ffffffff","types":{"roots":["nat32"],"table":[]},"expect":{"values":[{"nat32":"4294967295"}]}}, +{"id":"prim.test.did: nat32: 4294967295 (right)","kind":"parse","text":"(4294967295)","types":{"roots":["nat32"],"table":[]},"expect":{"values":[{"nat32":"4294967295"}]}}, {"id":"prim.test.did: nat32: too short #72","kind":"decode","bytes":"4449444c000179","types":{"roots":["nat32"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: nat32: too short #73","kind":"decode","bytes":"4449444c00017900","types":{"roots":["nat32"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: nat32: too short #74","kind":"decode","bytes":"4449444c0001790000","types":{"roots":["nat32"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: nat32: too short #75","kind":"decode","bytes":"4449444c000179000000","types":{"roots":["nat32"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: nat32: too long","kind":"decode","bytes":"4449444c0001790000000000","types":{"roots":["nat32"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: nat64: 0","kind":"decode","bytes":"4449444c0001780000000000000000","types":{"roots":["nat64"],"table":[]},"expect":{"values":[{"nat64":"0"}]}}, +{"id":"prim.test.did: nat64: 0 (right)","kind":"parse","text":"(0)","types":{"roots":["nat64"],"table":[]},"expect":{"values":[{"nat64":"0"}]}}, {"id":"prim.test.did: nat64: 1","kind":"decode","bytes":"4449444c0001780100000000000000","types":{"roots":["nat64"],"table":[]},"expect":{"values":[{"nat64":"1"}]}}, +{"id":"prim.test.did: nat64: 1 (right)","kind":"parse","text":"(1)","types":{"roots":["nat64"],"table":[]},"expect":{"values":[{"nat64":"1"}]}}, {"id":"prim.test.did: nat64: 255","kind":"decode","bytes":"4449444c000178ff00000000000000","types":{"roots":["nat64"],"table":[]},"expect":{"values":[{"nat64":"255"}]}}, +{"id":"prim.test.did: nat64: 255 (right)","kind":"parse","text":"(255)","types":{"roots":["nat64"],"table":[]},"expect":{"values":[{"nat64":"255"}]}}, {"id":"prim.test.did: nat64: 256","kind":"decode","bytes":"4449444c0001780001000000000000","types":{"roots":["nat64"],"table":[]},"expect":{"values":[{"nat64":"256"}]}}, +{"id":"prim.test.did: nat64: 256 (right)","kind":"parse","text":"(256)","types":{"roots":["nat64"],"table":[]},"expect":{"values":[{"nat64":"256"}]}}, {"id":"prim.test.did: nat64: 65535","kind":"decode","bytes":"4449444c000178ffff000000000000","types":{"roots":["nat64"],"table":[]},"expect":{"values":[{"nat64":"65535"}]}}, +{"id":"prim.test.did: nat64: 65535 (right)","kind":"parse","text":"(65535)","types":{"roots":["nat64"],"table":[]},"expect":{"values":[{"nat64":"65535"}]}}, {"id":"prim.test.did: nat64: 4294967295","kind":"decode","bytes":"4449444c000178ffffffff00000000","types":{"roots":["nat64"],"table":[]},"expect":{"values":[{"nat64":"4294967295"}]}}, +{"id":"prim.test.did: nat64: 4294967295 (right)","kind":"parse","text":"(4294967295)","types":{"roots":["nat64"],"table":[]},"expect":{"values":[{"nat64":"4294967295"}]}}, {"id":"prim.test.did: nat64: 18446744073709551615","kind":"decode","bytes":"4449444c000178ffffffffffffffff","types":{"roots":["nat64"],"table":[]},"expect":{"values":[{"nat64":"18446744073709551615"}]}}, +{"id":"prim.test.did: nat64: 18446744073709551615 (right)","kind":"parse","text":"(18446744073709551615)","types":{"roots":["nat64"],"table":[]},"expect":{"values":[{"nat64":"18446744073709551615"}]}}, {"id":"prim.test.did: nat64: too short #84","kind":"decode","bytes":"4449444c000178","types":{"roots":["nat64"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: nat64: too short #85","kind":"decode","bytes":"4449444c00017800","types":{"roots":["nat64"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: nat64: too short #86","kind":"decode","bytes":"4449444c0001780000","types":{"roots":["nat64"],"table":[]},"expect":"reject"}, @@ -270,36 +410,57 @@ {"id":"prim.test.did: nat64: too short #91","kind":"decode","bytes":"4449444c00017800000000000000","types":{"roots":["nat64"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: nat64: too long","kind":"decode","bytes":"4449444c000178000000000000000000","types":{"roots":["nat64"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: int8: 0","kind":"decode","bytes":"4449444c00017700","types":{"roots":["int8"],"table":[]},"expect":{"values":[{"int8":"0"}]}}, +{"id":"prim.test.did: int8: 0 (right)","kind":"parse","text":"(0)","types":{"roots":["int8"],"table":[]},"expect":{"values":[{"int8":"0"}]}}, {"id":"prim.test.did: int8: 1","kind":"decode","bytes":"4449444c00017701","types":{"roots":["int8"],"table":[]},"expect":{"values":[{"int8":"1"}]}}, +{"id":"prim.test.did: int8: 1 (right)","kind":"parse","text":"(1)","types":{"roots":["int8"],"table":[]},"expect":{"values":[{"int8":"1"}]}}, {"id":"prim.test.did: int8: -1","kind":"decode","bytes":"4449444c000177ff","types":{"roots":["int8"],"table":[]},"expect":{"values":[{"int8":"-1"}]}}, +{"id":"prim.test.did: int8: -1 (right)","kind":"parse","text":"(-1)","types":{"roots":["int8"],"table":[]},"expect":{"values":[{"int8":"-1"}]}}, {"id":"prim.test.did: int8: too short","kind":"decode","bytes":"4449444c000177","types":{"roots":["int8"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: int8: too long","kind":"decode","bytes":"4449444c0001770000","types":{"roots":["int8"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: int16: 0","kind":"decode","bytes":"4449444c0001760000","types":{"roots":["int16"],"table":[]},"expect":{"values":[{"int16":"0"}]}}, +{"id":"prim.test.did: int16: 0 (right)","kind":"parse","text":"(0)","types":{"roots":["int16"],"table":[]},"expect":{"values":[{"int16":"0"}]}}, {"id":"prim.test.did: int16: 1","kind":"decode","bytes":"4449444c0001760100","types":{"roots":["int16"],"table":[]},"expect":{"values":[{"int16":"1"}]}}, +{"id":"prim.test.did: int16: 1 (right)","kind":"parse","text":"(1)","types":{"roots":["int16"],"table":[]},"expect":{"values":[{"int16":"1"}]}}, {"id":"prim.test.did: int16: 255","kind":"decode","bytes":"4449444c000176ff00","types":{"roots":["int16"],"table":[]},"expect":{"values":[{"int16":"255"}]}}, +{"id":"prim.test.did: int16: 255 (right)","kind":"parse","text":"(255)","types":{"roots":["int16"],"table":[]},"expect":{"values":[{"int16":"255"}]}}, {"id":"prim.test.did: int16: 256","kind":"decode","bytes":"4449444c0001760001","types":{"roots":["int16"],"table":[]},"expect":{"values":[{"int16":"256"}]}}, +{"id":"prim.test.did: int16: 256 (right)","kind":"parse","text":"(256)","types":{"roots":["int16"],"table":[]},"expect":{"values":[{"int16":"256"}]}}, {"id":"prim.test.did: int16: -1","kind":"decode","bytes":"4449444c000176ffff","types":{"roots":["int16"],"table":[]},"expect":{"values":[{"int16":"-1"}]}}, +{"id":"prim.test.did: int16: -1 (right)","kind":"parse","text":"(-1)","types":{"roots":["int16"],"table":[]},"expect":{"values":[{"int16":"-1"}]}}, {"id":"prim.test.did: int16: too short #103","kind":"decode","bytes":"4449444c000176","types":{"roots":["int16"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: int16: too short #104","kind":"decode","bytes":"4449444c00017600","types":{"roots":["int16"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: int16: too long","kind":"decode","bytes":"4449444c000176000000","types":{"roots":["int16"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: int32: 0","kind":"decode","bytes":"4449444c00017500000000","types":{"roots":["int32"],"table":[]},"expect":{"values":[{"int32":"0"}]}}, +{"id":"prim.test.did: int32: 0 (right)","kind":"parse","text":"(0)","types":{"roots":["int32"],"table":[]},"expect":{"values":[{"int32":"0"}]}}, {"id":"prim.test.did: int32: 1","kind":"decode","bytes":"4449444c00017501000000","types":{"roots":["int32"],"table":[]},"expect":{"values":[{"int32":"1"}]}}, +{"id":"prim.test.did: int32: 1 (right)","kind":"parse","text":"(1)","types":{"roots":["int32"],"table":[]},"expect":{"values":[{"int32":"1"}]}}, {"id":"prim.test.did: int32: 255","kind":"decode","bytes":"4449444c000175ff000000","types":{"roots":["int32"],"table":[]},"expect":{"values":[{"int32":"255"}]}}, +{"id":"prim.test.did: int32: 255 (right)","kind":"parse","text":"(255)","types":{"roots":["int32"],"table":[]},"expect":{"values":[{"int32":"255"}]}}, {"id":"prim.test.did: int32: 256","kind":"decode","bytes":"4449444c00017500010000","types":{"roots":["int32"],"table":[]},"expect":{"values":[{"int32":"256"}]}}, +{"id":"prim.test.did: int32: 256 (right)","kind":"parse","text":"(256)","types":{"roots":["int32"],"table":[]},"expect":{"values":[{"int32":"256"}]}}, {"id":"prim.test.did: int32: 65535","kind":"decode","bytes":"4449444c000175ffff0000","types":{"roots":["int32"],"table":[]},"expect":{"values":[{"int32":"65535"}]}}, +{"id":"prim.test.did: int32: 65535 (right)","kind":"parse","text":"(65535)","types":{"roots":["int32"],"table":[]},"expect":{"values":[{"int32":"65535"}]}}, {"id":"prim.test.did: int32: -1","kind":"decode","bytes":"4449444c000175ffffffff","types":{"roots":["int32"],"table":[]},"expect":{"values":[{"int32":"-1"}]}}, +{"id":"prim.test.did: int32: -1 (right)","kind":"parse","text":"(-1)","types":{"roots":["int32"],"table":[]},"expect":{"values":[{"int32":"-1"}]}}, {"id":"prim.test.did: int32: too short #112","kind":"decode","bytes":"4449444c000175","types":{"roots":["int32"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: int32: too short #113","kind":"decode","bytes":"4449444c00017500","types":{"roots":["int32"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: int32: too short #114","kind":"decode","bytes":"4449444c0001750000","types":{"roots":["int32"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: int32: too short #115","kind":"decode","bytes":"4449444c000175000000","types":{"roots":["int32"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: int32: too long","kind":"decode","bytes":"4449444c0001750000000000","types":{"roots":["int32"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: int64: 0","kind":"decode","bytes":"4449444c0001740000000000000000","types":{"roots":["int64"],"table":[]},"expect":{"values":[{"int64":"0"}]}}, +{"id":"prim.test.did: int64: 0 (right)","kind":"parse","text":"(0)","types":{"roots":["int64"],"table":[]},"expect":{"values":[{"int64":"0"}]}}, {"id":"prim.test.did: int64: 1","kind":"decode","bytes":"4449444c0001740100000000000000","types":{"roots":["int64"],"table":[]},"expect":{"values":[{"int64":"1"}]}}, +{"id":"prim.test.did: int64: 1 (right)","kind":"parse","text":"(1)","types":{"roots":["int64"],"table":[]},"expect":{"values":[{"int64":"1"}]}}, {"id":"prim.test.did: int64: 255","kind":"decode","bytes":"4449444c000174ff00000000000000","types":{"roots":["int64"],"table":[]},"expect":{"values":[{"int64":"255"}]}}, +{"id":"prim.test.did: int64: 255 (right)","kind":"parse","text":"(255)","types":{"roots":["int64"],"table":[]},"expect":{"values":[{"int64":"255"}]}}, {"id":"prim.test.did: int64: 256","kind":"decode","bytes":"4449444c0001740001000000000000","types":{"roots":["int64"],"table":[]},"expect":{"values":[{"int64":"256"}]}}, +{"id":"prim.test.did: int64: 256 (right)","kind":"parse","text":"(256)","types":{"roots":["int64"],"table":[]},"expect":{"values":[{"int64":"256"}]}}, {"id":"prim.test.did: int64: 65535","kind":"decode","bytes":"4449444c000174ffff000000000000","types":{"roots":["int64"],"table":[]},"expect":{"values":[{"int64":"65535"}]}}, +{"id":"prim.test.did: int64: 65535 (right)","kind":"parse","text":"(65535)","types":{"roots":["int64"],"table":[]},"expect":{"values":[{"int64":"65535"}]}}, {"id":"prim.test.did: int64: 4294967295","kind":"decode","bytes":"4449444c000174ffffffff00000000","types":{"roots":["int64"],"table":[]},"expect":{"values":[{"int64":"4294967295"}]}}, +{"id":"prim.test.did: int64: 4294967295 (right)","kind":"parse","text":"(4294967295)","types":{"roots":["int64"],"table":[]},"expect":{"values":[{"int64":"4294967295"}]}}, {"id":"prim.test.did: int64: -1","kind":"decode","bytes":"4449444c000174ffffffffffffffff","types":{"roots":["int64"],"table":[]},"expect":{"values":[{"int64":"-1"}]}}, +{"id":"prim.test.did: int64: -1 (right)","kind":"parse","text":"(-1)","types":{"roots":["int64"],"table":[]},"expect":{"values":[{"int64":"-1"}]}}, {"id":"prim.test.did: int64: too short #124","kind":"decode","bytes":"4449444c000174","types":{"roots":["int64"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: int64: too short #125","kind":"decode","bytes":"4449444c00017400","types":{"roots":["int64"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: int64: too short #126","kind":"decode","bytes":"4449444c0001740000","types":{"roots":["int64"],"table":[]},"expect":"reject"}, @@ -310,28 +471,45 @@ {"id":"prim.test.did: int64: too short #131","kind":"decode","bytes":"4449444c00017400000000000000","types":{"roots":["int64"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: int64: too long","kind":"decode","bytes":"4449444c000174000000000000000000","types":{"roots":["int64"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: float32: 0","kind":"decode","bytes":"4449444c00017300000000","types":{"roots":["float32"],"table":[]},"expect":{"values":[{"float32":"00000000"}]}}, +{"id":"prim.test.did: float32: 0 (right)","kind":"parse","text":"(0.)","types":{"roots":["float32"],"table":[]},"expect":{"values":[{"float32":"00000000"}]}}, {"id":"prim.test.did: float32: 3","kind":"decode","bytes":"4449444c00017300004040","types":{"roots":["float32"],"table":[]},"expect":{"values":[{"float32":"40400000"}]}}, +{"id":"prim.test.did: float32: 3 (right)","kind":"parse","text":"(3.)","types":{"roots":["float32"],"table":[]},"expect":{"values":[{"float32":"40400000"}]}}, {"id":"prim.test.did: float32: 0.5","kind":"decode","bytes":"4449444c0001730000003f","types":{"roots":["float32"],"table":[]},"expect":{"values":[{"float32":"3f000000"}]}}, +{"id":"prim.test.did: float32: 0.5 (right)","kind":"parse","text":"(0.5)","types":{"roots":["float32"],"table":[]},"expect":{"values":[{"float32":"3f000000"}]}}, {"id":"prim.test.did: float32: -0.5","kind":"decode","bytes":"4449444c000173000000bf","types":{"roots":["float32"],"table":[]},"expect":{"values":[{"float32":"bf000000"}]}}, +{"id":"prim.test.did: float32: -0.5 (right)","kind":"parse","text":"(-0.5)","types":{"roots":["float32"],"table":[]},"expect":{"values":[{"float32":"bf000000"}]}}, {"id":"prim.test.did: float32: too short","kind":"decode","bytes":"4449444c0001730000","types":{"roots":["float32"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: float32: too long","kind":"decode","bytes":"4449444c0001730000000000","types":{"roots":["float32"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: float64: 0","kind":"decode","bytes":"4449444c0001720000000000000000","types":{"roots":["float64"],"table":[]},"expect":{"values":[{"float64":"0000000000000000"}]}}, +{"id":"prim.test.did: float64: 0 (right)","kind":"parse","text":"(0.)","types":{"roots":["float64"],"table":[]},"expect":{"values":[{"float64":"0000000000000000"}]}}, {"id":"prim.test.did: float64: 3","kind":"decode","bytes":"4449444c0001720000000000000840","types":{"roots":["float64"],"table":[]},"expect":{"values":[{"float64":"4008000000000000"}]}}, +{"id":"prim.test.did: float64: 3 (right)","kind":"parse","text":"(3.)","types":{"roots":["float64"],"table":[]},"expect":{"values":[{"float64":"4008000000000000"}]}}, {"id":"prim.test.did: float64: 0.5","kind":"decode","bytes":"4449444c000172000000000000e03f","types":{"roots":["float64"],"table":[]},"expect":{"values":[{"float64":"3fe0000000000000"}]}}, +{"id":"prim.test.did: float64: 0.5 (right)","kind":"parse","text":"(0.5)","types":{"roots":["float64"],"table":[]},"expect":{"values":[{"float64":"3fe0000000000000"}]}}, {"id":"prim.test.did: float64: -0.5","kind":"decode","bytes":"4449444c000172000000000000e0bf","types":{"roots":["float64"],"table":[]},"expect":{"values":[{"float64":"bfe0000000000000"}]}}, +{"id":"prim.test.did: float64: -0.5 (right)","kind":"parse","text":"(-0.5)","types":{"roots":["float64"],"table":[]},"expect":{"values":[{"float64":"bfe0000000000000"}]}}, {"id":"prim.test.did: float64: NaN","kind":"decode","bytes":"4449444c000172010000000000f07f","types":{"roots":["float64"],"table":[]},"expect":{"values":[{"float64":"7ff0000000000001"}]}}, {"id":"prim.test.did: float64: max value","kind":"decode","bytes":"4449444c000172ffffffffffffef7f","types":{"roots":["float64"],"table":[]},"expect":{"values":[{"float64":"7fefffffffffffff"}]}}, {"id":"prim.test.did: float64: too short","kind":"decode","bytes":"4449444c00017200000000","types":{"roots":["float64"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: float64: too long","kind":"decode","bytes":"4449444c000172000000000000000000","types":{"roots":["float64"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: text: empty string","kind":"decode","bytes":"4449444c00017100","types":{"roots":["text"],"table":[]},"expect":{"values":[{"text":""}]}}, +{"id":"prim.test.did: text: empty string (right)","kind":"parse","text":"(\"\")","types":{"roots":["text"],"table":[]},"expect":{"values":[{"text":""}]}}, {"id":"prim.test.did: text: Motoko","kind":"decode","bytes":"4449444c000171064d6f746f6b6f","types":{"roots":["text"],"table":[]},"expect":{"values":[{"text":"Motoko"}]}}, +{"id":"prim.test.did: text: Motoko (right)","kind":"parse","text":"(\"Motoko\")","types":{"roots":["text"],"table":[]},"expect":{"values":[{"text":"Motoko"}]}}, {"id":"prim.test.did: text: too long","kind":"decode","bytes":"4449444c000171054d6f746f6b6f","types":{"roots":["text"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: text: too short","kind":"decode","bytes":"4449444c000171074d6f746f6b6f","types":{"roots":["text"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: text: overlong length leb","kind":"decode","bytes":"4449444c00017186004d6f746f6b6f","types":{"roots":["text"],"table":[]},"expect":{"values":[{"text":"Motoko"}]}}, {"id":"prim.test.did: text: Unicode","kind":"decode","bytes":"4449444c00017103e29883","types":{"roots":["text"],"table":[]},"expect":{"values":[{"text":"☃"}]}}, +{"id":"prim.test.did: text: Unicode (right)","kind":"parse","text":"(\"☃\")","types":{"roots":["text"],"table":[]},"expect":{"values":[{"text":"☃"}]}}, +{"id":"prim.test.did: text: Unicode escape","kind":"parse","text":"(\"\\u{2603}\")","types":{"roots":["text"],"table":[]},"expect":{"values":[{"text":"☃"}]}}, +{"id":"prim.test.did: text: Unicode escape (right)","kind":"parse","text":"(\"☃\")","types":{"roots":["text"],"table":[]},"expect":{"values":[{"text":"☃"}]}}, +{"id":"prim.test.did: text: Unicode escape with underscore","kind":"parse","text":"(\"\\u{26_03}\")","types":{"roots":["text"],"table":[]},"expect":{"values":[{"text":"☃"}]}}, +{"id":"prim.test.did: text: Unicode escape with underscore (right)","kind":"parse","text":"(\"☃\")","types":{"roots":["text"],"table":[]},"expect":{"values":[{"text":"☃"}]}}, +{"id":"prim.test.did: text: Unicode escape (unclosed)","kind":"parse","text":"(\"\\u{2603\")","types":{"roots":["text"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: text: Invalid utf8","kind":"decode","bytes":"4449444c00017103e228a1","types":{"roots":["text"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: text: Unicode overshoots","kind":"decode","bytes":"4449444c00017102e29883","types":{"roots":["text"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: text: Escape sequences","kind":"decode","bytes":"4449444c00017106090a0d22275c","types":{"roots":["text"],"table":[]},"expect":{"values":[{"text":"\u0009\n\r\"'\\"}]}}, +{"id":"prim.test.did: text: Escape sequences (right)","kind":"parse","text":"(\"\\t\\n\\r\\\"\\'\\\\\")","types":{"roots":["text"],"table":[]},"expect":{"values":[{"text":"\u0009\n\r\"'\\"}]}}, {"id":"prim.test.did: reserved from null","kind":"decode","bytes":"4449444c000170","types":{"roots":["reserved"],"table":[]},"expect":{"values":[null]}}, {"id":"prim.test.did: reserved from null (right)","kind":"decode","bytes":"4449444c00017f","types":{"roots":["reserved"],"table":[]},"expect":{"values":[null]}}, {"id":"prim.test.did: reserved from bool","kind":"decode","bytes":"4449444c000170","types":{"roots":["reserved"],"table":[]},"expect":{"values":[null]}}, @@ -345,25 +523,39 @@ {"id":"prim.test.did: reserved from invalid utf8 text","kind":"decode","bytes":"4449444c00017103e228a1","types":{"roots":["reserved"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: cannot decode empty type","kind":"decode","bytes":"4449444c00016f","types":{"roots":["empty"],"table":[]},"expect":"reject"}, {"id":"prim.test.did: okay to decode non-empty value","kind":"decode","bytes":"4449444c016e6f010000","types":{"roots":[0],"table":[{"opt":"empty"}]},"expect":{"values":[null]}}, +{"id":"prim.test.did: okay to decode non-empty value (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":"empty"}]},"expect":{"values":[null]}}, {"id":"prim.test.did: multiple arguments","kind":"decode","bytes":"4449444c000a7f7e7d7c7f707f7b7a79012a2a2a2a002a000000","types":{"roots":["null","bool","nat","int","null","reserved","null","nat8","nat16","nat32"],"table":[]},"expect":{"values":[null,true,{"nat":"42"},{"int":"42"},null,null,null,{"nat8":"42"},{"nat16":"42"},{"nat32":"42"}]}}, +{"id":"prim.test.did: multiple arguments (right)","kind":"parse","text":"(null, true, 42, 42, null, null, null, 42, 42, 42)","types":{"roots":["null","bool","nat","int","null","reserved","null","nat8","nat16","nat32"],"table":[]},"expect":{"values":[null,true,{"nat":"42"},{"int":"42"},null,null,null,{"nat8":"42"},{"nat16":"42"},{"nat32":"42"}]}}, {"id":"reference.test.did: principal: ic0","kind":"decode","bytes":"4449444c0001680100","types":{"roots":["principal"],"table":[]},"expect":{"values":[{"principal":""}]}}, +{"id":"reference.test.did: principal: ic0 (right)","kind":"parse","text":"(principal \"aaaaa-aa\")","types":{"roots":["principal"],"table":[]},"expect":{"values":[{"principal":""}]}}, {"id":"reference.test.did: principal #2","kind":"decode","bytes":"4449444c0001680103caffee","types":{"roots":["principal"],"table":[]},"expect":{"values":[{"principal":"caffee"}]}}, +{"id":"reference.test.did: principal #2 (right)","kind":"parse","text":"(principal \"w7x7r-cok77-xa\")","types":{"roots":["principal"],"table":[]},"expect":{"values":[{"principal":"caffee"}]}}, {"id":"reference.test.did: principal #3","kind":"decode","bytes":"4449444c0001680109efcdab000000000001","types":{"roots":["principal"],"table":[]},"expect":{"values":[{"principal":"efcdab000000000001"}]}}, +{"id":"reference.test.did: principal #3 (right)","kind":"parse","text":"(principal \"2chl6-4hpzw-vqaaa-aaaaa-c\")","types":{"roots":["principal"],"table":[]},"expect":{"values":[{"principal":"efcdab000000000001"}]}}, {"id":"reference.test.did: principal #4","kind":"decode","bytes":"4449444c0001680102caff","types":{"roots":["principal"],"table":[]},"expect":{"values":[{"principal":"caff"}]}}, +{"id":"reference.test.did: principal #4 (right)","kind":"parse","text":"(principal \"w7x7r-cok77-xa\")","types":{"roots":["principal"],"table":[]},"expect":{"values":[{"principal":"caffee"}]}}, {"id":"reference.test.did: principal: no tag","kind":"decode","bytes":"4449444c00016803caffee","types":{"roots":["principal"],"table":[]},"expect":"reject"}, {"id":"reference.test.did: principal: too short","kind":"decode","bytes":"4449444c0001680103caff","types":{"roots":["principal"],"table":[]},"expect":"reject"}, {"id":"reference.test.did: principal: too long","kind":"decode","bytes":"4449444c0001680103caffeeee","types":{"roots":["principal"],"table":[]},"expect":"reject"}, {"id":"reference.test.did: principal: not construct","kind":"decode","bytes":"4449444c016801000103caffee","types":{"roots":["principal"],"table":[]},"expect":"reject"}, {"id":"reference.test.did: service: not principal #9","kind":"decode","bytes":"4449444c0001680103caffee","types":{"roots":[0],"table":[{"service":[]}]},"expect":"reject"}, {"id":"reference.test.did: service: not primitive type","kind":"decode","bytes":"4449444c0001690103caffee","types":{"roots":[0],"table":[{"service":[]}]},"expect":"reject"}, +{"id":"reference.test.did: service: not principal #11","kind":"parse","text":"(principal \"w7x7r-cok77-xa\")","types":{"roots":[0],"table":[{"service":[]}]},"expect":"reject"}, {"id":"reference.test.did: service #12","kind":"decode","bytes":"4449444c01690001000103caffee","types":{"roots":[0],"table":[{"service":[]}]},"expect":{"values":[{"service":"caffee"}]}}, +{"id":"reference.test.did: service #12 (right)","kind":"parse","text":"(service \"w7x7r-cok77-xa\")","types":{"roots":[0],"table":[{"service":[]}]},"expect":{"values":[{"service":"caffee"}]}}, {"id":"reference.test.did: service #13","kind":"decode","bytes":"4449444c01690001000103caffee","types":{"roots":[0],"table":[{"service":[]}]},"expect":{"values":[{"service":"caffee"}]}}, +{"id":"reference.test.did: service #13 (right)","kind":"parse","text":"(service \"w7x7r-cok77-xa\")","types":{"roots":[0],"table":[{"service":[]}]},"expect":{"values":[{"service":"caffee"}]}}, {"id":"reference.test.did: service #14","kind":"decode","bytes":"4449444c026a0171017d00690103666f6f0001010103caffee","types":{"roots":[0],"table":[{"service":[["foo",1]]},{"func":{"annots":[],"args":["text"],"rets":["nat"]}}]},"expect":{"values":[{"service":"caffee"}]}}, +{"id":"reference.test.did: service #14 (right)","kind":"parse","text":"(service \"w7x7r-cok77-xa\")","types":{"roots":[0],"table":[{"service":[["foo",1]]},{"func":{"annots":[],"args":["text"],"rets":["nat"]}}]},"expect":{"values":[{"service":"caffee"}]}}, {"id":"reference.test.did: service #15","kind":"decode","bytes":"4449444c026a0171017d00690203666f6f0004666f6f320001010103caffee","types":{"roots":[0],"table":[{"service":[["foo",1],["foo2",2]]},{"func":{"annots":[],"args":["text"],"rets":["nat"]}},{"func":{"annots":[],"args":["text"],"rets":["nat"]}}]},"expect":{"values":[{"service":"caffee"}]}}, +{"id":"reference.test.did: service #15 (right)","kind":"parse","text":"(service \"w7x7r-cok77-xa\")","types":{"roots":[0],"table":[{"service":[["foo",1],["foo2",2]]},{"func":{"annots":[],"args":["text"],"rets":["nat"]}},{"func":{"annots":[],"args":["text"],"rets":["nat"]}}]},"expect":{"values":[{"service":"caffee"}]}}, {"id":"reference.test.did: service {} value decodes at principal","kind":"decode","bytes":"4449444c01690001000103caffee","types":{"roots":["principal"],"table":[]},"expect":{"values":[{"principal":"caffee"}]}}, +{"id":"reference.test.did: service {} value decodes at principal (right)","kind":"parse","text":"(principal \"w7x7r-cok77-xa\")","types":{"roots":["principal"],"table":[]},"expect":{"values":[{"principal":"caffee"}]}}, {"id":"reference.test.did: service {foo} value decodes at principal","kind":"decode","bytes":"4449444c026a0171017d00690103666f6f0001010103caffee","types":{"roots":["principal"],"table":[]},"expect":{"values":[{"principal":"caffee"}]}}, +{"id":"reference.test.did: service {foo} value decodes at principal (right)","kind":"parse","text":"(principal \"w7x7r-cok77-xa\")","types":{"roots":["principal"],"table":[]},"expect":{"values":[{"principal":"caffee"}]}}, {"id":"reference.test.did: principal: reject empty (bottom) wire type","kind":"decode","bytes":"4449444c00016f0103caffee","types":{"roots":["principal"],"table":[]},"expect":"reject"}, {"id":"reference.test.did: recursive service value decodes at principal","kind":"decode","bytes":"4449444c026901016d016a0001010001000103caffee","types":{"roots":["principal"],"table":[]},"expect":{"values":[{"principal":"caffee"}]}}, +{"id":"reference.test.did: recursive service value decodes at principal (right)","kind":"parse","text":"(principal \"w7x7r-cok77-xa\")","types":{"roots":["principal"],"table":[]},"expect":{"values":[{"principal":"caffee"}]}}, {"id":"reference.test.did: principal: reject func wire type","kind":"decode","bytes":"4449444c016a0000000100010103caffee0161","types":{"roots":["principal"],"table":[]},"expect":"reject"}, {"id":"reference.test.did: principal: reject record wire type","kind":"decode","bytes":"4449444c016c000100","types":{"roots":["principal"],"table":[]},"expect":"reject"}, {"id":"reference.test.did: principal: reject opt wire type","kind":"decode","bytes":"4449444c016e7d010000","types":{"roots":["principal"],"table":[]},"expect":"reject"}, @@ -374,16 +566,23 @@ {"id":"reference.test.did: service: invalid unicode","kind":"decode","bytes":"4449444c026a0171017d00690103e228a10001010103caffee","types":{"roots":["reserved"],"table":[]},"expect":"reject"}, {"id":"reference.test.did: service: duplicate","kind":"decode","bytes":"4449444c026a0171017d00690203666f6f0003666f6f0001010103caffee","types":{"roots":[0],"table":[{"service":[["foo",1]]},{"func":{"annots":[],"args":["text"],"rets":["nat"]}}]},"expect":"reject"}, {"id":"reference.test.did: service: unicode","kind":"decode","bytes":"4449444c05690203666f6f0104f09f9082026a000001026a0103010401016e7c6c0001000100","types":{"roots":[0],"table":[{"service":[["foo",1],["🐂",2]]},{"func":{"annots":["oneway"],"args":[],"rets":[]}},{"func":{"annots":["query"],"args":[3],"rets":[4]}},{"opt":"int"},{"record":[]}]},"expect":{"values":[{"service":""}]}}, +{"id":"reference.test.did: service: unicode (right)","kind":"parse","text":"(service \"aaaaa-aa\")","types":{"roots":[0],"table":[{"service":[["foo",1],["🐂",2]]},{"func":{"annots":["oneway"],"args":[],"rets":[]}},{"func":{"annots":["query"],"args":[3],"rets":[4]}},{"opt":"int"},{"record":[]}]},"expect":{"values":[{"service":""}]}}, {"id":"reference.test.did: service { foo: principal }","kind":"decode","bytes":"4449444c026a0171017d00690103666f6f6801010103caffee","types":{"roots":[0],"table":[{"service":[["foo",1]]},{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":"reject"}, {"id":"reference.test.did: service { foo: opt bool } #31","kind":"decode","bytes":"4449444c026e7e690103666f6f0001010103caffee","types":{"roots":[0],"table":[{"service":[["foo",1]]},{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":"reject"}, {"id":"reference.test.did: service { foo: opt bool } #32","kind":"decode","bytes":"4449444c02690103666f6f016e7e01010103caffee","types":{"roots":[0],"table":[{"service":[["foo",1]]},{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":"reject"}, {"id":"reference.test.did: func: quote name","kind":"decode","bytes":"4449444c016a0000000100010103caffee0161","types":{"roots":[0],"table":[{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[{"func":["caffee","a"]}]}}, +{"id":"reference.test.did: func: quote name (right)","kind":"parse","text":"(func \"w7x7r-cok77-xa\".\"a\")","types":{"roots":[0],"table":[{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[{"func":["caffee","a"]}]}}, {"id":"reference.test.did: func: non-quote name","kind":"decode","bytes":"4449444c016a0000000100010103caffee0161","types":{"roots":[0],"table":[{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[{"func":["caffee","a"]}]}}, +{"id":"reference.test.did: func: non-quote name (right)","kind":"parse","text":"(func \"w7x7r-cok77-xa\".a)","types":{"roots":[0],"table":[{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[{"func":["caffee","a"]}]}}, {"id":"reference.test.did: func: wrong tag","kind":"decode","bytes":"4449444c016a0000000100010003caffee0161","types":{"roots":[0],"table":[{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":"reject"}, {"id":"reference.test.did: func: unicode","kind":"decode","bytes":"4449444c026a027c7d010101016900010001010004f09f9082","types":{"roots":[0],"table":[{"func":{"annots":["query"],"args":["int","nat"],"rets":[1]}},{"service":[]}]},"expect":{"values":[{"func":["","🐂"]}]}}, +{"id":"reference.test.did: func: unicode (right)","kind":"parse","text":"(func \"aaaaa-aa\".\"🐂\")","types":{"roots":[0],"table":[{"func":{"annots":["query"],"args":["int","nat"],"rets":[1]}},{"service":[]}]},"expect":{"values":[{"func":["","🐂"]}]}}, {"id":"reference.test.did: func","kind":"decode","bytes":"4449444c016a0168017d000100010103caffee03666f6f","types":{"roots":[0],"table":[{"func":{"annots":[],"args":["principal"],"rets":["nat"]}}]},"expect":{"values":[{"func":["caffee","foo"]}]}}, +{"id":"reference.test.did: func (right)","kind":"parse","text":"(func \"w7x7r-cok77-xa\".foo)","types":{"roots":[0],"table":[{"func":{"annots":[],"args":["principal"],"rets":["nat"]}}]},"expect":{"values":[{"func":["caffee","foo"]}]}}, {"id":"reference.test.did: func: query","kind":"decode","bytes":"4449444c016a0171017d01010100010103caffee03666f6f","types":{"roots":[0],"table":[{"func":{"annots":["query"],"args":["text"],"rets":["nat"]}}]},"expect":{"values":[{"func":["caffee","foo"]}]}}, +{"id":"reference.test.did: func: query (right)","kind":"parse","text":"(func \"w7x7r-cok77-xa\".foo)","types":{"roots":[0],"table":[{"func":{"annots":["query"],"args":["text"],"rets":["nat"]}}]},"expect":{"values":[{"func":["caffee","foo"]}]}}, {"id":"reference.test.did: func: composite query","kind":"decode","bytes":"4449444c016a0171017d01030100010103caffee03666f6f","types":{"roots":[0],"table":[{"func":{"annots":["composite_query"],"args":["text"],"rets":["nat"]}}]},"expect":{"values":[{"func":["caffee","foo"]}]}}, +{"id":"reference.test.did: func: composite query (right)","kind":"parse","text":"(func \"w7x7r-cok77-xa\".foo)","types":{"roots":[0],"table":[{"func":{"annots":["composite_query"],"args":["text"],"rets":["nat"]}}]},"expect":{"values":[{"func":["caffee","foo"]}]}}, {"id":"reference.test.did: func: unknown annotation","kind":"decode","bytes":"4449444c016a0171017d0180010100010103caffee03666f6f","types":{"roots":[0],"table":[{"func":{"annots":["query"],"args":["text"],"rets":["nat"]}}]},"expect":"reject"}, {"id":"reference.test.did: func: not primitive","kind":"decode","bytes":"4449444c00016a010103caffee0161","types":{"roots":[0],"table":[{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":"reject"}, {"id":"reference.test.did: func: no tag","kind":"decode","bytes":"4449444c00016a0103caffee0161","types":{"roots":[0],"table":[{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":"reject"}, @@ -393,8 +592,10 @@ {"id":"reference.test.did: service { foo : (text) -> (nat) query } !<: service { foo : (text) -> (nat) }","kind":"decode","bytes":"4449444c026a0171017d0101690103666f6f0001010103caffee","types":{"roots":[0],"table":[{"service":[["foo",1]]},{"func":{"annots":[],"args":["text"],"rets":["nat"]}}]},"expect":"reject"}, {"id":"reference.test.did: service { foo : (text) -> (nat) } !<: service { foo : (text) -> (nat) query }","kind":"decode","bytes":"4449444c026a0171017d00690103666f6f0001010103caffee","types":{"roots":[0],"table":[{"service":[["foo",1]]},{"func":{"annots":["query"],"args":["text"],"rets":["nat"]}}]},"expect":"reject"}, {"id":"reference.test.did: service { foo : (text) -> (nat) } decodes at service {}","kind":"decode","bytes":"4449444c026a0171017d00690103666f6f0001010103caffee","types":{"roots":[0],"table":[{"service":[]}]},"expect":{"values":[{"service":"caffee"}]}}, +{"id":"reference.test.did: service { foo : (text) -> (nat) } decodes at service {} (right)","kind":"parse","text":"(service \"w7x7r-cok77-xa\")","types":{"roots":[0],"table":[{"service":[]}]},"expect":{"values":[{"service":"caffee"}]}}, {"id":"reference.test.did: func () -> () !<: func (text) -> (nat) }","kind":"decode","bytes":"4449444c016a0000000100010103caffee03666f6f","types":{"roots":[0],"table":[{"func":{"annots":[],"args":["text"],"rets":["nat"]}}]},"expect":"reject"}, {"id":"reference.test.did: func (text) -> (nat) decodes at func (text, opt text) -> () }","kind":"decode","bytes":"4449444c016a0171017d000100010103caffee03666f6f","types":{"roots":[0],"table":[{"func":{"annots":[],"args":["text",1],"rets":[]}},{"opt":"text"}]},"expect":{"values":[{"func":["caffee","foo"]}]}}, +{"id":"reference.test.did: func (text) -> (nat) decodes at func (text, opt text) -> () } (right)","kind":"parse","text":"(func \"w7x7r-cok77-xa\".foo)","types":{"roots":[0],"table":[{"func":{"annots":[],"args":["text",1],"rets":[]}},{"opt":"text"}]},"expect":{"values":[{"func":["caffee","foo"]}]}}, {"id":"spacebomb.test.did: vec null (extra argument)","kind":"decode","bytes":"4449444c016d7f01008094ebdc03","types":{"roots":[],"table":[]},"expect":"costly"}, {"id":"spacebomb.test.did: vec reserved (extra argument)","kind":"decode","bytes":"4449444c016d7001008094ebdc03","types":{"roots":[],"table":[]},"expect":"costly"}, {"id":"spacebomb.test.did: zero-sized record (extra argument)","kind":"decode","bytes":"4449444c046c03007f010102026c0100706c006d0001038094ebdc03","types":{"roots":[],"table":[]},"expect":"costly"}, @@ -413,61 +614,119 @@ {"id":"spacebomb.test.did: vec record {} (subtyping)","kind":"decode","bytes":"4449444c026d016c00010080ade204","types":{"roots":[0],"table":[{"opt":"nat"}]},"expect":"costly"}, {"id":"spacebomb.test.did: vec opt record with 2^20 null (subtyping)","kind":"decode","bytes":"4449444c176c02017f027f6c02010002006c02000101016c02000201026c02000301036c02000401046c02000501056c02000601066c02000701076c02000801086c02000901096c02000a010a6c02000b010b6c02000c010c6c02000d020d6c02000e010e6c02000f010f6c02001001106c02001101116c02001201126c02001301136e146d150116050101010101","types":{"roots":[0],"table":[{"vec":1},{"opt":2},{"record":[]}]},"expect":"costly"}, {"id":"subtypes.test.did: null <: null","kind":"decode","bytes":"4449444c016a00017f000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":["null"]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: null <: null (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":["null"]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: bool <: bool","kind":"decode","bytes":"4449444c016a00017e000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":["bool"]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: bool <: bool (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":["bool"]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: nat <: nat","kind":"decode","bytes":"4449444c016a00017d000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":["nat"]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: nat <: nat (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":["nat"]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: int <: int","kind":"decode","bytes":"4449444c016a00017c000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":["int"]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: int <: int (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":["int"]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: nat <: int","kind":"decode","bytes":"4449444c016a00017d000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":["int"]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: nat <: int (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":["int"]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: int () <: func () -> ()","kind":"decode","bytes":"4449444c026a000101006a0000000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: func () -> () <: func () -> () (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: func () -> () <: func () -> (opt empty)","kind":"decode","bytes":"4449444c026a000101006a0000000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[3]}},{"opt":"empty"}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: func () -> () <: func () -> (opt empty) (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[3]}},{"opt":"empty"}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: func () -> () <: func () -> (opt null)","kind":"decode","bytes":"4449444c026a000101006a0000000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[3]}},{"opt":"null"}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: func () -> () <: func () -> (opt null) (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[3]}},{"opt":"null"}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: func () -> () <: func () -> (reserved)","kind":"decode","bytes":"4449444c026a000101006a0000000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":["reserved"]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: func () -> () <: func () -> (reserved) (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":["reserved"]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: func () -> () (empty)","kind":"decode","bytes":"4449444c026a000101006a0000000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":["empty"]}}]},"expect":{"values":[null]}}, +{"id":"subtypes.test.did: func () -> () (empty) (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":["empty"]}}]},"expect":{"values":[null]}}, {"id":"subtypes.test.did: func () -> () (nat)","kind":"decode","bytes":"4449444c026a000101006a0000000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":["nat"]}}]},"expect":{"values":[null]}}, +{"id":"subtypes.test.did: func () -> () (nat) (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":["nat"]}}]},"expect":{"values":[null]}}, {"id":"subtypes.test.did: func () -> () <: func () -> (null)","kind":"decode","bytes":"4449444c026a000101006a0000000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":["null"]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: func () -> () <: func () -> (null) (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":["null"]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: func (opt empty) -> () <: func () -> ()","kind":"decode","bytes":"4449444c036a000101006a010200006e6f0100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: func (opt empty) -> () <: func () -> () (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: func (opt null) -> () <: func () -> ()","kind":"decode","bytes":"4449444c036a000101006a010200006e7f0100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: func (opt null) -> () <: func () -> () (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: func (reserved) -> () <: func () -> ()","kind":"decode","bytes":"4449444c026a000101006a017000000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: func (reserved) -> () <: func () -> () (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: func (empty) -> () ()","kind":"decode","bytes":"4449444c026a000101006a016f00000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[null]}}, +{"id":"subtypes.test.did: func (empty) -> () () (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[null]}}, {"id":"subtypes.test.did: func (nat) -> () ()","kind":"decode","bytes":"4449444c026a000101006a017d00000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[null]}}, +{"id":"subtypes.test.did: func (nat) -> () () (right)","kind":"parse","text":"(null)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[null]}}, {"id":"subtypes.test.did: func (null) -> () <: func () -> ()","kind":"decode","bytes":"4449444c026a000101006a017f00000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: func (null) -> () <: func () -> () (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"func":{"annots":[],"args":[],"rets":[]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: variant {} <: variant {}","kind":"decode","bytes":"4449444c026a000101006b000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"variant":[]}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: variant {} <: variant {} (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"variant":[]}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: variant {} <: variant {0 : nat}","kind":"decode","bytes":"4449444c026a000101006b000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"variant":[[0,"nat"]]}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: variant {} <: variant {0 : nat} (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"variant":[[0,"nat"]]}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: variant {0 : nat} <: variant {0 : nat}","kind":"decode","bytes":"4449444c026a000101006b01007d0100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"variant":[[0,"nat"]]}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: variant {0 : nat} <: variant {0 : nat} (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":[2]}},{"variant":[[0,"nat"]]}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: variant {0 : bool} ()} <: principal","kind":"decode","bytes":"4449444c036a000101006901016d026a0000000100010100016d","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":["principal"]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, +{"id":"subtypes.test.did: service {m:()->()} <: principal (right)","kind":"parse","text":"(opt func \"aaaaa-aa\".m)","types":{"roots":[0],"table":[{"opt":1},{"func":{"annots":[],"args":[],"rets":["principal"]}}]},"expect":{"values":[{"opt":{"func":["","m"]}}]}}, {"id":"subtypes.test.did: principal some (p == q) + | .ref _, .ref _ => none + | _, _ => some false + +/-- The same labels, each once, paired up as obligations; `none` if the label sets +differ. Well-formed tables do not repeat a label, so equal lengths and every label of +`fs` present in `gs` make the sets equal. -/ +def pairLabels [BEq α] (fs gs : List (α × Slot)) : Option (List (Slot × Slot)) := + if fs.length != gs.length then none + else fs.mapM fun (l, f) => ((gs.find? (·.1 == l)).map (·.2)).map fun g => (f, g) + +/-- The obligations one step in, on the composites a pair of references names. -/ +def equivExpand : Composite → Composite → Option (List (Slot × Slot)) + | .opt x, .opt y | .vec x, .vec y => some [(x, y)] + | .record fs, .record gs | .variant fs, .variant gs => pairLabels fs gs + | .func args rets ann, .func args' rets' ann' => + if annotsAgree ann ann' && args.length == args'.length && rets.length == rets'.length + then some (args.zip args' ++ rets.zip rets') + else none + | .service ms, .service ms' => pairLabels ms ms' + | .future op d, .future op' d' => if op == op' && d == d' then some [] else none + | _, _ => none + +/-- Discharge `goals`, each a type of `A` against one of `B`, given the reference pairs +assumed so far. -/ +def equivRun (A B : TypeTable) (seen : List (TypeRef × TypeRef)) : + List (Slot × Slot) → Bool + | [] => true + | (.ref i, .ref j) :: goals => + match _hx : A.lookup? i, _hy : B.lookup? j with + | some x, some y => + if _hs : seen.contains (i, j) then equivRun A B seen goals + else match equivExpand x y with + | some next => equivRun A B ((i, j) :: seen) (next ++ goals) + | none => false + -- Unreachable for roots that resolve in well-formed tables. + | _, _ => false + | (a, b) :: goals => + match equivLeaf a b with + | some true => equivRun A B seen goals + | _ => false +termination_by goals => (unseen A B seen, goals.length) +decreasing_by + all_goals first + | exact Prod.Lex.right _ (by simp) + | simp only [TypeTable.lookup?, RawTypeTable.lookup?] at _hx _hy + exact Prod.Lex.left _ _ + (countP_cons_lt (by simpa using _hs) + (mem_allPairs (Array.getElem?_eq_some_iff.1 _hx).1 + (Array.getElem?_eq_some_iff.1 _hy).1)) + +/-- Are `a`, a type in `A`, and `b`, a type in `B`, the same type? -/ +def decEquiv (A B : TypeTable) (a : A.Root) (b : B.Root) : Bool := + equivRun A B [] [(a.1, b.1)] + +end Candid diff --git a/lean/Candid/Float.lean b/lean/Candid/Float.lean new file mode 100644 index 00000000..9c138c13 --- /dev/null +++ b/lean/Candid/Float.lean @@ -0,0 +1,70 @@ +/- +Rounding a float literal to `float32` or `float64` (`lean/Spec/TextValues.md`, +Section 5.3). + +A literal's value is exact: a sign, and a magnitude `m * 10^e` or `m * 2^e`. It is +rounded once, directly to the target format, with IEEE 754's roundTiesToEven, which +overflows to infinity and keeps the literal's sign on a zero. All arithmetic is on +naturals, so nothing here is itself rounded. + +Exponents are unbounded in the syntax, so a magnitude far outside the format is decided +before anything is multiplied out: `1e999999999` must not compute `10^999999999`. The +bounds below are loose enough for `float64`, and so for `float32` too. Inside them the +numbers stay below about 2^2600 times the literal's digits. +-/ + +namespace Candid + +/-- An IEEE 754 binary interchange format. `p` counts the significand's bits including +the implicit one, and the bias is `emax`. -/ +structure FloatFormat where + width : Nat + p : Nat + emin : Int + emax : Int + +def binary32 : FloatFormat := { width := 32, p := 24, emin := -126, emax := 127 } +def binary64 : FloatFormat := { width := 64, p := 53, emin := -1022, emax := 1023 } + +/-- `n / d ≥ 2^k`, exactly. -/ +def atLeastPow2 (n d : Nat) (k : Int) : Bool := + if k ≥ 0 then n ≥ d * 2 ^ k.toNat else n * 2 ^ (-k).toNat ≥ d + +/-- The bits of the format's number nearest to `n / d`, ties to even, with sign bit +`sign`. `n` and `d` are positive. -/ +def roundRatio (f : FloatFormat) (sign n d : Nat) : Nat := + let inf := sign + (2 ^ (f.width - f.p) - 1) * 2 ^ (f.p - 1) + -- The binade: 2^e ≤ n/d < 2^(e+1). The difference of the bit lengths is within one. + let e0 : Int := (Nat.log2 n : Int) - Nat.log2 d + let e := if atLeastPow2 n d e0 then e0 else e0 - 1 + -- Below the normal range the step stays that of the smallest binade: subnormals. + let e := max e f.emin + -- The significand, `n/d` in units of the last place, rounded. + let k := e - (f.p - 1 : Nat) + let (num, den) := if k ≥ 0 then (n, d * 2 ^ k.toNat) else (n * 2 ^ (-k).toNat, d) + let q := num / den + let r := num % den + let q := if 2 * r > den || (2 * r == den && q % 2 == 1) then q + 1 else q + -- Rounding up can carry into the next binade. + let (q, e) := if q == 2 ^ f.p then (2 ^ (f.p - 1), e + 1) else (q, e) + if e > f.emax then inf + else if q < 2 ^ (f.p - 1) then sign + q -- subnormal, or zero + else sign + (e + f.emax).toNat * 2 ^ (f.p - 1) + (q - 2 ^ (f.p - 1)) + +/-- The bits of `(-1)^neg * m * base^exp` rounded to `f`, where `base` is 2 if `binary` +and 10 otherwise. -/ +def roundLiteral (f : FloatFormat) (neg : Bool) (m : Nat) (binary : Bool) (exp : Int) : Nat := + let sign := if neg then 2 ^ (f.width - 1) else 0 + let inf := sign + (2 ^ (f.width - f.p) - 1) * 2 ^ (f.p - 1) + if m == 0 then sign else + -- `m * base^exp` lies in [base^lo, base^(lo+1)). + let lo : Int := exp + (if binary then Nat.log2 m else (toString m).length - 1 : Nat) + let (tooBig, tooSmall) : Int × Int := if binary then (1100, -1200) else (400, -400) + if lo > tooBig then inf + else if lo < tooSmall then sign + else + let base := if binary then 2 else 10 + if exp ≥ 0 then roundRatio f sign (m * base ^ exp.toNat) 1 + else roundRatio f sign m (base ^ (-exp).toNat) + +end Candid diff --git a/lean/Candid/Principal.lean b/lean/Candid/Principal.lean new file mode 100644 index 00000000..477fab02 --- /dev/null +++ b/lean/Candid/Principal.lean @@ -0,0 +1,71 @@ +/- +The textual form of a principal (`lean/Spec/TextValues.md`, Appendix A). + +A principal is its bytes `p`. Its text is the base32 encoding (RFC 4648, lowercase, no +padding) of the CRC-32 of `p`, most significant byte first, followed by `p`, in groups of +five characters joined by `-`. Text is read by computing that form for the bytes it +encodes and comparing, ignoring ASCII case: any other spelling is rejected, so the +checksum, the base32 and the dashes are all checked by one comparison. + +The length of `p` is not bounded here. The Internet Computer bounds it at 29 bytes, and +so does `candid` 0.10; see `conformance/README.md`, "Findings". +-/ + +import Candid.Value + +namespace Candid + +/-- CRC-32 as ISO 3309 and zlib compute it: reflected, polynomial `0xEDB88320`, initial +value and final XOR all ones. Bitwise, eight steps per byte. -/ +def crc32 (bs : List UInt8) : UInt32 := + let step (c : UInt32) (_ : Nat) : UInt32 := + if c &&& 1 == 1 then (c >>> 1) ^^^ 0xEDB88320 else c >>> 1 + let byte (c : UInt32) (b : UInt8) : UInt32 := + (List.range 8).foldl step (c ^^^ b.toUInt32) + (bs.foldl byte 0xFFFFFFFF) ^^^ 0xFFFFFFFF + +def base32Alphabet : List Char := "abcdefghijklmnopqrstuvwxyz234567".toList + +/-- RFC 4648 base32, lowercase, without padding: five bits per character, the last +character padded with zero bits. -/ +def base32 (bs : List UInt8) : String := + let bits := bs.flatMap fun b => (List.range 8).reverse.map fun i => (b.toNat >>> i) % 2 + let groups := (List.range ((bits.length + 4) / 5)).map fun g => + ((bits.drop (5 * g)).take 5 ++ List.replicate 5 0).take 5 + String.ofList (groups.map fun g => base32Alphabet.getD (g.foldl (2 * · + ·) 0) 'a') + +/-- `s` in groups of five characters, joined by `-`. -/ +def dashed (s : String) : String := + let cs := s.toList + "-".intercalate ((List.range ((cs.length + 4) / 5)).map fun g => + String.ofList ((cs.drop (5 * g)).take 5)) + +def crcBytes (p : List UInt8) : List UInt8 := + let c := (crc32 p).toNat + [c >>> 24, c >>> 16, c >>> 8, c].map (·.toUInt8) + +/-- The canonical text of a principal. -/ +def principalText (p : PrincipalBytes) : String := dashed (base32 (crcBytes p ++ p)) + +/-- The value of a base32 character, either case. -/ +def base32Value? (c : Char) : Option Nat := + base32Alphabet.idxOf? c.toLower + +/-- The bytes that base32 text encodes, dropping the bits of a partial last byte. +Whether those bits were zero, and whether the length was one an encoder produces, is +left to the comparison with the canonical text. -/ +def unbase32? (s : String) : Option (List UInt8) := do + let vs ← s.toList.mapM base32Value? + let bits := vs.flatMap fun v => (List.range 5).reverse.map fun i => (v >>> i) % 2 + return (List.range (bits.length / 8)).map fun i => + ((bits.drop (8 * i)).take 8).foldl (2 * · + ·) 0 |>.toUInt8 + +/-- The principal that `s` is the text of, if any. -/ +def principalOfText? (s : String) : Option PrincipalBytes := do + let all ← unbase32? (String.ofList (s.toList.filter (· != '-'))) + guard (all.length ≥ 4) + let p := all.drop 4 + guard (principalText p == String.ofList (s.toList.map Char.toLower)) + return p + +end Candid diff --git a/lean/Candid/Text/Elab.lean b/lean/Candid/Text/Elab.lean new file mode 100644 index 00000000..aac14980 --- /dev/null +++ b/lean/Candid/Text/Elab.lean @@ -0,0 +1,186 @@ +/- +Elaboration: what an expression means at a type (`lean/Spec/TextValues.md`, Section 5). + +`elaborate T e t` is the rule `e ⇐ t ⇝ v` for a type `t` in table `T`: `some v`, or `none` +when the expression is rejected at that type. It recurses on the expression, so it +terminates whatever the type graph: a recursive type is unfolded only as far as the +expression goes. + +**Annotations** carry a type of their own, written as a nested `TypeExpr`. It is +flattened into a table of its own (`TypeExpr.toTable`), checked for well-formedness, and +compared with the expected type by `decEquiv`. The expression inside is then elaborated +in the annotation's table, which is why `elaborate` takes the table as an argument rather +than a parameter. Names in an annotation resolve in an environment of type +definitions; the model's is empty, so a name is rejected. + +**Well-annotated** expressions (Section 5.1) are checked by `wellAnnotated`, separately: +every annotation inside an expression must hold, even one inside a field or argument +that elaboration ignores, or at `reserved`. +-/ + +import Candid.Text.Parse +import Candid.Equiv +import Candid.Coerce +import Candid.Float + +namespace Candid.Text + +abbrev FlatM := ExceptT String (StateM (Array Composite)) + +def pushEntry (c : Composite) : FlatM Slot := do + let entries ← get + set (entries.push c) + return .ref entries.size + +mutual + +def flatten : TypeExpr → FlatM Slot + | .prim p => return .prim p + | .name n => throw s!"unbound type name {n}" + | .opt t => do pushEntry (.opt (← flatten t)) + | .vec t => do pushEntry (.vec (← flatten t)) + | .record fs => do pushEntry (.record (← flattenFields fs)) + | .variant fs => do pushEntry (.variant (← flattenFields fs)) + | .func args rets ann => do pushEntry (.func (← flattenList args) (← flattenList rets) ann) + | .service ms => do pushEntry (.service (← flattenMethods ms)) + +def flattenList : List TypeExpr → FlatM (List Slot) + | [] => return [] + | t :: ts => return (← flatten t) :: (← flattenList ts) + +def flattenFields : List (FieldId × TypeExpr) → FlatM (List (FieldId × Slot)) + | [] => return [] + | (id, t) :: fs => return (id, ← flatten t) :: (← flattenFields fs) + +def flattenMethods : List (String × TypeExpr) → FlatM (List (String × Slot)) + | [] => return [] + | (m, t) :: ms => return (m, ← flatten t) :: (← flattenMethods ms) + +end + +/-- The table an annotation's type is flattened into, and its root. `none` if a name +does not resolve or the type is malformed: a repeated field id, or a `oneway` function +with results. -/ +def TypeExpr.toTable? (t : TypeExpr) : Option ((T : TypeTable) × T.Root) := + match (flatten t).run.run #[] with + | (.ok root, entries) => do + let T ← ({ entries } : RawTypeTable).check + let r ← T.root? root + return ⟨T, r⟩ + | (.error _, _) => none + +/-- An integer literal at a primitive type: in range, or nothing. -/ +def intAt (z : Int) : Prim → Option Value + | .nat => if 0 ≤ z then some (.nat z.toNat) else none + | .int => some (.int z) + | .nat8 => if 0 ≤ z && z < 2 ^ 8 then some (.nat8 z.toNat.toUInt8) else none + | .nat16 => if 0 ≤ z && z < 2 ^ 16 then some (.nat16 z.toNat.toUInt16) else none + | .nat32 => if 0 ≤ z && z < 2 ^ 32 then some (.nat32 z.toNat.toUInt32) else none + | .nat64 => if 0 ≤ z && z < 2 ^ 64 then some (.nat64 z.toNat.toUInt64) else none + | .int8 => if -2 ^ 7 ≤ z && z < 2 ^ 7 then some (.int8 (Int8.ofInt z)) else none + | .int16 => if -2 ^ 15 ≤ z && z < 2 ^ 15 then some (.int16 (Int16.ofInt z)) else none + | .int32 => if -2 ^ 31 ≤ z && z < 2 ^ 31 then some (.int32 (Int32.ofInt z)) else none + | .int64 => if -2 ^ 63 ≤ z && z < 2 ^ 63 then some (.int64 (Int64.ofInt z)) else none + | _ => none + +/-- The fields of a record type the expression does not have: each is `null` if its +type accepts it, and the record is rejected otherwise. -/ +def fillFields (T : TypeTable) (given : List FieldId) (gs : List (FieldId × Slot)) : + Option (List (FieldId × Value)) := + (gs.filter fun (id, _) => !given.contains id).mapM fun (id, t) => + if T.acceptsNull t then some (id, .null) else none + +mutual + +/-- `e ⇐ t ⇝ v`, for `t` in `T`. -/ +def elaborate : TypeTable → Expr → Slot → Option Value + | T, .ann e te, t => do + let ⟨S, s⟩ ← te.toTable? + let r ← T.root? t + guard (decEquiv S T s r) + elaborate S e s.1 + | _, _, .prim .reserved => some .null + | _, .int z, .prim p => intAt z p + | _, .float neg m b x, .prim .float32 => + some (.float32 (roundLiteral binary32 neg m b x).toUInt32) + | _, .float neg m b x, .prim .float64 => + some (.float64 (roundLiteral binary64 neg m b x).toUInt64) + | _, .text bs, .prim .text => (String.fromUTF8? ⟨bs.toArray⟩).map .text + | _, .bool b, .prim .bool => some (.bool b) + | _, .null, .prim .null => some .null + | _, .principal p, .prim .principal => some (.principal p) + | T, e, .ref r => + match e, T.lookup? r with + | .null, some (.opt _) => some .null + | .opt e, some (.opt t) => (elaborate T e t).map .opt + | .vec es, some (.vec t) => (elaborateList T es t).map .vec + | .blob bs, some (.vec (.prim .nat8)) => some (.vec (bs.map .nat8)) + | .record fs, some (.record gs) => do + let given ← elaborateFields T fs gs + let filled ← fillFields T (fs.map (·.1)) gs + return .record (sortFields (given ++ filled)) + | .variant id e, some (.variant gs) => do + let t ← fieldAt gs id + return .variant id (← elaborate T e t) + | .service p, some (.service _) => some (.service p) + | .func p m, some (.func _ _ _) => some (.func p m) + | _, _ => none + | _, _, _ => none + +def elaborateList : TypeTable → List Expr → Slot → Option (List Value) + | _, [], _ => some [] + | T, e :: es, t => return (← elaborate T e t) :: (← elaborateList T es t) + +/-- The expression's fields that the type has, elaborated; the others are ignored. -/ +def elaborateFields : TypeTable → List (FieldId × Expr) → List (FieldId × Slot) → + Option (List (FieldId × Value)) + | _, [], _ => some [] + | T, (id, e) :: fs, gs => + match fieldAt gs id with + | some t => return (id, ← elaborate T e t) :: (← elaborateFields T fs gs) + | none => elaborateFields T fs gs + +end + +mutual + +/-- Every annotation in the expression holds (Section 5.1). -/ +def wellAnnotated : Expr → Bool + | .ann e te => + (match te.toTable? with + | some ⟨S, s⟩ => (elaborate S e s.1).isSome + | none => false) && wellAnnotated e + | .opt e | .variant _ e => wellAnnotated e + | .vec es => wellAnnotatedList es + | .record fs => wellAnnotatedFields fs + | .int _ | .float .. | .text _ | .bool _ | .null | .blob _ + | .principal _ | .service _ | .func _ _ => true + +def wellAnnotatedList : List Expr → Bool + | [] => true + | e :: es => wellAnnotated e && wellAnnotatedList es + +def wellAnnotatedFields : List (FieldId × Expr) → Bool + | [] => true + | (_, e) :: fs => wellAnnotated e && wellAnnotatedFields fs + +end + +/-- Arguments at their types, position by position (Section 5.9). Arguments beyond the +types are ignored, and types beyond the arguments must accept `null`. -/ +def elaborateArgsFrom (T : TypeTable) : List Expr → List Slot → Option (List Value) + | _, [] => some [] + | e :: es, t :: ts => return (← elaborate T e t) :: (← elaborateArgsFrom T es ts) + | [], t :: ts => if T.acceptsNull t then (.null :: ·) <$> elaborateArgsFrom T [] ts else none + +def elaborateArgs (T : TypeTable) (es : List Expr) (ts : List Slot) : Option (List Value) := + if es.all wellAnnotated then elaborateArgsFrom T es ts else none + +/-- Text read as an argument sequence at `ts`, types in `T`: the values, or `none` when +it is rejected, whether by the parser or by elaboration. -/ +def readArgs (src : String) (T : TypeTable) (ts : List Slot) : Option (List Value) := + match parseArgs src with + | .ok es => elaborateArgs T es ts + | .error _ => none + +end Candid.Text diff --git a/lean/Candid/Text/Lex.lean b/lean/Candid/Text/Lex.lean new file mode 100644 index 00000000..138bf232 --- /dev/null +++ b/lean/Candid/Text/Lex.lean @@ -0,0 +1,230 @@ +/- +The lexical structure of textual values (`lean/Spec/TextValues.md`, Section 3). + +Text is divided into tokens from left to right, longest match first. The number and +text scanners below each take the longest prefix the grammar allows and leave the rest, +so a spelling the grammar does not allow, like `1_`, becomes two tokens (`1` and the +identifier `_`), which the parser then rejects. Errors here are only for text that is +no token at all: an unterminated literal or comment, a bad escape, a stray character. + +A token keeps what the parser and elaboration need and no more. An integer literal is +its value and whether it was signed, which only a field label asks. A float literal is +exact: a sign and `m * base^exp`, rounded only when elaboration knows the format +(`Candid/Float.lean`). A text literal is bytes, since `\ff` need not be UTF-8: whether +that is acceptable depends on where it is used. +-/ + +namespace Candid.Text + +inductive Token where + | keyword (k : String) + | ident (s : String) + /-- An integer literal: its value, and whether it was written with a sign. -/ + | int (z : Int) (signed : Bool) + /-- A float literal: `(-1)^neg * m * base^exp`, where `base` is 2 if `binary`, else 10. -/ + | float (neg : Bool) (m : Nat) (binary : Bool) (exp : Int) + | text (bytes : List UInt8) + | punct (p : String) + deriving Repr, BEq, Inhabited + +def Token.describe : Token → String + | .keyword k => s!"keyword {k}" + | .ident s => s!"identifier {s}" + | .int z _ => s!"number {z}" + | .float .. => "a float literal" + | .text _ => "a text literal" + | .punct p => s!"`{p}`" + +/-- Reserved for the whole of Candid's syntax, including the parts values do not use. -/ +def keywords : List String := + [ "blob", "composite_query", "false", "func", "import", "null", "oneway", "opt" + , "principal", "query", "record", "service", "true", "type", "variant", "vec" ] + +def isDigit (c : Char) : Bool := '0' ≤ c && c ≤ '9' + +/-- A hexadecimal digit's value, either case. -/ +def hexVal? (c : Char) : Option Nat := + if isDigit c then some (c.toNat - '0'.toNat) + else if 'a' ≤ c && c ≤ 'f' then some (c.toNat - 'a'.toNat + 10) + else if 'A' ≤ c && c ≤ 'F' then some (c.toNat - 'A'.toNat + 10) + else none + +def isHex (c : Char) : Bool := (hexVal? c).isSome + +def isIdStart (c : Char) : Bool := c.isAlpha || c == '_' +def isIdChar (c : Char) : Bool := c.isAlphanum || c == '_' + +/-! ## Numbers -/ + +/-- The digits after a first one: digits, each optionally preceded by one underscore. +An underscore not followed by a digit is not taken. Returns the digits without +underscores, and the rest. -/ +def digitsAfter (p : Char → Bool) : List Char → List Char × List Char + | c :: cs => + if p c then + let (ds, r) := digitsAfter p cs + (c :: ds, r) + else if c == '_' then + match cs with + | d :: cs' => + if p d then + let (ds, r) := digitsAfter p cs' + (d :: ds, r) + else ([], c :: d :: cs') + | [] => ([], [c]) + else ([], c :: cs) + | [] => ([], []) + +/-- `decimal` or `hex`: at least one digit. -/ +def digits (p : Char → Bool) : List Char → Option (List Char × List Char) + | c :: cs => + if p c then + let (ds, r) := digitsAfter p cs + some (c :: ds, r) + else none + | [] => none + +def digitsValue (base : Nat) (ds : List Char) : Nat := + ds.foldl (fun n c => n * base + (hexVal? c).getD 0) 0 + +/-- `[sign] decimal`: an exponent, always decimal. -/ +def exponent (cs : List Char) : Option (Int × List Char) := + let (neg, rest) := match cs with + | '-' :: r => (true, r) + | '+' :: r => (false, r) + | r => (false, r) + (digits isDigit rest).map fun (ds, r) => + let v : Int := digitsValue 10 ds + (if neg then -v else v, r) + +/-- A number, after its sign. `cs` starts with a digit unless there was a sign, and a +sign without a number is an error: neither `+` nor `-` is a token on its own. -/ +def lexNumber (neg signed : Bool) (cs : List Char) : Except String (Token × List Char) := do + let (binary, start) := match cs with + | '0' :: 'x' :: c :: _ => if isHex c then (true, cs.drop 2) else (false, cs) + | _ => (false, cs) + let p := if binary then isHex else isDigit + let some (whole, r) := digits p start + | throw "a sign must be immediately followed by a number" + let (frac, isFloat, r) := match r with + | '.' :: r' => match digits p r' with + | some (fs, r'') => (fs, true, r'') + | none => ([], true, r') + | _ => ([], false, r) + let isExp (c : Char) := if binary then c == 'p' || c == 'P' else c == 'e' || c == 'E' + let (exp, isFloat, r) := match r with + | c :: r' => if isExp c then + match exponent r' with + | some (x, r'') => (x, true, r'') + | none => (0, isFloat, r) + else (0, isFloat, r) + | [] => (0, isFloat, r) + let m := digitsValue (if binary then 16 else 10) (whole ++ frac) + if isFloat then + let scale : Int := (if binary then 4 else 1) * frac.length + return (.float neg m binary (exp - scale), r) + else + return (.int (if neg then -(m : Int) else m) signed, r) + +/-! ## Text literals + +A state machine over characters, so that it recurses on the input alone. -/ + +inductive TextState where + | plain + /-- After `\`. -/ + | escape + /-- After `\` and one hexadecimal digit. -/ + | byte (hi : Nat) + /-- After `\u`. -/ + | brace + /-- Inside `\u{...}`: the value so far, whether a digit has been read, and whether + the last character was an underscore. -/ + | codepoint (n : Nat) (any : Bool) (underscore : Bool) + +def scalar? (n : Nat) : Option Char := + if n ≤ 0x10FFFF && !(0xD800 ≤ n && n ≤ 0xDFFF) then some (Char.ofNat n) else none + +/-- The bytes of a text literal, after its opening quote, and the rest. -/ +def lexText (st : TextState) (acc : Array UInt8) : List Char → Except String (List UInt8 × List Char) + | [] => throw "unterminated text literal" + | c :: cs => match st with + | .plain => + if c == '"' then return (acc.toList, cs) + else if c == '\\' then lexText .escape acc cs + else if c.toNat < 0x20 || c.toNat == 0x7F then + throw s!"raw control character U+{String.ofList (Nat.toDigits 16 c.toNat)} in a text literal" + else lexText .plain (acc ++ (String.utf8EncodeChar c).toArray) cs + | .escape => match c with + | 'n' => lexText .plain (acc.push 0x0A) cs + | 'r' => lexText .plain (acc.push 0x0D) cs + | 't' => lexText .plain (acc.push 0x09) cs + | '\\' => lexText .plain (acc.push 0x5C) cs + | '"' => lexText .plain (acc.push 0x22) cs + | '\'' => lexText .plain (acc.push 0x27) cs + | 'u' => lexText .brace acc cs + | _ => match hexVal? c with + | some h => lexText (.byte h) acc cs + | none => throw s!"unknown escape \\{c}" + | .byte hi => match hexVal? c with + | some lo => lexText .plain (acc.push (hi * 16 + lo).toUInt8) cs + | none => throw "a byte escape needs two hexadecimal digits" + | .brace => + if c == '{' then lexText (.codepoint 0 false false) acc cs + else throw "expected { after \\u" + | .codepoint n any underscore => + if c == '}' && any && !underscore then + match scalar? n with + | some ch => lexText .plain (acc ++ (String.utf8EncodeChar ch).toArray) cs + | none => throw "\\u{...} is not a Unicode scalar value" + else if c == '_' && any && !underscore then lexText (.codepoint n any true) acc cs + else match hexVal? c with + | some d => lexText (.codepoint (n * 16 + d) true false) acc cs + | none => throw "malformed \\u{...} escape" + +/-! ## Comments -/ + +/-- Skip to the end of `depth` open block comments. `none` if the input ends first. -/ +def skipBlock : Nat → List Char → Option (List Char) + | 0, cs => some cs + | n + 1, '*' :: '/' :: cs => skipBlock n cs + | n + 1, '/' :: '*' :: cs => skipBlock (n + 2) cs + | n + 1, _ :: cs => skipBlock (n + 1) cs + | _ + 1, [] => none + +/-! ## Tokens -/ + +/-- The tokens of `cs`. Every step consumes at least one character, so `fuel` at the +input's length plus one is never exhausted. -/ +def lexAux : Nat → List Char → Array Token → Except String (Array Token) + | _, [], acc => return acc + | 0, _, _ => throw "unreachable: the lexer ran out of fuel" + | fuel + 1, c :: cs, acc => do + if c == ' ' || c == '\t' || c == '\n' || c == '\r' then return ← lexAux fuel cs acc + match c, cs with + | '/', '/' :: rest => lexAux fuel (rest.dropWhile (· != '\n')) acc + | '/', '*' :: rest => match skipBlock 1 rest with + | some r => lexAux fuel r acc + | none => throw "unterminated block comment" + | '"', rest => + let (bs, r) ← lexText .plain #[] rest + lexAux fuel r (acc.push (.text bs)) + | '-', '>' :: rest => lexAux fuel rest (acc.push (.punct "->")) + | '-', rest | '+', rest => + let (t, r) ← lexNumber (c == '-') true rest + lexAux fuel r (acc.push t) + | _, _ => + if isDigit c then + let (t, r) ← lexNumber false false (c :: cs) + lexAux fuel r (acc.push t) + else if isIdStart c then + let (name, r) := (c :: cs).span isIdChar + let s := String.ofList name + lexAux fuel r (acc.push (if keywords.contains s then .keyword s else .ident s)) + else if "(){};,.:=".toList.contains c then + lexAux fuel cs (acc.push (.punct (String.singleton c))) + else throw s!"unexpected character {repr c}" + +def lex (s : String) : Except String (Array Token) := lexAux (s.length + 1) s.toList #[] + +end Candid.Text diff --git a/lean/Candid/Text/Parse.lean b/lean/Candid/Text/Parse.lean new file mode 100644 index 00000000..9588d68b --- /dev/null +++ b/lean/Candid/Text/Parse.lean @@ -0,0 +1,325 @@ +/- +The syntax of textual values (`lean/Spec/TextValues.md`, Section 4), and of the types +that annotations contain. + +Parsing produces the abstract syntax of Section 4.2: `Expr`, with every field id +explicit. Labels are hashed, positional fields numbered, and a record that repeats an id +is rejected here, since that does not depend on the type. A type in an annotation is +parsed into `TypeExpr`, which is nested, as written; `Elab.lean` flattens it into a type +table. + +The parser is recursive descent over the tokens. Each function takes `fuel`, which +bounds the depth of the calls. Along any chain of calls, every second call at most +consumes a token first, so twice the number of tokens, plus a little, is never exhausted. +-/ + +import Candid.Text.Lex +import Candid.Principal + +namespace Candid.Text + +/-- A type as written in an annotation. Names are kept: resolving them is the +environment's business, and the model's environment is empty. -/ +inductive TypeExpr where + | prim (p : Prim) + | name (n : String) + | opt (t : TypeExpr) + | vec (t : TypeExpr) + | record (fs : List (FieldId × TypeExpr)) + | variant (fs : List (FieldId × TypeExpr)) + | func (args rets : List TypeExpr) (annots : List FuncAnnot) + | service (methods : List (String × TypeExpr)) + deriving Repr, Inhabited + +/-- An expression: the abstract syntax of Section 4.2. -/ +inductive Expr where + | int (z : Int) + | float (neg : Bool) (m : Nat) (binary : Bool) (exp : Int) + | text (bytes : List UInt8) + | bool (b : Bool) + | null + | opt (e : Expr) + | vec (es : List Expr) + | blob (bytes : List UInt8) + /-- Field ids distinct, in the order written. -/ + | record (fs : List (FieldId × Expr)) + | variant (id : FieldId) (e : Expr) + | principal (p : PrincipalBytes) + | service (p : PrincipalBytes) + | func (p : PrincipalBytes) (method : String) + | ann (e : Expr) (t : TypeExpr) + deriving Repr, Inhabited + +/-- Primitive types that are spelled as identifiers. `null` and `principal` are keywords, +and are parsed as such. -/ +def primOfName? : String → Option Prim + | "bool" => some .bool | "nat" => some .nat | "int" => some .int + | "nat8" => some .nat8 | "nat16" => some .nat16 | "nat32" => some .nat32 + | "nat64" => some .nat64 | "int8" => some .int8 | "int16" => some .int16 + | "int32" => some .int32 | "int64" => some .int64 + | "float32" => some .float32 | "float64" => some .float64 + | "text" => some .text | "reserved" => some .reserved | "empty" => some .empty + | _ => none + +def utf8? (bs : List UInt8) : Except String String := + match String.fromUTF8? ⟨bs.toArray⟩ with + | some s => .ok s + | none => .error "a name must be UTF-8" + +/-- A `label`, if the token is one: an integer literal without a sign below 2^32, or a +name, hashed. -/ +def labelOf? : Token → Option (Except String FieldId) + | .int z false => some (if z < 2 ^ 32 then .ok z.toNat.toUInt32 else .error "field id out of range") + | .ident s => some (.ok (hashFieldName s)) + | .text bs => some ((utf8? bs).map hashFieldName) + | _ => none + +/-- A `name`, if the token is one. -/ +def nameOf? : Token → Option (Except String String) + | .ident s => some (.ok s) + | .text bs => some (utf8? bs) + | _ => none + +/-- Number positional fields, given the id of the field before, if any. -/ +def numberFrom (prev : Option FieldId) : + List (Option FieldId × α) → Except String (List (FieldId × α)) + | [] => return [] + | (some id, x) :: rest => return (id, x) :: (← numberFrom (some id) rest) + | (none, x) :: rest => do + let id ← match prev with + | none => pure 0 + | some p => + if p == 0xFFFFFFFF then throw "a positional field after id 2^32 - 1" else pure (p + 1) + return (id, x) :: (← numberFrom (some id) rest) + +/-- Give positional fields their ids, then reject a repeated one. A positional field +takes 0 first, and one more than the field before it otherwise. -/ +def numberFields (fs : List (Option FieldId × α)) : Except String (List (FieldId × α)) := do + let numbered ← numberFrom none fs + unless noDups (numbered.map (·.1)) do throw "a field id is repeated" + return numbered + +abbrev P := StateT Nat (Except String) + +section +variable (ts : Array Token) + +def peekAt (k : Nat) : P (Option Token) := do return ts[(← get) + k]? + +def next : P Token := do + match ts[← get]? with + | some t => modify (· + 1); return t + | none => throw "unexpected end of input" + +def isPunct (p : String) : P Bool := do return (← peekAt ts 0) == some (.punct p) + +/-- Take the punctuation `p` if it is next. -/ +def accept (p : String) : P Bool := do + if ← isPunct ts p then modify (· + 1); return true else return false + +def expect (p : String) : P Unit := do + let t ← next ts + unless t == .punct p do throw s!"expected `{p}`, got {t.describe}" + +def liftE (x : Except String α) : P α := StateT.lift x + +def tooDeep : String := "unreachable: the parser ran out of fuel" + +/-- After the opening punctuation of a list: items separated by `sep`, optionally +ending with one, up to `close`. -/ +def sepBy (item : P α) (sep close : String) : Nat → P (List α) + | 0 => throw tooDeep + | n + 1 => do + if ← accept ts close then return [] + let x ← item + if ← accept ts sep then return x :: (← sepBy item sep close n) + expect ts close + return [x] + +/-- Function annotations, as many as follow. -/ +def annots : Nat → P (List FuncAnnot) + | 0 => throw tooDeep + | n + 1 => do + let a? := match ← peekAt ts 0 with + | some (.keyword "query") => some FuncAnnot.query + | some (.keyword "oneway") => some .oneway + | some (.keyword "composite_query") => some .compositeQuery + | _ => none + match a? with + | some a => modify (· + 1); return a :: (← annots n) + | none => return [] + +mutual + +/-- ``. -/ +def type : Nat → P TypeExpr + | 0 => throw tooDeep + | n + 1 => do + match ← next ts with + | .ident s => return (match primOfName? s with | some p => .prim p | none => .name s) + | .keyword "null" => return .prim .null + | .keyword "principal" => return .prim .principal + | .keyword "opt" => return .opt (← type n) + | .keyword "vec" => return .vec (← type n) + | .keyword "blob" => return .vec (.prim .nat8) + | .keyword "record" => + expect ts "{" + return .record (← liftE (numberFields (← sepBy ts (fieldType n false) ";" "}" (2 * ts.size)))) + | .keyword "variant" => + expect ts "{" + return .variant (← liftE (numberFields (← sepBy ts (fieldType n true) ";" "}" (2 * ts.size)))) + | .keyword "func" => funcType n + | .keyword "service" => + expect ts "{" + return .service (← sepBy ts (method n) ";" "}" (2 * ts.size)) + | t => throw s!"expected a type, got {t.describe}" + +/-- ``. In a variant, a label alone has type `null`; in a record, a type +alone is positional. -/ +def fieldType : Nat → Bool → P (Option FieldId × TypeExpr) + | 0, _ => throw tooDeep + | n + 1, variant => do + let t ← peekAt ts 0 + let labelled := (← peekAt ts 1) == some (.punct ":") + match t.bind labelOf? with + | some l => + if labelled then + modify (· + 2) + return (some (← liftE l), ← type n) + if variant then + modify (· + 1) + return (some (← liftE l), .prim .null) + return (none, ← type n) + | none => + if variant then throw "expected a variant field" + return (none, ← type n) + +/-- ``: `(args) -> (results) annotations`. -/ +def funcType : Nat → P TypeExpr + | 0 => throw tooDeep + | n + 1 => do + expect ts "(" + let args ← sepBy ts (argType n) "," ")" (2 * ts.size) + expect ts "->" + expect ts "(" + let rets ← sepBy ts (argType n) "," ")" (2 * ts.size) + let names (xs : List (Option String × TypeExpr)) := xs.filterMap (·.1) + unless noDups (names args) && noDups (names rets) do + throw "a parameter or result name is repeated" + return .func (args.map (·.2)) (rets.map (·.2)) (← annots ts (ts.size + 1)) + +/-- ``: a type, optionally named. A name has no meaning, but must be UTF-8, and +must not repeat within its list (`spec/Candid.md`, "Named Parameters and Results"). -/ +def argType : Nat → P (Option String × TypeExpr) + | 0 => throw tooDeep + | n + 1 => do + if (← peekAt ts 1) == some (.punct ":") then + if let some name := (← peekAt ts 0).bind nameOf? then + let name ← liftE name + modify (· + 2) + return (some name, ← type n) + return (none, ← type n) + +/-- ``: a name, and a function type or the name of one. -/ +def method : Nat → P (String × TypeExpr) + | 0 => throw tooDeep + | n + 1 => do + let some name := (← next ts) |> nameOf? | throw "expected a method name" + let name ← liftE name + expect ts ":" + if ← isPunct ts "(" then return (name, ← funcType n) + match ← next ts with + | .ident s => return (name, .name s) + | t => throw s!"expected a function type, got {t.describe}" + +end + +/-- The bytes of a text literal, which must be next. -/ +def textLit : P (List UInt8) := do + match ← next ts with + | .text bs => return bs + | t => throw s!"expected a text literal, got {t.describe}" + +def principalLit : P PrincipalBytes := do + let bs ← textLit ts + let s ← liftE (utf8? bs) + match principalOfText? s with + | some p => return p + | none => throw s!"not the text of a principal: {repr s}" + +mutual + +/-- `annotated`: an expression, optionally followed by `: `. -/ +def annotated : Nat → P Expr + | 0 => throw tooDeep + | n + 1 => do + let e ← expr n + if ← accept ts ":" then return .ann e (← type ts (2 * ts.size + 2)) + return e + +/-- `expr`. -/ +def expr : Nat → P Expr + | 0 => throw tooDeep + | n + 1 => do + match ← next ts with + | .punct "(" => + let e ← annotated n + expect ts ")" + return e + | .int z _ => return .int z + | .float neg m b x => return .float neg m b x + | .text bs => return .text bs + | .keyword "true" => return .bool true + | .keyword "false" => return .bool false + | .keyword "null" => return .null + | .keyword "opt" => return .opt (← expr n) + | .keyword "vec" => + expect ts "{" + return .vec (← sepBy ts (annotated n) ";" "}" (2 * ts.size)) + | .keyword "blob" => return .blob (← textLit ts) + | .keyword "record" => + expect ts "{" + return .record (← liftE (numberFields (← sepBy ts (field n) ";" "}" (2 * ts.size)))) + | .keyword "variant" => + expect ts "{" + let some l := (← next ts) |> labelOf? | throw "expected a variant field" + let id ← liftE l + let e ← if ← accept ts "=" then annotated n else pure .null + expect ts "}" + return .variant id e + | .keyword "principal" => return .principal (← principalLit ts) + | .keyword "service" => return .service (← principalLit ts) + | .keyword "func" => + let p ← principalLit ts + expect ts "." + let some m := (← next ts) |> nameOf? | throw "expected a method name" + return .func p (← liftE m) + | t => throw s!"expected a value, got {t.describe}" + +/-- A record field: `label = annotated`, or `annotated` alone, positional. -/ +def field : Nat → P (Option FieldId × Expr) + | 0 => throw tooDeep + | n + 1 => do + if (← peekAt ts 1) == some (.punct "=") then + if let some l := (← peekAt ts 0).bind labelOf? then + modify (· + 2) + return (some (← liftE l), ← annotated n) + return (none, ← annotated n) + +end + +/-- `args`, and nothing after it. -/ +def args : P (List Expr) := do + expect ts "(" + let es ← sepBy ts (annotated ts (2 * ts.size + 2)) "," ")" (2 * ts.size) + if (← get) < ts.size then throw "unexpected input after the arguments" + return es + +end + +/-- Parse an argument sequence. -/ +def parseArgs (src : String) : Except String (List Expr) := do + let ts ← lex src + (args ts).run' 0 + +end Candid.Text diff --git a/lean/Main.lean b/lean/Main.lean index 14e5245a..45aba036 100644 --- a/lean/Main.lean +++ b/lean/Main.lean @@ -23,7 +23,7 @@ open Oracle /-- The vector files, by name. -/ def files : List (String × List Vector) := - [ ("handwritten.json", handwritten) + [ ("handwritten.json", handwritten ++ textChecks) , ("subtype-primitives.json", Generate.primitives) , ("subtype-small.json", Generate.smallTypes) , ("coerce-small.json", Generate.coercions) @@ -74,18 +74,16 @@ def selfTest : IO UInt32 := do if let some d := v.assertion.withinDisagreement then IO.println s!"FAIL {v.id}: {d}" problems := problems + 1 - for p in TestDid.violations do + for p in TestDid.violations ++ TestDid.parserViolations do IO.println s!"FAIL test/{p}" problems := problems + 1 - for (why, n) in TestDid.deferred do - IO.println s!"wait {n} assertions of test/ on {why}" for v in all do unless roundTrips v do IO.println s!"FAIL does not survive JSON: {v.id}" problems := problems + 1 -- Hand-written vectors are listed one by one: their names are the documentation. -- Generated ones only fail if the JSON or the generator is broken, so a count will do. - problems := problems + (← checkAll handwritten true) + problems := problems + (← checkAll (handwritten ++ textChecks) true) for (name, vs) in files.drop 1 do problems := problems + (← checkAll vs false) IO.println s!"ok {vs.length} vectors in {name}" diff --git a/lean/Oracle.lean b/lean/Oracle.lean index 54f70e4f..7585272f 100644 --- a/lean/Oracle.lean +++ b/lean/Oracle.lean @@ -7,5 +7,6 @@ Conformance vectors for the Candid model: what they are and how they are written import Oracle.Vector import Oracle.Handwritten +import Oracle.HandwrittenText import Oracle.Generate import Oracle.TestDid diff --git a/lean/Oracle/HandwrittenText.lean b/lean/Oracle/HandwrittenText.lean new file mode 100644 index 00000000..1aed2e64 --- /dev/null +++ b/lean/Oracle/HandwrittenText.lean @@ -0,0 +1,193 @@ +/- +Hand-written `parse` vectors: text, the types it is read at, and what it reads as, +following `lean/Spec/TextValues.md`. As in `Oracle/Handwritten.lean`, each expectation +is written down from the document rather than computed by the model, and the ids say +what each case shows. Section numbers in the ids are the document's. + +Where `candid` 0.10 answers differently, the document's Appendix B.2 says why; the +runner lists those vectors as known divergences. +-/ + +import Oracle.Handwritten + +namespace Oracle + +open Candid +open Candid.Slot +open Candid.Composite + +def expectParse (text : String) (ts : TypeSeq) (want : Option (List Value)) (id : String) : + Vector := + ⟨s!"text: {id}", .parse text ts want⟩ + +/-- One type that is one composite. -/ +def oneOf (c : Composite) : TypeSeq := typesOf do return [← intern c] + +def accepts (text : String) (ts : TypeSeq) (vs : List Value) (id : String) : Vector := + expectParse text ts (some vs) id + +def rejects (text : String) (ts : TypeSeq) (id : String) : Vector := + expectParse text ts none id + +/-! ## Lexical structure (Section 3) -/ + +def textLexChecks : List Vector := + [ accepts "( /* a /* nested */ b */ 1 // to the end of the line\n )" (prims [nat]) [.nat 1] + "comments nest, and a line comment ends at the line feed (3.2)" + , rejects "(1 /* unclosed)" (prims [nat]) "an unclosed block comment (3.2)" + , rejects "(record { opt = 1 })" (oneOf (recordOf [("opt", nat)])) + "a keyword is not a field name (3.3)" + , accepts "(record { \"opt\" = 1 })" (oneOf (recordOf [("opt", nat)])) + [.record [(hashFieldName "opt", .nat 1)]] "a field named like a keyword is written as text (3.3)" + , accepts "(record { nat = 1 })" (oneOf (recordOf [("nat", nat)])) + [.record [(hashFieldName "nat", .nat 1)]] "a type name is an identifier, not a keyword (3.3)" ] + +/-! ## Numbers (Sections 3.4 and 5.3) -/ + +def textIntChecks : List Vector := + [ accepts "(1_000)" (prims [nat]) [.nat 1000] "an underscore separates digits (3.4)" + , rejects "(1_)" (prims [nat]) "an underscore cannot end a number (3.4)" + , rejects "(1__0)" (prims [nat]) "an underscore cannot follow another (3.4)" + , rejects "(0x_ff)" (prims [nat]) "an underscore cannot begin the digits (3.4)" + , accepts "(0xFF)" (prims [nat]) [.nat 255] "hexadecimal digits in either case (3.4)" + , rejects "(0X10)" (prims [nat]) "the hexadecimal prefix is lowercase (3.4)" + , accepts "(+5)" (prims [nat]) [.nat 5] "a signed literal at nat (5.3)" + , accepts "(-0)" (prims [nat]) [.nat 0] "minus zero at nat (5.3)" + , rejects "(-1)" (prims [nat]) "a negative literal at nat (5.3)" + , rejects "(- 5)" (prims [int]) "a sign is part of its number (3.4)" + , accepts "(-0x10)" (prims [int]) [.int (-16)] "a signed hexadecimal literal (3.4)" + , accepts "(255, -128, 18446744073709551615, -9223372036854775808)" + (prims [nat8, int8, nat64, int64]) + [.nat8 255, .int8 (-128), .nat64 18446744073709551615, .int64 (-9223372036854775808)] + "the ends of the fixed-width ranges (5.3)" + , rejects "(256)" (prims [nat8]) "past the top of nat8 (5.3)" + , rejects "(-129)" (prims [int8]) "past the bottom of int8 (5.3)" + , rejects "(1)" (prims [float64]) "an integer literal at a float type (5.3)" + , rejects "(1.0)" (prims [nat]) "a float literal at an integer type (5.3)" ] + +def textFloatChecks : List Vector := + [ accepts "(1.5)" (prims [float32]) [.float32 0x3fc00000] "1.5 at float32 (5.3)" + , accepts "(0.1, 0.1)" (prims [float32, float64]) [.float32 0x3dcccccd, .float64 0x3fb999999999999a] + "0.1 rounded to each format (5.3)" + , accepts "(1.0000000596046448)" (prims [float32]) [.float32 0x3f800001] + "rounded once, not first to float64 (5.3)" + , accepts "(1e400, 1e999999999)" (prims [float64, float64]) + [.float64 0x7ff0000000000000, .float64 0x7ff0000000000000] "overflow rounds to infinity (5.3)" + , accepts "(-1e-400, -0.0)" (prims [float64, float32]) + [.float64 0x8000000000000000, .float32 0x80000000] "a zero keeps the literal's sign (5.3)" + , accepts "(1_000.5)" (prims [float64]) [.float64 0x408f440000000000] + "an underscore in a float literal (3.4)" + , rejects "(.5)" (prims [float64]) "a float literal begins with a digit (3.4)" + , accepts "(5.)" (prims [float64]) [.float64 0x4014000000000000] "a point with no fraction (3.4)" + , accepts "(0x1p4, 0x1.8p1)" (prims [float64, float32]) + [.float64 0x4030000000000000, .float32 0x40400000] "hexadecimal float literals (3.4)" ] + +/-! ## Text (Sections 3.5 and 5.4) -/ + +def textTextChecks : List Vector := + [ rejects "(\"a\tb\")" (prims [text]) "a raw tab in a text literal (3.5)" + , rejects "(\"a\nb\")" (prims [text]) "a raw line feed in a text literal (3.5)" + , accepts "(\"\\t\\n\\r\\\\\\\"\\'\")" (prims [text]) [.text "\t\n\r\\\"'"] "the simple escapes (3.5)" + , accepts "(\"\u0085\")" (prims [text]) [.text "\u0085"] "a raw C1 control character (3.5)" + , accepts "(\"\\e2\\98\\83\")" (prims [text]) [.text "☃"] "byte escapes that make UTF-8 (3.5)" + , rejects "(\"\\ff\")" (prims [text]) "byte escapes that do not make UTF-8, as text (5.4)" + , accepts "(blob \"\\ff\")" (oneOf (.vec nat8)) [.vec [.nat8 0xff]] + "byte escapes that do not make UTF-8, as a blob (4.2)" + , accepts "(blob \"é\")" (oneOf (.vec nat8)) [.vec [.nat8 0xc3, .nat8 0xa9]] + "a blob of a character is its UTF-8 (3.5)" + , accepts "(\"\\u{0000000041}\")" (prims [text]) [.text "A"] "leading zeros in a code point (3.5)" + , rejects "(\"\\u{d800}\")" (prims [text]) "a surrogate is not a scalar value (3.5)" + , rejects "(\"\\u{110000}\")" (prims [text]) "past the last code point (3.5)" + , rejects "(\"\\u{}\")" (prims [text]) "an empty code point (3.5)" + , rejects "(\"\\u{41_}\")" (prims [text]) "an underscore cannot end a code point (3.5)" + , rejects "(\"\\q\")" (prims [text]) "an unknown escape (3.5)" ] + +/-! ## Constructed values (Sections 4.2, 5.5 and 5.6) -/ + +def textOptVecChecks : List Vector := + [ rejects "(5)" (oneOf (.opt nat)) "a value is not made optional (5.5)" + , accepts "(opt 5)" (oneOf (.opt nat)) [.opt (.nat 5)] "opt (5.5)" + , accepts "(null, opt null)" (typesOf do let t ← intern (.opt null); return [t, t]) + [.null, .opt .null] "null is absent at opt null, and opt null is present (5.5)" + , accepts "(vec { 1; 2; })" (oneOf (.vec nat)) [.vec [.nat 1, .nat 2]] + "a trailing separator in a vector (4.1)" + , accepts "(vec {})" (oneOf (.vec nat8)) [.vec []] "an empty vector at blob (5.5)" + , rejects "(blob \"a\")" (oneOf (.vec nat16)) "a blob at a vector of something else (5.5)" ] + +def recordAT : TypeSeq := oneOf (recordOf [("a", nat)]) + +def textRecordChecks : List Vector := + [ accepts "(record { a = 1; 2 })" (oneOf (.record [(97, nat), (98, nat)])) + [.record [(97, .nat 1), (98, .nat 2)]] "a positional field follows the field before it (4.2)" + , accepts "(record { whatever = 0 })" (oneOf (.record [])) [.record []] + "a field the type does not have is ignored (5.6)" + , accepts "(record { a = 1; b = vec { 1; -1 } })" recordAT [.record [(97, .nat 1)]] + "an ignored field is not elaborated (5.6)" + , accepts "(record {})" (typesOf do + let o ← intern (.opt nat) + return [← intern (recordOf [("a", o), ("b", null), ("c", reserved)])]) + [.record [(97, .null), (98, .null), (99, .null)]] + "a missing field of type opt, null or reserved is null (5.6)" + , rejects "(record {})" recordAT "a missing field of type nat (5.6)" + , rejects "(record { a = 1; 97 = 2 })" (prims [reserved]) "a field id given twice (4.2)" + , rejects "(record { 4294967295 = 1; 2 })" (prims [reserved]) + "a positional field after the last id (4.2)" + , rejects "(record { 4294967296 = 1 })" (prims [reserved]) "a field id past 32 bits (4.2)" + , accepts "(record { 0x61 = 1 })" recordAT [.record [(97, .nat 1)]] "a hexadecimal field id (4.2)" + , accepts "(variant { a })" (oneOf (variantOf [("a", null), ("b", nat)])) + [.variant 97 .null] "a variant label alone is null (4.2)" + , rejects "(variant {})" (oneOf (.variant [])) "a variant has one field (4.1)" + , rejects "(variant { a; })" (oneOf (variantOf [("a", null)])) "no separator after a variant's field (4.1)" + , rejects "(variant { c = 1 })" (oneOf (variantOf [("a", null), ("b", nat)])) + "a variant field the type does not have (5.6)" ] + +/-! ## Reserved, empty, references, annotations, arguments -/ + +def textMiscChecks : List Vector := + [ accepts "(vec { 1; -1 })" (prims [reserved]) [.null] "anything is null at reserved (5.2)" + , rejects "(null)" (prims [empty]) "nothing elaborates at empty (5.8)" + , accepts "(principal \"aaaaa-aa\", principal \"AAAAA-AA\")" (prims [principal, principal]) + [.principal [], .principal []] "a principal's text, in either case (Appendix A)" + , rejects "(principal \"aaaaaaa\")" (prims [principal]) "a principal's dashes are checked (Appendix A)" + , rejects "(principal \"aaaaa-ab\")" (prims [principal]) "a principal's checksum is checked (Appendix A)" + , rejects "(principal \"w7x7r-cok77-xa\")" (oneOf (.service [])) "a principal is not a service (5.7)" + , accepts "(service \"aaaaa-aa\")" (oneOf (.service [])) [.service []] "a service reference (5.7)" + , accepts "(func \"aaaaa-aa\".\"mé\")" (oneOf (.func [nat] [] [.query])) [.func [] "mé"] + "a function reference, its method named by text (5.7)" ] + +def textAnnotationChecks : List Vector := + [ accepts "((5 : nat), 6 : nat8)" (prims [nat, nat8]) [.nat 5, .nat8 6] + "an annotation at its own type (5.1)" + , rejects "((5 : nat))" (prims [int]) "an annotation does not convert (5.1)" + , rejects "((1.5 : float64))" (prims [float32]) "an annotation does not narrow (5.1)" + , rejects "((1 : nat))" (prims [reserved]) "an annotation at reserved (5.1)" + , rejects "((1 : text))" (prims [reserved]) "an annotation that does not hold (5.1)" + , accepts "((record { a = 1; b = 2 } : record { b : nat; a : nat }))" + (oneOf (recordOf [("a", nat), ("b", nat)])) [.record [(97, .nat 1), (98, .nat 2)]] + "the same type, its fields in another order (5.1)" + , accepts "((func \"aaaaa-aa\".m : func (x : nat) -> () query))" + (oneOf (.func [nat] [] [.query])) [.func [] "m"] "an annotation with a function type (5.1)" + , rejects "((func \"aaaaa-aa\".m : func (x : nat, x : nat) -> ()))" (oneOf (.func [nat, nat] [] [])) + "a parameter name repeated in an annotation (5.1)" + , rejects "((func \"aaaaa-aa\".m : func (\"\\ff\" : nat) -> ()))" (oneOf (.func [nat] [] [])) + "a parameter name that is not UTF-8 in an annotation (5.1)" + , accepts "((func \"aaaaa-aa\".m : func (x : nat) -> (x : nat)))" (oneOf (.func [nat] [nat] [])) + [.func [] "m"] "a parameter and a result may share a name (5.1)" + , rejects "((1 : t))" (prims [nat]) "a type name with no definition (5.1)" + , rejects "(record { a = 1; b = (1 : text) })" recordAT + "an ignored field must be well-annotated (5.1)" ] + +def textArgChecks : List Vector := + [ accepts "(1, 2)" (prims [nat]) [.nat 1] "an argument beyond the types is ignored (5.9)" + , accepts "(1,)" (prims [nat]) [.nat 1] "a trailing comma (4.1)" + , accepts "()" (typesOf do return [← intern (.opt nat), null]) [.null, .null] + "missing arguments of type opt and null (5.9)" + , rejects "()" (prims [nat]) "a missing argument of type nat (5.9)" + , rejects "(1, (2 : text))" (prims [nat]) "an ignored argument must be well-annotated (5.9)" + , rejects "(1) 2" (prims [nat]) "input after the arguments" ] + +def textChecks : List Vector := + textLexChecks ++ textIntChecks ++ textFloatChecks ++ textTextChecks ++ textOptVecChecks ++ + textRecordChecks ++ textMiscChecks ++ textAnnotationChecks ++ textArgChecks + +end Oracle diff --git a/lean/Oracle/TestDid.lean b/lean/Oracle/TestDid.lean index b9ce6722..ecb26ba9 100644 --- a/lean/Oracle/TestDid.lean +++ b/lean/Oracle/TestDid.lean @@ -8,10 +8,10 @@ that they say more than an assertion did -- `assert blob "..." : (nat)` says onl a message is accepted, and its vector says as what. A message on the right of `==` is a vector of its own. -**Not every assertion is checked yet.** Those with text on the left wait on the textual -syntax (`deferral?`): whether text parses at a type is its question. Text on the -*right* of `==` is checked, against the values `candid_parser` reads it as, since there -it only spells out the expected values. +**Text** is read by the model (`Candid/Text`, after `lean/Spec/TextValues.md`), on +either side. The translation also carries the values `candid_parser` reads it as, and +the model is held to those too (`parserViolations`): text in `test/` is written for +`candid_parser`, so where the two differ, the assertion is not testing what it says. **`spacebomb.test.did` is about resources.** Its messages are valid, and take a lot of time or memory to decode; the file asserts that they are rejected, by a decoder with @@ -40,12 +40,6 @@ open Candid def Assert.id (a : Assert) : String := s!"{a.file}.test.did: {a.name}" -/-- Why an assertion is not checked yet, if it is not. See the header. -/ -def Assert.deferral? (a : Assert) : Option String := - match a.input with - | .text _ _ => some "the textual syntax" - | .blob _ => none - /-- Does the assertion claim its message is costly, rather than rejected? See the header. -/ def Assert.isCostly (a : Assert) : Bool := a.file == "spacebomb" @@ -55,14 +49,13 @@ def disputed : List (String × String) := [] def showValues (vs : List Value) : String := (expectValuesToJson (some vs)).compress -/-- The model's reading of one side: deserialised at `roots` in `B`, or for text, what -`candid_parser` reads it as. `none` is a rejection. -/ +/-- The model's reading of one side at `roots` in `B`: bytes deserialised, text read. +`none` is a rejection. -/ def Input.answer (B : TypeTable) (roots : List Slot) : Input → Except String (Option (List Value)) | .blob hex => do let bs ← bytesFromHex? (.str hex) return deserialize ⟨bs.toArray⟩ B roots - | .text src none => throw s!"candid_parser could not read {repr src} at these types" - | .text _ (some vs) => return some vs + | .text src _ => return Text.readArgs src B roots /-- Does the model agree with the claim? -/ def Assert.check (a : Assert) : Except String Unit := do @@ -94,7 +87,6 @@ def Assert.check (a : Assert) : Except String Unit := do /-- The vectors an assertion becomes: one per message, with the model's answer. -/ def Assert.vectors (a : Assert) : List Vector := - if a.deferral?.isSome then [] else match a.types.check? with | none => [] | some ⟨B, us⟩ => @@ -105,19 +97,22 @@ def Assert.vectors (a : Assert) : List Vector := [⟨a.id ++ suffix, if a.isCostly then .decodeCostly bs a.types else .decode bs a.types (deserialize bs B (us.map (·.1)))⟩] | .error _ => [] + let text (suffix src : String) : List Vector := + [⟨a.id ++ suffix, .parse src a.types (Text.readArgs src B (us.map (·.1)))⟩] + let side (suffix : String) : Input → List Vector + | .blob hex => one suffix hex + | .text src _ => text suffix src let right := match a.claim with - | .equals (.blob hex) | .differs (.blob hex) => one " (right)" hex + | .equals o | .differs o => side " (right)" o | _ => [] - (match a.input with - | .blob hex => one "" hex - | .text _ _ => []) ++ right + side "" a.input ++ right def vectors : List Vector := asserts.flatMap Assert.vectors /-- Assertions the model disagrees with that are not `disputed`, disputed ones it now agrees with, and disputes that name no assertion. -/ def violations : List String := - let checked := asserts.filter (·.deferral?.isNone) + let checked := asserts let disagreements := checked.filterMap fun a => match a.check, disputed.lookup a.id with | .ok (), none => none @@ -128,9 +123,20 @@ def violations : List String := if checked.any (·.id == id) then none else some s!"{id}: disputed, but no such assertion" disagreements ++ stale -/-- How many assertions wait, on what. -/ -def deferred : List (String × Nat) := - let reasons := asserts.filterMap (·.deferral?) - reasons.eraseDups.map fun r => (r, reasons.count r) +/-- Text that the model reads differently from `candid_parser`. See the header. -/ +def parserViolations : List String := + asserts.flatMap fun a => + match a.types.check? with + | none => [] + | some ⟨B, us⟩ => + let sides := a.input :: match a.claim with + | .equals o | .differs o => [o] + | _ => [] + sides.filterMap fun + | .text src parsed => + let got := Text.readArgs src B (us.map (·.1)) + if got == parsed then none + else some s!"{a.id}: the model reads {repr src} as {(expectValuesToJson got).compress}, candid_parser as {(expectValuesToJson parsed).compress}" + | .blob _ => none end Oracle.TestDid diff --git a/lean/Oracle/TestDid/Assert.lean b/lean/Oracle/TestDid/Assert.lean index df1340a8..55622028 100644 --- a/lean/Oracle/TestDid/Assert.lean +++ b/lean/Oracle/TestDid/Assert.lean @@ -5,8 +5,8 @@ The `.test.did` files are the suite this one replaces (`conformance/README.md`, "Migration"). Each assertion names a message, the types to read it at, and a claim about the outcome. The translation keeps all three and adds nothing: the types become a `TypeSeq`, and a message in the textual syntax keeps its source, with the values -`candid_parser` reads it as, since this model has no textual syntax yet. What the model -makes of each assertion is `Oracle/TestDid.lean`'s business. +`candid_parser` reads it as, for the model's own reading to be checked against. What the +model makes of each assertion is `Oracle/TestDid.lean`'s business. -/ import Oracle.Vector diff --git a/lean/Oracle/Vector.lean b/lean/Oracle/Vector.lean index b2b4b071..4cf4703c 100644 --- a/lean/Oracle/Vector.lean +++ b/lean/Oracle/Vector.lean @@ -3,8 +3,9 @@ Conformance vectors, and their JSON form. A vector is one assertion about the model that any Candid implementation can check: a field-id hash, whether a type table is well formed, whether one type is a subtype -of another, what a value coerces to at another type, or what a message decodes to. `conformance/README.md` specifies the JSON; this file is the reference -reader and writer for it, and the two must agree. +of another, what a value coerces to at another type, what a message decodes to, or +what text reads as. `conformance/README.md` specifies the JSON; this file is the +reference reader and writer for it, and the two must agree. The JSON is data, not a grammar: a type is its table and a root, exactly as the model holds it (`Candid/Types.lean`), so a runner in any language can read vector #1 before @@ -66,6 +67,9 @@ inductive Assertion where (`costlyLimit`), so it gives no answer. An implementation may reject them for lack of resources, or decode them; it must not crash. -/ | decodeCostly (bytes : ByteArray) (types : TypeSeq) + /-- `text`, read as an argument sequence at `types` (`lean/Spec/TextValues.md`), is + the values `want`, one per type, or is rejected when `want` is `none`. -/ + | parse (text : String) (types : TypeSeq) (want : Option (List Value)) structure Vector where /-- Unique within the suite. Human-readable, and stable: a runner's list of known @@ -205,6 +209,9 @@ def Vector.render (v : Vector) : String := | .decodeCostly bs ts => head "decode" ++ field "bytes" (.str (bytesToHex bs.toList)) ++ field "types" ts.toJson ++ field "expect" (.str "costly") ++ "}" + | .parse text ts want => + head "parse" ++ field "text" (.str text) ++ field "types" ts.toJson ++ + field "expect" (expectValuesToJson want) ++ "}" /-- A whole file: one vector per line, so that a change to the model shows up in a diff as exactly the vectors whose answers it changed. -/ @@ -410,6 +417,11 @@ def Vector.fromJson? (j : Json) : Except String Vector := do let expect ← j.getObjVal? "expect" if expect == .str "costly" then return .decodeCostly bs ts return .decode bs ts (← expectValuesFromJson? expect) + | "parse" => + expectKeys j ["id", "kind", "text", "types", "expect"] + return .parse (← (← j.getObjVal? "text").getStr?) + (← TypeSeq.fromJson? (← j.getObjVal? "types")) + (← expectValuesFromJson? (← j.getObjVal? "expect")) | k => throw s!"unknown vector kind {repr k}" : Except String Assertion) return { id, assertion } @@ -429,6 +441,11 @@ def parseFile (text : String) : Except String (List Vector) := do def relStr (b : Bool) : String := if b then "<:" else "!<:" +/-- Expected values, if any, are one of each type. -/ +def oneOfEach (B : TypeTable) (roots : List Slot) : Option (List Value) → Bool + | none => true + | some vs => vs.length == roots.length && (vs.zip roots).all fun (v, u) => B.hasType v u + /-- `none` if the model agrees with the assertion, otherwise what went wrong. -/ def Assertion.disagreement : Assertion → Option String | .hash name want => @@ -458,13 +475,22 @@ def Assertion.disagreement : Assertion → Option String match ts.check? with | some ⟨B, us⟩ => let roots := us.map (·.1) - if want.any fun vs => vs.length != roots.length || (vs.zip roots).any fun (v, u) => !B.hasType v u then - some "the expected values are not one of each type" + if !oneOfEach B roots want then some "the expected values are not one of each type" else let got := deserialize bs B roots if got == want then none else some s!"got {(expectValuesToJson got).compress}, want {(expectValuesToJson want).compress}" | none => some "a type is malformed, so decoding at it is not defined" + | .parse text ts want => + match ts.check? with + | some ⟨B, us⟩ => + let roots := us.map (·.1) + if !oneOfEach B roots want then some "the expected values are not one of each type" + else + let got := Text.readArgs text B roots + if got == want then none + else some s!"got {(expectValuesToJson got).compress}, want {(expectValuesToJson want).compress}" + | none => some "a type is malformed, so reading text at it is not defined" | .decodeCostly bs ts => match ts.check? with | some ⟨B, us⟩ => diff --git a/lean/README.md b/lean/README.md index 9a22c633..02f9a056 100644 --- a/lean/README.md +++ b/lean/README.md @@ -1,10 +1,10 @@ # `lean/` — Lean 4 reference model and specification -**Status: fifth slice.** Types, the field-id hash, subtyping — as both a relation and a -decision procedure — values with coercion, and the binary format in both directions, -with an executable that writes them out +**Status: sixth slice.** Types, the field-id hash, subtyping — as both a relation and a +decision procedure — values with coercion, the binary format in both directions, and +textual values, with an executable that writes them out as conformance vectors, reads them back, and is checked against `candid` 0.10 in CI. It -is also held to the decoding assertions of [test/](../test/). "Slice" is a label for +is also held to every assertion of [test/](../test/). "Slice" is a label for what landed in a merge window, applied after the fact; the forward-looking plan is the coverage checklist below and the tiers in [REWRITE.md §3](../REWRITE.md#3-lean-honestly). Nothing is published and nothing carries a compatibility promise. See [REWRITE.md](../REWRITE.md) for why this @@ -310,8 +310,9 @@ The `.test.did` files stay the source. `conformance/tools/test-did` reads them w `candid_parser` and writes [Oracle/TestDid/Data.lean](Oracle/TestDid/Data.lean): the bytes, the types as a table, and the claim. The model has no textual syntax yet, so text on the right of `==` arrives as the values `candid_parser` reads it as, at the -assertion's types. The 24 assertions left wait on the textual syntax (7, with text on -the left) and on the cost model (the 17 of `spacebomb.test.did`). +assertion's types. The 24 assertions left then waited on the cost model (the 17 of +`spacebomb.test.did`, since checked as costly messages) and on the textual syntax (7, +with text on the left, which slice 6 checks). **Future types.** A decoder that meets an opcode below `-24` now skips it as the spec says, keeping the type in the table as `Composite.future` and decoding its values to @@ -320,6 +321,34 @@ option, which is what `test/subtypes.test.did` asserts; the spec has no rule of for them, and the model adds only reflexivity. A future value's reference count is read and ignored, as 0.10 does, since the model has no reference table. +## What slice 6 established + +**Textual values, specified first in the new style.** [Spec/TextValues.md](Spec/TextValues.md) +is the first section of the new specification: RFC conventions, ABNF, and elaboration +rules `e ⇐ t ⇝ v` that give text a meaning at a type, which `spec/Candid.md` leaves to +"context". [Candid/Text/](Candid/Text/) implements it: a lexer, a parser into the +document's abstract syntax, and elaboration, one function per rule. The `parse` vectors +in `handwritten.json` follow the document section by section, with their expectations +written from it. + +**What it needed.** Type equivalence ([Candid/Equiv.lean](Candid/Equiv.lean)), since an +annotation must name the expected type: the subtype procedure's worklist in one +direction, with the same termination measure. The textual form of principals +([Candid/Principal.lean](Candid/Principal.lean)), CRC-32 and base32. And float literals +rounded once and exactly ([Candid/Float.lean](Candid/Float.lean)), on naturals, deciding +an absurd exponent before multiplying anything out. + +**Every assertion of `test/` is checked.** The 7 with text on the left are `parse` +vectors now, and text on the right of `==` is read by the model, not taken from +`candid_parser`. The model reads all of it as `candid_parser` does, which the oracle +checks (`TestDid.parserViolations`). Where the document and 0.10 differ, the runner lists +the vectors as known divergences, and the document's Appendix B.2 says why. Three of them +are panics in 0.10. + +**Types in annotations are nested and flattened.** `TypeExpr` is the type syntax as +written, and `flatten` interns it into a table, which is half of the `.did` work below. +Names are kept in the syntax but resolve in an empty environment, so a name is rejected. + ## Constraints - **No mathlib.** We do not need it, and depending on it would dominate build @@ -351,7 +380,8 @@ cover what actually breaks in production: rejects for lack of resources. The model's decoder stops at `costlyLimit` values, so the oracle can tell a message it cannot afford, such as those of `test/spacebomb.test.did`, from one it answers -- [ ] Textual value syntax +- [x] Textual value syntax — lexing, parsing and elaboration at a type, after + [Spec/TextValues.md](Spec/TextValues.md) ## Deferred, deliberately @@ -375,14 +405,17 @@ Named here so they are obligations rather than oversights. depth costs no stack. A hash set would make this linear in the pairs compared; the reference model keeps lists, which the termination measure is stated over, until a conformance vector makes that a problem. -- **Flattening the surface syntax.** A `.did` type is nested; a `Composite`'s - children are slots. The parser will need the pass that interns nested composites - into a table, and textual aliases (`type A = B;`) have to be resolved by it — - `intern`/`close` are only the hand-written-example half of that. -- **Encoding vectors, and type equivalence.** An encoding vector is a value and a type, - and the check is that an implementation's bytes decode to an *equivalent* type and - the same value. Equivalence -- the same infinite tree -- is decidable with the - subtype procedure's worklist and is not written yet. +- **`.did` files.** Type expressions are parsed and flattened + ([Candid/Text/](Candid/Text/)), but without names: definitions (`type A = B;`), which + may be recursive, need an environment that `flatten` resolves names in, and the + service and import syntax of a whole file are not parsed. +- **Encoding vectors.** An encoding vector is a value and a type, and the check is that + an implementation's bytes decode to an *equivalent* type and the same value. + Equivalence is written now ([Candid/Equiv.lean](Candid/Equiv.lean)); the vectors are not. +- **Elaboration's properties as theorems.** That it is deterministic, that its result + has the type it was elaborated at, and that it terminates are stated in + [Spec/TextValues.md](Spec/TextValues.md), Section 5.10. Termination holds by + construction, since it recurses on the expression; the other two are not proved. - **Verso.** Deliberately not yet: bundling an undocumented doc toolchain into the work whose purpose was de-risking the build would have doubled the unknowns. diff --git a/lean/Spec/TextValues.md b/lean/Spec/TextValues.md new file mode 100644 index 00000000..443d97aa --- /dev/null +++ b/lean/Spec/TextValues.md @@ -0,0 +1,688 @@ +# Candid Textual Values + +**Status:** Draft. Not normative. Until this document replaces the corresponding part of +[spec/Candid.md][CANDID] under the conditions of [REWRITE.md §6](../../REWRITE.md#6-the-ratchet), +that document governs wherever the two disagree. [Appendix B](#appendix-b-differences-from-candid-010-informative) +lists the places where this one decides something the current specification leaves open, +and where it disagrees with `candid` 0.10. + +**Abstract.** This document specifies the textual form of Candid values, as written by +people in tools, tests and debugging sessions, and what such text means at a given +Candid type. Reading text is defined in two steps: parsing the text into an expression, +and elaborating that expression at a type into a value. Either step can reject the input. + +## Table of Contents + +1. [Introduction](#1-introduction) +2. [Overview](#2-overview) +3. [Lexical Structure](#3-lexical-structure) +4. [Expressions](#4-expressions) +5. [Elaboration](#5-elaboration) +6. [Conformance](#6-conformance) +7. [Security Considerations](#7-security-considerations) +8. [References](#8-references) +- [Appendix A. Textual Form of Principals](#appendix-a-textual-form-of-principals) +- [Appendix B. Differences from candid 0.10 (Informative)](#appendix-b-differences-from-candid-010-informative) +- [Appendix C. Examples (Informative)](#appendix-c-examples-informative) + +## 1. Introduction + +### 1.1. Scope + +This document defines: + +- the lexical structure of textual values ([Section 3](#3-lexical-structure)); +- the syntax of value expressions and of argument sequences ([Section 4](#4-expressions)); +- the meaning of an expression at a type: the value it denotes, or that it is rejected + ([Section 5](#5-elaboration)). + +The following are out of scope: + +- **The syntax of types.** Annotations ([Section 4.1](#41-grammar)) contain types written + in the type syntax of [CANDID]. This document refers to that syntax but does not + define it. +- **Printing.** This document does not say how an implementation prints a value as text. + It does say which text reads back as a given value, which an implementation that + prints values can use to check its printer. +- **Reading text without a type.** An implementation MAY offer to read an expression + when no type is given, inferring one from the expression. This document does not + define that inference. Without a type, an expression has no meaning here. + +### 1.2. Requirements Language + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD +NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to +be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they +appear in all capitals, as shown here. + +### 1.3. Notation + +**Grammar.** Grammars are written in ABNF [RFC5234] with two departures: + +1. Terminals denote Unicode scalar values [UNICODE], not bytes. `%x41` is the character + U+0041, and `%xE000-10FFFF` is a range of characters. +2. Quoted strings are case-sensitive, as if written with the `%s` prefix of [RFC7405]. + `"opt"` matches `opt` and nothing else. + +**Rules.** The meaning of an expression is given by rules of the form + +``` +e ⇐ t ⇝ v if +``` + +read "the expression *e*, elaborated at the type *t*, yields the value *v*, provided +that the premises hold". A rule without premises holds unconditionally. Elaboration is +the least relation closed under the rules; an expression that no rule relates to any +value is *rejected* at that type. + +**Values and types.** Values and types are those of the Candid data model [CANDID], and +are written as follows. `nat(n)`, `int(n)`, `nat8(n)` … `int64(n)` are numbers of the +named type. `float32(b)` and `float64(b)` are IEEE 754 [IEEE754] floating-point data, +identified by their bit patterns. `text(s)` holds a sequence of Unicode scalar values. +`bool(b)`, `opt(v)`, `vec(v₁, …, vₙ)`, `record{i₁ = v₁; …; iₙ = vₙ}` with field ids in +ascending order, `variant{i = v}`, `principal(p)`, `service(p)` and `func(p, m)` are as +expected. The single value `null` is the value of type `null`, the absent value of any +option type, and the value of type `reserved`. + +**Sequences of bytes and characters.** `⟨b₁, …, bₙ⟩` is a sequence; `‖` concatenates. +`utf8(c)` is the UTF-8 encoding [UNICODE] of the character *c*. For a well-formed +UTF-8 sequence β, `utf8⁻¹(β)` is the sequence of characters it encodes. + +### 1.4. Terminology + +**text** +: A sequence of Unicode scalar values. When text arrives as bytes, it is decoded as + UTF-8, and bytes that are not well-formed UTF-8 MUST be rejected. + +**reject** +: To report that the input has no meaning, and to produce no value. How the rejection + is reported is up to the implementation. + +**resolve** +: To replace a type name by the type it is defined as, repeatedly, until the outermost + form is not a name. Every rule in [Section 5](#5-elaboration) applies to a type after + it is resolved. + +## 2. Overview + +Text is read in two steps: + +``` +text ──parse──▶ expression ──elaborate at t──▶ value of type t + (Sections 3, 4) (Section 5) +``` + +Parsing does not depend on the type. It turns `record { name = "x"; 5 }` into a record +expression with two fields, the second numbered by position, and it rejects text that +is not well-formed regardless of type, such as an unterminated string or a field id +used twice. + +Elaboration is guided by the type, and gives literals their meaning. The literal `5` +denotes `nat8(5)` at `nat8`, `int(5)` at `int`, and nothing at `float64` or `text`. + +Elaboration is not coercion. Coercion, defined in [CANDID], converts a value of one +type into a value of another, and is how a decoder takes binary data sent at one type +into the type it expects. Elaboration reads an expression that was written for the +type it is read at. Where the two could differ, elaboration is stricter. The +expression `5` is rejected at `opt nat`, where it would have to be written `opt 5`, +even though coercion would turn a `nat` into an `opt nat`. Two choices do follow the +binary format, because a value written as text should mean the same thing as one sent +in binary. Fields that the type does not mention are ignored +([Section 5.6](#56-records-and-variants)), and missing optional arguments are filled +in ([Section 5.9](#59-argument-sequences)). + +## 3. Lexical Structure + +### 3.1. Tokens + +Text is divided into tokens from left to right, by taking at each point the longest +prefix that forms a token. Whitespace and comments may appear between tokens and are +otherwise ignored. Text that cannot be divided this way MUST be rejected. + +```abnf +token = keyword / identifier / number / text-literal / punctuation +punctuation = "(" / ")" / "{" / "}" / ";" / "," / "." / ":" / "=" / "->" +whitespace = %x09 / %x0A / %x0D / %x20 +``` + +The arrow `->` occurs only in function types, which can appear in annotations. + +The longest-match rule decides cases like `optional`, which is an identifier and not +the keyword `opt` followed by `ional`, and `1e5`, which is one number. + +### 3.2. Comments + +```abnf +comment = line-comment / block-comment +line-comment = "//" *line-char +line-char = %x00-09 / %x0B-10FFFF +block-comment = "/*" block-body "*/" +``` + +A `line-comment` extends to the end of the line, not including the line feed. + +A `block-body` is any text in which every `/*` begins a nested `block-comment`, and +which contains no `*/` other than those that close one. Block comments therefore nest: +`/* a /* b */ c */` is one comment. A block comment that is not closed MUST be rejected. + +### 3.3. Keywords and Identifiers + +```abnf +keyword = "blob" / "composite_query" / "false" / "func" / "import" / "null" + / "oneway" / "opt" / "principal" / "query" / "record" / "service" + / "true" / "type" / "variant" / "vec" +identifier = (ALPHA / "_") *(ALPHA / DIGIT / "_") ; that is not a keyword +``` + +Keywords are reserved for the whole of Candid's syntax, including the parts this +document does not define, so some keywords never occur in a value. A field named like +a keyword is written as text: `record { "opt" = 1 }`. + +Names of types, such as `nat` and `text`, are identifiers, not keywords. + +### 3.4. Numbers + +```abnf +number = [sign] (decimal / "0x" hex) + / [sign] decimal "." [decimal] + / [sign] decimal ["." [decimal]] ("e" / "E") [sign] decimal + / [sign] "0x" hex "." [hex] + / [sign] "0x" hex ["." [hex]] ("p" / "P") [sign] decimal + +sign = "+" / "-" +decimal = DIGIT *(["_"] DIGIT) +hex = HEXDIG *(["_"] HEXDIG) +``` + +`HEXDIG` here includes the lowercase letters `a` to `f`; the prefix `0x` does not have +an uppercase form. An underscore separates digits and has no other effect; the grammar +does not let one begin or end a sequence of digits, or follow another underscore. A sign +is part of its number, so whitespace or a comment between the two leaves a sign that is +not a token, and the text is rejected. + +A number is an **integer literal** if it is written in the first form, and a **float +literal** otherwise. The two are distinct in what follows, even when they denote the +same quantity: `1` is an integer literal and `1.0` is a float literal. + +The **value of an integer literal** is the integer that its digits denote in base 10, +or in base 16 after `0x`, negated if the sign is `-`. It has no bound. + +The **value of a float literal** is a sign and a non-negative rational number: + +- The sign is negative if the literal begins with `-`, and positive otherwise. +- After `0x`, the digits before and after the point denote a hexadecimal fraction, and + `p` gives a power of 2 by which it is multiplied. Otherwise the fraction is decimal, + and `e` gives a power of 10. + +The exponent is always written in decimal. The sign is kept separately so that `-0.0` +and `0.0` remain distinct. + +### 3.5. Text Literals + +```abnf +text-literal = DQUOTE *(text-char / escape) DQUOTE +text-char = %x20-21 / %x23-5B / %x5D-7E / %x80-10FFFF +escape = "\" ( "n" / "r" / "t" / "\" / DQUOTE / "'" + / HEXDIG HEXDIG + / "u{" hex "}" ) +``` + +A text literal may not contain a raw C0 control character (U+0000 to U+001F), +including a line break, or the character U+007F; these are written as escapes. Other +characters, including the C1 controls U+0080 to U+009F, may appear raw, as in [WASM]. + +The **value of a text literal** is a sequence of bytes, the concatenation of one +sequence for each `text-char` or `escape` in order: + +| Written | Bytes | +|---|---| +| a `text-char` *c* | `utf8(c)` | +| `\n`, `\r`, `\t` | `⟨0x0A⟩`, `⟨0x0D⟩`, `⟨0x09⟩` | +| `\\`, `\"`, `\'` | `⟨0x5C⟩`, `⟨0x22⟩`, `⟨0x27⟩` | +| `\` followed by two hexadecimal digits *h* | the single byte *h* | +| `\u{` *h* `}` | `utf8(c)`, where *c* is the character whose code point is *h* | + +In the last form, *h* MUST denote a Unicode scalar value: at most `10FFFF`, and not in +the range `D800` to `DFFF`. Leading zeros are permitted. + +The value is bytes rather than characters because a byte escape can produce a sequence +that is not UTF-8, such as `"\ff"`. Such a literal is still a token. Whether its value +is acceptable depends on where it is used: it is acceptable as a `blob` +([Section 4.1](#41-grammar)), and not as text or as a name. A use that requires text +MUST reject a value that is not well-formed UTF-8. + +## 4. Expressions + +### 4.1. Grammar + +The grammar in this section is over the tokens of [Section 3](#3-lexical-structure). +Whitespace and comments may appear between any two tokens. + +```abnf +args = "(" [annotated *("," annotated) [","]] ")" + +annotated = expr [":" type] + +expr = "(" annotated ")" + / number / text-literal / "true" / "false" / "null" + / "opt" expr + / "vec" "{" [annotated *(";" annotated) [";"]] "}" + / "blob" text-literal + / "record" "{" [field *(";" field) [";"]] "}" + / "variant" "{" (label "=" annotated / label) "}" + / "principal" text-literal + / "service" text-literal + / "func" text-literal "." name + +field = label "=" annotated / annotated +label = number / name ; an integer literal without a sign +name = identifier / text-literal +``` + +`type` is the type syntax of [CANDID]. + +A sequence, record or argument list may end with a separator. A variant has exactly +one field, so `variant {}` and `variant { a; }` are both rejected. + +In a record, `"a" = 1` is a named field and `"a"` alone is a positional field whose +expression is text. One token of lookahead, `=` or not, tells them apart. The same +holds for a numeric label such as `1 = 2` and the positional `1`. + +### 4.2. Abstract Syntax + +Parsing produces an expression in the following abstract syntax. The meaning of a +parsed expression in [Section 5](#5-elaboration) is defined on this form alone. + +``` +e ::= intlit(z) integer literal with value z + | floatlit(s, q) float literal with sign s and magnitude q + | textlit(β) text literal with bytes β + | true | false | null + | opt e + | vec(e₁, …, eₙ) + | blob(β) + | record{i₁ = e₁; …; iₙ = eₙ} field ids, distinct + | variant{i = e} + | principal(p) | service(p) | func(p, m) + | (e : t) +``` + +The rest of this section says how the concrete syntax maps to this form, and which +further inputs MUST be rejected during parsing. + +**Parentheses.** `( a )` is the expression *a*. `( e : t )` is the annotation +`(e : t)`, and so is `e : t` where the grammar permits it: as an argument, an element +of a vector, or a field value. + +**Field ids.** A `label` denotes a field id, a natural number less than 2³²: + +- A `number` label MUST be an integer literal without a sign, such as `5` or `0xff`, + and denotes its value. A label whose value is 2³² or more MUST be rejected. +- A `name` denotes the field-id hash of [CANDID] applied to the name's bytes. A name + written as a text literal MUST be well-formed UTF-8. + +**Positional fields.** A record field without a label is given an id from its +position. The first field of a record, if positional, is given id 0. Any later +positional field is given one more than the id of the field immediately before it, +whether that field was labelled or positional. A positional field after a field with id +2³² − 1 MUST be rejected. So `record { a = 1; 2 }` gives the second field the id +`hash("a") + 1`. + +**Distinct ids.** If two fields of one record have the same id, after labels are +hashed and positions numbered, the record MUST be rejected. This includes two +different names whose hashes collide. + +**Variants.** A variant field written as a `label` alone has the value `null`: +`variant { a }` is `variant{hash("a") = null}`. + +**References.** In `principal`, `service` and `func`, the text literal MUST be the +textual form of a principal, as defined in +[Appendix A](#appendix-a-textual-form-of-principals), and *p* is that principal. The +method name *m* of a `func` is the name's bytes, which MUST be well-formed UTF-8. + +**Blobs.** `blob(β)` takes the bytes β of its text literal as they are; they need not +be UTF-8. + +## 5. Elaboration + +An expression *e* is elaborated at a type *t*, written `e ⇐ t`. The rules in this +section say which value, if any, it yields. If no rule yields a value, the expression +MUST be rejected at that type. + +In each rule, *t* stands for the type after it is resolved. A type name in an annotation +is resolved in the environment of type definitions supplied with the input, which may be +empty. + +### 5.1. Annotations + +An annotation does not convert. It states the type its expression is written at, and +that type must be the expected one: + +``` +(e : s) ⇐ t ⇝ v if s ≡ t and e ⇐ s ⇝ v +``` + +`s ≡ t` holds when *s* and *t* are the same type: equal after every name is replaced by +its definition, expanding recursive types as far as needed. Two types can be the same +without being written the same way. A name and its definition are the same type, and +so are two record types that list the same fields in a different order. + +An expression is **well-annotated** if, for every annotated subexpression `(e' : s)`, +`e' ⇐ s` yields a value. The rules of [Sections 5.2 to 5.9](#52-reserved) apply only to +well-annotated expressions. An expression that is not well-annotated MUST be rejected at +every type, including where [Section 5.2](#52-reserved) or +[Section 5.6](#56-records-and-variants) would otherwise ignore it. + +### 5.2. Reserved + +Every expression that is not an annotation elaborates to `null` at `reserved`: + +``` +e ⇐ reserved ⇝ null if e is not of the form (e' : s) +``` + +### 5.3. Numbers + +An integer literal elaborates at each integer type whose range contains its value: + +``` +intlit(z) ⇐ nat ⇝ nat(z) if 0 ≤ z +intlit(z) ⇐ natN ⇝ natN(z) if 0 ≤ z < 2^N (N = 8, 16, 32, 64) +intlit(z) ⇐ int ⇝ int(z) +intlit(z) ⇐ intN ⇝ intN(z) if −2^(N−1) ≤ z < 2^(N−1) (N = 8, 16, 32, 64) +``` + +The sign of an integer literal does not matter beyond its value. `+5` and `-0` both +elaborate at `nat`. + +A float literal elaborates at each floating-point type by rounding: + +``` +floatlit(s, q) ⇐ float32 ⇝ float32(round₃₂(s, q)) +floatlit(s, q) ⇐ float64 ⇝ float64(round₆₄(s, q)) +``` + +`roundₖ(s, q)` is the binaryk number that the roundTiesToEven attribute of [IEEE754], +Section 4.3.1, gives for the exact number q with the sign s. In particular, a magnitude +too large for the format rounds to infinity, and a result of zero carries the sign *s*, +so `-1e-400` yields negative zero. The literal is rounded once, directly to the +target format. An implementation MUST NOT round it first to a wider format. + +An integer literal does not elaborate at a floating-point type, and a float literal does +not elaborate at an integer type. `1` is rejected at `float64` and `1.0` at `nat`. + +There is no literal for a NaN; see [Appendix B](#appendix-b-differences-from-candid-010-informative). + +### 5.4. Text, Booleans and Null + +``` +textlit(β) ⇐ text ⇝ text(utf8⁻¹(β)) if β is well-formed UTF-8 +true ⇐ bool ⇝ bool(true) +false ⇐ bool ⇝ bool(false) +null ⇐ null ⇝ null +``` + +### 5.5. Options and Vectors + +``` +null ⇐ opt t ⇝ null +opt e ⇐ opt t ⇝ opt(v) if e ⇐ t ⇝ v +vec(e₁, …, eₙ) ⇐ vec t ⇝ vec(v₁, …, vₙ) if eᵢ ⇐ t ⇝ vᵢ for each i +blob(β) ⇐ vec t ⇝ vec(nat8(b₁), …, nat8(bₙ)) if t is nat8, and β = ⟨b₁, …, bₙ⟩ +``` + +These are the only rules for option types. A value is not implicitly made optional: at +`opt nat`, `opt 5` is accepted and `5` is rejected. At `opt null`, `null` is the +absent value, and `opt null` is the present one. + +### 5.6. Records and Variants + +A record expression elaborates at a record type field by field, following the type: + +``` +record{i₁ = e₁; …; iₙ = eₙ} ⇐ record{j₁ : t₁; …; jₘ : tₘ} ⇝ record{j₁ = w₁; …; jₘ = wₘ} + if, for each k from 1 to m: + either jₖ = iₗ for some l, and eₗ ⇐ tₖ ⇝ wₖ, + or jₖ is no iₗ, tₖ is null, reserved, or an option type, and wₖ = null +``` + +A field of the expression whose id the type does not have is ignored, as decoding +ignores such a field in binary data. A field of the type that the expression does not +have is filled with `null` if its type has that value, and the record is rejected +otherwise. + +A variant expression elaborates if its field is one of the type's: + +``` +variant{i = e} ⇐ variant{…; i : t; …} ⇝ variant{i = v} if e ⇐ t ⇝ v +``` + +### 5.7. References + +``` +principal(p) ⇐ principal ⇝ principal(p) +service(p) ⇐ service {…} ⇝ service(p) +func(p, m) ⇐ func (…) → (…) ⇝ func(p, m) +``` + +A reference is not checked against the service or function type it is elaborated at. +Text alone cannot show whether principal *p* provides a method *m*. + +The three kinds of reference do not mix. A `principal` expression is rejected at a +service type, and a `service` expression at `principal`. + +### 5.8. Empty + +No rule yields a value at `empty`, so every expression is rejected there. + +### 5.9. Argument Sequences + +An argument sequence elaborates at a sequence of types position by position. Arguments +beyond the types are ignored, and types beyond the arguments are filled as missing +record fields are: + +``` +(e₁, …, eₙ) ⇐ (t₁, …, tₘ) ⇝ (v₁, …, vₘ) + if, for each k from 1 to m: + either k ≤ n, and eₖ ⇐ tₖ ⇝ vₖ, + or k > n, tₖ is null, reserved, or an option type, and vₖ = null +``` + +Every argument MUST still be well-annotated ([Section 5.1](#51-annotations)), including +one that is ignored. + +### 5.10. Properties (Informative) + +The rules are designed so that the following hold. They are stated here as obligations +on the rules, to be proved about the reference model. + +- **Determinism.** For each expression and type, at most one value is yielded. +- **Well-typedness.** If `e ⇐ t ⇝ v`, then `v` is a value of type `t`. +- **Termination.** Elaboration recurses on the expression, so it terminates even at a + recursive type. + +## 6. Conformance + +An implementation conforms to this document if, for every text and every type, it +either rejects the text or yields the value given by [Sections 3 to 5](#3-lexical-structure), +and it rejects only where those sections require it or where +[Section 7](#7-security-considerations) permits it. + +Conformance vectors of kind `parse`, each a text, its types and the expected values or +a rejection, are written from the reference model in [lean/](../), whose +[Candid/Text/](../Candid/Text/) implements this document, and published under +[conformance/](../../conformance/). + +## 7. Security Considerations + +Text values are often typed by people and are rarely hostile. They can still come from +outside: a web interface may send typed text to a service that parses it. An +implementation MAY reject a well-formed input for lack of resources, as described for +binary data in [CANDID], and SHOULD bound the following: + +- **Nesting.** Every bracket adds a level of recursion to a straightforward parser. +- **Integer literals.** The value of an integer literal is unbounded, and converting a + long literal from decimal costs time quadratic in its length in a naïve + implementation. +- **Float literals.** The exponent is unbounded. An implementation SHOULD round + without computing 10 raised to the exponent: `1e999999999` rounds to infinity, and + should take no more time to read than `1e999`. + +## 8. References + +### 8.1. Normative References + +[CANDID] +: DFINITY, "Candid Specification", [spec/Candid.md](../../spec/Candid.md). For the type + syntax, the data model, the field-id hash, and coercion. + +[IEEE754] +: IEEE, "IEEE Standard for Floating-Point Arithmetic", IEEE Std 754-2019. + +[RFC2119] +: Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, + RFC 2119, March 1997. + +[RFC4648] +: Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, October 2006. + +[RFC5234] +: Crocker, D., Ed., and P. Overell, "Augmented BNF for Syntax Specifications: ABNF", + STD 68, RFC 5234, January 2008. + +[RFC7405] +: Kyzivat, P., "Case-Sensitive String Support in ABNF", RFC 7405, December 2014. + +[RFC8174] +: Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, + RFC 8174, May 2017. + +[UNICODE] +: The Unicode Consortium, "The Unicode Standard". + +### 8.2. Informative References + +[IC-INTERFACE] +: DFINITY, "The Internet Computer Interface Specification", section "Principals". + +[WASM] +: WebAssembly Community Group, "WebAssembly Core Specification", section "Text Format: + Values". The escapes of text literals and the forms of float literals follow it. + +## Appendix A. Textual Form of Principals + +A principal is a sequence of bytes *p*. Its **canonical textual form** is computed as +follows: + +1. Let *c* be the CRC-32 of *p*, as four bytes, most significant first. CRC-32 is the + checksum of ISO 3309 and ITU-T V.42, the one computed by zlib. +2. Let *s* be the base32 encoding [RFC4648] of `c ‖ p`, in lowercase and without padding. +3. Split *s* into groups of five characters, the last of which may be shorter, and join + them with `-`. + +For example, the empty principal has the form `aaaaa-aa`. + +A text denotes the principal *p* if it equals the canonical form of *p*, ignoring the +case of ASCII letters. Any other text MUST be rejected: one that is not base32, whose +checksum does not match, or whose dashes are not where the canonical form puts them. + +This document does not bound the length of *p*. The Internet Computer limits principals +to 29 bytes [IC-INTERFACE], and an implementation for it MAY reject a longer one. + +## Appendix B. Differences from candid 0.10 (Informative) + +This appendix lists where this document decides something [CANDID] does not, and where +it differs from `candid_parser` 0.10, the Rust implementation. Each item that differs +from 0.10 is a proposed change to 0.10, or a decision this draft still has to make. + +### B.1. Decided Here, Silent in Candid.md + +- The meaning of text at a type ([Section 5](#5-elaboration)). [CANDID] says only that + types are known from context. +- Fields not in the type are ignored, and missing fields and arguments of type `null`, + `reserved` or an option type are filled with `null` + ([Sections 5.6](#56-records-and-variants) and [5.9](#59-argument-sequences)). This + agrees with 0.10 and with `test/construct.test.did`. +- Every expression elaborates at `reserved` ([Section 5.2](#52-reserved)), agreeing + with 0.10. +- Keywords cannot be identifiers ([Section 3.3](#33-keywords-and-identifiers)), agreeing + with 0.10. [CANDID] lists no keywords. +- Comments may appear in values, agreeing with 0.10. [CANDID] defines comments only for + types. +- Text that is not UTF-8 after byte escapes is rejected as text and accepted as a blob, + agreeing with 0.10. +- The textual form of principals ([Appendix A](#appendix-a-textual-form-of-principals)). + [CANDID] calls it a "URI". +- A positional field after id 2³² − 1 is rejected. [CANDID]'s "previous + 1" is + undefined there. +- A signed hexadecimal integer such as `-0x10` is accepted, agreeing with 0.10. + [CANDID]'s grammar has hexadecimal only for naturals. + +### B.2. Different from candid 0.10 + +| Input | candid 0.10 | This document | +|---|---|---| +| `record { 4294967295 = 1; 2 }` | panics in a debug build; in a release build the second field gets id 0 | rejected | +| `0X10` | panics | rejected: not a token | +| `(5 : nat)` at `int` | accepted | rejected: an annotation does not convert ([5.1](#51-annotations)) | +| `(1.5 : float64)` at `float32` | accepted, rounded | rejected, as above | +| `(1 : nat)` at `reserved` | accepted | rejected, as above | +| `()` at `(opt nat)` | accepted by `annotate_types`, rejected by `didc encode` | accepted: `(null)` | +| `(1, 2)` at `(nat)` | `annotate_types` panics; `didc encode` accepts | accepted: `(nat(1))` | +| A float literal at `float32` | rounded to `float64`, then to `float32` | rounded once ([5.3](#53-numbers)) | +| `-0` at `nat` | rejected | accepted: 0 | +| `- 5`, `-/* */5` | accepted | rejected: a sign is part of its number | +| `1_`, `1__0`, `"\u{41_}"` | accepted | rejected | +| `1_000.5` | rejected | accepted | +| `.5` | accepted | rejected | +| `0x1p4` | rejected | accepted: 16 | +| A raw tab, line break, other C0 control character or U+007F in a text literal | accepted | rejected | +| A principal of more than 29 bytes | rejected | accepted ([Appendix A](#appendix-a-textual-form-of-principals)) | + +`candid_parser` also builds a `String` from bytes that may not be UTF-8 before it checks +them, which Rust's rules forbid, even when the check then rejects the literal. + +### B.3. Open Issues + +- **NaN.** No literal denotes a NaN, so there is no text for a `float32` or `float64` + value that is one. Neither this document nor 0.10 can write every value of a float + type. +- **Annotations.** [Section 5.1](#51-annotations) requires the annotated type to be + the expected one. The alternative is to allow any subtype and coerce, as decoding does. + That would accept `(5 : nat)` at `int`, but also turn `("x" : text)` at `opt nat` into + `null` without complaint. + +## Appendix C. Examples (Informative) + +| Text | Type | Result | +|---|---|---| +| `(5)` | `(nat8)` | `nat8(5)` | +| `(5)` | `(opt nat)` | rejected | +| `(opt 5)` | `(opt nat)` | `opt(nat(5))` | +| `(null)` | `(opt null)` | `null` | +| `(opt null)` | `(opt null)` | `opt(null)` | +| `(0x_ff)` | `(nat)` | rejected: an underscore may not follow `0x` | +| `(0xff)` | `(nat16)` | `nat16(255)` | +| `(1)` | `(float64)` | rejected | +| `(-0.0)` | `(float64)` | `float64(0x8000000000000000)` | +| `(1e400)` | `(float64)` | `float64(0x7FF0000000000000)`, infinity | +| `("\u{2603}")` | `(text)` | `text("☃")` | +| `("\u{26_03}")` | `(text)` | `text("☃")` | +| `("\u{2603")` | `(text)` | rejected: unterminated escape | +| `("\ff")` | `(text)` | rejected: not UTF-8 | +| `(blob "\ff")` | `(blob)` | `vec(nat8(255))` | +| `(record { whatever = 0 })` | `(record {})` | `record{}` | +| `(record { a = 1; 2 })` | `(record { a : nat; 98 : nat })` | `record{97 = nat(1); 98 = nat(2)}` | +| `(record {})` | `(record { a : opt nat })` | `record{97 = null}` | +| `(record { a = 1; 97 = 2 })` | any | rejected: id 97 twice | +| `(variant {})` | `(variant {})` | rejected: no syntax | +| `(vec { 1; -1 })` | `(vec nat)` | rejected: `-1` is not a `nat` | +| `(vec { 1; -1 })` | `(reserved)` | `null` | +| `((1 : text))` | `(reserved)` | rejected: not well-annotated | +| `(principal "aaaaa-aa")` | `(principal)` | `principal(⟨⟩)` | +| `(principal "w7x7r-cok77-xa")` | `(service {})` | rejected: a principal is not a service | +| `(func "aaaaa-aa".m)` | `(func () -> ())` | `func(⟨⟩, "m")` | +| `(1, 2)` | `(nat)` | `(nat(1))` | +| `()` | `(opt nat, null)` | `(null, null)` | + +[CANDID]: ../../spec/Candid.md