Compare commits
No commits in common. "old" and "master" have entirely different histories.
45 changed files with 1049 additions and 2411 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,5 +1,2 @@
|
||||||
/target
|
/target
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
||||||
graph.*
|
|
||||||
ouais.rs
|
|
|
@ -1,3 +1,2 @@
|
||||||
syn-serde
|
syn-serde
|
||||||
symbol
|
symbol
|
||||||
/ouais.rs
|
|
||||||
|
|
418
Cargo.lock
generated
418
Cargo.lock
generated
|
@ -1,10 +1,5 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "0.1.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -23,19 +18,6 @@ dependencies = [
|
||||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bit-set"
|
|
||||||
version = "0.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bit-vec"
|
|
||||||
version = "0.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
|
@ -43,22 +25,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.2.0"
|
version = "3.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "byteorder"
|
|
||||||
version = "1.3.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "c2-chacha"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "0.1.10"
|
version = "0.1.10"
|
||||||
|
@ -83,9 +52,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"enterprise-compiler 0.1.0",
|
"enterprise-compiler 0.1.0",
|
||||||
"parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"proptest 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"stdweb 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
"stdweb 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"symbol 0.1.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -93,17 +60,17 @@ name = "enterprise-compiler"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bimap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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 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)",
|
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"symbol 0.1.0",
|
"symbol 0.1.0",
|
||||||
"syn 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syn-serde 0.2.0",
|
"syn-serde 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -111,14 +78,12 @@ dependencies = [
|
||||||
name = "enterprise-macros"
|
name = "enterprise-macros"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"enterprise 0.1.0",
|
|
||||||
"enterprise-compiler 0.1.0",
|
"enterprise-compiler 0.1.0",
|
||||||
"proc-macro2 1.0.8 (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)",
|
|
||||||
"quote 1.0.2 (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)",
|
"serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"symbol 0.1.0",
|
"symbol 0.1.0",
|
||||||
"syn 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syn-serde 0.2.0",
|
"syn-serde 0.2.0",
|
||||||
"thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
@ -128,26 +93,6 @@ name = "fixedbitset"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fnv"
|
|
||||||
version = "1.0.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fuchsia-cprng"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "getrandom"
|
|
||||||
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.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)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "helloworld"
|
name = "helloworld"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -160,7 +105,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.3.2"
|
version = "1.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -178,7 +123,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.67"
|
version = "0.2.66"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -186,7 +131,7 @@ name = "lock_api"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -198,12 +143,9 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "maplit"
|
||||||
version = "0.2.11"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
|
||||||
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
|
@ -221,7 +163,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"redox_syscall 0.1.56 (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)",
|
"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)",
|
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -233,20 +175,7 @@ version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fixedbitset 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fixedbitset 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"indexmap 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
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]]
|
[[package]]
|
||||||
|
@ -257,48 +186,6 @@ dependencies = [
|
||||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proptest"
|
|
||||||
version = "0.9.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rusty-fork 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"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]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
|
@ -307,165 +194,11 @@ dependencies = [
|
||||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand"
|
|
||||||
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.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)",
|
|
||||||
"rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand"
|
|
||||||
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.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)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_chacha"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_chacha"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_hc"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_hc"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_isaac"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_jitter"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"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)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_os"
|
|
||||||
version = "0.1.3"
|
|
||||||
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.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)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_pcg"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_xorshift"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rdrand"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.1.56"
|
version = "0.1.56"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-syntax"
|
|
||||||
version = "0.6.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "remove_dir_all"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
@ -474,17 +207,6 @@ dependencies = [
|
||||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rusty-fork"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"wait-timeout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
|
@ -492,7 +214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.1.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -523,7 +245,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syn 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -575,7 +297,7 @@ dependencies = [
|
||||||
"quote 1.0.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 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)",
|
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syn 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -590,7 +312,7 @@ dependencies = [
|
||||||
"serde_derive 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)",
|
||||||
"serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syn 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -610,17 +332,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "0.15.44"
|
version = "1.0.14"
|
||||||
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.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -637,20 +349,7 @@ dependencies = [
|
||||||
"serde 1.0.104 (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)",
|
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syn 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tempfile"
|
|
||||||
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.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)",
|
|
||||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -668,42 +367,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syn 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "todomvc"
|
name = "todomvc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
|
||||||
"enterprise 0.1.0",
|
|
||||||
"enterprise-compiler 0.1.0",
|
|
||||||
"enterprise-macros 0.1.0",
|
|
||||||
"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]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wait-timeout"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasi"
|
|
||||||
version = "0.9.0+wasi-snapshot-preview1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.58"
|
version = "0.2.58"
|
||||||
|
@ -718,12 +393,12 @@ name = "wasm-bindgen-backend"
|
||||||
version = "0.2.58"
|
version = "0.2.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bumpalo 3.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 1.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)",
|
||||||
"log 0.4.8 (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)",
|
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syn 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
"wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -743,7 +418,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syn 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
"wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
"wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
@ -773,63 +448,31 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
|
|
||||||
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||||
"checksum base-x 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1b20b618342cf9891c292c4f5ac2cde7287cc5c87e87e9c769d617793607dec1"
|
"checksum base-x 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1b20b618342cf9891c292c4f5ac2cde7287cc5c87e87e9c769d617793607dec1"
|
||||||
"checksum bimap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "783204f24fd7724ea274d327619cfa6a6018047bb0561a68aadff6f56787591b"
|
"checksum bimap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "783204f24fd7724ea274d327619cfa6a6018047bb0561a68aadff6f56787591b"
|
||||||
"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 bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
"checksum bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742"
|
"checksum bumpalo 3.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5fb8038c1ddc0a5f73787b130f4cc75151e96ed33e417fde765eb5a81e3532f4"
|
||||||
"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"
|
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||||
"checksum discard 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
|
"checksum discard 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
|
||||||
"checksum fixedbitset 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
|
"checksum fixedbitset 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
|
||||||
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
|
"checksum indexmap 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b54058f0a6ff80b6803da8faf8997cde53872b38f4023728f6830b06cd3c0dc"
|
||||||
"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.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 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 lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
"checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"
|
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
|
||||||
"checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b"
|
"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 log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||||
"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
|
"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||||
"checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc"
|
"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 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 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 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 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"
|
|
||||||
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
|
||||||
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
|
|
||||||
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
|
||||||
"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
|
||||||
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
|
||||||
"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
|
|
||||||
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
|
||||||
"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
|
|
||||||
"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
|
|
||||||
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
|
||||||
"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
|
|
||||||
"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
|
|
||||||
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
|
||||||
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||||
"checksum regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06"
|
|
||||||
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
|
|
||||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
"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 ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
|
||||||
"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
|
||||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
"checksum semver-parser 0.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"
|
"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
|
||||||
|
@ -842,15 +485,10 @@ 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-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-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 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 syn 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a0294dc449adc58bb6592fff1a23d3e5e6e235afc6a0ffca2657d19e7bbffe5"
|
|
||||||
"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 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 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 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"
|
|
||||||
"checksum wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "5205e9afdf42282b192e2310a5b463a6d1c1d774e30dc3c791ac37ab42d2616c"
|
"checksum wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "5205e9afdf42282b192e2310a5b463a6d1c1d774e30dc3c791ac37ab42d2616c"
|
||||||
"checksum wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "11cdb95816290b525b32587d76419facd99662a07e59d3cdb560488a819d9a45"
|
"checksum wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "11cdb95816290b525b32587d76419facd99662a07e59d3cdb560488a819d9a45"
|
||||||
"checksum wasm-bindgen-macro 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "574094772ce6921576fb6f2e3f7497b8a76273b6db092be18fc48a082de09dc3"
|
"checksum wasm-bindgen-macro 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "574094772ce6921576fb6f2e3f7497b8a76273b6db092be18fc48a082de09dc3"
|
||||||
|
|
|
@ -24,7 +24,3 @@ web = ["stdweb"]
|
||||||
enterprise-compiler = { path = "enterprise-compiler" }
|
enterprise-compiler = { path = "enterprise-compiler" }
|
||||||
stdweb = { version = "0.4.20", optional = true }
|
stdweb = { version = "0.4.20", optional = true }
|
||||||
parking_lot = "0.10.0"
|
parking_lot = "0.10.0"
|
||||||
symbol = { path = "symbol" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
proptest = "0.9.5"
|
|
||||||
|
|
|
@ -4,19 +4,17 @@ version = "0.1.0"
|
||||||
authors = ["Michael Zhang <iptq@protonmail.com>"]
|
authors = ["Michael Zhang <iptq@protonmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
proptest = "0.9.5"
|
|
||||||
proptest-derive = "0.1.2"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bimap = "0.4.0"
|
bimap = "0.4.0"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
maplit = "1.0.2"
|
||||||
petgraph = "0.5.0"
|
petgraph = "0.5.0"
|
||||||
proc-macro2 = "1.0.8"
|
proc-macro2 = "1.0.8"
|
||||||
quote = "1.0.2"
|
quote = "1.0.2"
|
||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
serde_derive = "1.0.104"
|
serde_derive = "1.0.104"
|
||||||
serde_json = "1.0.48"
|
|
||||||
spin = "0.5.2"
|
spin = "0.5.2"
|
||||||
symbol = { path = "../symbol" }
|
symbol = { path = "../symbol" }
|
||||||
syn = { version = "1.0.14", features = ["extra-traits", "full"] }
|
|
||||||
syn-serde = { path = "../syn-serde" }
|
syn-serde = { path = "../syn-serde" }
|
||||||
|
syn = { version = "1.0.14", features = ["extra-traits", "full"] }
|
||||||
|
serde_json = "1.0.48"
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate quote;
|
extern crate quote;
|
||||||
|
extern crate maplit;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
|
||||||
mod graph;
|
|
||||||
pub mod model;
|
pub mod model;
|
||||||
mod utils;
|
mod tuple_map;
|
||||||
mod visitor;
|
mod visitor;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
@ -13,42 +13,28 @@ use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use petgraph::dot::Dot;
|
use crate::model::Component;
|
||||||
|
use crate::visitor::Visitor;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::ToTokens;
|
|
||||||
use symbol::Symbol;
|
use symbol::Symbol;
|
||||||
|
|
||||||
use crate::model::Component;
|
pub fn build(
|
||||||
pub use crate::visitor::Visitor;
|
// name: impl AsRef<str>,
|
||||||
|
// datamodel: &HashMap<String, String>,
|
||||||
pub fn build(component: &Component) -> TokenStream {
|
// datainit: &HashMap<String, String>,
|
||||||
|
// dom: &[Rsx],
|
||||||
|
component: &Component,
|
||||||
|
) -> TokenStream {
|
||||||
let name = &component.name;
|
let name = &component.name;
|
||||||
|
|
||||||
let mut visitor = Visitor::new();
|
let mut visitor = Visitor::new();
|
||||||
visitor.load_model(&component.model);
|
visitor.load_model(&component.model);
|
||||||
let tagged_dom = visitor.make_graph(&component.view);
|
let new_dom = visitor.make_graph(&component.view);
|
||||||
let toplevel_names = visitor.gen_code(&tagged_dom);
|
let toplevel_names = visitor.gen_code(&new_dom);
|
||||||
|
|
||||||
println!("Code segments:");
|
// let graph: Graph<_, _, _> = visitor.deps.clone().into_graph();
|
||||||
for (l, r) in visitor.code_segments() {
|
// println!("{:?}", Dot::new(&graph));
|
||||||
println!("{:?}: {}", l, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
// output the "model"
|
|
||||||
// looks a little bit like
|
|
||||||
// struct Name<B> {
|
|
||||||
// _b: PhantomData<B>,
|
|
||||||
// name: Type,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// impl<B: Backend> Name {
|
|
||||||
// pub fn new() -> Self {
|
|
||||||
// Self {
|
|
||||||
// _b: PhantomData::new(),
|
|
||||||
// name: value,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
let name = format_ident!("{}", name);
|
let name = format_ident!("{}", name);
|
||||||
let mut model = TokenStream::new();
|
let mut model = TokenStream::new();
|
||||||
let mut init = TokenStream::new();
|
let mut init = TokenStream::new();
|
||||||
|
@ -56,9 +42,9 @@ pub fn build(component: &Component) -> TokenStream {
|
||||||
let name = format_ident!("{}", name.as_str());
|
let name = format_ident!("{}", name.as_str());
|
||||||
let ty: syn::Type = ty.into();
|
let ty: syn::Type = ty.into();
|
||||||
let value: syn::Expr = value.into();
|
let value: syn::Expr = value.into();
|
||||||
model.extend(quote! { #name : std::sync::Arc<enterprise::parking_lot::RwLock<#ty>> , });
|
model.extend(quote! { #name : std::sync::Arc<enterprise::parking_lot::Mutex<#ty>> , });
|
||||||
init.extend(
|
init.extend(
|
||||||
quote! { #name : std::sync::Arc::new(enterprise::parking_lot::RwLock::new(#value .into())) , },
|
quote! { #name : std::sync::Arc::new(enterprise::parking_lot::Mutex::new(#value .into())) , },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,31 +53,21 @@ pub fn build(component: &Component) -> TokenStream {
|
||||||
for fn_name in toplevel_names.iter() {
|
for fn_name in toplevel_names.iter() {
|
||||||
let fn_name = format_ident!("{}", fn_name);
|
let fn_name = format_ident!("{}", fn_name);
|
||||||
init_el_code.extend(quote! {
|
init_el_code.extend(quote! {
|
||||||
let sub = self.#fn_name();
|
{
|
||||||
el.append_child(&sub);
|
use enterprise::stdweb::web::INode;
|
||||||
|
let sub = self.#fn_name();
|
||||||
|
el.append_child(&sub);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let altgraph = visitor.graph().altgraph();
|
|
||||||
let dot = Dot::new(&altgraph);
|
|
||||||
{
|
|
||||||
let mut file = File::create("graph.dot").unwrap();
|
|
||||||
write!(file, "{:?}", dot).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
// use std::convert::TryFrom;
|
|
||||||
use enterprise::{Component, ValueUpdatable};
|
|
||||||
use enterprise::std::{List};
|
|
||||||
use enterprise::stdweb::web::{INode, IElement, Node, IEventTarget};
|
|
||||||
use crate::enterprise::stdweb::unstable::TryFrom;
|
|
||||||
|
|
||||||
pub struct #name<B> {
|
pub struct #name<B> {
|
||||||
_b: std::marker::PhantomData<B>,
|
_b: std::marker::PhantomData<B>,
|
||||||
#model
|
#model
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: enterprise::Backend> #name<B> {
|
impl<B> #name<B> {
|
||||||
pub fn new(_: &B) -> Self {
|
pub fn new(_: &B) -> Self {
|
||||||
#name {
|
#name {
|
||||||
_b: std::marker::PhantomData::default(),
|
_b: std::marker::PhantomData::default(),
|
||||||
|
@ -103,10 +79,8 @@ pub fn build(component: &Component) -> TokenStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: enterprise::Backend> enterprise::Component<B> for #name<B> {
|
impl<B: enterprise::Backend> enterprise::Component<B> for #name<B> {
|
||||||
fn render(&self) -> Node {
|
fn create(&self, el: &enterprise::stdweb::web::Element) {
|
||||||
let el = enterprise::stdweb::web::document().create_element("div").unwrap();
|
|
||||||
#init_el_code
|
#init_el_code
|
||||||
el.as_node().clone()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
94
enterprise-compiler/src/model.rs
Normal file
94
enterprise-compiler/src/model.rs
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,281 +0,0 @@
|
||||||
//! The enterprise DSL model.
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod props;
|
|
||||||
#[cfg(test)]
|
|
||||||
pub use self::props::*;
|
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::iter::{self, FromIterator};
|
|
||||||
|
|
||||||
use proc_macro2::{Span, TokenStream, TokenTree};
|
|
||||||
use quote::ToTokens;
|
|
||||||
use symbol::Symbol;
|
|
||||||
use syn_serde::{Expr, Pat, Syn, Type};
|
|
||||||
|
|
||||||
pub type Id = Symbol;
|
|
||||||
|
|
||||||
pub type ModelMap = BTreeMap<Symbol, (Type, Expr)>;
|
|
||||||
|
|
||||||
pub fn convert_map<T: Ord>(map: BTreeMap<T, (syn::Type, syn::Expr)>) -> BTreeMap<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::utils::tuple_map")]
|
|
||||||
pub model: ModelMap,
|
|
||||||
pub view: Vec<Rsx>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for Component {
|
|
||||||
fn to_tokens(&self, stream: &mut TokenStream) {
|
|
||||||
let name = format_ident!("{}", self.name);
|
|
||||||
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 #name {
|
|
||||||
model { #model }
|
|
||||||
view { #view }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialOrd, Ord, PartialEq, Eq)]
|
|
||||||
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 PartialEq<TagRhs> for TagRhs {
|
|
||||||
fn eq(&self, other: &TagRhs) -> bool {
|
|
||||||
match (self, other) {
|
|
||||||
(TagRhs::Code(expr), TagRhs::Code(other)) => {
|
|
||||||
syn::Expr::from_adapter(expr) == syn::Expr::from_adapter(other)
|
|
||||||
}
|
|
||||||
(TagRhs::Text(string), TagRhs::Text(other)) => string == other,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for TagRhs {}
|
|
||||||
|
|
||||||
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, PartialEq, Eq)]
|
|
||||||
pub struct Elem<T> {
|
|
||||||
pub tag: String,
|
|
||||||
#[serde(with = "crate::utils::tuple_map")]
|
|
||||||
pub attrs: BTreeMap<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! { / > });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Context = BTreeMap<Id, ContextVar>;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
pub enum ContextVar {
|
|
||||||
Model(ModelValue),
|
|
||||||
LoopExpr(Id),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
pub enum ModelValue {
|
|
||||||
Index(Box<ModelValue>, Symbol),
|
|
||||||
Leaf(Symbol),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// "Rust + XML", taken from JSX: This represents a node in a DOM tree
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub enum Rsx {
|
|
||||||
Elem(Elem<Rsx>),
|
|
||||||
Code(Context, Expr),
|
|
||||||
Text(String),
|
|
||||||
ForLoop(Pat, Expr, Type, Vec<Rsx>),
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
_Nonexhaustive,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<Rsx> for Rsx {
|
|
||||||
fn eq(&self, other: &Rsx) -> bool {
|
|
||||||
match (self, other) {
|
|
||||||
(Rsx::Elem(this), Rsx::Elem(other)) => this == other,
|
|
||||||
(Rsx::Code(ctx, expr), Rsx::Code(ctx2, other)) => {
|
|
||||||
ctx == ctx2 && syn::Expr::from_adapter(expr) == syn::Expr::from_adapter(other)
|
|
||||||
}
|
|
||||||
(Rsx::Text(this), Rsx::Text(other)) => this == other,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Rsx {}
|
|
||||||
|
|
||||||
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::ForLoop(pat, expr, ty, inner) => {
|
|
||||||
let expr = syn::Expr::from_adapter(expr);
|
|
||||||
let pat = syn::Pat::from_adapter(pat);
|
|
||||||
let ty = syn::Type::from_adapter(ty);
|
|
||||||
let mut inner_stream = TokenStream::new();
|
|
||||||
for rsx in inner {
|
|
||||||
inner_stream.extend(rsx.to_token_stream());
|
|
||||||
}
|
|
||||||
stream.extend(quote!([ for #pat in #expr : #ty ] #inner_stream [ / for ]));
|
|
||||||
}
|
|
||||||
Rsx::_Nonexhaustive => unreachable!("should never be constructed"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The same as RSX, except at this point every node has been assigned an ID.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum TaggedRsx {
|
|
||||||
Elem(Id, Elem<TaggedRsx>),
|
|
||||||
Code(Id, Context, Box<Expr>),
|
|
||||||
Text(Id, String),
|
|
||||||
ForLoop(Id, Pat, Expr, Vec<TaggedRsx>),
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
_Nonexhaustive,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TaggedRsx {
|
|
||||||
pub fn get_id(&self) -> Id {
|
|
||||||
match self {
|
|
||||||
TaggedRsx::Elem(id, _)
|
|
||||||
| TaggedRsx::Code(id, _, _)
|
|
||||||
| TaggedRsx::Text(id, _)
|
|
||||||
| TaggedRsx::ForLoop(id, _, _, _) => *id,
|
|
||||||
_ => unimplemented!("tagged rsx"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for TaggedRsx {
|
|
||||||
fn to_tokens(&self, stream: &mut TokenStream) {
|
|
||||||
match self {
|
|
||||||
TaggedRsx::Elem(_, elem) => {
|
|
||||||
stream.extend(quote!(#elem));
|
|
||||||
}
|
|
||||||
TaggedRsx::Code(_, _, expr) => {
|
|
||||||
let expr = syn::Expr::from_adapter(expr);
|
|
||||||
stream.extend(quote!({ #expr }));
|
|
||||||
}
|
|
||||||
TaggedRsx::Text(_, string) => {
|
|
||||||
let string = syn::Lit::Str(syn::LitStr::new(string.as_ref(), Span::call_site()));
|
|
||||||
stream.extend(quote!(#string));
|
|
||||||
}
|
|
||||||
TaggedRsx::ForLoop(_, pat, expr, inner) => {
|
|
||||||
let expr = syn::Expr::from_adapter(expr);
|
|
||||||
let pat = syn::Pat::from_adapter(pat);
|
|
||||||
let mut inner_stream = TokenStream::new();
|
|
||||||
for rsx in inner {
|
|
||||||
inner_stream.extend(rsx.to_token_stream());
|
|
||||||
}
|
|
||||||
stream.extend(quote!([ for #pat in #expr ] #inner_stream [ / for ]));
|
|
||||||
}
|
|
||||||
TaggedRsx::_Nonexhaustive => unreachable!("should never be constructed"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
//! Utility functions for property checking.
|
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use proptest::{
|
|
||||||
collection::{btree_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 btree_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(
|
|
||||||
BTreeMap::new(),
|
|
||||||
syn::parse_str::<Expr>("()").unwrap().to_adapter()
|
|
||||||
)),
|
|
||||||
string_regex(r"[:print:]+").unwrap().prop_map(Rsx::Text),
|
|
||||||
];
|
|
||||||
leaf.prop_recursive(2, 4, 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: BTreeMap::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()
|
|
||||||
}
|
|
|
@ -1,22 +1,18 @@
|
||||||
//! Utility library for converting a map whose keys are not strings to JSON, which requires string keys.
|
// https://github.com/daboross/serde-tuple-vec-map/blob/master/src/lib.rs
|
||||||
//!
|
|
||||||
//! https://github.com/daboross/serde-tuple-vec-map/blob/master/src/lib.rs
|
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::hash::Hash;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use serde::{
|
use serde::{
|
||||||
de::{Deserialize, Deserializer, SeqAccess, Visitor},
|
de::{Deserialize, Deserializer, SeqAccess, Visitor},
|
||||||
ser::{Serialize, Serializer},
|
ser::{Serialize, Serializer},
|
||||||
};
|
};
|
||||||
|
|
||||||
trait Delegate: Ord {}
|
|
||||||
|
|
||||||
struct TupleVecMapVisitor<K, V> {
|
struct TupleVecMapVisitor<K, V> {
|
||||||
marker: PhantomData<BTreeMap<K, V>>,
|
marker: PhantomData<HashMap<K, V>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, V> TupleVecMapVisitor<K, V> {
|
impl<K, V> TupleVecMapVisitor<K, V> {
|
||||||
|
@ -27,12 +23,12 @@ impl<K, V> TupleVecMapVisitor<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de, K: Ord, V> Visitor<'de> for TupleVecMapVisitor<K, V>
|
impl<'de, K: Eq + Hash, V> Visitor<'de> for TupleVecMapVisitor<K, V>
|
||||||
where
|
where
|
||||||
K: Deserialize<'de>,
|
K: Deserialize<'de>,
|
||||||
V: Deserialize<'de>,
|
V: Deserialize<'de>,
|
||||||
{
|
{
|
||||||
type Value = BTreeMap<K, V>;
|
type Value = HashMap<K, V>;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
formatter.write_str("a map")
|
formatter.write_str("a map")
|
||||||
|
@ -40,7 +36,7 @@ where
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn visit_unit<E>(self) -> Result<Self::Value, E> {
|
fn visit_unit<E>(self) -> Result<Self::Value, E> {
|
||||||
Ok(BTreeMap::new())
|
Ok(HashMap::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -48,7 +44,7 @@ where
|
||||||
where
|
where
|
||||||
T: SeqAccess<'de>,
|
T: SeqAccess<'de>,
|
||||||
{
|
{
|
||||||
let mut values = BTreeMap::new();
|
let mut values = HashMap::new();
|
||||||
|
|
||||||
while let Some((key, value)) = seq.next_element()? {
|
while let Some((key, value)) = seq.next_element()? {
|
||||||
values.insert(key, value);
|
values.insert(key, value);
|
||||||
|
@ -58,14 +54,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize an array of `(K, V)` pairs as if it were a `BTreeMap<K, V>`.
|
/// Serialize an array of `(K, V)` pairs as if it were a `HashMap<K, V>`.
|
||||||
///
|
///
|
||||||
/// In formats where dictionaries are ordered, this maintains the input data's order. Each pair is treated as a single
|
/// In formats where dictionaries are ordered, this maintains the input data's order. Each pair is treated as a single
|
||||||
/// entry into the dictionary.
|
/// entry into the dictionary.
|
||||||
///
|
///
|
||||||
/// Behavior when duplicate keys are present in the data is unspecified and serializer-dependent. This function does
|
/// Behavior when duplicate keys are present in the data is unspecified and serializer-dependent. This function does
|
||||||
/// not check for duplicate keys and will not warn the serializer.
|
/// not check for duplicate keys and will not warn the serializer.
|
||||||
pub fn serialize<K: Ord, V, S>(data: &BTreeMap<K, V>, serializer: S) -> Result<S::Ok, S::Error>
|
pub fn serialize<K: Eq + Hash, V, S>(data: &HashMap<K, V>, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
K: Serialize,
|
K: Serialize,
|
||||||
|
@ -74,12 +70,12 @@ where
|
||||||
serializer.collect_seq(data.iter().map(|x| (x.0, x.1)))
|
serializer.collect_seq(data.iter().map(|x| (x.0, x.1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialize to a `Vec<(K, V)>` as if it were a `BTreeMap<K, V>`.
|
/// Deserialize to a `Vec<(K, V)>` as if it were a `HashMap<K, V>`.
|
||||||
///
|
///
|
||||||
/// This directly deserializes into the returned vec with no intermediate allocation.
|
/// This directly deserializes into the returned vec with no intermediate allocation.
|
||||||
///
|
///
|
||||||
/// In formats where dictionaries are ordered, this maintains the input data's order.
|
/// In formats where dictionaries are ordered, this maintains the input data's order.
|
||||||
pub fn deserialize<'de, K: Ord, V, D>(deserializer: D) -> Result<BTreeMap<K, V>, D::Error>
|
pub fn deserialize<'de, K: Eq + Hash, V, D>(deserializer: D) -> Result<HashMap<K, V>, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
K: Deserialize<'de>,
|
K: Deserialize<'de>,
|
|
@ -1,46 +0,0 @@
|
||||||
pub mod tuple_map;
|
|
||||||
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use symbol::Symbol;
|
|
||||||
use syn::*;
|
|
||||||
|
|
||||||
pub fn process_for_loop_pat(pat: &Pat) -> (Option<Symbol>, Symbol) {
|
|
||||||
let mut first = None;
|
|
||||||
let mut second = None;
|
|
||||||
if let Pat::Tuple(PatTuple { elems, .. }) = pat {
|
|
||||||
let mut iter = elems.iter();
|
|
||||||
if elems.len() == 2 {
|
|
||||||
// first one is the key
|
|
||||||
if let Some(Pat::Ident(PatIdent { ident, .. })) = iter.next() {
|
|
||||||
first = Some(Symbol::from(ident.to_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(Pat::Ident(PatIdent { ident, .. })) = iter.next() {
|
|
||||||
second = Some(Symbol::from(ident.to_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(first, second.unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_pat_names(pat: &Pat) -> HashSet<Symbol> {
|
|
||||||
let mut result = HashSet::new();
|
|
||||||
match pat {
|
|
||||||
Pat::Box(PatBox { pat, .. }) => {
|
|
||||||
result.extend(get_pat_names(pat));
|
|
||||||
}
|
|
||||||
Pat::Ident(PatIdent { ident, subpat, .. }) => {
|
|
||||||
result.insert(Symbol::from(ident.to_string()));
|
|
||||||
if let Some((_, boxpat)) = subpat {
|
|
||||||
result.extend(get_pat_names(boxpat));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Pat::Tuple(PatTuple { elems, .. }) => {
|
|
||||||
for pat in elems.iter() {
|
|
||||||
result.extend(get_pat_names(pat));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
|
@ -1,31 +1,76 @@
|
||||||
//! Visitor that traverses a model and generates code.
|
use std::collections::HashMap;
|
||||||
//!
|
use std::collections::HashSet;
|
||||||
//! Most of the code here implemetns control flow analysis
|
|
||||||
|
|
||||||
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
|
use petgraph::graphmap::DiGraphMap;
|
||||||
|
use petgraph::visit::Dfs;
|
||||||
use petgraph::dot::Dot;
|
|
||||||
use proc_macro2::{TokenStream, TokenTree};
|
use proc_macro2::{TokenStream, TokenTree};
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use syn::{Expr, Type};
|
use syn::{Expr, Type};
|
||||||
use syn_serde::Syn;
|
use syn_serde::Syn;
|
||||||
|
|
||||||
use crate::graph::{Action as DepAction, DependencyGraph, Dfs, InnerGraph, Node as DepNode};
|
use crate::model::{Elem, Id, ModelMap, Rsx, TagLhs, TagRhs, TaggedRsx};
|
||||||
use crate::model::{
|
|
||||||
Context, ContextVar, Elem, Id, ModelMap, ModelValue, Rsx, TagLhs, TagRhs, TaggedRsx,
|
|
||||||
};
|
|
||||||
use crate::utils;
|
|
||||||
use crate::Symbol;
|
use crate::Symbol;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)]
|
||||||
|
pub enum DepNode {
|
||||||
|
// This is an attribute on an element
|
||||||
|
// Not read-only
|
||||||
|
RsxAttr(Symbol, Symbol),
|
||||||
|
// This is a text node (innertext)
|
||||||
|
// These are read-only
|
||||||
|
RsxSpan(Symbol),
|
||||||
|
// This is something in the model
|
||||||
|
ModelValue(Symbol),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DepNode {
|
||||||
|
fn gen_update_code(
|
||||||
|
&self,
|
||||||
|
// model_bimap: &BiHashMap<Id, String>,
|
||||||
|
updates: &mut TokenStream,
|
||||||
|
update_func: &mut TokenStream,
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
DepNode::ModelValue(sym) => {
|
||||||
|
let sym_name = format_ident!("{}", sym.to_string());
|
||||||
|
let inner_lock = format_ident!("inner_lock_{}", Symbol::gensym().as_str());
|
||||||
|
updates.extend(quote! {
|
||||||
|
let #inner_lock = self.#sym_name.clone();
|
||||||
|
});
|
||||||
|
update_func.extend(quote! {
|
||||||
|
{
|
||||||
|
let mut locked = #inner_lock.lock();
|
||||||
|
*locked = new_value.clone();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
DepNode::RsxSpan(id) => {
|
||||||
|
let id_str = id.as_str();
|
||||||
|
update_func.extend(quote! {
|
||||||
|
{
|
||||||
|
use enterprise::stdweb::web::{INonElementParentNode, INode};
|
||||||
|
if let Some(target) = enterprise::stdweb::web::document().get_element_by_id(#id_str) {
|
||||||
|
target.set_text_content(&new_value.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DependencyGraph = DiGraphMap<DepNode, ()>;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Visitor {
|
pub struct Visitor {
|
||||||
idx: u32,
|
idx: u32,
|
||||||
pub(crate) deps: DependencyGraph,
|
pub(crate) deps: DependencyGraph,
|
||||||
model: HashMap<Id, (Type, Expr)>,
|
model: HashMap<Id, (Type, Expr)>,
|
||||||
code_segments: HashMap<Id, TokenStream>,
|
|
||||||
pub(crate) impl_code: TokenStream,
|
pub(crate) impl_code: TokenStream,
|
||||||
elem_attr_map: HashMap<Id, HashSet<Id>>,
|
elem_attr_map: HashMap<Id, HashSet<Id>>,
|
||||||
elem_evt_map: HashMap<Id, HashSet<Id>>,
|
// symbol maps
|
||||||
|
// model_bimap: BiHashMap<Id, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Visitor {
|
impl Visitor {
|
||||||
|
@ -35,232 +80,41 @@ impl Visitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn impl_code(&self) -> &TokenStream {
|
|
||||||
&self.impl_code
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn code_segments(&self) -> &HashMap<Id, TokenStream> {
|
|
||||||
&self.code_segments
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn graph(&self) -> &DependencyGraph {
|
|
||||||
&self.deps
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_model(&mut self, model: &ModelMap) {
|
pub fn load_model(&mut self, model: &ModelMap) {
|
||||||
for (key, (ty, init)) in model {
|
for (key, (ty, init)) in model {
|
||||||
let ty = syn::Type::from_adapter(ty);
|
let ty = Syn::from_adapter(&*ty);
|
||||||
let init = syn::Expr::from_adapter(init);
|
let init = Syn::from_adapter(&*init);
|
||||||
self.model.insert(key.clone(), (ty, init));
|
self.model.insert(key.clone(), (ty, init));
|
||||||
}
|
}
|
||||||
}
|
// self.model.extend(model.clone());
|
||||||
|
|
||||||
fn hook_attrs(&mut self, node_id: Symbol, attrs: &BTreeMap<TagLhs, TagRhs>) {
|
|
||||||
for (lhs, rhs) in attrs {
|
|
||||||
match (lhs, rhs) {
|
|
||||||
// If the left-hand side contains bind:attr="name", put that attribute as a dependency of name
|
|
||||||
(TagLhs::Bind(attr), TagRhs::Text(text)) => {
|
|
||||||
let text_sym = Symbol::from(text);
|
|
||||||
// check if the model actually contains the key that you're trying to bind to
|
|
||||||
if self.model.contains_key(&text_sym) {
|
|
||||||
let attr_node = DepNode::RsxAttr(node_id, Symbol::from(attr));
|
|
||||||
let model_node = DepNode::ModelValue(ModelValue::Leaf(text_sym));
|
|
||||||
self.deps.add_edge(
|
|
||||||
attr_node.clone(),
|
|
||||||
model_node.clone(),
|
|
||||||
DepAction::ValueChange,
|
|
||||||
);
|
|
||||||
self.deps
|
|
||||||
.add_edge(model_node, attr_node, DepAction::ValueChange);
|
|
||||||
if let Some(set) = self.elem_attr_map.get_mut(&node_id) {
|
|
||||||
set.insert(Symbol::from(attr));
|
|
||||||
} else {
|
|
||||||
let mut set = HashSet::new();
|
|
||||||
set.insert(Symbol::from(attr));
|
|
||||||
self.elem_attr_map.insert(node_id, set);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(TagLhs::On(evt), TagRhs::Code(expr)) => {
|
|
||||||
let syn_expr = syn::Expr::from_adapter(expr);
|
|
||||||
let unit_type = syn::parse_str::<syn::Type>("()").unwrap();
|
|
||||||
let code_node_id =
|
|
||||||
self.hook_code_segment(&Context::default(), &syn_expr, Some(unit_type));
|
|
||||||
|
|
||||||
// add hook from attr to the code segment
|
|
||||||
let from = DepNode::RsxEvent(node_id, Symbol::from(evt));
|
|
||||||
let to = DepNode::CodeSeg(code_node_id);
|
|
||||||
self.deps.add_edge(from, to, DepAction::ValueChange);
|
|
||||||
|
|
||||||
if let Some(set) = self.elem_evt_map.get_mut(&node_id) {
|
|
||||||
set.insert(Symbol::from(evt));
|
|
||||||
} else {
|
|
||||||
let mut set = HashSet::new();
|
|
||||||
set.insert(Symbol::from(evt));
|
|
||||||
self.elem_evt_map.insert(node_id, set);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hook_code_segment(
|
|
||||||
&mut self,
|
|
||||||
ctx: &Context,
|
|
||||||
expr: &syn::Expr,
|
|
||||||
return_type: Option<Type>,
|
|
||||||
) -> Symbol {
|
|
||||||
let code_node_id = Symbol::gensym();
|
|
||||||
println!(
|
|
||||||
"INPUT[{}]: {:?} {} : {:?}",
|
|
||||||
code_node_id,
|
|
||||||
ctx,
|
|
||||||
expr.to_token_stream(),
|
|
||||||
return_type
|
|
||||||
);
|
|
||||||
|
|
||||||
// see if we need to parse i@, o@
|
|
||||||
let mut has_io = false;
|
|
||||||
let mut input_vars = HashSet::new();
|
|
||||||
let mut output_vars = HashSet::new();
|
|
||||||
let actual_expr = if let syn::Expr::Closure(syn::ExprClosure { inputs, body, .. }) = expr {
|
|
||||||
for arg in inputs.iter() {
|
|
||||||
if let syn::Pat::Ident(syn::PatIdent {
|
|
||||||
ident,
|
|
||||||
subpat: Some((_, subpat)),
|
|
||||||
..
|
|
||||||
}) = arg
|
|
||||||
{
|
|
||||||
let bind = ident.to_string();
|
|
||||||
match (bind.as_ref(), &(**subpat)) {
|
|
||||||
("i", syn::Pat::Ident(syn::PatIdent { ident, .. })) => {
|
|
||||||
// input variable
|
|
||||||
// this means the code segment depends on this variable
|
|
||||||
let sym = Symbol::from(ident.to_string());
|
|
||||||
let from = DepNode::ModelValue(ModelValue::Leaf(sym));
|
|
||||||
let to = DepNode::CodeSeg(code_node_id);
|
|
||||||
self.deps.add_edge(from, to, DepAction::ValueChange);
|
|
||||||
input_vars.insert(sym);
|
|
||||||
has_io = true;
|
|
||||||
}
|
|
||||||
("o", syn::Pat::Ident(syn::PatIdent { ident, .. })) => {
|
|
||||||
// output variable
|
|
||||||
// this means the code segment propagates updates to the variable
|
|
||||||
let sym = Symbol::from(ident.to_string());
|
|
||||||
let from = DepNode::CodeSeg(code_node_id);
|
|
||||||
let to = DepNode::ModelValue(ModelValue::Leaf(sym));
|
|
||||||
self.deps.add_edge(from, to, DepAction::ValueChange);
|
|
||||||
output_vars.insert(sym);
|
|
||||||
has_io = true;
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if has_io {
|
|
||||||
(**body).clone()
|
|
||||||
} else {
|
|
||||||
expr.clone()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
expr.clone()
|
|
||||||
};
|
|
||||||
// perform a rudimentary search and put them in as input vars if they aren't already in there
|
|
||||||
for sym in self.extract_model_dependencies_from_expr(&expr, &self.get_model_names()) {
|
|
||||||
if !output_vars.contains(&sym) {
|
|
||||||
input_vars.insert(sym);
|
|
||||||
has_io = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("==> IO[{}]: {:?} {:?}", has_io, input_vars, output_vars);
|
|
||||||
|
|
||||||
let code_fn = format_ident!("code_segment_{}", code_node_id.to_string());
|
|
||||||
let mut prelude = TokenStream::new();
|
|
||||||
// add context variables
|
|
||||||
for (id, ctxvar) in ctx.iter() {
|
|
||||||
let id = format_ident!("{}", id.to_string());
|
|
||||||
prelude.extend(quote!(let #id =));
|
|
||||||
match ctxvar {
|
|
||||||
ContextVar::Model(model_value) => {
|
|
||||||
// let line = Indexable::index(self.todos, key)
|
|
||||||
fn gen_model_value_code(val: &ModelValue) -> TokenStream {
|
|
||||||
match val {
|
|
||||||
ModelValue::Leaf(sym) => {
|
|
||||||
let name = format_ident!("{}", sym.to_string());
|
|
||||||
quote!(self . #name)
|
|
||||||
}
|
|
||||||
ModelValue::Index(val, idx) => {
|
|
||||||
let idx = format_ident!("{}", idx.to_string());
|
|
||||||
let val = gen_model_value_code(val);
|
|
||||||
quote!(Indexable :: index ( #val , #idx ))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mv_code = gen_model_value_code(model_value);
|
|
||||||
prelude.extend(quote!(#mv_code ;));
|
|
||||||
}
|
|
||||||
ContextVar::LoopExpr(id) => {
|
|
||||||
// this means put the loop expression in there
|
|
||||||
let id_str = format_ident!("code_segment_{}", id.to_string());
|
|
||||||
prelude.extend(quote!(self . #id_str () . read () ;));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// stick the model variables into the prelude
|
|
||||||
if has_io {
|
|
||||||
for (id, _) in self.model.iter() {
|
|
||||||
let id_str = format_ident!("{}", id.to_string());
|
|
||||||
if input_vars.contains(id) {
|
|
||||||
prelude.extend(quote!(let #id_str = self . # id_str . clone () ;));
|
|
||||||
}
|
|
||||||
if output_vars.contains(id) {
|
|
||||||
prelude.extend(quote!(let #id_str = self . # id_str . clone () ;));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.code_segments
|
|
||||||
.insert(code_node_id, quote!(#actual_expr));
|
|
||||||
let return_type = return_type
|
|
||||||
.unwrap_or_else(|| syn::parse_str::<syn::Type>("impl std::any::Any").unwrap());
|
|
||||||
self.impl_code.extend(quote! {
|
|
||||||
fn #code_fn(&self) -> #return_type {
|
|
||||||
#prelude
|
|
||||||
#actual_expr . into()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// look for model references in the code segment
|
|
||||||
// let names = self.get_model_names();
|
|
||||||
// let deps = self.extract_model_dependencies_from_expr(&expr, &names);
|
|
||||||
// for dep in deps {
|
|
||||||
// let from = DepNode::ModelValue(ModelValue::Leaf(dep));
|
|
||||||
// let to = DepNode::CodeSeg(code_node_id);
|
|
||||||
// self.deps.add_edge(from, to, DepAction::ValueChange);
|
|
||||||
// }
|
|
||||||
|
|
||||||
code_node_id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_graph(&mut self, nodes: &[Rsx]) -> Vec<TaggedRsx> {
|
pub fn make_graph(&mut self, nodes: &[Rsx]) -> Vec<TaggedRsx> {
|
||||||
self.make_graph_rec(Context::default(), nodes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_graph_rec(&mut self, ctx: Context, nodes: &[Rsx]) -> Vec<TaggedRsx> {
|
|
||||||
let mut new_nodes = Vec::new();
|
let mut new_nodes = Vec::new();
|
||||||
for node in nodes {
|
for node in nodes {
|
||||||
let node_id = Symbol::gensym();
|
let node_id = Symbol::gensym();
|
||||||
let new_node = match node {
|
let new_node = match node {
|
||||||
// Process a < /> tag
|
|
||||||
Rsx::Elem(Elem { tag, attrs, inner }) => {
|
Rsx::Elem(Elem { tag, attrs, inner }) => {
|
||||||
let tag_inner = inner
|
let tag_inner = self.make_graph(&inner);
|
||||||
.as_ref()
|
for (lhs, rhs) in attrs {
|
||||||
.map(|inner| self.make_graph_rec(ctx.clone(), inner));
|
if let TagLhs::Bind(attr) = lhs {
|
||||||
|
if let TagRhs::Text(text) = rhs {
|
||||||
// add deps for the attributes
|
let text_sym = Symbol::from(text);
|
||||||
self.hook_attrs(node_id, attrs);
|
if self.model.contains_key(&text_sym) {
|
||||||
|
let from = DepNode::RsxAttr(node_id, Symbol::from(attr));
|
||||||
|
let to = DepNode::ModelValue(text_sym);
|
||||||
|
self.deps.add_edge(from, to, ());
|
||||||
|
if let Some(set) = self.elem_attr_map.get_mut(&node_id) {
|
||||||
|
set.insert(Symbol::from(attr));
|
||||||
|
} else {
|
||||||
|
let mut set = HashSet::new();
|
||||||
|
set.insert(Symbol::from(attr));
|
||||||
|
self.elem_attr_map.insert(node_id, set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
TaggedRsx::Elem(
|
TaggedRsx::Elem(
|
||||||
node_id,
|
node_id,
|
||||||
Elem {
|
Elem {
|
||||||
|
@ -270,85 +124,19 @@ impl Visitor {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Code changes are dependent on variables within the code segment in the model
|
Rsx::Code(expr) => {
|
||||||
// Every time the model changes, the code segment must re-evaluate
|
|
||||||
Rsx::Code(_, expr) => {
|
|
||||||
let syn_expr = Syn::from_adapter(&*expr);
|
let syn_expr = Syn::from_adapter(&*expr);
|
||||||
let string_type =
|
let deps = self.extract_model_dependencies(&syn_expr);
|
||||||
syn::parse_str::<syn::Type>("impl std::string::ToString").unwrap();
|
for dep in deps {
|
||||||
let code_node_id = self.hook_code_segment(&ctx, &syn_expr, Some(string_type));
|
let from = DepNode::ModelValue(dep);
|
||||||
|
let to = DepNode::RsxSpan(node_id);
|
||||||
// let names = self.get_model_names();
|
self.deps.add_edge(from, to, ());
|
||||||
// let deps = self.extract_model_dependencies_from_expr(&syn_expr, &names);
|
|
||||||
// for dep in deps {
|
|
||||||
// let from = DepNode::ModelValue(ModelValue::Leaf(dep));
|
|
||||||
// let to = DepNode::RsxSpan(node_id);
|
|
||||||
// self.deps.add_edge(from, to, DepAction::ValueChange);
|
|
||||||
// }
|
|
||||||
|
|
||||||
TaggedRsx::Code(
|
|
||||||
code_node_id,
|
|
||||||
ctx.clone(),
|
|
||||||
Box::new(syn_expr.clone().to_adapter()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Rsx::Text(literal) => TaggedRsx::Text(node_id, literal.clone()),
|
|
||||||
// Process a for-loop
|
|
||||||
// The for-loop should exist by itself in the dep tree
|
|
||||||
// Everything inside the for loop is a dependency
|
|
||||||
Rsx::ForLoop(pat, expr, ty, inner) => {
|
|
||||||
let syn_pat = syn::Pat::from_adapter(&pat);
|
|
||||||
|
|
||||||
let mut names = self.get_model_names();
|
|
||||||
names.extend(utils::get_pat_names(&syn_pat));
|
|
||||||
|
|
||||||
// figure out which variables in the iterator that it depends on
|
|
||||||
// for example, [for x in y] depends on y.
|
|
||||||
// This says that whenever something in y changes, the iterator should also change
|
|
||||||
let syn_expr = syn::Expr::from_adapter(expr);
|
|
||||||
let syn_ty = syn::Type::from_adapter(ty);
|
|
||||||
let wrapped =
|
|
||||||
syn::parse2::<syn::Type>(quote!(Arc < RwLock < #syn_ty > >)).unwrap();
|
|
||||||
let code_node_id = self.hook_code_segment(&ctx, &syn_expr, Some(wrapped));
|
|
||||||
let from = DepNode::CodeSeg(code_node_id);
|
|
||||||
let to = DepNode::Iterator(node_id);
|
|
||||||
self.deps.add_edge(from, to, DepAction::ValueChange);
|
|
||||||
|
|
||||||
let ctx = {
|
|
||||||
let mut context = ctx.clone();
|
|
||||||
let (key, loopvar) = utils::process_for_loop_pat(&syn_pat);
|
|
||||||
// let key = key.unwrap_or_else(|| Symbol::gensym());
|
|
||||||
// context.insert(key, ContextVar::LoopKey(code_node_id));
|
|
||||||
// context.insert(loopvar, ContextVar::Index(ContextVar::LoopExpr(code_node_id), ContextVar::LoopKey));
|
|
||||||
context.insert(loopvar, ContextVar::LoopExpr(code_node_id));
|
|
||||||
// get the actual variable name
|
|
||||||
context
|
|
||||||
};
|
|
||||||
let new_inner = self.make_graph_rec(ctx, inner.as_ref());
|
|
||||||
|
|
||||||
// let deps = self.extract_model_dependencies_from_expr(&syn_expr, &names);
|
|
||||||
// for dep in deps {
|
|
||||||
// let from = DepNode::ModelValue(ModelValue::Leaf(dep));
|
|
||||||
// let to = DepNode::Iterator(node_id);
|
|
||||||
// self.deps
|
|
||||||
// .add_edge(from, to, DepAction::IndexChange(node_id));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// all of its children are dependencies of the iterator
|
|
||||||
// Every time the iterator updates, the children update
|
|
||||||
// - Using the List iterator, there's an update method that uses keys
|
|
||||||
for child in inner {
|
|
||||||
let deps = self.extract_rsx_dependents(&child, &names);
|
|
||||||
let from = DepNode::Iterator(node_id);
|
|
||||||
for dep in deps {
|
|
||||||
self.deps
|
|
||||||
.add_edge(from.clone(), dep, DepAction::ValueChange);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TaggedRsx::ForLoop(node_id, pat.clone(), expr.clone(), new_inner)
|
TaggedRsx::Code(node_id, Box::new(syn_expr.clone().to_adapter()))
|
||||||
}
|
}
|
||||||
unknown => unimplemented!("unknown rsx: {:?}", unknown),
|
Rsx::Text(literal) => TaggedRsx::Text(node_id, literal.clone()),
|
||||||
|
_ => unimplemented!(),
|
||||||
};
|
};
|
||||||
new_nodes.push(new_node);
|
new_nodes.push(new_node);
|
||||||
}
|
}
|
||||||
|
@ -356,10 +144,6 @@ impl Visitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_code(&mut self, nodes: &[TaggedRsx]) -> Vec<String> {
|
pub fn gen_code(&mut self, nodes: &[TaggedRsx]) -> Vec<String> {
|
||||||
self.gen_code_rec(nodes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gen_code_rec(&mut self, nodes: &[TaggedRsx]) -> Vec<String> {
|
|
||||||
let mut names = Vec::new();
|
let mut names = Vec::new();
|
||||||
for node in nodes {
|
for node in nodes {
|
||||||
let node_str = node.get_id().as_str();
|
let node_str = node.get_id().as_str();
|
||||||
|
@ -367,22 +151,23 @@ impl Visitor {
|
||||||
match node {
|
match node {
|
||||||
TaggedRsx::Elem(node_id, Elem { tag, inner, .. }) => {
|
TaggedRsx::Elem(node_id, Elem { tag, inner, .. }) => {
|
||||||
let mut updates = TokenStream::new();
|
let mut updates = TokenStream::new();
|
||||||
// check attrs to see which ones are bound
|
|
||||||
// once found, add event listeners that propagate changes once these are changed.
|
|
||||||
if let Some(this_attrs) = self.elem_attr_map.get(node_id) {
|
if let Some(this_attrs) = self.elem_attr_map.get(node_id) {
|
||||||
for attr in this_attrs {
|
for attr in this_attrs {
|
||||||
let starting = DepNode::RsxAttr(*node_id, *attr);
|
let starting = DepNode::RsxAttr(*node_id, *attr);
|
||||||
|
let mut dfs = Dfs::new(&self.deps, starting);
|
||||||
let mut update_func = TokenStream::new();
|
let mut update_func = TokenStream::new();
|
||||||
let mut dfs = Dfs::new(&self.deps, &starting).unwrap();
|
while let Some(nx) = dfs.next(&self.deps) {
|
||||||
|
if nx != starting {
|
||||||
while let Some(node) = dfs.next() {
|
nx.gen_update_code(
|
||||||
if node != &starting {
|
// &self.model_bimap,
|
||||||
node.gen_update_code(&mut updates, &mut update_func);
|
&mut updates,
|
||||||
|
&mut update_func,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updates.extend(quote! {
|
updates.extend(quote! {
|
||||||
{
|
{
|
||||||
// need to clone this inside so it can be moved
|
use enterprise::stdweb::{web::IEventTarget, unstable::TryFrom};
|
||||||
let inner_el = el.clone();
|
let inner_el = el.clone();
|
||||||
el.add_event_listener(move |evt: enterprise::stdweb::web::event::InputEvent| {
|
el.add_event_listener(move |evt: enterprise::stdweb::web::event::InputEvent| {
|
||||||
let new_value = enterprise::stdweb::web::html_element::InputElement::try_from(inner_el.clone()).unwrap().raw_value();
|
let new_value = enterprise::stdweb::web::html_element::InputElement::try_from(inner_el.clone()).unwrap().raw_value();
|
||||||
|
@ -392,171 +177,56 @@ impl Visitor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// add event listeners for the events
|
|
||||||
if let Some(this_evts) = self.elem_evt_map.get(node_id) {
|
|
||||||
for evt in this_evts {
|
|
||||||
let evt_str = evt.to_string();
|
|
||||||
let evt_type = match evt.as_ref() {
|
|
||||||
"submit" => {
|
|
||||||
Some(quote!(enterprise::stdweb::web::event::SubmitEvent))
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
if let Some(evt_type) = evt_type {
|
|
||||||
let starting = DepNode::RsxEvent(*node_id, *evt);
|
|
||||||
let mut update_func = TokenStream::new();
|
|
||||||
let mut dfs = Dfs::new(&self.deps, &starting).unwrap();
|
|
||||||
|
|
||||||
while let Some(node) = dfs.next() {
|
|
||||||
if node != &starting {
|
|
||||||
node.gen_update_code(&mut updates, &mut update_func);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updates.extend(quote! {
|
|
||||||
{
|
|
||||||
// need to clone this inside so it can be moved
|
|
||||||
let inner_el = el.clone();
|
|
||||||
el.add_event_listener(move |evt: #evt_type| {
|
|
||||||
let new_value = enterprise::stdweb::web::html_element::InputElement::try_from(inner_el.clone()).unwrap().raw_value();
|
|
||||||
#update_func
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let elem_as_str = format!("{}", node.to_token_stream());
|
|
||||||
self.impl_code.extend(quote! {
|
self.impl_code.extend(quote! {
|
||||||
#[doc = #elem_as_str]
|
fn #make_node_id(&self) -> impl enterprise::stdweb::web::INode {
|
||||||
fn #make_node_id(&self) -> enterprise::stdweb::web::Node {
|
use enterprise::stdweb::web::IElement;
|
||||||
let el = enterprise::stdweb::web::document().create_element(#tag).unwrap();
|
let el = enterprise::stdweb::web::document().create_element(#tag).unwrap();
|
||||||
el.set_attribute("id", #node_str).unwrap();
|
el.set_attribute("id", #node_str).unwrap();
|
||||||
#updates
|
#updates
|
||||||
el.as_node().clone()
|
el
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if let Some(inner) = inner {
|
self.gen_code(&inner);
|
||||||
self.gen_code_rec(inner);
|
|
||||||
}
|
|
||||||
names.push(format!("{}", make_node_id));
|
|
||||||
}
|
}
|
||||||
TaggedRsx::Code(code_node_id, ctx, expr) => {
|
TaggedRsx::Code(_, _) => {
|
||||||
// let expr = syn::Expr::from_adapter(expr);
|
|
||||||
// // let code_id = format_ident!("code_{}", node_str);
|
|
||||||
// let code_node_id = self.hook_code_segment(&ctx, &expr);
|
|
||||||
|
|
||||||
self.impl_code.extend(quote! {
|
self.impl_code.extend(quote! {
|
||||||
/// Code
|
#[inline]
|
||||||
fn #make_node_id(&self) -> enterprise::stdweb::web::Node {
|
fn #make_node_id(&self) -> impl enterprise::stdweb::web::INode {
|
||||||
|
use enterprise::stdweb::web::IElement;
|
||||||
let el = enterprise::stdweb::web::document().create_element("span").expect("shouldn't fail");
|
let el = enterprise::stdweb::web::document().create_element("span").expect("shouldn't fail");
|
||||||
el.set_attribute("id", #node_str).unwrap();
|
el.set_attribute("id", #node_str).unwrap();
|
||||||
el.as_node().clone()
|
el
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
names.push(format!("{}", make_node_id));
|
|
||||||
}
|
}
|
||||||
TaggedRsx::Text(_, literal) => {
|
TaggedRsx::Text(_, literal) => {
|
||||||
self.impl_code.extend(quote! {
|
self.impl_code.extend(quote! {
|
||||||
/// Text node
|
#[inline]
|
||||||
fn #make_node_id(&self) -> enterprise::stdweb::web::Node {
|
fn #make_node_id(&self) -> impl enterprise::stdweb::web::INode {
|
||||||
let text = enterprise::std::widgets::Text::new(#literal);
|
enterprise::stdweb::web::document().create_text_node(#literal)
|
||||||
text.render()
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
names.push(format!("{}", make_node_id));
|
|
||||||
}
|
|
||||||
TaggedRsx::ForLoop(_, _, _, inner) => {
|
|
||||||
let init_loop_id = format_ident!("init_loop_{}", node_str);
|
|
||||||
let update_loop_id = format_ident!("update_loop_{}", node_str);
|
|
||||||
|
|
||||||
// Generate code for the initial creation of the loop
|
|
||||||
let mut func_calls = TokenStream::new();
|
|
||||||
for name in self.gen_code_rec(&inner) {
|
|
||||||
let name = format_ident!("{}", name);
|
|
||||||
func_calls.extend(quote! {
|
|
||||||
let sub = self.#name();
|
|
||||||
el.append_child(&sub);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.impl_code.extend(quote! {
|
|
||||||
/// Initialize for-loop
|
|
||||||
fn #init_loop_id(&self) -> enterprise::stdweb::web::Node {
|
|
||||||
let el = enterprise::stdweb::web::document().create_element("div").expect("shouldn't fail");
|
|
||||||
#func_calls
|
|
||||||
el.as_node().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update for-loop
|
|
||||||
fn #update_loop_id(&self) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
names.push(format!("{}", init_loop_id));
|
|
||||||
}
|
}
|
||||||
_ => unimplemented!("gen_code tagged rsx"),
|
_ => unimplemented!("gen_code tagged rsx"),
|
||||||
}
|
}
|
||||||
|
names.push(format!("{}", make_node_id));
|
||||||
}
|
}
|
||||||
names
|
names
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the names that exist in the model right now
|
|
||||||
fn get_model_names(&self) -> HashSet<Symbol> {
|
|
||||||
self.model.keys().cloned().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_rsx_dependents(&self, rsx: &Rsx, names: &HashSet<Symbol>) -> HashSet<DepNode> {
|
|
||||||
let mut result = HashSet::new();
|
|
||||||
match rsx {
|
|
||||||
Rsx::Elem(elem) => {
|
|
||||||
if let Some(inner) = &elem.inner {
|
|
||||||
for child in inner.iter() {
|
|
||||||
self.extract_rsx_dependents(child, names);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Rsx::Code(_, code) => {
|
|
||||||
let code = syn::Expr::from_adapter(code);
|
|
||||||
let code_deps = self
|
|
||||||
.extract_model_dependencies_from_expr(&code, names)
|
|
||||||
.into_iter()
|
|
||||||
.map(|sym| DepNode::ModelValue(ModelValue::Leaf(sym)))
|
|
||||||
.collect::<HashSet<_>>();
|
|
||||||
result.extend(code_deps);
|
|
||||||
}
|
|
||||||
Rsx::ForLoop(_pat, _expr, _ty, inner) => {
|
|
||||||
for child in inner.iter() {
|
|
||||||
self.extract_rsx_dependents(child, names);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is using a really dumb heuristic
|
/// This is using a really dumb heuristic
|
||||||
fn extract_model_dependencies_from_expr(
|
fn extract_model_dependencies(&self, expr: &Expr) -> HashSet<Symbol> {
|
||||||
&self,
|
|
||||||
expr: &Expr,
|
|
||||||
names: &HashSet<Symbol>,
|
|
||||||
) -> HashSet<Symbol> {
|
|
||||||
let tokens = expr.to_token_stream();
|
let tokens = expr.to_token_stream();
|
||||||
let mut result = HashSet::new();
|
let mut result = HashSet::new();
|
||||||
|
|
||||||
let mut queue = tokens.into_iter().collect::<VecDeque<_>>();
|
for token in tokens.into_iter() {
|
||||||
while !queue.is_empty() {
|
if let TokenTree::Ident(ident) = token {
|
||||||
let token = queue.pop_front().unwrap();
|
// if let Some(id) = self.model_bimap.get_by_right(&ident.to_string()) {
|
||||||
// for token in tokens.into_iter() {
|
let sym = Symbol::from(ident.to_string());
|
||||||
match token {
|
if self.model.contains_key(&sym) {
|
||||||
TokenTree::Ident(ident) => {
|
result.insert(sym);
|
||||||
let sym = Symbol::from(ident.to_string());
|
|
||||||
if names.contains(&sym) {
|
|
||||||
result.insert(sym);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
TokenTree::Group(group) => {
|
// result.insert(format!("{}", ident));
|
||||||
queue.extend(group.stream().into_iter());
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
|
|
17
enterprise-macros-old/Cargo.toml
Normal file
17
enterprise-macros-old/Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[package]
|
||||||
|
name = "enterprise-macros"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Michael Zhang <iptq@protonmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
lalrpop = "0.17.2"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
proc-macro2 = { version = "1.0.7", features = ["span-locations"] }
|
||||||
|
quote = "1.0.2"
|
||||||
|
thiserror = "1.0.9"
|
||||||
|
lalrpop-util = "0.17.2"
|
3
enterprise-macros-old/build.rs
Normal file
3
enterprise-macros-old/build.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
fn main() {
|
||||||
|
lalrpop::process_root().unwrap();
|
||||||
|
}
|
34
enterprise-macros-old/src/ast.rs
Normal file
34
enterprise-macros-old/src/ast.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Toplevel {
|
||||||
|
Use(Use),
|
||||||
|
Component(Component),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Use(pub Vec<String>);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Component {
|
||||||
|
pub name: String,
|
||||||
|
pub body: Vec<ComponentBody>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ComponentBody {
|
||||||
|
Constructor(),
|
||||||
|
View(Rsx),
|
||||||
|
Fn(),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Rsx {
|
||||||
|
Tag {
|
||||||
|
tag: String,
|
||||||
|
attrs: HashMap<String, String>,
|
||||||
|
inner: Vec<Rsx>,
|
||||||
|
},
|
||||||
|
CodeSegment(String),
|
||||||
|
Text(String),
|
||||||
|
}
|
128
enterprise-macros-old/src/flatten.rs
Normal file
128
enterprise-macros-old/src/flatten.rs
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use lalrpop_util::ParseError;
|
||||||
|
use proc_macro2::{
|
||||||
|
Delimiter, Group, Ident, LineColumn, Literal, Punct, Spacing, Span as Span2, TokenStream,
|
||||||
|
TokenTree,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::parser::parser::__ToTriple;
|
||||||
|
|
||||||
|
macro_rules! generate_token {
|
||||||
|
([$($keyword_name:ident: $keyword:ident),* $(,)?]) => {
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum TokenType {
|
||||||
|
Ident(Ident),
|
||||||
|
Punct(char, Punct),
|
||||||
|
Literal(Literal),
|
||||||
|
|
||||||
|
$($keyword_name(Span),)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for TokenType {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use TokenType::*;
|
||||||
|
match self {
|
||||||
|
Ident(ident) => ident.fmt(f),
|
||||||
|
Punct(_, punct) => punct.fmt(f),
|
||||||
|
Literal(literal) => literal.fmt(f),
|
||||||
|
$($keyword_name(_) => f.write_str(stringify!($keyword)),)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TokenType {
|
||||||
|
pub fn span(&self) -> Span {
|
||||||
|
use TokenType::*;
|
||||||
|
match self {
|
||||||
|
Ident(ident) => Span(ident.span()),
|
||||||
|
Punct(_, punct) => Span(punct.span()),
|
||||||
|
Literal(literal) => Span(literal.span()),
|
||||||
|
|
||||||
|
$($keyword_name(span) => *span,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flatten_tree(token_tree: TokenTree) -> Vec<Token> {
|
||||||
|
match token_tree {
|
||||||
|
TokenTree::Group(group) => {
|
||||||
|
use Delimiter::*;
|
||||||
|
let mut result = flatten(group.stream());
|
||||||
|
let surround = match group.delimiter() {
|
||||||
|
Brace => Some(construct_group_tokens('{', '}', group)),
|
||||||
|
Parenthesis => Some(construct_group_tokens('(', ')', group)),
|
||||||
|
Bracket => Some(construct_group_tokens('[', ']', group)),
|
||||||
|
None => Option::None,
|
||||||
|
};
|
||||||
|
if let Some((start, end)) = surround {
|
||||||
|
result.insert(0, start);
|
||||||
|
result.push(end);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
},
|
||||||
|
TokenTree::Ident(ident) => {
|
||||||
|
let token = match ident.to_string().as_ref() {
|
||||||
|
$(stringify!($keyword) => TokenType::$keyword_name(Span(ident.span())),)*
|
||||||
|
_ => TokenType::Ident(ident),
|
||||||
|
};
|
||||||
|
vec![construct_token(token)]
|
||||||
|
}
|
||||||
|
TokenTree::Punct(punct) => {
|
||||||
|
let token = TokenType::Punct(punct.as_char(), punct);
|
||||||
|
vec![construct_token(token)]
|
||||||
|
}
|
||||||
|
TokenTree::Literal(literal) => {
|
||||||
|
let token = TokenType::Literal(literal);
|
||||||
|
vec![construct_token(token)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_token!([
|
||||||
|
Component: component,
|
||||||
|
Constructor: constructor,
|
||||||
|
Fn: fn,
|
||||||
|
Use: use,
|
||||||
|
View: view,
|
||||||
|
]);
|
||||||
|
|
||||||
|
pub type Token = Result<(Span, TokenType, Span), ()>;
|
||||||
|
|
||||||
|
pub fn flatten(token_stream: TokenStream) -> Vec<Token> {
|
||||||
|
token_stream.into_iter().flat_map(flatten_tree).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn construct_group_tokens(left: char, right: char, group: Group) -> (Token, Token) {
|
||||||
|
let mut left_punct = Punct::new(left, Spacing::Alone);
|
||||||
|
left_punct.set_span(group.span_open());
|
||||||
|
let mut right_punct = Punct::new(right, Spacing::Alone);
|
||||||
|
right_punct.set_span(group.span_close());
|
||||||
|
(
|
||||||
|
construct_token(TokenType::Punct(left, left_punct)),
|
||||||
|
construct_token(TokenType::Punct(right, right_punct)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn construct_token(token: TokenType) -> Token {
|
||||||
|
let span = token.span();
|
||||||
|
Ok((span, token, span))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct Span(pub Span2);
|
||||||
|
|
||||||
|
impl fmt::Display for Span {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Span {
|
||||||
|
fn default() -> Self {
|
||||||
|
Span(Span2::call_site())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
80
enterprise-macros-old/src/lib.rs
Normal file
80
enterprise-macros-old/src/lib.rs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
extern crate proc_macro;
|
||||||
|
extern crate thiserror;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lalrpop_util;
|
||||||
|
|
||||||
|
mod ast;
|
||||||
|
mod flatten;
|
||||||
|
|
||||||
|
mod parser {
|
||||||
|
#![allow(dead_code, unused_variables, unknown_lints, non_snake_case)]
|
||||||
|
lalrpop_mod!(pub parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
use crate::flatten::{Span, TokenType};
|
||||||
|
use crate::parser::parser::*;
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn component(input_tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let input_tokens: TokenStream = input_tokens.into();
|
||||||
|
let tokens = flatten::flatten(input_tokens);
|
||||||
|
|
||||||
|
eprintln!("Tokens:");
|
||||||
|
for token in tokens.iter() {
|
||||||
|
eprintln!("- {:?}", token);
|
||||||
|
}
|
||||||
|
eprintln!();
|
||||||
|
|
||||||
|
let parser = ProgramParser::new();
|
||||||
|
let result = parser.parse(tokens);
|
||||||
|
match result {
|
||||||
|
Ok(result) => eprintln!("success: {:?}", result),
|
||||||
|
Err(err) => {
|
||||||
|
use lalrpop_util::ParseError::*;
|
||||||
|
match err {
|
||||||
|
User { ref error } => print!("user error: {:?}", error),
|
||||||
|
InvalidToken { ref location } => print!("Invalid token at {}", location),
|
||||||
|
UnrecognizedEOF {
|
||||||
|
ref location,
|
||||||
|
ref expected,
|
||||||
|
} => {
|
||||||
|
print!("Unrecognized EOF found at {}", location);
|
||||||
|
fmt_expected(expected)
|
||||||
|
}
|
||||||
|
UnrecognizedToken {
|
||||||
|
token: (ref start, ref token, ref end),
|
||||||
|
ref expected,
|
||||||
|
} => {
|
||||||
|
print!("Unrecognized token `{}` found at {}:{}", token, start, end);
|
||||||
|
fmt_expected(expected)
|
||||||
|
}
|
||||||
|
ExtraToken {
|
||||||
|
token: (ref start, ref token, ref end),
|
||||||
|
} => print!("Extra token {} found at {}:{}", token, start, end),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic!();
|
||||||
|
|
||||||
|
let result = quote! {};
|
||||||
|
result.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format a list of expected tokens.
|
||||||
|
fn fmt_expected(expected: &[String]) {
|
||||||
|
if !expected.is_empty() {
|
||||||
|
println!();
|
||||||
|
for (i, e) in expected.iter().enumerate() {
|
||||||
|
let sep = match i {
|
||||||
|
0 => "Expected one of",
|
||||||
|
_ if i < expected.len() - 1 => ",",
|
||||||
|
// Last expected message to be written
|
||||||
|
_ => " or",
|
||||||
|
};
|
||||||
|
print!("{} {}", sep, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
162
enterprise-macros-old/src/parser.lalrpop
Normal file
162
enterprise-macros-old/src/parser.lalrpop
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
use std::iter::FromIterator;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use proc_macro2::{Ident, Punct, TokenTree, Delimiter, TokenStream, Group, Spacing, Literal};
|
||||||
|
|
||||||
|
use crate::{TokenType, Span};
|
||||||
|
use crate::ast::*;
|
||||||
|
|
||||||
|
grammar;
|
||||||
|
|
||||||
|
pub Program: Vec<Toplevel> = Toplevel*;
|
||||||
|
|
||||||
|
pub Toplevel: Toplevel = {
|
||||||
|
Component => Toplevel::Component(<>),
|
||||||
|
Use => Toplevel::Use(<>),
|
||||||
|
};
|
||||||
|
|
||||||
|
Use: Use = {
|
||||||
|
"use" <path:Delim<Ident, (":" ":")>> ";" => Use(path.into_iter().map(|ident| ident.to_string()).collect()),
|
||||||
|
};
|
||||||
|
|
||||||
|
Component: Component = {
|
||||||
|
"component" <name:Ident> <body:Body<ComponentBody*>> => {
|
||||||
|
Component {
|
||||||
|
name: name.to_string(),
|
||||||
|
body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ComponentBody: ComponentBody = {
|
||||||
|
"constructor" "(" ")" BraceGrouper => {
|
||||||
|
ComponentBody::Constructor()
|
||||||
|
},
|
||||||
|
"view" <rsx:Body<Rsx>> => {
|
||||||
|
ComponentBody::View(rsx)
|
||||||
|
},
|
||||||
|
"fn" Ident "(" Delim<Arg, ","> ")" BraceGrouper => {
|
||||||
|
ComponentBody::Fn()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: finish this
|
||||||
|
Arg: () = {
|
||||||
|
Punct Ident Ident => {},
|
||||||
|
Ident ":" Ident => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
Rsx: Rsx = {
|
||||||
|
"<" <tag:Ident> <attrs:Attrs> "/" ">" => { Rsx::Tag { tag: tag.to_string(), attrs, inner: Vec::new(), } },
|
||||||
|
"<" <tag:Ident> <attrs:Attrs> ">" <inner:Rsx*> "<" "/" <closeTag:Ident> ">" => {
|
||||||
|
assert_eq!(tag, closeTag, "Tags {} and {} do not match.", tag, closeTag);
|
||||||
|
Rsx::Tag { tag: tag.to_string(), attrs, inner }
|
||||||
|
},
|
||||||
|
BraceGrouper => { Rsx::CodeSegment(<>.to_string()) },
|
||||||
|
AnyText => { Rsx::Text(<>) },
|
||||||
|
};
|
||||||
|
|
||||||
|
Attrs: HashMap<String, String> = {
|
||||||
|
(AttrLhs "=" BraceGrouper)* => {
|
||||||
|
<>.into_iter()
|
||||||
|
.map(|(lhs, _, rhs)| (lhs.to_string(), rhs.to_string()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
AttrLhs: String = {
|
||||||
|
<a:Ident> ":" <b:Ident> => format!("{}:{}", a, b),
|
||||||
|
Ident => <>.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
pub ArbitraryBlocks: Vec<TokenTree> = AnyToken*;
|
||||||
|
|
||||||
|
Grouper: TokenTree = {
|
||||||
|
BraceGrouper => <>,
|
||||||
|
"(" <b:AnyToken*> ")" => TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::from_iter(b.into_iter()))),
|
||||||
|
"[" <b:AnyToken*> "]" => TokenTree::Group(Group::new(Delimiter::Bracket, TokenStream::from_iter(b.into_iter()))),
|
||||||
|
};
|
||||||
|
|
||||||
|
BraceGrouper: TokenTree = {
|
||||||
|
"{" <b:AnyToken*> "}" => TokenTree::Group(Group::new(Delimiter::Brace, TokenStream::from_iter(b.into_iter()))),
|
||||||
|
};
|
||||||
|
|
||||||
|
AnyText: String = {
|
||||||
|
Ident => <>.to_string(),
|
||||||
|
Punct => <>.to_string(),
|
||||||
|
Literal => <>.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
AnyToken: TokenTree = {
|
||||||
|
Grouper => <>,
|
||||||
|
|
||||||
|
"component" => TokenTree::Ident(Ident::new("component", <>.0)),
|
||||||
|
"constructor" => TokenTree::Ident(Ident::new("constructor", <>.0)),
|
||||||
|
"fn" => TokenTree::Ident(Ident::new("fn", <>.0)),
|
||||||
|
"use" => TokenTree::Ident(Ident::new("use", <>.0)),
|
||||||
|
"view" => TokenTree::Ident(Ident::new("view", <>.0)),
|
||||||
|
|
||||||
|
":" => TokenTree::Punct(Punct::new(':', Spacing::Alone)),
|
||||||
|
";" => TokenTree::Punct(Punct::new(';', Spacing::Alone)),
|
||||||
|
"," => TokenTree::Punct(Punct::new(',', Spacing::Alone)),
|
||||||
|
"<" => TokenTree::Punct(Punct::new('<', Spacing::Alone)),
|
||||||
|
">" => TokenTree::Punct(Punct::new('>', Spacing::Alone)),
|
||||||
|
"/" => TokenTree::Punct(Punct::new('/', Spacing::Alone)),
|
||||||
|
"=" => TokenTree::Punct(Punct::new('=', Spacing::Alone)),
|
||||||
|
|
||||||
|
Ident => TokenTree::Ident(<>),
|
||||||
|
Punct => TokenTree::Punct(<>),
|
||||||
|
Literal => TokenTree::Literal(<>),
|
||||||
|
};
|
||||||
|
|
||||||
|
Body<T>: T = {
|
||||||
|
"{" <body:T> "}" => body,
|
||||||
|
};
|
||||||
|
|
||||||
|
Delim<T, Sep>: Vec<T> = {
|
||||||
|
<all:(T ((Sep T)+ Sep?)?)?> => {
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
if let Some((initial, rest)) = all {
|
||||||
|
vec.push(initial);
|
||||||
|
if let Some((rest, _)) = rest {
|
||||||
|
for (_, item) in rest {
|
||||||
|
vec.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vec
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
extern {
|
||||||
|
type Location = Span;
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
enum TokenType {
|
||||||
|
"component" => TokenType::Component(<Span>),
|
||||||
|
"constructor" => TokenType::Constructor(<Span>),
|
||||||
|
"fn" => TokenType::Fn(<Span>),
|
||||||
|
"use" => TokenType::Use(<Span>),
|
||||||
|
"view" => TokenType::View(<Span>),
|
||||||
|
|
||||||
|
":" => TokenType::Punct(':', _),
|
||||||
|
";" => TokenType::Punct(';', _),
|
||||||
|
"," => TokenType::Punct(',', _),
|
||||||
|
"{" => TokenType::Punct('{', _),
|
||||||
|
"}" => TokenType::Punct('}', _),
|
||||||
|
"(" => TokenType::Punct('(', _),
|
||||||
|
")" => TokenType::Punct(')', _),
|
||||||
|
"[" => TokenType::Punct('[', _),
|
||||||
|
"]" => TokenType::Punct(']', _),
|
||||||
|
"<" => TokenType::Punct('<', _),
|
||||||
|
">" => TokenType::Punct('>', _),
|
||||||
|
"/" => TokenType::Punct('/', _),
|
||||||
|
"=" => TokenType::Punct('=', _),
|
||||||
|
|
||||||
|
Punct => TokenType::Punct(_, <Punct>),
|
||||||
|
Ident => TokenType::Ident(<Ident>),
|
||||||
|
Literal => TokenType::Literal(<Literal>),
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,20 +7,12 @@ edition = "2018"
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "test"
|
|
||||||
path = "src/tests.rs"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
proptest = "0.9.5"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
enterprise = { path = ".." }
|
|
||||||
enterprise-compiler = { path = "../enterprise-compiler" }
|
|
||||||
proc-macro2 = { version = "1.0.7", features = ["span-locations"] }
|
proc-macro2 = { version = "1.0.7", features = ["span-locations"] }
|
||||||
quote = "1.0.2"
|
quote = "1.0.2"
|
||||||
serde_json = "1.0.48"
|
|
||||||
symbol = { path = "../symbol" }
|
|
||||||
syn = { version = "1.0.14", features = ["extra-traits", "full"] }
|
|
||||||
syn-serde = { path = "../syn-serde" }
|
|
||||||
thiserror = "1.0.9"
|
thiserror = "1.0.9"
|
||||||
|
symbol = { path = "../symbol" }
|
||||||
|
enterprise-compiler = { path = "../enterprise-compiler" }
|
||||||
|
syn-serde = { path = "../syn-serde" }
|
||||||
|
syn = { version = "1.0.14", features = ["extra-traits", "full"] }
|
||||||
|
serde_json = "1.0.48"
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
# 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 c87725a641776defa0a321d11950acba1b25c60d510345f7a487df38d08795bc # shrinks to tree = Component { name: "a", model: {}, view: [] }
|
|
|
@ -2,12 +2,298 @@ extern crate proc_macro;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate quote;
|
extern crate quote;
|
||||||
|
|
||||||
mod parser;
|
|
||||||
mod rsx;
|
mod rsx;
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use std::collections::HashMap;
|
||||||
|
use std::iter::FromIterator;
|
||||||
|
use std::iter::Peekable;
|
||||||
|
|
||||||
use crate::parser::Visitor;
|
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());
|
||||||
|
println!("SHIET");
|
||||||
|
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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn component(input_tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn component(input_tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
|
|
@ -1,380 +0,0 @@
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::iter::FromIterator;
|
|
||||||
use std::iter::Peekable;
|
|
||||||
|
|
||||||
use enterprise_compiler::model::{Component, Elem, ModelMap, Rsx, TagLhs, TagRhs};
|
|
||||||
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, Lit, Pat, 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,
|
|
||||||
UnexpectedToken(TokenTree),
|
|
||||||
MissingModel,
|
|
||||||
MissingView,
|
|
||||||
ClosedTooFar,
|
|
||||||
WrongClosingTag(String, String),
|
|
||||||
UnrecognizedLiteral(Lit),
|
|
||||||
// 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 = BTreeMap::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> {
|
|
||||||
enum Container {
|
|
||||||
Tag(String, BTreeMap<TagLhs, TagRhs>),
|
|
||||||
ForLoop(Pat, Expr, Type),
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut rsx_parser = RsxParser::new(self.0.clone());
|
|
||||||
let mut result = Vec::new();
|
|
||||||
let mut tag_stack = Vec::new();
|
|
||||||
|
|
||||||
while let Some(next_token) = rsx_parser.next() {
|
|
||||||
match next_token? {
|
|
||||||
RsxToken::EmptyTag(tag, attrs) => {
|
|
||||||
let elem = Elem {
|
|
||||||
tag: tag.to_string(),
|
|
||||||
attrs,
|
|
||||||
inner: None,
|
|
||||||
};
|
|
||||||
let el = Rsx::Elem(elem);
|
|
||||||
result.push(el);
|
|
||||||
}
|
|
||||||
RsxToken::OpeningTag(tag, attrs) => {
|
|
||||||
tag_stack.push((Container::Tag(tag.to_string(), attrs), result.clone()));
|
|
||||||
result.clear();
|
|
||||||
}
|
|
||||||
RsxToken::ClosingTag(tag) => {
|
|
||||||
if let Some((Container::Tag(last_tag, attrs), mut last_result)) =
|
|
||||||
tag_stack.pop()
|
|
||||||
{
|
|
||||||
if tag.as_str() == last_tag.as_str() {
|
|
||||||
last_result.push(Rsx::Elem(Elem {
|
|
||||||
tag: tag.to_string(),
|
|
||||||
attrs: attrs.clone(),
|
|
||||||
inner: Some(result),
|
|
||||||
}));
|
|
||||||
result = last_result;
|
|
||||||
} else {
|
|
||||||
return Err(ParseError::WrongClosingTag(
|
|
||||||
last_tag.to_string(),
|
|
||||||
tag.to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(ParseError::ClosedTooFar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RsxToken::Code(expr) => {
|
|
||||||
result.push(Rsx::Code(BTreeMap::new(), expr.to_adapter()));
|
|
||||||
}
|
|
||||||
RsxToken::Str(string) => {
|
|
||||||
result.push(Rsx::Text(string));
|
|
||||||
}
|
|
||||||
RsxToken::OpeningFor(pat, expr, ty) => {
|
|
||||||
tag_stack.push((Container::ForLoop(pat, expr, ty), result.clone()));
|
|
||||||
}
|
|
||||||
RsxToken::ClosingFor => {
|
|
||||||
if let Some((Container::ForLoop(pat, expr, ty), mut last_result)) =
|
|
||||||
tag_stack.pop()
|
|
||||||
{
|
|
||||||
last_result.push(Rsx::ForLoop(
|
|
||||||
pat.to_adapter(),
|
|
||||||
expr.to_adapter(),
|
|
||||||
ty.to_adapter(),
|
|
||||||
result,
|
|
||||||
));
|
|
||||||
result = last_result;
|
|
||||||
} else {
|
|
||||||
return Err(ParseError::ClosedTooFar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use enterprise_compiler::model::*;
|
|
||||||
use proptest::prelude::*;
|
|
||||||
use quote::ToTokens;
|
|
||||||
use syn_serde::Syn;
|
|
||||||
|
|
||||||
use super::Visitor;
|
|
||||||
|
|
||||||
fn convert<K: Clone + Ord>(
|
|
||||||
map: &BTreeMap<K, (syn_serde::Type, syn_serde::Expr)>,
|
|
||||||
) -> BTreeMap<K, (syn::Type, syn::Expr)> {
|
|
||||||
map.iter()
|
|
||||||
.map(|(name, (ty, expr))| {
|
|
||||||
let ty = syn::Type::from_adapter(ty);
|
|
||||||
let expr = syn::Expr::from_adapter(expr);
|
|
||||||
(name.clone(), (ty, expr))
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
// compare the trees
|
|
||||||
prop_assert_eq!(tree.name, tree2.name, "name");
|
|
||||||
prop_assert_eq!(convert(&tree.model), convert(&tree2.model), "model");
|
|
||||||
prop_assert_eq!(tree.view, tree2.view, "view");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +1,15 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::HashMap;
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
use enterprise_compiler::model::{TagLhs, TagRhs};
|
use enterprise_compiler::model::{TagLhs, TagRhs};
|
||||||
use proc_macro2::{token_stream::IntoIter, Delimiter, Ident, TokenStream, TokenTree};
|
use proc_macro2::{token_stream::IntoIter, Delimiter, Ident, TokenStream, TokenTree};
|
||||||
use symbol::Symbol;
|
use symbol::Symbol;
|
||||||
use syn::{
|
use syn::{Expr, Lit};
|
||||||
braced,
|
|
||||||
parse::{Parse, ParseStream},
|
|
||||||
token::Brace,
|
|
||||||
Expr, Lit, Pat, Result as SynResult, Token, Type,
|
|
||||||
};
|
|
||||||
use syn_serde::Syn;
|
use syn_serde::Syn;
|
||||||
|
|
||||||
use crate::parser::{consume_ident, consume_punct, ParseError};
|
use crate::ParseError;
|
||||||
|
use crate::{consume_ident, consume_punct};
|
||||||
|
|
||||||
pub(crate) struct RsxParser(Peekable<IntoIter>);
|
pub(crate) struct RsxParser(Peekable<IntoIter>);
|
||||||
|
|
||||||
|
@ -51,8 +47,6 @@ impl RsxParser {
|
||||||
|
|
||||||
let name = self.consume_ident()?;
|
let name = self.consume_ident()?;
|
||||||
if is_closing {
|
if is_closing {
|
||||||
// TODO: assert next is >
|
|
||||||
self.0.next();
|
|
||||||
return Ok(Some(RsxToken::ClosingTag(Symbol::from(name.to_string()))));
|
return Ok(Some(RsxToken::ClosingTag(Symbol::from(name.to_string()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +77,7 @@ impl RsxParser {
|
||||||
buf.push(next_token);
|
buf.push(next_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut attrs = BTreeMap::new();
|
let mut attrs = HashMap::new();
|
||||||
let mut iter = buf.into_iter().peekable();
|
let mut iter = buf.into_iter().peekable();
|
||||||
loop {
|
loop {
|
||||||
// consume a single attr
|
// consume a single attr
|
||||||
|
@ -140,71 +134,24 @@ impl RsxParser {
|
||||||
} else {
|
} else {
|
||||||
RsxToken::OpeningTag
|
RsxToken::OpeningTag
|
||||||
};
|
};
|
||||||
Ok(Some(variant(Symbol::from(name.to_string()), attrs)))
|
return Ok(Some(variant(Symbol::from(name.to_string()), attrs)));
|
||||||
}
|
}
|
||||||
TokenTree::Literal(lit) => {
|
TokenTree::Literal(lit) => {
|
||||||
let stream = TokenStream::from(TokenTree::Literal(lit));
|
let stream = TokenStream::from(TokenTree::Literal(lit));
|
||||||
let lit = syn::parse2::<Lit>(stream)?;
|
let lit = syn::parse2::<Lit>(stream)?;
|
||||||
|
|
||||||
if let Lit::Str(string) = lit {
|
if let Lit::Str(string) = lit {
|
||||||
Ok(Some(RsxToken::Str(string.value())))
|
return Ok(Some(RsxToken::Str(string.value())));
|
||||||
} else {
|
|
||||||
Err(ParseError::UnrecognizedLiteral(lit))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TokenTree::Group(group) if group.delimiter() == Delimiter::Brace => {
|
TokenTree::Group(group) if group.delimiter() == Delimiter::Brace => {
|
||||||
let expr = syn::parse2::<Expr>(group.stream())?;
|
let expr = syn::parse2::<Expr>(group.stream())?;
|
||||||
Ok(Some(RsxToken::Code(expr)))
|
return Ok(Some(RsxToken::Code(expr)));
|
||||||
}
|
}
|
||||||
TokenTree::Group(group) if group.delimiter() == Delimiter::Bracket => {
|
_ => unimplemented!("TOKEN: {:?}", token),
|
||||||
// for loop syntax
|
};
|
||||||
#[derive(Debug)]
|
|
||||||
struct ForLoopHeader {
|
|
||||||
kw_for: Token![for],
|
|
||||||
pat: Pat,
|
|
||||||
kw_in: Token![in],
|
|
||||||
bruh: Brace,
|
|
||||||
expr: Expr,
|
|
||||||
colon: Token![:],
|
|
||||||
ty: Type,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for ForLoopHeader {
|
unimplemented!("the fuck")
|
||||||
fn parse(input: ParseStream) -> SynResult<Self> {
|
|
||||||
let expr;
|
|
||||||
Ok(ForLoopHeader {
|
|
||||||
kw_for: input.parse()?,
|
|
||||||
pat: input.parse()?,
|
|
||||||
kw_in: input.parse()?,
|
|
||||||
bruh: braced!(expr in input),
|
|
||||||
expr: expr.parse()?,
|
|
||||||
colon: input.parse()?,
|
|
||||||
ty: input.parse()?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut stream = group.stream().into_iter().peekable();
|
|
||||||
match stream.peek() {
|
|
||||||
Some(TokenTree::Ident(ident)) if &ident.to_string() == "for" => {
|
|
||||||
let for_loop = syn::parse2::<ForLoopHeader>(group.stream())?;
|
|
||||||
Ok(Some(RsxToken::OpeningFor(
|
|
||||||
for_loop.pat,
|
|
||||||
for_loop.expr,
|
|
||||||
for_loop.ty,
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
Some(TokenTree::Punct(punct)) if punct.as_char() == '/' => {
|
|
||||||
stream.next();
|
|
||||||
// TODO: check that it's actuall closing the for-loop
|
|
||||||
Ok(Some(RsxToken::ClosingFor))
|
|
||||||
}
|
|
||||||
Some(token) => Err(ParseError::UnexpectedToken(token.clone())),
|
|
||||||
None => Err(ParseError::UnexpectedEOF),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
token => Err(ParseError::UnexpectedToken(token)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consume_ident(&mut self) -> Result<Ident, ParseError> {
|
fn consume_ident(&mut self) -> Result<Ident, ParseError> {
|
||||||
|
@ -224,13 +171,11 @@ impl RsxParser {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum RsxToken {
|
pub(crate) enum RsxToken {
|
||||||
OpeningTag(Symbol, BTreeMap<TagLhs, TagRhs>),
|
OpeningTag(Symbol, HashMap<TagLhs, TagRhs>),
|
||||||
EmptyTag(Symbol, BTreeMap<TagLhs, TagRhs>),
|
EmptyTag(Symbol, HashMap<TagLhs, TagRhs>),
|
||||||
ClosingTag(Symbol),
|
ClosingTag(Symbol),
|
||||||
Str(String),
|
Str(String),
|
||||||
Code(Expr),
|
Code(Expr),
|
||||||
OpeningFor(Pat, Expr, Type),
|
|
||||||
ClosingFor,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for RsxParser {
|
impl Iterator for RsxParser {
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
#[macro_use]
|
|
||||||
extern crate enterprise_macros;
|
|
||||||
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
use enterprise_compiler::model::Component;
|
|
||||||
use enterprise_compiler::Visitor;
|
|
||||||
use quote::ToTokens;
|
|
||||||
|
|
||||||
component! {
|
|
||||||
component TodoMVC {
|
|
||||||
model {
|
|
||||||
value: String = "",
|
|
||||||
todos: List<String> = List::new(),
|
|
||||||
}
|
|
||||||
|
|
||||||
view {
|
|
||||||
<input bind:value="value" on:submit={|o@todos, o@value| { set!(todos = todos.with(value)); set!(value = ""); }} />
|
|
||||||
<ul>
|
|
||||||
[for (key, line) in {todos} : List<String>]
|
|
||||||
<li>{line}</li>
|
|
||||||
[/for]
|
|
||||||
</ul>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod enterprise {
|
|
||||||
include!("../../ouais.rs");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let component: Component = serde_json::from_str(TodoMVC.as_ref()).unwrap();
|
|
||||||
|
|
||||||
// let mut visitor = Visitor::new();
|
|
||||||
// visitor.load_model(&component.model);
|
|
||||||
// visitor.make_graph(&component.view);
|
|
||||||
// println!("Tagged dom: {:?}", tagged_dom);
|
|
||||||
|
|
||||||
// let toplevel_names = visitor.gen_code(&tagged_dom);
|
|
||||||
// println!("Toplevel names: {:?}", toplevel_names);
|
|
||||||
// println!("Impl code: {}", &visitor.impl_code());
|
|
||||||
let all_code = enterprise_compiler::build(&component);
|
|
||||||
{
|
|
||||||
let mut file = File::create("ouais.rs").unwrap();
|
|
||||||
write!(file, "{}", all_code).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,7 +8,6 @@ component! {
|
||||||
}
|
}
|
||||||
|
|
||||||
view {
|
view {
|
||||||
<input bind:value="name" />
|
|
||||||
<input bind:value="name" />
|
<input bind:value="name" />
|
||||||
"Hello, " {name} "!"
|
"Hello, " {name} "!"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,141 +1,20 @@
|
||||||
// #[macro_use]
|
#[macro_use]
|
||||||
// extern crate enterprise;
|
extern crate enterprise;
|
||||||
|
|
||||||
// enterprise_mod!(helloworld);
|
enterprise_mod!(helloworld);
|
||||||
|
|
||||||
// use enterprise::{Backend, Web};
|
use std::sync::Arc;
|
||||||
|
|
||||||
// use crate::helloworld::HelloWorld;
|
use enterprise::{Backend, Component, Web};
|
||||||
|
|
||||||
// fn main() {
|
use crate::helloworld::HelloWorld;
|
||||||
// stdweb::initialize();
|
|
||||||
|
|
||||||
// let web = Web;
|
fn main() {
|
||||||
// let app = HelloWorld::new(&web);
|
stdweb::initialize();
|
||||||
// web.initialize(app, "app".into());
|
|
||||||
|
|
||||||
// stdweb::event_loop();
|
let web = Web;
|
||||||
// }
|
let app = HelloWorld::new(&web);
|
||||||
|
web.initialize(app, "app".into());
|
||||||
|
|
||||||
use stdweb::web::INode;
|
stdweb::event_loop();
|
||||||
|
|
||||||
pub struct HelloWorld<B> {
|
|
||||||
_b: std::marker::PhantomData<B>,
|
|
||||||
name: std::sync::Arc<enterprise::parking_lot::Mutex<String>>,
|
|
||||||
}
|
|
||||||
impl<B: enterprise::Backend> HelloWorld<B> {
|
|
||||||
pub fn new(_: &B) -> Self {
|
|
||||||
HelloWorld {
|
|
||||||
_b: std::marker::PhantomData::default(),
|
|
||||||
name: std::sync::Arc::new(enterprise::parking_lot::Mutex::new("hello".into())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn make_sym_0(&self) -> B::NodeType {
|
|
||||||
use enterprise::stdweb::web::IElement;
|
|
||||||
let el = enterprise::stdweb::web::document()
|
|
||||||
.create_element("input")
|
|
||||||
.unwrap();
|
|
||||||
el.set_attribute("id", "sym_0").unwrap();
|
|
||||||
let inner_lock_sym_5 = self.name.clone();
|
|
||||||
{
|
|
||||||
use enterprise::stdweb::{unstable::TryFrom, web::IEventTarget};
|
|
||||||
let inner_el = el.clone();
|
|
||||||
el.add_event_listener(move |evt: enterprise::stdweb::web::event::InputEvent| {
|
|
||||||
let new_value =
|
|
||||||
enterprise::stdweb::web::html_element::InputElement::try_from(inner_el.clone())
|
|
||||||
.unwrap()
|
|
||||||
.raw_value();
|
|
||||||
{
|
|
||||||
let mut locked = inner_lock_sym_5.lock();
|
|
||||||
*locked = new_value.clone();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
use enterprise::stdweb::web::{INode, INonElementParentNode};
|
|
||||||
if let Some(target) =
|
|
||||||
enterprise::stdweb::web::document().get_element_by_id("sym_3")
|
|
||||||
{
|
|
||||||
target.set_text_content(&new_value.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
el.as_node().clone()
|
|
||||||
}
|
|
||||||
fn make_sym_1(&self) -> B::NodeType {
|
|
||||||
use enterprise::stdweb::web::IElement;
|
|
||||||
let el = enterprise::stdweb::web::document()
|
|
||||||
.create_element("input")
|
|
||||||
.unwrap();
|
|
||||||
el.set_attribute("id", "sym_1").unwrap();
|
|
||||||
let inner_lock_sym_6 = self.name.clone();
|
|
||||||
{
|
|
||||||
use enterprise::stdweb::{unstable::TryFrom, web::IEventTarget};
|
|
||||||
let inner_el = el.clone();
|
|
||||||
el.add_event_listener(move |evt: enterprise::stdweb::web::event::InputEvent| {
|
|
||||||
let new_value =
|
|
||||||
enterprise::stdweb::web::html_element::InputElement::try_from(inner_el.clone())
|
|
||||||
.unwrap()
|
|
||||||
.raw_value();
|
|
||||||
{
|
|
||||||
let mut locked = inner_lock_sym_6.lock();
|
|
||||||
*locked = new_value.clone();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
use enterprise::stdweb::web::{INode, INonElementParentNode};
|
|
||||||
if let Some(target) =
|
|
||||||
enterprise::stdweb::web::document().get_element_by_id("sym_3")
|
|
||||||
{
|
|
||||||
target.set_text_content(&new_value.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
el.as_node().clone()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn make_sym_2(&self) -> B::NodeType {
|
|
||||||
enterprise::stdweb::web::document().create_text_node("Hello, ")
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn make_sym_3(&self) -> B::NodeType {
|
|
||||||
use enterprise::stdweb::web::IElement;
|
|
||||||
let el = enterprise::stdweb::web::document()
|
|
||||||
.create_element("span")
|
|
||||||
.expect("shouldn't fail");
|
|
||||||
el.set_attribute("id", "sym_3").unwrap();
|
|
||||||
el.as_node().clone()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn make_sym_4(&self) -> B::NodeType {
|
|
||||||
enterprise::stdweb::web::document().create_text_node("!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<B: enterprise::Backend> enterprise::Component<B> for HelloWorld<B> {
|
|
||||||
fn create(&self, el: &enterprise::stdweb::web::Element) {
|
|
||||||
{
|
|
||||||
use enterprise::stdweb::web::INode;
|
|
||||||
let sub = self.make_sym_0();
|
|
||||||
el.append_child(&sub);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
use enterprise::stdweb::web::INode;
|
|
||||||
let sub = self.make_sym_1();
|
|
||||||
el.append_child(&sub);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
use enterprise::stdweb::web::INode;
|
|
||||||
let sub = self.make_sym_2();
|
|
||||||
el.append_child(&sub);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
use enterprise::stdweb::web::INode;
|
|
||||||
let sub = self.make_sym_3();
|
|
||||||
el.append_child(&sub);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
use enterprise::stdweb::web::INode;
|
|
||||||
let sub = self.make_sym_4();
|
|
||||||
el.append_child(&sub);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,7 @@ name = "todomvc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Michael Zhang <iptq@protonmail.com>"]
|
authors = ["Michael Zhang <iptq@protonmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
build = "src/build.rs"
|
|
||||||
|
|
||||||
[build-dependencies]
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
enterprise-compiler = { path = "../../enterprise-compiler" }
|
|
||||||
enterprise-macros = { path = "../../enterprise-macros" }
|
|
||||||
enterprise = { path = "../.." }
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
stdweb = "0.4.20"
|
|
||||||
enterprise-macros = { path = "../../enterprise-macros" }
|
|
||||||
enterprise = { path = "../.." }
|
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
#[macro_use]
|
|
||||||
extern crate enterprise_macros;
|
|
||||||
|
|
||||||
component! {
|
|
||||||
component TodoMVC {
|
|
||||||
model {
|
|
||||||
value: String = "",
|
|
||||||
todos: List<String> = List::new(),
|
|
||||||
}
|
|
||||||
|
|
||||||
view {
|
|
||||||
<input bind:value="value" on:submit={|o@todos, o@value| { set!(todos = todos.with(value)); set!(value = ""); }} />
|
|
||||||
<ul>
|
|
||||||
[for (key, line) in {todos} : List<String>]
|
|
||||||
<li>{line}</li>
|
|
||||||
[/for]
|
|
||||||
</ul>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
enterprise_compiler::process("todomvc", TodoMVC);
|
|
||||||
}
|
|
|
@ -1,18 +1,3 @@
|
||||||
#[macro_use]
|
|
||||||
extern crate enterprise;
|
|
||||||
|
|
||||||
enterprise_mod!(todomvc);
|
|
||||||
|
|
||||||
use enterprise::{Backend, Web};
|
|
||||||
|
|
||||||
use crate::todomvc::TodoMVC;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
stdweb::initialize();
|
println!("Hello, world!");
|
||||||
|
|
||||||
let web = Web;
|
|
||||||
let app = TodoMVC::new(&web);
|
|
||||||
web.initialize(app, "app".into());
|
|
||||||
|
|
||||||
stdweb::event_loop();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>what the Hek</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
<script src="todomvc.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,8 +0,0 @@
|
||||||
# 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 996e4a6b3f149a995d2d85203c8d824bf86295dc1fb078ed87cdb77489d8a44b # shrinks to mut vec = [0, 0], index = Index(0)
|
|
||||||
cc 369740f4b70d4b7bec3d743873870dcd5fee9abe687d2aaac66784532d49ae4f # shrinks to mut vec = [], index = Index(0)
|
|
|
@ -13,8 +13,4 @@ pub trait Backend: Sized {
|
||||||
|
|
||||||
/// Initializes the backend with the given component.
|
/// Initializes the backend with the given component.
|
||||||
fn initialize<C: Component<Self>>(&self, _: C, _: Self::InitParams);
|
fn initialize<C: Component<Self>>(&self, _: C, _: Self::InitParams);
|
||||||
|
|
||||||
type NodeType: Node<Self>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Node<B: Backend> {}
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::backend::Node;
|
use stdweb::web::{document, INonElementParentNode};
|
||||||
use stdweb::web::{document, INode, INonElementParentNode, Node as WebNode};
|
|
||||||
|
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::Component;
|
use crate::Component;
|
||||||
|
@ -13,12 +12,7 @@ impl Backend for Web {
|
||||||
fn initialize<C: Component<Self>>(&self, component: C, params: Self::InitParams) {
|
fn initialize<C: Component<Self>>(&self, component: C, params: Self::InitParams) {
|
||||||
let id = params.as_ref();
|
let id = params.as_ref();
|
||||||
if let Some(el) = document().get_element_by_id(id) {
|
if let Some(el) = document().get_element_by_id(id) {
|
||||||
let sub = component.render();
|
component.create(&el);
|
||||||
el.append_child(&sub);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type NodeType = WebNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node<Web> for WebNode {}
|
|
||||||
|
|
4
src/compiler.rs
Normal file
4
src/compiler.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
//! Compiler-related procedures.
|
||||||
|
|
||||||
|
/// Processes a set of component definitions.
|
||||||
|
pub fn process() {}
|
|
@ -1,9 +0,0 @@
|
||||||
// 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> {}
|
|
33
src/lib.rs
33
src/lib.rs
|
@ -1,30 +1,22 @@
|
||||||
//! Enterprise is a backend-agnostic framework for developing server-client GUI applications.
|
//! Enterprise is a backend-agnostic framework for developing server-client GUI applications.
|
||||||
|
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
pub extern crate enterprise_compiler;
|
pub extern crate enterprise_compiler;
|
||||||
extern crate std as rust_std;
|
|
||||||
|
|
||||||
// re-exports
|
// re-exports
|
||||||
pub extern crate parking_lot;
|
pub extern crate parking_lot;
|
||||||
pub extern crate stdweb;
|
pub extern crate stdweb;
|
||||||
|
|
||||||
mod backend;
|
mod backend;
|
||||||
pub mod std;
|
pub mod compiler;
|
||||||
|
|
||||||
mod forloop;
|
|
||||||
|
|
||||||
use rust_std::sync::Arc;
|
|
||||||
|
|
||||||
use parking_lot::RwLock;
|
|
||||||
use stdweb::web::Node;
|
|
||||||
|
|
||||||
pub use crate::backend::{Backend, Web};
|
pub use crate::backend::{Backend, Web};
|
||||||
|
|
||||||
/// Components are the building-blocks of enterprise applications.
|
/// Components are the building-blocks of enterprise applications.
|
||||||
pub trait Component<B: Backend> {
|
pub trait Component<B: Backend> {
|
||||||
/// TODO: replace this with a real init function.
|
/// TODO: replace this with a real init function.
|
||||||
// fn create(&self, el: &crate::stdweb::web::Element);
|
fn create(&self, el: &crate::stdweb::web::Element);
|
||||||
|
|
||||||
fn render(&self) -> Node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Declares a mod
|
/// Declares a mod
|
||||||
|
@ -36,20 +28,3 @@ macro_rules! enterprise_mod {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! set {
|
|
||||||
($name:ident = $expr:expr) => {
|
|
||||||
ValueUpdatable::update($name, $expr.into())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ValueUpdatable {
|
|
||||||
fn update(_: Arc<RwLock<Self>>, _: Self);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ValueUpdatable for String {
|
|
||||||
fn update(value_ref: Arc<RwLock<Self>>, new_value: String) {
|
|
||||||
*value_ref.write() = new_value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
268
src/std/list.rs
268
src/std/list.rs
|
@ -1,268 +0,0 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fmt::{self, Debug, Formatter};
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::ops::Index;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use std::ptr::NonNull;
|
|
||||||
|
|
||||||
use parking_lot::RwLock;
|
|
||||||
use symbol::Symbol;
|
|
||||||
|
|
||||||
use crate::ValueUpdatable;
|
|
||||||
|
|
||||||
/// A list that guarantees:
|
|
||||||
/// - O(1) insertion
|
|
||||||
/// - O(1) deletion
|
|
||||||
/// - O(1) lookup
|
|
||||||
pub struct List<T> {
|
|
||||||
head: Option<NonNull<Node<T>>>,
|
|
||||||
tail: Option<NonNull<Node<T>>>,
|
|
||||||
map: HashMap<Symbol, NonNull<Node<T>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Node<T> {
|
|
||||||
prev: Option<NonNull<Node<T>>>,
|
|
||||||
next: Option<NonNull<Node<T>>>,
|
|
||||||
data: Option<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Referential iterator
|
|
||||||
pub struct Iter<'a, T>(Option<NonNull<Node<T>>>, PhantomData<&'a T>);
|
|
||||||
|
|
||||||
impl<'a, T> Iterator for Iter<'a, T> {
|
|
||||||
type Item = &'a T;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if let Some(node) = self.0 {
|
|
||||||
self.0 = unsafe { (*node.as_ptr()).next };
|
|
||||||
unsafe { &(*node.as_ptr()).data }.as_ref()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Item iterator
|
|
||||||
pub struct IntoIter<T>(Option<NonNull<Node<T>>>);
|
|
||||||
|
|
||||||
impl<T> Iterator for IntoIter<T> {
|
|
||||||
type Item = T;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if let Some(node) = self.0 {
|
|
||||||
let node_ptr = node.as_ptr();
|
|
||||||
self.0 = unsafe { (*node_ptr).next };
|
|
||||||
unsafe { Box::from_raw(&mut (*node_ptr).data) }.take()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Default for List<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
List {
|
|
||||||
head: None,
|
|
||||||
tail: None,
|
|
||||||
map: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone> Clone for List<T> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
let mut new_list = List::new();
|
|
||||||
for item in self.iter() {
|
|
||||||
new_list.push(item.clone());
|
|
||||||
}
|
|
||||||
new_list
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Debug> Debug for List<T> {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "[")?;
|
|
||||||
let mut is_first = true;
|
|
||||||
for item in self.iter() {
|
|
||||||
if is_first {
|
|
||||||
is_first = false;
|
|
||||||
} else {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
write!(f, "{:?}", item)?;
|
|
||||||
}
|
|
||||||
write!(f, "]")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Index<Symbol> for List<T> {
|
|
||||||
type Output = T;
|
|
||||||
fn index(&self, key: Symbol) -> &Self::Output {
|
|
||||||
self.get(&key).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> List<T> {
|
|
||||||
/// Creates a new List<T>
|
|
||||||
pub fn new() -> Self {
|
|
||||||
List::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an iterator for the list
|
|
||||||
pub fn iter<'a>(&'a self) -> Iter<'a, T> {
|
|
||||||
Iter(self.head, PhantomData::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a consuming iterator for the list
|
|
||||||
pub fn into_iter(self) -> IntoIter<T> {
|
|
||||||
IntoIter(self.head)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the item using its key
|
|
||||||
pub fn get(&self, key: &Symbol) -> Option<&T> {
|
|
||||||
if let Some(node) = self.map.get(key) {
|
|
||||||
unsafe { node.as_ref() }.data.as_ref()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the head of the list
|
|
||||||
pub fn head(&self) -> Option<&T> {
|
|
||||||
if let Some(ref node) = self.head {
|
|
||||||
unsafe { node.as_ref() }.data.as_ref()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the tail of the list
|
|
||||||
pub fn tail(&self) -> Option<&T> {
|
|
||||||
if let Some(ref node) = self.tail {
|
|
||||||
unsafe { node.as_ref() }.data.as_ref()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Immutable insert
|
|
||||||
pub fn with(mut self, item: T) -> List<T> {
|
|
||||||
self.push(item);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts the specified item into the list
|
|
||||||
pub fn push(&mut self, item: T) -> Symbol {
|
|
||||||
let node = Node {
|
|
||||||
prev: None,
|
|
||||||
next: None,
|
|
||||||
data: Some(item),
|
|
||||||
};
|
|
||||||
let mut node = unsafe { NonNull::new_unchecked(Box::into_raw(Box::new(node))) };
|
|
||||||
let new_key = Symbol::gensym();
|
|
||||||
|
|
||||||
if let Some(ref mut tail) = self.tail {
|
|
||||||
(*unsafe { node.as_mut() }).prev = Some(*tail);
|
|
||||||
(*unsafe { tail.as_mut() }).next = Some(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let None = self.head {
|
|
||||||
self.head = Some(node);
|
|
||||||
}
|
|
||||||
self.tail = Some(node);
|
|
||||||
|
|
||||||
self.map.insert(new_key, node);
|
|
||||||
new_key
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes an element from the list
|
|
||||||
pub fn remove(&mut self, key: Symbol) -> Option<T> {
|
|
||||||
if let Some(node) = self.map.get(&key) {
|
|
||||||
let mut node = unsafe { Box::from_raw(node.as_ptr()) };
|
|
||||||
let data = node.data.take();
|
|
||||||
|
|
||||||
let prev = node.prev;
|
|
||||||
let next = node.next;
|
|
||||||
if let Some(mut prev) = prev {
|
|
||||||
(*unsafe { prev.as_mut() }).next = next;
|
|
||||||
} else if let Some(_) = self.head {
|
|
||||||
// if node.prev is None that means node is the head
|
|
||||||
self.head = next;
|
|
||||||
} else {
|
|
||||||
// shouldn't be any other cases
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
data
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> ValueUpdatable for List<T> {
|
|
||||||
fn update(value_ref: Arc<RwLock<Self>>, new_value: Self) {
|
|
||||||
// TODO: merge?
|
|
||||||
let mut locked = value_ref.write();
|
|
||||||
*locked = new_value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::List;
|
|
||||||
use proptest::{
|
|
||||||
arbitrary::any,
|
|
||||||
collection::{vec, SizeRange},
|
|
||||||
prelude::*,
|
|
||||||
sample::Index,
|
|
||||||
};
|
|
||||||
|
|
||||||
proptest! {
|
|
||||||
#[test]
|
|
||||||
fn test_list_equivalence(vec in vec(any::<u32>(), SizeRange::default())) {
|
|
||||||
let mut list = List::new();
|
|
||||||
for item in vec.iter() {
|
|
||||||
list.insert(*item);
|
|
||||||
}
|
|
||||||
prop_assert!(list.iter().eq(vec.iter()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn return_ends(vec in vec(any::<u32>(), SizeRange::default())) {
|
|
||||||
prop_assume!(vec.len() > 0, "can't return any elements of a 0-sized vec");
|
|
||||||
let mut list = List::new();
|
|
||||||
for item in vec.iter() {
|
|
||||||
list.insert(*item);
|
|
||||||
}
|
|
||||||
|
|
||||||
prop_assert_eq!(list.head(), vec.first(), "heads");
|
|
||||||
prop_assert_eq!(list.tail(), vec.last(), "tails");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_remove_by_key(mut vec in vec(any::<u32>(), SizeRange::default()), index in any::<Index>()) {
|
|
||||||
prop_assume!(vec.len() > 0, "can't remove from a 0-sized vec");
|
|
||||||
let index = index.index(vec.len());
|
|
||||||
prop_assume!(index < vec.len(), "ind is bigger than vec");
|
|
||||||
|
|
||||||
let mut list = List::new();
|
|
||||||
let mut saved_key = None;
|
|
||||||
for (i, item) in vec.iter().enumerate() {
|
|
||||||
let key = list.insert(*item);
|
|
||||||
if i == index {
|
|
||||||
saved_key = Some(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prop_assert!(saved_key.is_some());
|
|
||||||
let saved_key = saved_key.unwrap();
|
|
||||||
|
|
||||||
list.remove(saved_key);
|
|
||||||
vec.remove(index);
|
|
||||||
prop_assert!(list.iter().eq(vec.iter()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
//! Standard modules
|
|
||||||
|
|
||||||
mod list;
|
|
||||||
mod widgets;
|
|
||||||
|
|
||||||
pub use self::list::List;
|
|
||||||
pub use self::widgets::*;
|
|
|
@ -1,28 +0,0 @@
|
||||||
use std::any::TypeId;
|
|
||||||
use std::borrow::Borrow;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::backend::{Backend, Web};
|
|
||||||
use crate::Component;
|
|
||||||
|
|
||||||
thread_local! {
|
|
||||||
static INPUT_BOX: HashMap<TypeId, Box<dyn Fn(String) -> Box<dyn InputBox>>> = HashMap::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait InputBox {}
|
|
||||||
|
|
||||||
pub fn InputBox<B: 'static + Backend>(name: impl AsRef<str>) -> Option<Box<dyn InputBox>> {
|
|
||||||
let name = name.as_ref();
|
|
||||||
let ty = TypeId::of::<B>();
|
|
||||||
INPUT_BOX.with(move |input_box| {
|
|
||||||
if let Some(func) = input_box.get(&ty) {
|
|
||||||
Some(func(name.to_string()))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct InputBoxWeb {}
|
|
||||||
|
|
||||||
impl InputBox for InputBoxWeb {}
|
|
|
@ -1,5 +0,0 @@
|
||||||
mod input_box;
|
|
||||||
mod text;
|
|
||||||
|
|
||||||
use self::input_box::*;
|
|
||||||
use self::text::*;
|
|
|
@ -1,17 +0,0 @@
|
||||||
use crate::backend::Web;
|
|
||||||
use crate::Component;
|
|
||||||
use stdweb::web::{document, INode, Node};
|
|
||||||
|
|
||||||
pub struct Text(String);
|
|
||||||
|
|
||||||
impl Text {
|
|
||||||
pub fn new(string: impl AsRef<str>) -> Self {
|
|
||||||
Self(string.as_ref().to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Component<Web> for Text {
|
|
||||||
fn render(&self) -> Node {
|
|
||||||
document().create_text_node(&self.0).as_node().clone()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -22,7 +22,7 @@ proc-macro2 = { version = "1.0", default-features = false }
|
||||||
serde = { version = "1.0.99", features = ["derive"] }
|
serde = { version = "1.0.99", features = ["derive"] }
|
||||||
serde_derive = "1.0.99" # This is necessary to make `-Z minimal-versions` build successful.
|
serde_derive = "1.0.99" # This is necessary to make `-Z minimal-versions` build successful.
|
||||||
serde_json = { version = "1.0", optional = true }
|
serde_json = { version = "1.0", optional = true }
|
||||||
syn = { version = "1.0.5", default-features = false, features = ["extra-traits", "full"] }
|
syn = { version = "1.0.5", default-features = false, features = ["full"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
|
|
|
@ -11,6 +11,7 @@ ast_struct! {
|
||||||
/// the XID_Start property.
|
/// the XID_Start property.
|
||||||
/// - All following characters must be Unicode code points with the XID_Continue
|
/// - All following characters must be Unicode code points with the XID_Continue
|
||||||
/// property.
|
/// property.
|
||||||
|
#[derive(Clone)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct Lifetime {
|
pub struct Lifetime {
|
||||||
pub(crate) ident: Ident,
|
pub(crate) ident: Ident,
|
||||||
|
|
|
@ -3,7 +3,7 @@ macro_rules! ast_struct {
|
||||||
[$($attrs_pub:tt)*]
|
[$($attrs_pub:tt)*]
|
||||||
struct $name:ident $($rest:tt)*
|
struct $name:ident $($rest:tt)*
|
||||||
) => {
|
) => {
|
||||||
#[derive(Clone, Debug, crate::Serialize, crate::Deserialize)]
|
#[derive(Debug, crate::Serialize, crate::Deserialize)]
|
||||||
$($attrs_pub)* struct $name $($rest)*
|
$($attrs_pub)* struct $name $($rest)*
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ macro_rules! ast_enum {
|
||||||
[$($attrs_pub:tt)*]
|
[$($attrs_pub:tt)*]
|
||||||
enum $name:ident $($rest:tt)*
|
enum $name:ident $($rest:tt)*
|
||||||
) => (
|
) => (
|
||||||
#[derive(Clone, Debug, crate::Serialize, crate::Deserialize)]
|
#[derive(Debug, crate::Serialize, crate::Deserialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
$($attrs_pub)* enum $name $($rest)*
|
$($attrs_pub)* enum $name $($rest)*
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,7 +6,7 @@ ast_struct! {
|
||||||
///
|
///
|
||||||
/// This type provides interfaces for iterating over token trees and for
|
/// This type provides interfaces for iterating over token trees and for
|
||||||
/// collecting token trees into one stream.
|
/// collecting token trees into one stream.
|
||||||
#[derive(Default)]
|
#[derive(Clone, Default)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct TokenStream {
|
pub struct TokenStream {
|
||||||
inner: Vec<TokenTree>,
|
inner: Vec<TokenTree>,
|
||||||
|
@ -25,6 +25,7 @@ impl TokenStream {
|
||||||
|
|
||||||
ast_enum! {
|
ast_enum! {
|
||||||
/// A single token or a delimited sequence of token trees (e.g. `[1, (), ..]`).
|
/// A single token or a delimited sequence of token trees (e.g. `[1, (), ..]`).
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum TokenTree {
|
pub enum TokenTree {
|
||||||
/// A token stream surrounded by bracket delimiters.
|
/// A token stream surrounded by bracket delimiters.
|
||||||
Group(Group),
|
Group(Group),
|
||||||
|
@ -43,6 +44,7 @@ ast_struct! {
|
||||||
///
|
///
|
||||||
/// A `Group` internally contains a `TokenStream` which is surrounded by
|
/// A `Group` internally contains a `TokenStream` which is surrounded by
|
||||||
/// `Delimiter`s.
|
/// `Delimiter`s.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Group {
|
pub struct Group {
|
||||||
delimiter: Delimiter,
|
delimiter: Delimiter,
|
||||||
stream: TokenStream,
|
stream: TokenStream,
|
||||||
|
@ -51,7 +53,7 @@ ast_struct! {
|
||||||
|
|
||||||
ast_enum! {
|
ast_enum! {
|
||||||
/// Describes how a sequence of token trees is delimited.
|
/// Describes how a sequence of token trees is delimited.
|
||||||
#[derive(Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum Delimiter {
|
pub enum Delimiter {
|
||||||
/// `( ... )`
|
/// `( ... )`
|
||||||
Parenthesis,
|
Parenthesis,
|
||||||
|
@ -75,7 +77,7 @@ ast_struct! {
|
||||||
///
|
///
|
||||||
/// Multicharacter operators like `+=` are represented as two instances of
|
/// Multicharacter operators like `+=` are represented as two instances of
|
||||||
/// `Punct` with different forms of `Spacing` returned.
|
/// `Punct` with different forms of `Spacing` returned.
|
||||||
#[derive(Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Punct {
|
pub struct Punct {
|
||||||
op: char,
|
op: char,
|
||||||
spacing: Spacing,
|
spacing: Spacing,
|
||||||
|
@ -85,7 +87,7 @@ ast_struct! {
|
||||||
ast_enum! {
|
ast_enum! {
|
||||||
/// Whether an `Punct` is followed immediately by another `Punct` or followed by
|
/// Whether an `Punct` is followed immediately by another `Punct` or followed by
|
||||||
/// another token or whitespace.
|
/// another token or whitespace.
|
||||||
#[derive(Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum Spacing {
|
pub enum Spacing {
|
||||||
/// E.g. `+` is `Alone` in `+ =`, `+ident` or `+()`.
|
/// E.g. `+` is `Alone` in `+ =`, `+ident` or `+()`.
|
||||||
Alone,
|
Alone,
|
||||||
|
@ -106,7 +108,7 @@ ast_struct! {
|
||||||
///
|
///
|
||||||
/// - The empty string is not an identifier. Use `Option<Ident>`.
|
/// - The empty string is not an identifier. Use `Option<Ident>`.
|
||||||
/// - A lifetime is not an identifier. Use `syn::Lifetime` instead.
|
/// - A lifetime is not an identifier. Use `syn::Lifetime` instead.
|
||||||
#[derive(Eq, PartialEq)]
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct Ident {
|
pub struct Ident {
|
||||||
inner: String,
|
inner: String,
|
||||||
|
@ -120,6 +122,7 @@ ast_struct! {
|
||||||
///
|
///
|
||||||
/// Boolean literals like `true` and `false` do not belong here, they are
|
/// Boolean literals like `true` and `false` do not belong here, they are
|
||||||
/// `Ident`s.
|
/// `Ident`s.
|
||||||
|
#[derive(Clone)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct Literal {
|
pub struct Literal {
|
||||||
pub(crate) text: String,
|
pub(crate) text: String,
|
||||||
|
|
Loading…
Reference in a new issue