From f9c478276d1f99ef46136ff5e6c08342e488ea61 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Fri, 21 Feb 2020 00:03:25 -0600 Subject: [PATCH] proptests for parser --- Cargo.lock | 95 ++++-- enterprise-compiler/Cargo.toml | 8 +- enterprise-compiler/src/lib.rs | 1 - enterprise-compiler/src/model.rs | 94 ------ enterprise-compiler/src/model/mod.rs | 192 +++++++++++ enterprise-compiler/src/model/props.rs | 57 ++++ enterprise-compiler/src/visitor.rs | 6 +- enterprise-macros/Cargo.toml | 3 + .../proptest-regressions/lib.txt | 7 + enterprise-macros/src/lib.rs | 291 +---------------- enterprise-macros/src/parser.rs | 309 ++++++++++++++++++ enterprise-macros/src/rsx.rs | 3 +- examples/todomvc/src/build.rs | 8 +- src/forloop.rs | 9 + src/lib.rs | 2 + syn-serde/src/lifetime.rs | 1 - syn-serde/src/macros.rs | 4 +- syn-serde/src/token_stream.rs | 13 +- 18 files changed, 672 insertions(+), 431 deletions(-) delete mode 100644 enterprise-compiler/src/model.rs create mode 100644 enterprise-compiler/src/model/mod.rs create mode 100644 enterprise-compiler/src/model/props.rs create mode 100644 enterprise-macros/proptest-regressions/lib.txt create mode 100644 enterprise-macros/src/parser.rs create mode 100644 src/forloop.rs diff --git a/Cargo.lock b/Cargo.lock index 3ade300..0cbd771 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/enterprise-compiler/Cargo.toml b/enterprise-compiler/Cargo.toml index 7bff3dd..25f8041 100644 --- a/enterprise-compiler/Cargo.toml +++ b/enterprise-compiler/Cargo.toml @@ -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" } diff --git a/enterprise-compiler/src/lib.rs b/enterprise-compiler/src/lib.rs index b8403b7..7167090 100644 --- a/enterprise-compiler/src/lib.rs +++ b/enterprise-compiler/src/lib.rs @@ -1,6 +1,5 @@ #[macro_use] extern crate quote; -extern crate maplit; #[macro_use] extern crate serde_derive; diff --git a/enterprise-compiler/src/model.rs b/enterprise-compiler/src/model.rs deleted file mode 100644 index b0ce36f..0000000 --- a/enterprise-compiler/src/model.rs +++ /dev/null @@ -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; - -pub fn convert_map( - map: HashMap, -) -> HashMap { - 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, -} - -#[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 { - pub tag: String, - #[serde(with = "crate::tuple_map")] - pub attrs: HashMap, - pub inner: Vec, -} - -#[derive(Debug, Serialize, Deserialize)] -pub enum Rsx { - Elem(Elem), - Code(Expr), - Text(String), - - #[doc(hidden)] - _Nonexhaustive, -} - -#[derive(Debug)] -pub enum TaggedRsx { - Elem(Id, Elem), - Code(Id, Box), - 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"), - } - } -} diff --git a/enterprise-compiler/src/model/mod.rs b/enterprise-compiler/src/model/mod.rs new file mode 100644 index 0000000..1f4f263 --- /dev/null +++ b/enterprise-compiler/src/model/mod.rs @@ -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; + +pub fn convert_map( + map: HashMap, +) -> HashMap { + 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, +} + +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 { + pub tag: String, + #[serde(with = "crate::tuple_map")] + pub attrs: HashMap, + pub inner: Option>, +} + +impl ToTokens for Elem { + 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), + 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), + Code(Id, Box), + 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"), + } + } +} diff --git a/enterprise-compiler/src/model/props.rs b/enterprise-compiler/src/model/props.rs new file mode 100644 index 0000000..5b71983 --- /dev/null +++ b/enterprise-compiler/src/model/props.rs @@ -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::("()").unwrap().to_adapter()), Just(syn::parse_str::("()").unwrap().to_adapter())), + SizeRange::default()), + view in vec(arbitrary_view(), SizeRange::default()), + ) -> Component { + Component { + name, + model, + view, + } + } +} + +pub fn arbitrary_view() -> impl Strategy { + let leaf = prop_oneof![ + Just(Rsx::Code( + syn::parse_str::("()").unwrap().to_adapter() + )), + any::().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 { + // https://doc.rust-lang.org/reference/identifiers.html + string_regex(r"([A-Za-z][A-Za-z0-9_]*)|(_[A-Za-z0-9_]+)").unwrap() +} diff --git a/enterprise-compiler/src/visitor.rs b/enterprise-compiler/src/visitor.rs index f19bde7..de667d6 100644 --- a/enterprise-compiler/src/visitor.rs +++ b/enterprise-compiler/src/visitor.rs @@ -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! { diff --git a/enterprise-macros/Cargo.toml b/enterprise-macros/Cargo.toml index 48bf765..9d36b75 100644 --- a/enterprise-macros/Cargo.toml +++ b/enterprise-macros/Cargo.toml @@ -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" diff --git a/enterprise-macros/proptest-regressions/lib.txt b/enterprise-macros/proptest-regressions/lib.txt new file mode 100644 index 0000000..ace6147 --- /dev/null +++ b/enterprise-macros/proptest-regressions/lib.txt @@ -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: [] } diff --git a/enterprise-macros/src/lib.rs b/enterprise-macros/src/lib.rs index 37ac44d..0b52e3e 100644 --- a/enterprise-macros/src/lib.rs +++ b/enterprise-macros/src/lib.rs @@ -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 for ParseError { - fn from(err: SynError) -> Self { - ParseError::Syn(err) - } -} - -enum ComponentBlock { - Model(ModelMap), - View(Vec), -} - -struct Visitor(Peekable); - -impl Visitor { - fn from_tokens(stream: TokenStream) -> Self { - Visitor(stream.into_iter().peekable()) - } - - fn consume_component(&mut self) -> Result, 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, 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 { - #[derive(Debug)] - struct ModelEntry { - name: Ident, - colon: Token![:], - ty: Type, - eq: Token![=], - init: Expr, - } - - impl Parse for ModelEntry { - fn parse(input: ParseStream) -> SynResult { - Ok(ModelEntry { - name: input.parse()?, - colon: input.parse()?, - ty: input.parse()?, - eq: input.parse()?, - init: input.parse()?, - }) - } - } - - let mut single_def = || -> Result, 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::(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, 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) -> 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 { - 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>, - equals: Option, -) -> Result { - 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>, -) -> Result { - 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; - - fn next(&mut self) -> Option { - 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 { diff --git a/enterprise-macros/src/parser.rs b/enterprise-macros/src/parser.rs new file mode 100644 index 0000000..3e6174d --- /dev/null +++ b/enterprise-macros/src/parser.rs @@ -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 for ParseError { + fn from(err: SynError) -> Self { + ParseError::Syn(err) + } +} + +enum ComponentBlock { + Model(ModelMap), + View(Vec), +} + +pub(crate) struct Visitor(Peekable); + +impl Visitor { + pub fn from_tokens(stream: TokenStream) -> Self { + Visitor(stream.into_iter().peekable()) + } + + fn consume_component(&mut self) -> Result, 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, 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 { + #[derive(Debug)] + struct ModelEntry { + name: Ident, + colon: Token![:], + ty: Type, + eq: Token![=], + init: Expr, + } + + impl Parse for ModelEntry { + fn parse(input: ParseStream) -> SynResult { + Ok(ModelEntry { + name: input.parse()?, + colon: input.parse()?, + ty: input.parse()?, + eq: input.parse()?, + init: input.parse()?, + }) + } + } + + let mut single_def = || -> Result, 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::(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, 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) -> 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 { + 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>, + equals: Option, +) -> Result { + 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>, +) -> Result { + 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; + + fn next(&mut self) -> Option { + 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())); + } + } +} diff --git a/enterprise-macros/src/rsx.rs b/enterprise-macros/src/rsx.rs index 012ad51..3e92212 100644 --- a/enterprise-macros/src/rsx.rs +++ b/enterprise-macros/src/rsx.rs @@ -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); diff --git a/examples/todomvc/src/build.rs b/examples/todomvc/src/build.rs index 357cb9f..4554650 100644 --- a/examples/todomvc/src/build.rs +++ b/examples/todomvc/src/build.rs @@ -8,8 +8,12 @@ component! { } view { - - "Hello, " {name} "!" + +
    + {#for (key, todo) in todos} +
  • {todo} [x]
  • + {/for} +
} } } diff --git a/src/forloop.rs b/src/forloop.rs new file mode 100644 index 0000000..72f8158 --- /dev/null +++ b/src/forloop.rs @@ -0,0 +1,9 @@ +// TODO: move this to a location that makes sense + +use crate::std::List; + +trait ForEachable {} + +impl ForEachable for Vec {} + +impl ForEachable for List {} diff --git a/src/lib.rs b/src/lib.rs index 26b4b0a..3c9c9db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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. diff --git a/syn-serde/src/lifetime.rs b/syn-serde/src/lifetime.rs index 72fa739..cdd4d31 100644 --- a/syn-serde/src/lifetime.rs +++ b/syn-serde/src/lifetime.rs @@ -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, diff --git a/syn-serde/src/macros.rs b/syn-serde/src/macros.rs index 31dce72..d6366db 100644 --- a/syn-serde/src/macros.rs +++ b/syn-serde/src/macros.rs @@ -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)* ); diff --git a/syn-serde/src/token_stream.rs b/syn-serde/src/token_stream.rs index 24cdf55..5c1fd44 100644 --- a/syn-serde/src/token_stream.rs +++ b/syn-serde/src/token_stream.rs @@ -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, @@ -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`. /// - 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,