proptests for parser
This commit is contained in:
parent
bf13304f9e
commit
f9c478276d
18 changed files with 672 additions and 431 deletions
95
Cargo.lock
generated
95
Cargo.lock
generated
|
@ -43,7 +43,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.1.2"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
@ -93,10 +93,10 @@ name = "enterprise-compiler"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bimap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"petgraph 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proptest 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proptest-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -113,6 +113,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"enterprise-compiler 0.1.0",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proptest 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"symbol 0.1.0",
|
||||
|
@ -142,7 +143,7 @@ version = "0.1.14"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -158,7 +159,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.3.1"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -176,7 +177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.66"
|
||||
version = "0.2.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
@ -184,7 +185,7 @@ name = "lock_api"
|
|||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -195,11 +196,6 @@ dependencies = [
|
|||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maplit"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.11"
|
||||
|
@ -224,7 +220,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -236,7 +232,7 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"fixedbitset 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indexmap 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -244,6 +240,14 @@ name = "ppv-lite86"
|
|||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "0.4.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.8"
|
||||
|
@ -271,11 +275,29 @@ dependencies = [
|
|||
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proptest-derive"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.6.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.2"
|
||||
|
@ -290,7 +312,7 @@ version = "0.6.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -308,7 +330,7 @@ version = "0.7.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -382,7 +404,7 @@ name = "rand_jitter"
|
|||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -394,7 +416,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -469,7 +491,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
@ -585,6 +607,16 @@ dependencies = [
|
|||
"spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.15.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.14"
|
||||
|
@ -613,7 +645,7 @@ version = "3.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -648,6 +680,11 @@ dependencies = [
|
|||
"stdweb 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
|
@ -658,7 +695,7 @@ name = "wait-timeout"
|
|||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -680,7 +717,7 @@ name = "wasm-bindgen-backend"
|
|||
version = "0.2.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bumpalo 3.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -742,7 +779,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80"
|
||||
"checksum bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb"
|
||||
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
"checksum bumpalo 3.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5fb8038c1ddc0a5f73787b130f4cc75151e96ed33e417fde765eb5a81e3532f4"
|
||||
"checksum bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742"
|
||||
"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
|
||||
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
@ -752,21 +789,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
|
||||
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
|
||||
"checksum indexmap 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b54058f0a6ff80b6803da8faf8997cde53872b38f4023728f6830b06cd3c0dc"
|
||||
"checksum indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292"
|
||||
"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
|
||||
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
|
||||
"checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"
|
||||
"checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b"
|
||||
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||
"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
|
||||
"checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc"
|
||||
"checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1"
|
||||
"checksum petgraph 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29c127eea4a29ec6c85d153c59dc1213f33ec74cead30fe4730aecc88cc1fd92"
|
||||
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
|
||||
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||
"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548"
|
||||
"checksum proptest 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bf6147d103a7c9d7598f4105cf049b15c99e2ecd93179bf024f0fd349be5ada4"
|
||||
"checksum proptest-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d31edb17edac73aeacc947bd61462dda15220584268896a58e12f053d767f15b"
|
||||
"checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
|
||||
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
||||
"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
|
@ -789,7 +828,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
"checksum rusty-fork 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3dd93264e10c577503e926bd1430193eeb5d21b059148910082245309b424fae"
|
||||
"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
|
||||
"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
|
||||
"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
|
||||
|
@ -802,10 +841,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum stdweb-derive 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
|
||||
"checksum stdweb-internal-macros 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
|
||||
"checksum stdweb-internal-runtime 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
|
||||
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
||||
"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5"
|
||||
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||
"checksum thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ee14bf8e6767ab4c687c9e8bc003879e042a96fd67a3ba5934eadb6536bef4db"
|
||||
"checksum thiserror-impl 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "a7b51e1fbc44b5a0840be594fbc0f960be09050f2617e61e6aa43bef97cd3ef4"
|
||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
"checksum wait-timeout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
|
||||
"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
|
|
@ -6,15 +6,15 @@ edition = "2018"
|
|||
|
||||
[dependencies]
|
||||
bimap = "0.4.0"
|
||||
lazy_static = "1.4.0"
|
||||
maplit = "1.0.2"
|
||||
petgraph = "0.5.0"
|
||||
proc-macro2 = "1.0.8"
|
||||
proptest = "0.9.5"
|
||||
proptest-derive = "0.1.2"
|
||||
quote = "1.0.2"
|
||||
serde = "1.0.104"
|
||||
serde_derive = "1.0.104"
|
||||
serde_json = "1.0.48"
|
||||
spin = "0.5.2"
|
||||
symbol = { path = "../symbol" }
|
||||
syn-serde = { path = "../syn-serde" }
|
||||
syn = { version = "1.0.14", features = ["extra-traits", "full"] }
|
||||
serde_json = "1.0.48"
|
||||
syn-serde = { path = "../syn-serde" }
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#[macro_use]
|
||||
extern crate quote;
|
||||
extern crate maplit;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
|
||||
use symbol::Symbol;
|
||||
use syn_serde::{Expr, Syn, Type};
|
||||
|
||||
pub type Id = Symbol;
|
||||
|
||||
pub type ModelMap = HashMap<Symbol, (Type, Expr)>;
|
||||
|
||||
pub fn convert_map<T: Hash + Eq, S: BuildHasher>(
|
||||
map: HashMap<T, (syn::Type, syn::Expr), S>,
|
||||
) -> HashMap<T, (Type, Expr)> {
|
||||
map.into_iter()
|
||||
.map(|(left, (ty, expr))| {
|
||||
let ty = ty.to_adapter();
|
||||
let expr = expr.to_adapter();
|
||||
(left, (ty, expr))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Component {
|
||||
pub name: String,
|
||||
#[serde(with = "crate::tuple_map")]
|
||||
pub model: ModelMap,
|
||||
pub view: Vec<Rsx>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum TagLhs {
|
||||
Bind(String),
|
||||
Plain(String),
|
||||
On(String),
|
||||
|
||||
#[doc(hidden)]
|
||||
_Nonexhaustive,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum TagRhs {
|
||||
Code(Expr),
|
||||
Text(String),
|
||||
}
|
||||
|
||||
impl Clone for TagRhs {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
TagRhs::Code(expr) => {
|
||||
let expr: syn::Expr = Syn::from_adapter(&*expr);
|
||||
TagRhs::Code(expr.to_adapter())
|
||||
}
|
||||
TagRhs::Text(string) => TagRhs::Text(string.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct Elem<T> {
|
||||
pub tag: String,
|
||||
#[serde(with = "crate::tuple_map")]
|
||||
pub attrs: HashMap<TagLhs, TagRhs>,
|
||||
pub inner: Vec<T>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum Rsx {
|
||||
Elem(Elem<Rsx>),
|
||||
Code(Expr),
|
||||
Text(String),
|
||||
|
||||
#[doc(hidden)]
|
||||
_Nonexhaustive,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TaggedRsx {
|
||||
Elem(Id, Elem<TaggedRsx>),
|
||||
Code(Id, Box<Expr>),
|
||||
Text(Id, String),
|
||||
|
||||
#[doc(hidden)]
|
||||
_Nonexhaustive,
|
||||
}
|
||||
|
||||
impl TaggedRsx {
|
||||
pub fn get_id(&self) -> Id {
|
||||
match self {
|
||||
TaggedRsx::Elem(id, _) | TaggedRsx::Code(id, _) | TaggedRsx::Text(id, _) => *id,
|
||||
_ => unimplemented!("tagged rsx"),
|
||||
}
|
||||
}
|
||||
}
|
192
enterprise-compiler/src/model/mod.rs
Normal file
192
enterprise-compiler/src/model/mod.rs
Normal file
|
@ -0,0 +1,192 @@
|
|||
mod props;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
use std::iter::{self, FromIterator};
|
||||
|
||||
use proc_macro2::{Span, TokenStream, TokenTree};
|
||||
use quote::ToTokens;
|
||||
use symbol::Symbol;
|
||||
use syn_serde::{Expr, Syn, Type};
|
||||
|
||||
pub use self::props::*;
|
||||
|
||||
pub type Id = Symbol;
|
||||
|
||||
pub type ModelMap = HashMap<Symbol, (Type, Expr)>;
|
||||
|
||||
pub fn convert_map<T: Hash + Eq, S: BuildHasher>(
|
||||
map: HashMap<T, (syn::Type, syn::Expr), S>,
|
||||
) -> HashMap<T, (Type, Expr)> {
|
||||
map.into_iter()
|
||||
.map(|(left, (ty, expr))| {
|
||||
let ty = ty.to_adapter();
|
||||
let expr = expr.to_adapter();
|
||||
(left, (ty, expr))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Component {
|
||||
pub name: String,
|
||||
#[serde(with = "crate::tuple_map")]
|
||||
pub model: ModelMap,
|
||||
pub view: Vec<Rsx>,
|
||||
}
|
||||
|
||||
impl ToTokens for Component {
|
||||
fn to_tokens(&self, stream: &mut TokenStream) {
|
||||
let model = TokenStream::from_iter(self.model.iter().map(|(name, (ty, init))| {
|
||||
let name = format_ident!("{}", name.as_str());
|
||||
let ty = syn::Type::from_adapter(ty);
|
||||
let init = syn::Expr::from_adapter(init);
|
||||
quote! { #name : #ty = #init , }
|
||||
}));
|
||||
let view = TokenStream::from_iter(self.view.iter().map(|rsx| rsx.to_token_stream()));
|
||||
stream.extend(quote! {
|
||||
component {
|
||||
model { #model }
|
||||
view { #view }
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum TagLhs {
|
||||
Bind(String),
|
||||
Plain(String),
|
||||
On(String),
|
||||
|
||||
#[doc(hidden)]
|
||||
_Nonexhaustive,
|
||||
}
|
||||
|
||||
impl ToTokens for TagLhs {
|
||||
fn to_tokens(&self, stream: &mut TokenStream) {
|
||||
match self {
|
||||
TagLhs::Bind(name) => {
|
||||
let name = format_ident!("{}", name);
|
||||
stream.extend(quote! { bind : #name });
|
||||
}
|
||||
TagLhs::Plain(name) => {
|
||||
let name = format_ident!("{}", name);
|
||||
stream.extend(iter::once(TokenTree::from(name)));
|
||||
}
|
||||
TagLhs::On(name) => {
|
||||
let name = format_ident!("{}", name);
|
||||
stream.extend(quote! { on : #name });
|
||||
}
|
||||
TagLhs::_Nonexhaustive => unreachable!("should never be constructed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum TagRhs {
|
||||
Code(Expr),
|
||||
Text(String),
|
||||
}
|
||||
|
||||
impl Clone for TagRhs {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
TagRhs::Code(expr) => {
|
||||
let expr: syn::Expr = Syn::from_adapter(&*expr);
|
||||
TagRhs::Code(expr.to_adapter())
|
||||
}
|
||||
TagRhs::Text(string) => TagRhs::Text(string.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for TagRhs {
|
||||
fn to_tokens(&self, stream: &mut TokenStream) {
|
||||
match self {
|
||||
TagRhs::Code(expr) => {
|
||||
let expr = syn::Expr::from_adapter(expr);
|
||||
stream.extend(quote! { { #expr } });
|
||||
}
|
||||
TagRhs::Text(string) => {
|
||||
let string = syn::Lit::Str(syn::LitStr::new(string.as_ref(), Span::call_site()));
|
||||
stream.extend(quote! { #string });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct Elem<T> {
|
||||
pub tag: String,
|
||||
#[serde(with = "crate::tuple_map")]
|
||||
pub attrs: HashMap<TagLhs, TagRhs>,
|
||||
pub inner: Option<Vec<T>>,
|
||||
}
|
||||
|
||||
impl<T: ToTokens> ToTokens for Elem<T> {
|
||||
fn to_tokens(&self, stream: &mut TokenStream) {
|
||||
let tag = format_ident!("{}", self.tag);
|
||||
stream.extend(quote! { < #tag });
|
||||
for (lhs, rhs) in self.attrs.iter() {
|
||||
stream.extend(quote! { #lhs = #rhs })
|
||||
}
|
||||
if let Some(ref inner) = self.inner {
|
||||
stream.extend(quote! { > });
|
||||
for rsx in inner {
|
||||
rsx.to_tokens(stream);
|
||||
}
|
||||
stream.extend(quote! { < / #tag > });
|
||||
} else {
|
||||
stream.extend(quote! { / > });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum Rsx {
|
||||
Elem(Elem<Rsx>),
|
||||
Code(Expr),
|
||||
Text(String),
|
||||
|
||||
#[doc(hidden)]
|
||||
_Nonexhaustive,
|
||||
}
|
||||
|
||||
impl ToTokens for Rsx {
|
||||
fn to_tokens(&self, stream: &mut TokenStream) {
|
||||
match self {
|
||||
Rsx::Elem(elem) => {
|
||||
stream.extend(quote! { #elem });
|
||||
}
|
||||
Rsx::Code(expr) => {
|
||||
let expr = syn::Expr::from_adapter(expr);
|
||||
stream.extend(quote! { #expr });
|
||||
}
|
||||
Rsx::Text(string) => {
|
||||
let string = syn::Lit::Str(syn::LitStr::new(string.as_ref(), Span::call_site()));
|
||||
stream.extend(quote! { #string });
|
||||
}
|
||||
Rsx::_Nonexhaustive => unreachable!("should never be constructed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TaggedRsx {
|
||||
Elem(Id, Elem<TaggedRsx>),
|
||||
Code(Id, Box<Expr>),
|
||||
Text(Id, String),
|
||||
|
||||
#[doc(hidden)]
|
||||
_Nonexhaustive,
|
||||
}
|
||||
|
||||
impl TaggedRsx {
|
||||
pub fn get_id(&self) -> Id {
|
||||
match self {
|
||||
TaggedRsx::Elem(id, _) | TaggedRsx::Code(id, _) | TaggedRsx::Text(id, _) => *id,
|
||||
_ => unimplemented!("tagged rsx"),
|
||||
}
|
||||
}
|
||||
}
|
57
enterprise-compiler/src/model/props.rs
Normal file
57
enterprise-compiler/src/model/props.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use proptest::{
|
||||
collection::{hash_map, vec, SizeRange},
|
||||
option::{self, Probability},
|
||||
prelude::*,
|
||||
string::string_regex,
|
||||
};
|
||||
use symbol::Symbol;
|
||||
use syn::{Expr, Type};
|
||||
use syn_serde::Syn;
|
||||
|
||||
use super::{Component, Elem, Rsx};
|
||||
|
||||
prop_compose! {
|
||||
pub fn arbitrary_component() (
|
||||
name in ident_strategy(),
|
||||
model in hash_map(
|
||||
ident_strategy().prop_map(|ident| Symbol::from(ident)),
|
||||
// TODO: maybe actually have tests for syn?
|
||||
(Just(syn::parse_str::<Type>("()").unwrap().to_adapter()), Just(syn::parse_str::<Expr>("()").unwrap().to_adapter())),
|
||||
SizeRange::default()),
|
||||
view in vec(arbitrary_view(), SizeRange::default()),
|
||||
) -> Component {
|
||||
Component {
|
||||
name,
|
||||
model,
|
||||
view,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arbitrary_view() -> impl Strategy<Value = Rsx> {
|
||||
let leaf = prop_oneof![
|
||||
Just(Rsx::Code(
|
||||
syn::parse_str::<Expr>("()").unwrap().to_adapter()
|
||||
)),
|
||||
any::<String>().prop_map(Rsx::Text),
|
||||
];
|
||||
leaf.prop_recursive(4, 16, 5, |inner| {
|
||||
prop_oneof![(
|
||||
ident_strategy(),
|
||||
option::weighted(Probability::new(0.9), vec(inner, SizeRange::default())),
|
||||
)
|
||||
.prop_map(|(tag, inner)| Rsx::Elem(Elem {
|
||||
tag,
|
||||
// TODO: ouais
|
||||
attrs: HashMap::new(),
|
||||
inner,
|
||||
}))]
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ident_strategy() -> impl Strategy<Value = String> {
|
||||
// https://doc.rust-lang.org/reference/identifiers.html
|
||||
string_regex(r"([A-Za-z][A-Za-z0-9_]*)|(_[A-Za-z0-9_]+)").unwrap()
|
||||
}
|
|
@ -95,7 +95,7 @@ impl Visitor {
|
|||
let node_id = Symbol::gensym();
|
||||
let new_node = match node {
|
||||
Rsx::Elem(Elem { tag, attrs, inner }) => {
|
||||
let tag_inner = self.make_graph(&inner);
|
||||
let tag_inner = inner.as_ref().map(|inner| self.make_graph(inner));
|
||||
for (lhs, rhs) in attrs {
|
||||
if let TagLhs::Bind(attr) = lhs {
|
||||
if let TagRhs::Text(text) = rhs {
|
||||
|
@ -186,7 +186,9 @@ impl Visitor {
|
|||
el
|
||||
}
|
||||
});
|
||||
self.gen_code(&inner);
|
||||
if let Some(inner) = inner {
|
||||
self.gen_code(inner);
|
||||
}
|
||||
}
|
||||
TaggedRsx::Code(_, _) => {
|
||||
self.impl_code.extend(quote! {
|
||||
|
|
|
@ -7,6 +7,9 @@ edition = "2018"
|
|||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dev-dependencies]
|
||||
proptest = "0.9.5"
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = { version = "1.0.7", features = ["span-locations"] }
|
||||
quote = "1.0.2"
|
||||
|
|
7
enterprise-macros/proptest-regressions/lib.txt
Normal file
7
enterprise-macros/proptest-regressions/lib.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Seeds for failure cases proptest has generated in the past. It is
|
||||
# automatically read and these particular cases re-run before any
|
||||
# novel cases are generated.
|
||||
#
|
||||
# It is recommended to check this file in to source control so that
|
||||
# everyone who runs the test benefits from these saved cases.
|
||||
cc 2a51d2b2a9a6442d273f2835de2bc13bc778cab5d1333e190cc6fb90c5d5e50a # shrinks to tree = Component { name: "A", model: {}, view: [] }
|
|
@ -2,297 +2,12 @@ extern crate proc_macro;
|
|||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
mod parser;
|
||||
mod rsx;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::iter::FromIterator;
|
||||
use std::iter::Peekable;
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
use enterprise_compiler::model::{Component, Elem, ModelMap, Rsx};
|
||||
use proc_macro2::{
|
||||
token_stream::IntoIter, Delimiter, Group, Ident, Punct, Spacing, TokenStream, TokenTree,
|
||||
};
|
||||
use symbol::Symbol;
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
Error as SynError, Expr, Result as SynResult, Token, Type,
|
||||
};
|
||||
use syn_serde::Syn;
|
||||
|
||||
use crate::rsx::{RsxParser, RsxToken};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ParseError {
|
||||
ExpectedKeyword(Symbol, Ident),
|
||||
ExpectedIdent(TokenTree),
|
||||
ExpectedGroup(TokenTree),
|
||||
ExpectedPunct(TokenTree),
|
||||
WrongDelimiter(Delimiter, Delimiter),
|
||||
WrongPunct(char, Punct),
|
||||
Syn(SynError),
|
||||
UnexpectedEOF,
|
||||
UnexpectedKeyword,
|
||||
MissingModel,
|
||||
MissingView,
|
||||
// InvalidRsx(TokenTree),
|
||||
UnmatchedOpenTag(TokenTree),
|
||||
}
|
||||
|
||||
impl From<SynError> for ParseError {
|
||||
fn from(err: SynError) -> Self {
|
||||
ParseError::Syn(err)
|
||||
}
|
||||
}
|
||||
|
||||
enum ComponentBlock {
|
||||
Model(ModelMap),
|
||||
View(Vec<Rsx>),
|
||||
}
|
||||
|
||||
struct Visitor(Peekable<IntoIter>);
|
||||
|
||||
impl Visitor {
|
||||
fn from_tokens(stream: TokenStream) -> Self {
|
||||
Visitor(stream.into_iter().peekable())
|
||||
}
|
||||
|
||||
fn consume_component(&mut self) -> Result<Option<Component>, ParseError> {
|
||||
if let None = self.0.peek() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
self.consume_keyword("component")?;
|
||||
let name = consume_ident(&mut self.0)?.to_string();
|
||||
let def = self.consume_group(Delimiter::Brace)?;
|
||||
let mut def_visitor = Visitor::from_tokens(def.stream());
|
||||
let mut model_map = None;
|
||||
let mut view = None;
|
||||
while let Some(block) = def_visitor.next_inner_block()? {
|
||||
match block {
|
||||
ComponentBlock::Model(inner) => model_map = Some(inner),
|
||||
ComponentBlock::View(inner) => view = Some(inner),
|
||||
}
|
||||
}
|
||||
|
||||
let model = match model_map {
|
||||
Some(model_map) => model_map,
|
||||
None => return Err(ParseError::MissingModel),
|
||||
};
|
||||
let view = match view {
|
||||
Some(view) => view,
|
||||
None => return Err(ParseError::MissingView),
|
||||
};
|
||||
Ok(Some(Component { name, model, view }))
|
||||
}
|
||||
|
||||
fn next_inner_block(&mut self) -> Result<Option<ComponentBlock>, ParseError> {
|
||||
let next_token = self.0.peek();
|
||||
if next_token.is_none() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let next_ident = consume_ident(&mut self.0)?;
|
||||
match next_ident.to_string().as_ref() {
|
||||
"model" => {
|
||||
let next_group = self.consume_group(Delimiter::Brace)?;
|
||||
let mut model_visitor = Visitor::from_tokens(next_group.stream());
|
||||
let model_map = model_visitor.consume_model_map()?;
|
||||
Ok(Some(ComponentBlock::Model(model_map)))
|
||||
}
|
||||
"view" => {
|
||||
let next_group = self.consume_group(Delimiter::Brace)?;
|
||||
let mut view_visitor = Visitor::from_tokens(next_group.stream());
|
||||
let view = view_visitor.consume_view()?;
|
||||
Ok(Some(ComponentBlock::View(view)))
|
||||
}
|
||||
_ => Err(ParseError::UnexpectedKeyword),
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_model_map(&mut self) -> Result<ModelMap, ParseError> {
|
||||
#[derive(Debug)]
|
||||
struct ModelEntry {
|
||||
name: Ident,
|
||||
colon: Token![:],
|
||||
ty: Type,
|
||||
eq: Token![=],
|
||||
init: Expr,
|
||||
}
|
||||
|
||||
impl Parse for ModelEntry {
|
||||
fn parse(input: ParseStream) -> SynResult<Self> {
|
||||
Ok(ModelEntry {
|
||||
name: input.parse()?,
|
||||
colon: input.parse()?,
|
||||
ty: input.parse()?,
|
||||
eq: input.parse()?,
|
||||
init: input.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let mut single_def = || -> Result<Option<(Symbol, Type, Expr, bool)>, ParseError> {
|
||||
let next_token = self.0.peek();
|
||||
if next_token.is_none() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// read until comma or end
|
||||
let mut buf = Vec::new();
|
||||
let mut hit_comma = false;
|
||||
loop {
|
||||
let next_token = self.0.peek();
|
||||
if next_token.is_none() {
|
||||
break;
|
||||
}
|
||||
let next_token = self.0.next().expect("unreachable");
|
||||
if let TokenTree::Punct(ref punct) = next_token {
|
||||
if punct.as_char() == ',' && punct.spacing() == Spacing::Alone {
|
||||
hit_comma = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
buf.push(next_token);
|
||||
}
|
||||
|
||||
// probably shouldn't happen?
|
||||
if buf.len() == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let stream = TokenStream::from_iter(buf);
|
||||
let item = syn::parse2::<ModelEntry>(stream)?;
|
||||
// println!("ITEM: {:?}", item);
|
||||
|
||||
Ok(Some((
|
||||
Symbol::from(item.name.to_string()),
|
||||
item.ty,
|
||||
item.init,
|
||||
hit_comma,
|
||||
)))
|
||||
};
|
||||
|
||||
let mut map = HashMap::new();
|
||||
while let Some((name, ty, init, comma)) = single_def()? {
|
||||
map.insert(name, (ty, init));
|
||||
if comma {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(enterprise_compiler::model::convert_map(map))
|
||||
}
|
||||
|
||||
fn consume_view(&mut self) -> Result<Vec<Rsx>, ParseError> {
|
||||
let mut rsx_parser = RsxParser::new(self.0.clone());
|
||||
let mut result = Vec::new();
|
||||
|
||||
while let Some(next_token) = rsx_parser.next() {
|
||||
match next_token? {
|
||||
RsxToken::EmptyTag(name, attrs) => {
|
||||
let elem = Elem {
|
||||
tag: name.to_string(),
|
||||
attrs,
|
||||
inner: vec![],
|
||||
};
|
||||
let el = Rsx::Elem(elem);
|
||||
result.push(el);
|
||||
}
|
||||
RsxToken::Code(expr) => {
|
||||
result.push(Rsx::Code(expr.to_adapter()));
|
||||
}
|
||||
RsxToken::Str(string) => {
|
||||
result.push(Rsx::Text(string));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn consume_keyword(&mut self, keyword: impl AsRef<str>) -> Result<(), ParseError> {
|
||||
let keyword = keyword.as_ref();
|
||||
let ident = consume_ident(&mut self.0)?;
|
||||
let ident_str = ident.to_string();
|
||||
|
||||
if keyword == &ident_str {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ParseError::ExpectedKeyword(Symbol::from(keyword), ident))
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_group(&mut self, delimiter: Delimiter) -> Result<Group, ParseError> {
|
||||
let next_token = self.0.peek();
|
||||
if next_token.is_none() {
|
||||
return Err(ParseError::UnexpectedEOF);
|
||||
}
|
||||
|
||||
let next_token = self.0.next().expect("unreachable");
|
||||
if let TokenTree::Group(group) = next_token {
|
||||
if delimiter == group.delimiter() {
|
||||
Ok(group)
|
||||
} else {
|
||||
Err(ParseError::WrongDelimiter(delimiter, group.delimiter()))
|
||||
}
|
||||
} else {
|
||||
Err(ParseError::ExpectedGroup(next_token))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_punct(
|
||||
iter: &mut Peekable<impl Iterator<Item = TokenTree>>,
|
||||
equals: Option<char>,
|
||||
) -> Result<Punct, ParseError> {
|
||||
let next_token = iter.peek();
|
||||
if next_token.is_none() {
|
||||
return Err(ParseError::UnexpectedEOF);
|
||||
}
|
||||
|
||||
let next_token = iter.next().expect("unreachable");
|
||||
if let TokenTree::Punct(punct) = next_token {
|
||||
if let Some(equals) = equals {
|
||||
if punct.as_char() == equals {
|
||||
Ok(punct)
|
||||
} else {
|
||||
Err(ParseError::WrongPunct(equals, punct))
|
||||
}
|
||||
} else {
|
||||
Ok(punct)
|
||||
}
|
||||
} else {
|
||||
Err(ParseError::ExpectedPunct(next_token))
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_ident(
|
||||
iter: &mut Peekable<impl Iterator<Item = TokenTree>>,
|
||||
) -> Result<Ident, ParseError> {
|
||||
let next_token = iter.peek();
|
||||
if next_token.is_none() {
|
||||
return Err(ParseError::UnexpectedEOF);
|
||||
}
|
||||
|
||||
let next_token = iter.next().expect("unreachable");
|
||||
if let TokenTree::Ident(ident) = next_token {
|
||||
Ok(ident)
|
||||
} else {
|
||||
Err(ParseError::ExpectedIdent(next_token))
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Visitor {
|
||||
type Item = Result<Component, ParseError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.consume_component() {
|
||||
Ok(Some(component)) => Some(Ok(component)),
|
||||
Ok(None) => None,
|
||||
Err(err) => Some(Err(err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::parser::Visitor;
|
||||
|
||||
#[proc_macro]
|
||||
pub fn component(input_tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
|
|
309
enterprise-macros/src/parser.rs
Normal file
309
enterprise-macros/src/parser.rs
Normal file
|
@ -0,0 +1,309 @@
|
|||
use std::collections::HashMap;
|
||||
use std::iter::FromIterator;
|
||||
use std::iter::Peekable;
|
||||
|
||||
use enterprise_compiler::model::{Component, Elem, ModelMap, Rsx};
|
||||
use proc_macro2::{
|
||||
token_stream::IntoIter, Delimiter, Group, Ident, Punct, Spacing, TokenStream, TokenTree,
|
||||
};
|
||||
use symbol::Symbol;
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
Error as SynError, Expr, Result as SynResult, Token, Type,
|
||||
};
|
||||
use syn_serde::Syn;
|
||||
|
||||
use crate::rsx::{RsxParser, RsxToken};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ParseError {
|
||||
ExpectedKeyword(Symbol, Ident),
|
||||
ExpectedIdent(TokenTree),
|
||||
ExpectedGroup(TokenTree),
|
||||
ExpectedPunct(TokenTree),
|
||||
WrongDelimiter(Delimiter, Delimiter),
|
||||
WrongPunct(char, Punct),
|
||||
Syn(SynError),
|
||||
UnexpectedEOF,
|
||||
UnexpectedKeyword,
|
||||
MissingModel,
|
||||
MissingView,
|
||||
// InvalidRsx(TokenTree),
|
||||
UnmatchedOpenTag(TokenTree),
|
||||
}
|
||||
|
||||
impl From<SynError> for ParseError {
|
||||
fn from(err: SynError) -> Self {
|
||||
ParseError::Syn(err)
|
||||
}
|
||||
}
|
||||
|
||||
enum ComponentBlock {
|
||||
Model(ModelMap),
|
||||
View(Vec<Rsx>),
|
||||
}
|
||||
|
||||
pub(crate) struct Visitor(Peekable<IntoIter>);
|
||||
|
||||
impl Visitor {
|
||||
pub fn from_tokens(stream: TokenStream) -> Self {
|
||||
Visitor(stream.into_iter().peekable())
|
||||
}
|
||||
|
||||
fn consume_component(&mut self) -> Result<Option<Component>, ParseError> {
|
||||
if let None = self.0.peek() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
self.consume_keyword("component")?;
|
||||
let name = consume_ident(&mut self.0)?.to_string();
|
||||
let def = self.consume_group(Delimiter::Brace)?;
|
||||
let mut def_visitor = Visitor::from_tokens(def.stream());
|
||||
let mut model_map = None;
|
||||
let mut view = None;
|
||||
while let Some(block) = def_visitor.next_inner_block()? {
|
||||
match block {
|
||||
ComponentBlock::Model(inner) => model_map = Some(inner),
|
||||
ComponentBlock::View(inner) => view = Some(inner),
|
||||
}
|
||||
}
|
||||
|
||||
let model = match model_map {
|
||||
Some(model_map) => model_map,
|
||||
None => return Err(ParseError::MissingModel),
|
||||
};
|
||||
let view = match view {
|
||||
Some(view) => view,
|
||||
None => return Err(ParseError::MissingView),
|
||||
};
|
||||
Ok(Some(Component { name, model, view }))
|
||||
}
|
||||
|
||||
fn next_inner_block(&mut self) -> Result<Option<ComponentBlock>, ParseError> {
|
||||
let next_token = self.0.peek();
|
||||
if next_token.is_none() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let next_ident = consume_ident(&mut self.0)?;
|
||||
match next_ident.to_string().as_ref() {
|
||||
"model" => {
|
||||
let next_group = self.consume_group(Delimiter::Brace)?;
|
||||
let mut model_visitor = Visitor::from_tokens(next_group.stream());
|
||||
let model_map = model_visitor.consume_model_map()?;
|
||||
Ok(Some(ComponentBlock::Model(model_map)))
|
||||
}
|
||||
"view" => {
|
||||
let next_group = self.consume_group(Delimiter::Brace)?;
|
||||
let mut view_visitor = Visitor::from_tokens(next_group.stream());
|
||||
let view = view_visitor.consume_view()?;
|
||||
Ok(Some(ComponentBlock::View(view)))
|
||||
}
|
||||
_ => Err(ParseError::UnexpectedKeyword),
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_model_map(&mut self) -> Result<ModelMap, ParseError> {
|
||||
#[derive(Debug)]
|
||||
struct ModelEntry {
|
||||
name: Ident,
|
||||
colon: Token![:],
|
||||
ty: Type,
|
||||
eq: Token![=],
|
||||
init: Expr,
|
||||
}
|
||||
|
||||
impl Parse for ModelEntry {
|
||||
fn parse(input: ParseStream) -> SynResult<Self> {
|
||||
Ok(ModelEntry {
|
||||
name: input.parse()?,
|
||||
colon: input.parse()?,
|
||||
ty: input.parse()?,
|
||||
eq: input.parse()?,
|
||||
init: input.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let mut single_def = || -> Result<Option<(Symbol, Type, Expr, bool)>, ParseError> {
|
||||
let next_token = self.0.peek();
|
||||
if next_token.is_none() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// read until comma or end
|
||||
let mut buf = Vec::new();
|
||||
let mut hit_comma = false;
|
||||
loop {
|
||||
let next_token = self.0.peek();
|
||||
if next_token.is_none() {
|
||||
break;
|
||||
}
|
||||
let next_token = self.0.next().expect("unreachable");
|
||||
if let TokenTree::Punct(ref punct) = next_token {
|
||||
if punct.as_char() == ',' && punct.spacing() == Spacing::Alone {
|
||||
hit_comma = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
buf.push(next_token);
|
||||
}
|
||||
|
||||
// probably shouldn't happen?
|
||||
if buf.len() == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let stream = TokenStream::from_iter(buf);
|
||||
let item = syn::parse2::<ModelEntry>(stream)?;
|
||||
// println!("ITEM: {:?}", item);
|
||||
|
||||
Ok(Some((
|
||||
Symbol::from(item.name.to_string()),
|
||||
item.ty,
|
||||
item.init,
|
||||
hit_comma,
|
||||
)))
|
||||
};
|
||||
|
||||
let mut map = HashMap::new();
|
||||
while let Some((name, ty, init, comma)) = single_def()? {
|
||||
map.insert(name, (ty, init));
|
||||
if comma {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(enterprise_compiler::model::convert_map(map))
|
||||
}
|
||||
|
||||
fn consume_view(&mut self) -> Result<Vec<Rsx>, ParseError> {
|
||||
let mut rsx_parser = RsxParser::new(self.0.clone());
|
||||
let mut result = Vec::new();
|
||||
|
||||
while let Some(next_token) = rsx_parser.next() {
|
||||
match next_token? {
|
||||
RsxToken::EmptyTag(name, attrs) => {
|
||||
let elem = Elem {
|
||||
tag: name.to_string(),
|
||||
attrs,
|
||||
inner: None,
|
||||
};
|
||||
let el = Rsx::Elem(elem);
|
||||
result.push(el);
|
||||
}
|
||||
RsxToken::Code(expr) => {
|
||||
result.push(Rsx::Code(expr.to_adapter()));
|
||||
}
|
||||
RsxToken::Str(string) => {
|
||||
result.push(Rsx::Text(string));
|
||||
}
|
||||
unknown => unimplemented!("rsx token: {:?}", unknown),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn consume_keyword(&mut self, keyword: impl AsRef<str>) -> Result<(), ParseError> {
|
||||
let keyword = keyword.as_ref();
|
||||
let ident = consume_ident(&mut self.0)?;
|
||||
let ident_str = ident.to_string();
|
||||
|
||||
if keyword == &ident_str {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ParseError::ExpectedKeyword(Symbol::from(keyword), ident))
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_group(&mut self, delimiter: Delimiter) -> Result<Group, ParseError> {
|
||||
let next_token = self.0.peek();
|
||||
if next_token.is_none() {
|
||||
return Err(ParseError::UnexpectedEOF);
|
||||
}
|
||||
|
||||
let next_token = self.0.next().expect("unreachable");
|
||||
if let TokenTree::Group(group) = next_token {
|
||||
if delimiter == group.delimiter() {
|
||||
Ok(group)
|
||||
} else {
|
||||
Err(ParseError::WrongDelimiter(delimiter, group.delimiter()))
|
||||
}
|
||||
} else {
|
||||
Err(ParseError::ExpectedGroup(next_token))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn consume_punct(
|
||||
iter: &mut Peekable<impl Iterator<Item = TokenTree>>,
|
||||
equals: Option<char>,
|
||||
) -> Result<Punct, ParseError> {
|
||||
let next_token = iter.peek();
|
||||
if next_token.is_none() {
|
||||
return Err(ParseError::UnexpectedEOF);
|
||||
}
|
||||
|
||||
let next_token = iter.next().expect("unreachable");
|
||||
if let TokenTree::Punct(punct) = next_token {
|
||||
if let Some(equals) = equals {
|
||||
if punct.as_char() == equals {
|
||||
Ok(punct)
|
||||
} else {
|
||||
Err(ParseError::WrongPunct(equals, punct))
|
||||
}
|
||||
} else {
|
||||
Ok(punct)
|
||||
}
|
||||
} else {
|
||||
Err(ParseError::ExpectedPunct(next_token))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn consume_ident(
|
||||
iter: &mut Peekable<impl Iterator<Item = TokenTree>>,
|
||||
) -> Result<Ident, ParseError> {
|
||||
let next_token = iter.peek();
|
||||
if next_token.is_none() {
|
||||
return Err(ParseError::UnexpectedEOF);
|
||||
}
|
||||
|
||||
let next_token = iter.next().expect("unreachable");
|
||||
if let TokenTree::Ident(ident) = next_token {
|
||||
Ok(ident)
|
||||
} else {
|
||||
Err(ParseError::ExpectedIdent(next_token))
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Visitor {
|
||||
type Item = Result<Component, ParseError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.consume_component() {
|
||||
Ok(Some(component)) => Some(Ok(component)),
|
||||
Ok(None) => None,
|
||||
Err(err) => Some(Err(err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use enterprise_compiler::model::*;
|
||||
use proptest::prelude::*;
|
||||
use quote::ToTokens;
|
||||
use super::Visitor;
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn tokens_parse_compatibility(tree in arbitrary_component()) {
|
||||
let tokens = tree.to_token_stream();
|
||||
|
||||
let mut visitor = Visitor::from_tokens(tokens.clone());
|
||||
let tree2 = visitor.next().unwrap().unwrap();
|
||||
|
||||
prop_assert_eq!(format!("{}", tokens), format!("{}", tree2.to_token_stream()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,8 +8,7 @@ use symbol::Symbol;
|
|||
use syn::{Expr, Lit};
|
||||
use syn_serde::Syn;
|
||||
|
||||
use crate::ParseError;
|
||||
use crate::{consume_ident, consume_punct};
|
||||
use crate::parser::{ParseError, consume_ident, consume_punct};
|
||||
|
||||
pub(crate) struct RsxParser(Peekable<IntoIter>);
|
||||
|
||||
|
|
|
@ -8,8 +8,12 @@ component! {
|
|||
}
|
||||
|
||||
view {
|
||||
<input bind:value="name" />
|
||||
"Hello, " {name} "!"
|
||||
<input on:submit={|evt| { todos.push(evt.name); }} />
|
||||
<ul>
|
||||
{#for (key, todo) in todos}
|
||||
<li>{todo} <a on:click={|_| { todos.remove(key); }}>[x]</a></li>
|
||||
{/for}
|
||||
</ul>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
9
src/forloop.rs
Normal file
9
src/forloop.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
// TODO: move this to a location that makes sense
|
||||
|
||||
use crate::std::List;
|
||||
|
||||
trait ForEachable {}
|
||||
|
||||
impl<T> ForEachable for Vec<T> {}
|
||||
|
||||
impl<T> ForEachable for List<T> {}
|
|
@ -11,6 +11,8 @@ pub extern crate stdweb;
|
|||
mod backend;
|
||||
pub mod std;
|
||||
|
||||
mod forloop;
|
||||
|
||||
pub use crate::backend::{Backend, Web};
|
||||
|
||||
/// Components are the building-blocks of enterprise applications.
|
||||
|
|
|
@ -11,7 +11,6 @@ ast_struct! {
|
|||
/// the XID_Start property.
|
||||
/// - All following characters must be Unicode code points with the XID_Continue
|
||||
/// property.
|
||||
#[derive(Clone)]
|
||||
#[serde(transparent)]
|
||||
pub struct Lifetime {
|
||||
pub(crate) ident: Ident,
|
||||
|
|
|
@ -3,7 +3,7 @@ macro_rules! ast_struct {
|
|||
[$($attrs_pub:tt)*]
|
||||
struct $name:ident $($rest:tt)*
|
||||
) => {
|
||||
#[derive(Debug, crate::Serialize, crate::Deserialize)]
|
||||
#[derive(Clone, Debug, crate::Serialize, crate::Deserialize)]
|
||||
$($attrs_pub)* struct $name $($rest)*
|
||||
};
|
||||
|
||||
|
@ -17,7 +17,7 @@ macro_rules! ast_enum {
|
|||
[$($attrs_pub:tt)*]
|
||||
enum $name:ident $($rest:tt)*
|
||||
) => (
|
||||
#[derive(Debug, crate::Serialize, crate::Deserialize)]
|
||||
#[derive(Clone, Debug, crate::Serialize, crate::Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
$($attrs_pub)* enum $name $($rest)*
|
||||
);
|
||||
|
|
|
@ -6,7 +6,7 @@ ast_struct! {
|
|||
///
|
||||
/// This type provides interfaces for iterating over token trees and for
|
||||
/// collecting token trees into one stream.
|
||||
#[derive(Clone, Default)]
|
||||
#[derive(Default)]
|
||||
#[serde(transparent)]
|
||||
pub struct TokenStream {
|
||||
inner: Vec<TokenTree>,
|
||||
|
@ -25,7 +25,6 @@ impl TokenStream {
|
|||
|
||||
ast_enum! {
|
||||
/// A single token or a delimited sequence of token trees (e.g. `[1, (), ..]`).
|
||||
#[derive(Clone)]
|
||||
pub enum TokenTree {
|
||||
/// A token stream surrounded by bracket delimiters.
|
||||
Group(Group),
|
||||
|
@ -44,7 +43,6 @@ ast_struct! {
|
|||
///
|
||||
/// A `Group` internally contains a `TokenStream` which is surrounded by
|
||||
/// `Delimiter`s.
|
||||
#[derive(Clone)]
|
||||
pub struct Group {
|
||||
delimiter: Delimiter,
|
||||
stream: TokenStream,
|
||||
|
@ -53,7 +51,7 @@ ast_struct! {
|
|||
|
||||
ast_enum! {
|
||||
/// Describes how a sequence of token trees is delimited.
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Copy)]
|
||||
pub enum Delimiter {
|
||||
/// `( ... )`
|
||||
Parenthesis,
|
||||
|
@ -77,7 +75,7 @@ ast_struct! {
|
|||
///
|
||||
/// Multicharacter operators like `+=` are represented as two instances of
|
||||
/// `Punct` with different forms of `Spacing` returned.
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Copy)]
|
||||
pub struct Punct {
|
||||
op: char,
|
||||
spacing: Spacing,
|
||||
|
@ -87,7 +85,7 @@ ast_struct! {
|
|||
ast_enum! {
|
||||
/// Whether an `Punct` is followed immediately by another `Punct` or followed by
|
||||
/// another token or whitespace.
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Copy)]
|
||||
pub enum Spacing {
|
||||
/// E.g. `+` is `Alone` in `+ =`, `+ident` or `+()`.
|
||||
Alone,
|
||||
|
@ -108,7 +106,7 @@ ast_struct! {
|
|||
///
|
||||
/// - The empty string is not an identifier. Use `Option<Ident>`.
|
||||
/// - A lifetime is not an identifier. Use `syn::Lifetime` instead.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
#[derive(Eq, PartialEq)]
|
||||
#[serde(transparent)]
|
||||
pub struct Ident {
|
||||
inner: String,
|
||||
|
@ -122,7 +120,6 @@ ast_struct! {
|
|||
///
|
||||
/// Boolean literals like `true` and `false` do not belong here, they are
|
||||
/// `Ident`s.
|
||||
#[derive(Clone)]
|
||||
#[serde(transparent)]
|
||||
pub struct Literal {
|
||||
pub(crate) text: String,
|
||||
|
|
Loading…
Reference in a new issue