split into crates, made changes
This commit is contained in:
parent
3e9f05bbfd
commit
b732e1ff3d
30 changed files with 1490 additions and 1285 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,3 @@
|
||||||
/target
|
/target
|
||||||
/pkg
|
cxgraph-web/pkg
|
||||||
/.vscode
|
/.vscode
|
||||||
|
|
431
Cargo.lock
generated
431
Cargo.lock
generated
|
@ -46,9 +46,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.0.1"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
|
checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
@ -108,10 +108,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ash"
|
name = "ascii-canvas"
|
||||||
version = "0.37.2+1.3.238"
|
version = "3.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "28bf19c1f0a470be5fbf7522a308a05df06610252c5bcf5143e1b23f629a9a03"
|
checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6"
|
||||||
|
dependencies = [
|
||||||
|
"term",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ash"
|
||||||
|
version = "0.37.3+1.3.251"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libloading 0.7.4",
|
"libloading 0.7.4",
|
||||||
]
|
]
|
||||||
|
@ -160,9 +169,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.2.1"
|
version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "24a6904aef64d73cf10ab17ebace7befb918b82164785cb89907993be7f83813"
|
checksum = "6776fc96284a0bb647b615056fc496d1fe1644a7ab01829818a6d91cae888b84"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block"
|
name = "block"
|
||||||
|
@ -191,9 +200,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.12.2"
|
version = "3.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b"
|
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
|
@ -203,10 +212,11 @@ checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "calloop"
|
name = "calloop"
|
||||||
version = "0.10.5"
|
version = "0.10.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a59225be45a478d772ce015d9743e49e92798ece9e34eda9a6aa2a6a7f40192"
|
checksum = "52e0d00eb1ea24371a97d2da6201c6747a633dc6dc1988ef503403b4c59504a8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
"log",
|
"log",
|
||||||
"nix 0.25.1",
|
"nix 0.25.1",
|
||||||
"slotmap",
|
"slotmap",
|
||||||
|
@ -338,22 +348,33 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxgraph"
|
name = "crunchy"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cxgraph-desktop"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
|
||||||
"cgmath",
|
|
||||||
"console_error_panic_hook",
|
|
||||||
"console_log",
|
|
||||||
"encase",
|
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"libcxgraph",
|
||||||
"log",
|
"log",
|
||||||
"pollster",
|
"pollster",
|
||||||
"raw-window-handle",
|
"winit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cxgraph-web"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"console_error_panic_hook",
|
||||||
|
"console_log",
|
||||||
|
"libcxgraph",
|
||||||
|
"log",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"wgpu",
|
|
||||||
"winit",
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -368,6 +389,33 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "diff"
|
||||||
|
version = "0.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-next"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"dirs-sys-next",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys-next"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"redox_users",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dispatch"
|
name = "dispatch"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -376,11 +424,11 @@ checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dlib"
|
name = "dlib"
|
||||||
version = "0.5.0"
|
version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794"
|
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libloading 0.7.4",
|
"libloading 0.8.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -389,6 +437,21 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ena"
|
||||||
|
version = "0.14.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encase"
|
name = "encase"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
|
@ -418,7 +481,7 @@ checksum = "3fe2568f851fd6144a45fa91cfed8fe5ca8fc0b56ba6797bfc1ed2771b90e37c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.15",
|
"syn 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -464,6 +527,12 @@ dependencies = [
|
||||||
"simd-adler32",
|
"simd-adler32",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fixedbitset"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.26"
|
version = "1.0.26"
|
||||||
|
@ -491,9 +560,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.9"
|
version = "0.2.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
|
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -508,9 +577,9 @@ checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glow"
|
name = "glow"
|
||||||
version = "0.12.1"
|
version = "0.12.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4e007a07a24de5ecae94160f141029e9a347282cfe25d1d58d85d845cf3130f1"
|
checksum = "807edf58b70c0b5b2181dd39fe1839dbdb3ba02645630dc5f753e23da307f762"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"slotmap",
|
"slotmap",
|
||||||
|
@ -636,9 +705,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-lifetimes"
|
name = "io-lifetimes"
|
||||||
version = "1.0.10"
|
version = "1.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
|
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -657,6 +726,15 @@ dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jni-sys"
|
name = "jni-sys"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -674,9 +752,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.61"
|
version = "0.3.63"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
|
checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
@ -692,6 +770,38 @@ dependencies = [
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lalrpop"
|
||||||
|
version = "0.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da4081d44f4611b66c6dd725e6de3169f9f63905421e8626fcb86b6a898998b8"
|
||||||
|
dependencies = [
|
||||||
|
"ascii-canvas",
|
||||||
|
"bit-set",
|
||||||
|
"diff",
|
||||||
|
"ena",
|
||||||
|
"is-terminal",
|
||||||
|
"itertools",
|
||||||
|
"lalrpop-util",
|
||||||
|
"petgraph",
|
||||||
|
"pico-args",
|
||||||
|
"regex",
|
||||||
|
"regex-syntax",
|
||||||
|
"string_cache",
|
||||||
|
"term",
|
||||||
|
"tiny-keccak",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lalrpop-util"
|
||||||
|
version = "0.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d"
|
||||||
|
dependencies = [
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -700,9 +810,23 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.144"
|
version = "0.2.146"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libcxgraph"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cgmath",
|
||||||
|
"encase",
|
||||||
|
"lalrpop",
|
||||||
|
"lalrpop-util",
|
||||||
|
"log",
|
||||||
|
"num-complex",
|
||||||
|
"raw-window-handle",
|
||||||
|
"wgpu",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
|
@ -726,15 +850,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.3.7"
|
version = "0.3.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f"
|
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
|
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
|
@ -742,12 +866,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.17"
|
version = "0.4.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "malloc_buf"
|
name = "malloc_buf"
|
||||||
|
@ -823,21 +944,21 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "0.8.6"
|
version = "0.8.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
|
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"wasi",
|
"wasi",
|
||||||
"windows-sys 0.45.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "naga"
|
name = "naga"
|
||||||
version = "0.12.0"
|
version = "0.12.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f00ce114f2867153c079d4489629dbd27aa4b5387a8ba5341bd3f6dfe870688f"
|
checksum = "80cd00bd6180a8790f1c020ed258a46b8d73dd5bd6af104a238c9d71f806938e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit-set",
|
"bit-set",
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
|
@ -882,6 +1003,12 @@ dependencies = [
|
||||||
"jni-sys",
|
"jni-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "new_debug_unreachable"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.24.3"
|
version = "0.24.3"
|
||||||
|
@ -917,6 +1044,15 @@ dependencies = [
|
||||||
"minimal-lexical",
|
"minimal-lexical",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-complex"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
@ -994,18 +1130,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.30.3"
|
version = "0.30.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
|
checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.17.1"
|
version = "1.18.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "orbclient"
|
name = "orbclient"
|
||||||
|
@ -1037,22 +1173,47 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot_core"
|
name = "parking_lot_core"
|
||||||
version = "0.9.7"
|
version = "0.9.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
|
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall 0.2.16",
|
"redox_syscall 0.3.5",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-sys 0.45.0",
|
"windows-targets 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.2.0"
|
version = "2.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "petgraph"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4"
|
||||||
|
dependencies = [
|
||||||
|
"fixedbitset",
|
||||||
|
"indexmap",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_shared"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
|
||||||
|
dependencies = [
|
||||||
|
"siphasher",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pico-args"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
|
@ -1079,6 +1240,12 @@ version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"
|
checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "precomputed-hash"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-crate"
|
name = "proc-macro-crate"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
|
@ -1091,9 +1258,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.56"
|
version = "1.0.59"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
@ -1106,9 +1273,9 @@ checksum = "332cd62e95873ea4f41f3dfd6bbbfc5b52aec892d7e8d534197c4720a0bbbab2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.27"
|
version = "1.0.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
|
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
@ -1144,10 +1311,21 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "redox_users"
|
||||||
version = "1.8.1"
|
version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
|
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"redox_syscall 0.2.16",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -1156,9 +1334,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.7.1"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
|
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "renderdoc-sys"
|
name = "renderdoc-sys"
|
||||||
|
@ -1192,6 +1370,12 @@ dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -1223,6 +1407,12 @@ version = "0.3.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f"
|
checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "siphasher"
|
||||||
|
version = "0.3.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slotmap"
|
name = "slotmap"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
@ -1275,9 +1465,22 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strict-num"
|
name = "strict-num"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9df65f20698aeed245efdde3628a6b559ea1239bbb871af1b6e3b58c413b2bd1"
|
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "string_cache"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
|
||||||
|
dependencies = [
|
||||||
|
"new_debug_unreachable",
|
||||||
|
"once_cell",
|
||||||
|
"parking_lot",
|
||||||
|
"phf_shared",
|
||||||
|
"precomputed-hash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
|
@ -1292,15 +1495,26 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.15"
|
version = "2.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
|
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "term"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-next",
|
||||||
|
"rustversion",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
@ -1327,7 +1541,16 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.15",
|
"syn 2.0.18",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiny-keccak"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
||||||
|
dependencies = [
|
||||||
|
"crunchy",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1357,15 +1580,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "0.6.1"
|
version = "0.6.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
|
checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.19.8"
|
version = "0.19.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13"
|
checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
|
@ -1380,9 +1603,9 @@ checksum = "44dcf002ae3b32cd25400d6df128c5babec3927cd1eb7ce813cfff20eb6c3746"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.8"
|
version = "1.0.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
|
@ -1416,9 +1639,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.84"
|
version = "0.2.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
|
checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"wasm-bindgen-macro",
|
"wasm-bindgen-macro",
|
||||||
|
@ -1426,24 +1649,24 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-backend"
|
name = "wasm-bindgen-backend"
|
||||||
version = "0.2.84"
|
version = "0.2.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
|
checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.109",
|
"syn 2.0.18",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-futures"
|
name = "wasm-bindgen-futures"
|
||||||
version = "0.4.34"
|
version = "0.4.36"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454"
|
checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
@ -1453,9 +1676,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.84"
|
version = "0.2.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
|
checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
|
@ -1463,22 +1686,22 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.84"
|
version = "0.2.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
|
checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.109",
|
"syn 2.0.18",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.84"
|
version = "0.2.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wayland-client"
|
name = "wayland-client"
|
||||||
|
@ -1555,9 +1778,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.61"
|
version = "0.3.63"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
|
checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
@ -1565,9 +1788,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu"
|
name = "wgpu"
|
||||||
version = "0.16.0"
|
version = "0.16.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13edd72c7b08615b7179dd7e778ee3f0bdc870ef2de9019844ff2cceeee80b11"
|
checksum = "3059ea4ddec41ca14f356833e2af65e7e38c0a8f91273867ed526fb9bafcca95"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
@ -1589,13 +1812,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-core"
|
name = "wgpu-core"
|
||||||
version = "0.16.0"
|
version = "0.16.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "625bea30a0ba50d88025f95c80211d1a85c86901423647fb74f397f614abbd9a"
|
checksum = "8f478237b4bf0d5b70a39898a66fa67ca3a007d79f2520485b8b0c3dfc46f8c2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"bit-vec",
|
"bit-vec",
|
||||||
"bitflags 2.2.1",
|
"bitflags 2.3.1",
|
||||||
"codespan-reporting",
|
"codespan-reporting",
|
||||||
"log",
|
"log",
|
||||||
"naga",
|
"naga",
|
||||||
|
@ -1612,15 +1835,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-hal"
|
name = "wgpu-hal"
|
||||||
version = "0.16.0"
|
version = "0.16.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "41af2ea7d87bd41ad0a37146252d5f7c26490209f47f544b2ee3b3ff34c7732e"
|
checksum = "74851c2c8e5d97652e74c241d41b0656b31c924a45dcdecde83975717362cfa4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android_system_properties",
|
"android_system_properties",
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"ash",
|
"ash",
|
||||||
"bit-set",
|
"bit-set",
|
||||||
"bitflags 2.2.1",
|
"bitflags 2.3.1",
|
||||||
"block",
|
"block",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"d3d12",
|
"d3d12",
|
||||||
|
@ -1658,7 +1881,7 @@ version = "0.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5bd33a976130f03dcdcd39b3810c0c3fc05daf86f0aaf867db14bfb7c4a9a32b"
|
checksum = "5bd33a976130f03dcdcd39b3810c0c3fc05daf86f0aaf867db14bfb7c4a9a32b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.2.1",
|
"bitflags 2.3.1",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
@ -1907,6 +2130,6 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xml-rs"
|
name = "xml-rs"
|
||||||
version = "0.8.10"
|
version = "0.8.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc95a04ea24f543cd9be5aab44f963fa35589c99e18415c38fb2b17e133bf8d2"
|
checksum = "52839dc911083a8ef63efa4d039d1f58b5e409f923e44c80828f206f66e5541c"
|
||||||
|
|
34
Cargo.toml
34
Cargo.toml
|
@ -1,30 +1,8 @@
|
||||||
[package]
|
[workspace]
|
||||||
name = "cxgraph"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[lib]
|
members = [
|
||||||
crate-type = ["cdylib", "rlib"]
|
"libcxgraph",
|
||||||
|
"cxgraph-desktop",
|
||||||
|
"cxgraph-web",
|
||||||
|
]
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
cfg-if = "1"
|
|
||||||
wgpu = "0.16"
|
|
||||||
cgmath = "0.18"
|
|
||||||
encase = { version = "0.6", features = ["cgmath"] }
|
|
||||||
raw-window-handle = "0.5"
|
|
||||||
winit = "0.28"
|
|
||||||
log = "0.4"
|
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
|
||||||
pollster = "0.3"
|
|
||||||
env_logger = "0.10"
|
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
|
||||||
console_error_panic_hook = "0.1"
|
|
||||||
console_log = "1.0"
|
|
||||||
wgpu = { version = "0.16", features = ["webgl"]}
|
|
||||||
wasm-bindgen = "0.2"
|
|
||||||
wasm-bindgen-futures = "0.4"
|
|
||||||
web-sys = { version = "0.3", features = ["Document", "Window", "Element"]}
|
|
||||||
|
|
11
cxgraph-desktop/Cargo.toml
Normal file
11
cxgraph-desktop/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "cxgraph-desktop"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libcxgraph = { path = "../libcxgraph" }
|
||||||
|
log = "0.4"
|
||||||
|
env_logger = "0.10"
|
||||||
|
winit = "0.28"
|
||||||
|
pollster = "0.3"
|
|
@ -1,15 +1,13 @@
|
||||||
|
|
||||||
use cxgraph::{renderer::WgpuState, language::compile};
|
use libcxgraph::{renderer::WgpuState, language::compile};
|
||||||
use winit::{event_loop::EventLoop, window::Window, event::{Event, WindowEvent}};
|
use winit::{event_loop::EventLoop, window::Window, event::{Event, WindowEvent}};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env_logger::builder()
|
env_logger::builder()
|
||||||
.filter_level(log::LevelFilter::Info)
|
.filter_level(log::LevelFilter::Warn)
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
let src = r#"
|
let src = "plot(z) = 27^z - 9^z - 3^z";
|
||||||
plot(z) = z^2 -> w, z*w
|
|
||||||
"#;
|
|
||||||
let wgsl = compile(src).unwrap();
|
let wgsl = compile(src).unwrap();
|
||||||
println!("{wgsl}");
|
println!("{wgsl}");
|
||||||
|
|
||||||
|
@ -27,7 +25,7 @@ async fn run(event_loop: EventLoop<()>, window: Window, code: &str) {
|
||||||
state.load_shaders(code);
|
state.load_shaders(code);
|
||||||
|
|
||||||
state.set_bounds((-5.0, -5.0), (5.0, 5.0));
|
state.set_bounds((-5.0, -5.0), (5.0, 5.0));
|
||||||
state.set_shading_intensity(0.01);
|
state.set_shading_intensity(0.05);
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
control_flow.set_wait();
|
control_flow.set_wait();
|
17
cxgraph-web/Cargo.toml
Normal file
17
cxgraph-web/Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[package]
|
||||||
|
name = "cxgraph-web"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libcxgraph = { path = "../libcxgraph", features = ["webgl"] }
|
||||||
|
log = "0.4"
|
||||||
|
winit = "0.28"
|
||||||
|
console_error_panic_hook = "0.1"
|
||||||
|
console_log = "1.0"
|
||||||
|
wasm-bindgen = "0.2"
|
||||||
|
wasm-bindgen-futures = "0.4"
|
||||||
|
web-sys = { version = "0.3", features = ["Document", "Window", "Element"]}
|
25
cxgraph-web/index.html
Normal file
25
cxgraph-web/index.html
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>cxgraph</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="canvas-container">
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
<canvas id="grid_canvas"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="menu">
|
||||||
|
<input id="menu_checkbox" type="checkbox" checked>
|
||||||
|
<div class="menu-inner" id="menu_inner">
|
||||||
|
<div><input type="button" id="button_graph" value="Graph"></div>
|
||||||
|
<textarea id="source_text">f(z) = z^2 + 3i plot(z) = 5z^2 + f(1/z) - 1</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import('./index.js').then(m => window.module = m)
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
89
cxgraph-web/index.js
Normal file
89
cxgraph-web/index.js
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
menu_checkbox.addEventListener("change", () => {
|
||||||
|
menu_inner.hidden = !menu_checkbox.checked;
|
||||||
|
});
|
||||||
|
|
||||||
|
import init, * as cxgraph from "./pkg/cxgraph_web.js";
|
||||||
|
await init();
|
||||||
|
|
||||||
|
let graphView = {
|
||||||
|
xoff: 0,
|
||||||
|
yoff: 0,
|
||||||
|
scale: 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
function redraw() {
|
||||||
|
cxgraph.redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onViewChange() {
|
||||||
|
let width = window.innerWidth;
|
||||||
|
let height = window.innerHeight;
|
||||||
|
let aspect = width / height;
|
||||||
|
cxgraph.set_bounds(
|
||||||
|
graphView.xoff - graphView.scale * aspect,
|
||||||
|
graphView.yoff - graphView.scale,
|
||||||
|
graphView.xoff + graphView.scale * aspect,
|
||||||
|
graphView.yoff + graphView.scale
|
||||||
|
);
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onResize() {
|
||||||
|
let width = window.innerWidth;
|
||||||
|
let height = window.innerHeight;
|
||||||
|
cxgraph.resize(width, height);
|
||||||
|
grid_canvas.width = width;
|
||||||
|
grid_canvas.height = height;
|
||||||
|
canvas.style.width = "100vw";
|
||||||
|
canvas.style.height = "100vh";
|
||||||
|
onViewChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mouseX = 0.0;
|
||||||
|
let mouseY = 0.0;
|
||||||
|
let mousePressed = false;
|
||||||
|
|
||||||
|
function onWheel(e) {
|
||||||
|
graphView.scale *= Math.exp(e.deltaY * 0.0007);
|
||||||
|
onViewChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseDown(e) {
|
||||||
|
mousePressed = true;
|
||||||
|
mouseX = e.offsetX;
|
||||||
|
mouseY = e.offsetY;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseUp(e) {
|
||||||
|
mousePressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseMove(e) {
|
||||||
|
if(mousePressed) {
|
||||||
|
let dX = e.offsetX - mouseX;
|
||||||
|
let dY = e.offsetY - mouseY;
|
||||||
|
mouseX = e.offsetX;
|
||||||
|
mouseY = e.offsetY;
|
||||||
|
graphView.xoff -= 2.0 * graphView.scale * dX / window.innerHeight;
|
||||||
|
graphView.yoff += 2.0 * graphView.scale * dY / window.innerHeight;
|
||||||
|
onViewChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onGraph() {
|
||||||
|
let src = document.getElementById("source_text").value;
|
||||||
|
cxgraph.load_shader(src);
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("resize", onResize);
|
||||||
|
canvas.addEventListener("wheel", onWheel);
|
||||||
|
canvas.addEventListener("mousedown", onMouseDown);
|
||||||
|
canvas.addEventListener("mouseup", onMouseUp);
|
||||||
|
canvas.addEventListener("mousemove", onMouseMove);
|
||||||
|
button_graph.addEventListener("click", onGraph);
|
||||||
|
|
||||||
|
onResize();
|
||||||
|
onGraph();
|
||||||
|
|
||||||
|
// menu
|
|
@ -1,8 +1,7 @@
|
||||||
#![cfg(target_arch="wasm32")]
|
use libcxgraph::{renderer::WgpuState, language::compile};
|
||||||
|
use winit::{window::WindowBuilder, event_loop::EventLoop, platform::web::WindowBuilderExtWebSys};
|
||||||
use crate::{renderer::WgpuState, language::compile};
|
use wasm_bindgen::{prelude::*, JsValue};
|
||||||
use winit::{event_loop::EventLoop, window::Window};
|
use web_sys::HtmlCanvasElement;
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
|
|
||||||
// wasm is single-threaded so there's no possibility of Bad happening
|
// wasm is single-threaded so there's no possibility of Bad happening
|
||||||
// due to mutable global state. this will be Some(..) once start runs
|
// due to mutable global state. this will be Some(..) once start runs
|
||||||
|
@ -23,20 +22,22 @@ pub async fn start() {
|
||||||
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||||
console_log::init_with_level(log::Level::Info).expect("Couldn't initialize logger");
|
console_log::init_with_level(log::Level::Info).expect("Couldn't initialize logger");
|
||||||
|
|
||||||
let event_loop = EventLoop::new();
|
let canvas_elem = web_sys::window()
|
||||||
let window = Window::new(&event_loop).unwrap();
|
|
||||||
|
|
||||||
window.set_inner_size(PhysicalSize::new(100, 100));
|
|
||||||
web_sys::window()
|
|
||||||
.and_then(|win| win.document())
|
.and_then(|win| win.document())
|
||||||
.and_then(|doc| {
|
.and_then(|doc| Some(doc.get_element_by_id("canvas")?))
|
||||||
let dst = doc.get_element_by_id("canvas_container")?;
|
.expect("Could not find canvas element");
|
||||||
let canvas = web_sys::Element::from(window.canvas());
|
|
||||||
dst.append_child(&canvas).ok()?;
|
|
||||||
Some(())
|
|
||||||
}).expect("Couldn't append canvas to document body.");
|
|
||||||
|
|
||||||
window.set_title("window");
|
let canvas: HtmlCanvasElement = canvas_elem
|
||||||
|
.dyn_into()
|
||||||
|
.expect("Canvas was not a canvas");
|
||||||
|
|
||||||
|
let event_loop = EventLoop::new();
|
||||||
|
let window = WindowBuilder::new()
|
||||||
|
.with_canvas(Some(canvas))
|
||||||
|
.with_inner_size(PhysicalSize::new(100, 100))
|
||||||
|
.with_title("window")
|
||||||
|
.build(&event_loop)
|
||||||
|
.expect("Failed to build window");
|
||||||
|
|
||||||
let size = window.inner_size();
|
let size = window.inner_size();
|
||||||
let mut state = WgpuState::new(&window, size.into()).await;
|
let mut state = WgpuState::new(&window, size.into()).await;
|
||||||
|
@ -46,19 +47,15 @@ pub async fn start() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn load_shader(src: &str) {
|
pub fn load_shader(src: &str) -> Result<(), JsValue> {
|
||||||
let wgsl = compile(src).unwrap();
|
let wgsl = compile(src).map_err(|e| e.to_string())?;
|
||||||
with_state(|state| {
|
with_state(|state| state.load_shaders(&wgsl));
|
||||||
state.load_shaders(&wgsl);
|
Ok(())
|
||||||
state.redraw();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn redraw() {
|
pub fn redraw() {
|
||||||
with_state(|state| {
|
with_state(|state| state.redraw());
|
||||||
state.redraw();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
46
cxgraph-web/style.css
Normal file
46
cxgraph-web/style.css
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
body, html {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canvas-container {
|
||||||
|
position: absolute;
|
||||||
|
left: 0px;
|
||||||
|
top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
position: absolute;
|
||||||
|
left: 0px;
|
||||||
|
top: 0px;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#canvas {
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#grid_canvas {
|
||||||
|
z-index: 1;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
position: absolute;
|
||||||
|
left: 0px;
|
||||||
|
top: 0px;
|
||||||
|
margin: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
background: #334;
|
||||||
|
color: #fff;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#source_text {
|
||||||
|
width: 300px;
|
||||||
|
height: 500px;
|
||||||
|
}
|
41
index.html
41
index.html
|
@ -1,41 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>cxgraph</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="main">
|
|
||||||
<div id="header">
|
|
||||||
<h1>CXGraph</h1>
|
|
||||||
</div>
|
|
||||||
<div id="content">
|
|
||||||
<div id="sidebar" class="split left">
|
|
||||||
<textarea id="srcinput"></textarea>
|
|
||||||
</div>
|
|
||||||
<div id="canvas_container" class="split right">
|
|
||||||
<!-- canvas -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script type="module">
|
|
||||||
import init, * as cxgraph from "./pkg/cxgraph.js";
|
|
||||||
await init();
|
|
||||||
canvas.style.width = "100%";
|
|
||||||
canvas.style.height = "100%";
|
|
||||||
await cxgraph.load_shader("f(z) = z^8 + 15z^4 - 16\nfp(z) = 8z^7 + 60z^3\nn(z) = z - f(z)/fp(z)\nni: iter n, 60\nplot(z) = f(z)");
|
|
||||||
await cxgraph.redraw();
|
|
||||||
let canvas = document.getElementsByTagName("canvas")[0];
|
|
||||||
|
|
||||||
new ResizeObserver(async () => {
|
|
||||||
let width = canvas.clientWidth;
|
|
||||||
let height = canvas.clientHeight;
|
|
||||||
await cxgraph.resize(width*2, height*2);
|
|
||||||
await cxgraph.set_bounds(-5*width/height, -5, 5*width/height, 5);
|
|
||||||
await cxgraph.redraw();
|
|
||||||
}).observe(canvas);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
20
libcxgraph/Cargo.toml
Normal file
20
libcxgraph/Cargo.toml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
[package]
|
||||||
|
name = "libcxgraph"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
webgl = ["wgpu/webgl"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.4"
|
||||||
|
lalrpop-util = { version = "0.20.0", features = ["lexer", "unicode"] }
|
||||||
|
num-complex = "0.4"
|
||||||
|
wgpu = "0.16"
|
||||||
|
cgmath = "0.18"
|
||||||
|
encase = { version = "0.6", features = ["cgmath"] }
|
||||||
|
raw-window-handle = "0.5"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
lalrpop = "0.20.0"
|
3
libcxgraph/build.rs
Normal file
3
libcxgraph/build.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
fn main() {
|
||||||
|
lalrpop::process_root().unwrap();
|
||||||
|
}
|
126
libcxgraph/src/language/ast.rs
Normal file
126
libcxgraph/src/language/ast.rs
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use num_complex::Complex64 as Complex;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum BinaryOp {
|
||||||
|
Add, Sub, Mul, Div, Pow,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum UnaryOp {
|
||||||
|
Pos, Neg, Conj
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum ExpressionType<'a> {
|
||||||
|
Number(Complex),
|
||||||
|
Name(&'a str),
|
||||||
|
Binary(BinaryOp),
|
||||||
|
Unary(UnaryOp),
|
||||||
|
FnCall(&'a str),
|
||||||
|
Store(&'a str),
|
||||||
|
Sum { countvar: &'a str, min: i32, max: i32 },
|
||||||
|
Prod { countvar: &'a str, min: i32, max: i32 },
|
||||||
|
Iter { itervar: &'a str, count: i32 },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Expression<'a> {
|
||||||
|
pub ty: ExpressionType<'a>,
|
||||||
|
pub children: Vec<Expression<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Expression<'a> {
|
||||||
|
pub fn new_number(x: f64) -> Self {
|
||||||
|
Self { ty: ExpressionType::Number(Complex::new(x, 0.0)), children: Vec::with_capacity(0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_name(n: &'a str) -> Self {
|
||||||
|
Self { ty: ExpressionType::Name(n), children: Vec::with_capacity(0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_unary(op: UnaryOp, arg: Self) -> Self {
|
||||||
|
Self { ty: ExpressionType::Unary(op), children: vec![arg] }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_binary(op: BinaryOp, arg0: Self, arg1: Self) -> Self {
|
||||||
|
Self { ty: ExpressionType::Binary(op), children: vec![arg0, arg1] }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_fncall(name: &'a str, args: Vec<Self>) -> Self {
|
||||||
|
Self { ty: ExpressionType::FnCall(name), children: args }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_store(expr: Self, name: &'a str) -> Self {
|
||||||
|
Self { ty: ExpressionType::Store(name), children: vec![expr] }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_sum(countvar: &'a str, min: i32, max: i32, body: Vec<Self>) -> Self {
|
||||||
|
Self {
|
||||||
|
ty: ExpressionType::Sum { countvar, min, max },
|
||||||
|
children: body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_prod(accvar: &'a str, min: i32, max: i32, body: Vec<Self>) -> Self {
|
||||||
|
Self {
|
||||||
|
ty: ExpressionType::Prod { countvar: accvar, min, max },
|
||||||
|
children: body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_iter(itervar: &'a str, count: i32, init: Self, mut body: Vec<Self>) -> Self {
|
||||||
|
body.push(init);
|
||||||
|
Self {
|
||||||
|
ty: ExpressionType::Iter { itervar, count },
|
||||||
|
children: body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Definition<'a> {
|
||||||
|
Constant { name: &'a str, value: Vec<Expression<'a>> },
|
||||||
|
Function { name: &'a str, args: Vec<&'a str>, value: Vec<Expression<'a>> },
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_expr(w: &mut impl fmt::Write, expr: &Expression, depth: usize) -> fmt::Result {
|
||||||
|
let indent = depth*2;
|
||||||
|
match expr.ty {
|
||||||
|
ExpressionType::Number(n) => write!(w, "{:indent$}NUMBER {n:?}", "", indent=indent)?,
|
||||||
|
ExpressionType::Name(n) => write!(w, "{:indent$}NAME {n}", "", indent=indent)?,
|
||||||
|
ExpressionType::Binary(op) => write!(w, "{:indent$}OP {op:?}", "", indent=indent)?,
|
||||||
|
ExpressionType::Unary(op) => write!(w, "{:indent$}OP {op:?}", "", indent=indent)?,
|
||||||
|
ExpressionType::FnCall(f) => write!(w, "{:indent$}CALL {f}", "", indent=indent)?,
|
||||||
|
ExpressionType::Store(n) => write!(w, "{:indent$}STORE {n}", "", indent=indent)?,
|
||||||
|
ExpressionType::Sum { countvar, min, max } => write!(w, "{:indent$}SUM {countvar} {min} {max}", "", indent=indent)?,
|
||||||
|
ExpressionType::Prod { countvar, min, max } => write!(w, "{:indent$}PROD {countvar} {min} {max}", "", indent=indent)?,
|
||||||
|
ExpressionType::Iter { itervar, count } => write!(w, "{:indent$}ITER {itervar} {count}", "", indent=indent)?,
|
||||||
|
}
|
||||||
|
writeln!(w)?;
|
||||||
|
for child in &expr.children {
|
||||||
|
display_expr(w, child, depth + 1)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn display_def(w: &mut impl fmt::Write, def: &Definition) -> fmt::Result {
|
||||||
|
match def {
|
||||||
|
Definition::Constant { name, value } => {
|
||||||
|
writeln!(w, "CONSTANT {name}")?;
|
||||||
|
for expr in value {
|
||||||
|
display_expr(w, expr, 1)?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Definition::Function { name, args, value } => {
|
||||||
|
writeln!(w, "FUNCTION {name}")?;
|
||||||
|
for arg in args {
|
||||||
|
writeln!(w, " ARG {arg}")?;
|
||||||
|
}
|
||||||
|
for expr in value {
|
||||||
|
display_expr(w, expr, 1)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
105
libcxgraph/src/language/builtins.rs
Normal file
105
libcxgraph/src/language/builtins.rs
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
use std::{collections::HashMap, ops, f64::consts::{TAU, E}};
|
||||||
|
|
||||||
|
use num_complex::Complex64 as Complex;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Func {
|
||||||
|
One(fn(Complex) -> Complex),
|
||||||
|
Two(fn(Complex, Complex) -> Complex),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Func {
|
||||||
|
pub fn argc(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Func::One(_) => 1,
|
||||||
|
Func::Two(_) => 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn neg(z: Complex) -> Complex { -z }
|
||||||
|
fn re(z: Complex) -> Complex { Complex::from(z.re) }
|
||||||
|
fn im(z: Complex) -> Complex { Complex::from(z.im) }
|
||||||
|
fn abs_sq(z: Complex) -> Complex { Complex::from(z.norm_sqr()) }
|
||||||
|
fn abs(z: Complex) -> Complex { Complex::from(z.norm()) }
|
||||||
|
fn arg(z: Complex) -> Complex { Complex::from(z.arg()) }
|
||||||
|
fn recip(z: Complex) -> Complex { Complex::new(1.0, 0.0) / z }
|
||||||
|
fn conj(z: Complex) -> Complex { z.conj() }
|
||||||
|
|
||||||
|
|
||||||
|
fn gamma(z: Complex) -> Complex {
|
||||||
|
let reflect = z.re < 0.5;
|
||||||
|
let zp = if reflect { 1.0 - z } else { z };
|
||||||
|
let mut w = gamma_inner(zp + 3.0) / (zp*(zp+1.0)*(zp+2.0)*(zp+3.0));
|
||||||
|
if reflect {
|
||||||
|
w = TAU * 0.5 / ((TAU * 0.5 * z).sin() * w);
|
||||||
|
}
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yang, ZH., Tian, JF. An accurate approximation formula for gamma function. J Inequal Appl 2018, 56 (2018).
|
||||||
|
// https://doi.org/10.1186/s13660-018-1646-6
|
||||||
|
fn gamma_inner(z: Complex) -> Complex {
|
||||||
|
let z2 = z * z;
|
||||||
|
let z3 = z2 * z;
|
||||||
|
|
||||||
|
let a = (TAU * z).sqrt();
|
||||||
|
let b = (1.0 / (E * E) * z3 * (1.0/z).sinh()).powc(0.5 * z);
|
||||||
|
let c = ((7.0/324.0) / (z3 * (35.0 * z2 + 33.0))).exp();
|
||||||
|
|
||||||
|
return a * b * c;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
pub static BUILTIN_FUNCS: HashMap<&'static str, (&'static str, Func)> = {
|
||||||
|
let mut m = HashMap::new();
|
||||||
|
m.insert("pos", ("c_pos", Func::One(std::convert::identity)));
|
||||||
|
m.insert("neg", ("c_neg", Func::One(neg)));
|
||||||
|
m.insert("recip", ("c_recip", Func::One(recip)));
|
||||||
|
m.insert("conj", ("c_conj", Func::One(conj)));
|
||||||
|
|
||||||
|
m.insert("re", ("c_re", Func::One(re)));
|
||||||
|
m.insert("im", ("c_im", Func::One(im)));
|
||||||
|
m.insert("abs_sq", ("c_abs_sq", Func::One(abs_sq)));
|
||||||
|
m.insert("abs", ("c_abs", Func::One(abs)));
|
||||||
|
m.insert("arg", ("c_arg", Func::One(arg)));
|
||||||
|
|
||||||
|
m.insert("add", ("c_add", Func::Two(<Complex as ops::Add>::add)));
|
||||||
|
m.insert("sub", ("c_sub", Func::Two(<Complex as ops::Sub>::sub)));
|
||||||
|
m.insert("mul", ("c_mul", Func::Two(<Complex as ops::Mul>::mul)));
|
||||||
|
m.insert("div", ("c_div", Func::Two(<Complex as ops::Div>::div)));
|
||||||
|
m.insert("pow", ("c_pow", Func::Two(Complex::powc)));
|
||||||
|
|
||||||
|
m.insert("exp", ("c_exp", Func::One(Complex::exp)));
|
||||||
|
m.insert("log", ("c_log", Func::One(Complex::ln)));
|
||||||
|
m.insert("sqrt", ("c_sqrt", Func::One(Complex::sqrt)));
|
||||||
|
|
||||||
|
m.insert("sin", ("c_sin", Func::One(Complex::sin)));
|
||||||
|
m.insert("cos", ("c_cos", Func::One(Complex::cos)));
|
||||||
|
m.insert("tan", ("c_tan", Func::One(Complex::tan)));
|
||||||
|
m.insert("sinh", ("c_sinh", Func::One(Complex::sinh)));
|
||||||
|
m.insert("cosh", ("c_cosh", Func::One(Complex::cosh)));
|
||||||
|
m.insert("tanh", ("c_tanh", Func::One(Complex::tanh)));
|
||||||
|
|
||||||
|
m.insert("asin", ("c_asin", Func::One(Complex::asin)));
|
||||||
|
m.insert("acos", ("c_acos", Func::One(Complex::acos)));
|
||||||
|
m.insert("atan", ("c_atan", Func::One(Complex::atan)));
|
||||||
|
m.insert("asinh", ("c_asinh", Func::One(Complex::asinh)));
|
||||||
|
m.insert("acosh", ("c_acosh", Func::One(Complex::acosh)));
|
||||||
|
m.insert("atanh", ("c_atanh", Func::One(Complex::atanh)));
|
||||||
|
|
||||||
|
m.insert("gamma", ("c_gamma", Func::One(gamma)));
|
||||||
|
m.insert("\u{0393}", ("c_gamma", Func::One(gamma)));
|
||||||
|
|
||||||
|
m
|
||||||
|
};
|
||||||
|
|
||||||
|
pub static BUILTIN_CONSTS: HashMap<&'static str, (&'static str, Complex)> = {
|
||||||
|
let mut m = HashMap::new();
|
||||||
|
m.insert("tau", ("C_TAU", Complex::new(std::f64::consts::TAU, 0.0)));
|
||||||
|
m.insert("\u{03C4}", ("C_TAU", Complex::new(std::f64::consts::TAU, 0.0)));
|
||||||
|
m.insert("e", ("C_E", Complex::new(std::f64::consts::E, 0.0)));
|
||||||
|
m.insert("i", ("C_I", Complex::new(0.0, 1.0)));
|
||||||
|
m
|
||||||
|
}
|
||||||
|
}
|
307
libcxgraph/src/language/compiler.rs
Normal file
307
libcxgraph/src/language/compiler.rs
Normal file
|
@ -0,0 +1,307 @@
|
||||||
|
use std::{collections::{HashSet, HashMap}, fmt};
|
||||||
|
|
||||||
|
use super::{ast::{Definition, Expression, ExpressionType, BinaryOp, UnaryOp}, builtins::{BUILTIN_CONSTS, BUILTIN_FUNCS}};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct CompileError(String);
|
||||||
|
|
||||||
|
impl fmt::Display for CompileError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str(&self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for CompileError {}
|
||||||
|
|
||||||
|
impl From<String> for CompileError {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<fmt::Error> for CompileError {
|
||||||
|
fn from(value: fmt::Error) -> Self {
|
||||||
|
Self(value.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const GREEK_LOWER: [&str; 25] = [
|
||||||
|
"Al", "Be", "Ga", "De", "Ep",
|
||||||
|
"Ze", "Et", "Th", "Io", "Ka",
|
||||||
|
"La", "Mu", "Nu", "Xi", "Om",
|
||||||
|
"Pi", "Rh", "Sj", "Si", "Ta",
|
||||||
|
"Yp", "Ph", "Ch", "Ps", "Oa",
|
||||||
|
];
|
||||||
|
|
||||||
|
const GREEK_UPPER: [&str; 25] = [
|
||||||
|
"AL", "BE", "GA", "DE", "EP",
|
||||||
|
"ZE", "ET", "TH", "IO", "KA",
|
||||||
|
"LA", "MU", "NU", "XI", "OM",
|
||||||
|
"PI", "RH", "SJ", "SI", "TA",
|
||||||
|
"YP", "PH", "CH", "PS", "OA",
|
||||||
|
];
|
||||||
|
|
||||||
|
fn format_char(buf: &mut String, c: char) {
|
||||||
|
match c {
|
||||||
|
'a'..='z' | 'A'..='Z' | '0'..='9' => buf.push(c),
|
||||||
|
'_' => buf.push_str("__"),
|
||||||
|
'\'' => buf.push_str("_p"),
|
||||||
|
'\u{0391}'..='\u{03A9}' => {
|
||||||
|
buf.push('_');
|
||||||
|
buf.push_str(GREEK_UPPER[c as usize - 0x0391])
|
||||||
|
},
|
||||||
|
'\u{03B1}'..='\u{03C9}' => {
|
||||||
|
buf.push('_');
|
||||||
|
buf.push_str(GREEK_LOWER[c as usize - 0x03B1])
|
||||||
|
},
|
||||||
|
c => {
|
||||||
|
buf.push('_');
|
||||||
|
let mut b = [0u8; 8];
|
||||||
|
let s = c.encode_utf8(&mut b);
|
||||||
|
for b in s.bytes() {
|
||||||
|
buf.push(char::from_digit(b as u32 >> 4, 16).unwrap());
|
||||||
|
buf.push(char::from_digit(b as u32 & 0x0f, 16).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_name(prefix: &str, name: &str) -> String {
|
||||||
|
let mut result = prefix.to_owned();
|
||||||
|
result.reserve(name.len());
|
||||||
|
for c in name.chars() {
|
||||||
|
format_char(&mut result, c);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_func(name: &str) -> String { format_name("func_", name) }
|
||||||
|
fn format_const(name: &str) -> String { format_name("const_", name) }
|
||||||
|
fn format_arg(name: &str) -> String { format_name("arg_", name) }
|
||||||
|
fn format_local(name: &str) -> String { format_name("local_", name) }
|
||||||
|
fn format_tmp(idx: usize) -> String { format!("tmp_{}", idx) }
|
||||||
|
|
||||||
|
pub struct Compiler<'w, 'i, W: fmt::Write> {
|
||||||
|
buf: &'w mut W,
|
||||||
|
global_funcs: HashMap<&'i str, usize>,
|
||||||
|
global_consts: HashSet<&'i str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct LocalState<'i> {
|
||||||
|
local_vars: HashSet<&'i str>,
|
||||||
|
next_tmp: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i> LocalState<'i> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
local_vars: HashSet::new(),
|
||||||
|
next_tmp: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_tmp(&mut self) -> String {
|
||||||
|
let n = self.next_tmp;
|
||||||
|
self.next_tmp += 1;
|
||||||
|
format_tmp(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'w, 'i, W: fmt::Write> Compiler<'w, 'i, W> {
|
||||||
|
pub fn new(buf: &'w mut W) -> Self {
|
||||||
|
Self {
|
||||||
|
buf,
|
||||||
|
global_consts: HashSet::new(),
|
||||||
|
global_funcs: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile_defn(&mut self, defn: &Definition<'i>) -> Result<(), CompileError> {
|
||||||
|
match defn {
|
||||||
|
Definition::Function { name, args, value } => {
|
||||||
|
if self.global_consts.contains(name) || self.global_funcs.contains_key(name) {
|
||||||
|
return Err(format!("name {name} is already declared in global scope").into())
|
||||||
|
}
|
||||||
|
write!(self.buf, "fn {}(", format_func(name))?;
|
||||||
|
for arg in args {
|
||||||
|
write!(self.buf, "{}: vec2f, ", format_arg(arg))?;
|
||||||
|
}
|
||||||
|
writeln!(self.buf, ") -> vec2f {{")?;
|
||||||
|
|
||||||
|
let mut local = LocalState::new();
|
||||||
|
for arg in args {
|
||||||
|
writeln!(self.buf, "var {} = {};", format_local(arg), format_arg(arg))?;
|
||||||
|
local.local_vars.insert(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut last = String::with_capacity(0);
|
||||||
|
for expr in value {
|
||||||
|
last = self.compile_expr(&mut local, expr)?;
|
||||||
|
}
|
||||||
|
writeln!(self.buf, "return {last};\n}}")?;
|
||||||
|
|
||||||
|
self.global_funcs.insert(name, args.len());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Definition::Constant { name, value } => {
|
||||||
|
if self.global_consts.contains(name) || self.global_funcs.contains_key(name) {
|
||||||
|
return Err(format!("name {name} is already declared in global scope").into())
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(self.buf, "fn {}() -> vec2f {{", format_const(name))?;
|
||||||
|
let mut local = LocalState::new();
|
||||||
|
|
||||||
|
let mut last = String::with_capacity(0);
|
||||||
|
for expr in value {
|
||||||
|
last = self.compile_expr(&mut local, expr)?;
|
||||||
|
}
|
||||||
|
writeln!(self.buf, "return {last};\n}}")?;
|
||||||
|
|
||||||
|
self.global_consts.insert(name);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure_plot_defined(&self) -> Result<(), CompileError> {
|
||||||
|
if self.global_funcs.contains_key("plot") {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err("No plot function defined".to_owned().into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_expr(&mut self, local: &mut LocalState<'i>, expr: &Expression<'i>)
|
||||||
|
-> Result<String, CompileError> {
|
||||||
|
match expr.ty {
|
||||||
|
ExpressionType::Name(v) => self.resolve_var(local, v),
|
||||||
|
ExpressionType::Store(var) => {
|
||||||
|
let a = self.compile_expr(local, &expr.children[0])?;
|
||||||
|
let name = format_local(var);
|
||||||
|
|
||||||
|
if !local.local_vars.contains(var) {
|
||||||
|
write!(self.buf, "var ")?;
|
||||||
|
local.local_vars.insert(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(self.buf, "{name} = {a};")?;
|
||||||
|
Ok(name)
|
||||||
|
},
|
||||||
|
ExpressionType::Number(n) => {
|
||||||
|
let name = local.next_tmp();
|
||||||
|
writeln!(self.buf, "var {name} = vec2f({:?}, {:?});", n.re, n.im)?;
|
||||||
|
Ok(name)
|
||||||
|
},
|
||||||
|
ExpressionType::Binary(op) => {
|
||||||
|
let a = self.compile_expr(local, &expr.children[0])?;
|
||||||
|
let b = self.compile_expr(local, &expr.children[1])?;
|
||||||
|
let name = local.next_tmp();
|
||||||
|
|
||||||
|
match op {
|
||||||
|
BinaryOp::Add => writeln!(self.buf, "var {name} = {a} + {b};")?,
|
||||||
|
BinaryOp::Sub => writeln!(self.buf, "var {name} = {a} - {b};")?,
|
||||||
|
BinaryOp::Mul => writeln!(self.buf, "var {name} = c_mul({a}, {b});")?,
|
||||||
|
BinaryOp::Div => writeln!(self.buf, "var {name} = c_div({a}, {b});")?,
|
||||||
|
BinaryOp::Pow => writeln!(self.buf, "var {name} = c_pow({a}, {b});")?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(name)
|
||||||
|
},
|
||||||
|
ExpressionType::Unary(op) => {
|
||||||
|
let a = self.compile_expr(local, &expr.children[0])?;
|
||||||
|
let name = local.next_tmp();
|
||||||
|
|
||||||
|
match op {
|
||||||
|
UnaryOp::Pos => writeln!(self.buf, "var {name} = {a};")?,
|
||||||
|
UnaryOp::Neg => writeln!(self.buf, "var {name} = -{a};")?,
|
||||||
|
UnaryOp::Conj => writeln!(self.buf, "var {name} = c_conj({a});")?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(name)
|
||||||
|
},
|
||||||
|
ExpressionType::FnCall(f) => {
|
||||||
|
let (fname, argc) = self.resolve_func(f)?;
|
||||||
|
if argc != expr.children.len() {
|
||||||
|
return Err(format!("function {f} expected {argc} args, got {}", expr.children.len()).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut args = Vec::with_capacity(expr.children.len());
|
||||||
|
for child in &expr.children {
|
||||||
|
args.push(self.compile_expr(local, child)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = local.next_tmp();
|
||||||
|
write!(self.buf, "var {name} = {fname}(", )?;
|
||||||
|
for arg in args {
|
||||||
|
write!(self.buf, "{arg}, ")?;
|
||||||
|
}
|
||||||
|
writeln!(self.buf, ");")?;
|
||||||
|
|
||||||
|
Ok(name)
|
||||||
|
},
|
||||||
|
ExpressionType::Sum { countvar, min, max }
|
||||||
|
| ExpressionType::Prod { countvar, min, max } => {
|
||||||
|
let acc = local.next_tmp();
|
||||||
|
let ivar = local.next_tmp();
|
||||||
|
if matches!(expr.ty, ExpressionType::Sum { .. }) {
|
||||||
|
writeln!(self.buf, "var {acc} = vec2f(0.0, 0.0);")?;
|
||||||
|
} else {
|
||||||
|
writeln!(self.buf, "var {acc} = vec2f(1.0, 0.0);")?;
|
||||||
|
}
|
||||||
|
writeln!(self.buf, "for(var {ivar}: i32 = {min}; {ivar} <= {max}; {ivar}++) {{")?;
|
||||||
|
writeln!(self.buf, "var {} = vec2f(f32({ivar}), 0.0);", format_local(countvar))?;
|
||||||
|
let mut loop_local = local.clone();
|
||||||
|
loop_local.local_vars.insert(countvar);
|
||||||
|
let mut last = String::new();
|
||||||
|
for child in &expr.children {
|
||||||
|
last = self.compile_expr(&mut loop_local, child)?;
|
||||||
|
}
|
||||||
|
if matches!(expr.ty, ExpressionType::Sum { .. }) {
|
||||||
|
writeln!(self.buf, "{acc} = {acc} + {last};\n}}")?;
|
||||||
|
} else {
|
||||||
|
writeln!(self.buf, "{acc} = c_mul({acc}, {last});\n}}")?;
|
||||||
|
}
|
||||||
|
Ok(acc)
|
||||||
|
},
|
||||||
|
ExpressionType::Iter { itervar, count } => {
|
||||||
|
let init = expr.children.last().unwrap();
|
||||||
|
let itervar_fmt = format_local(itervar);
|
||||||
|
let v = self.compile_expr(local, init)?;
|
||||||
|
writeln!(self.buf, "var {itervar_fmt} = {v};")?;
|
||||||
|
let ivar = local.next_tmp();
|
||||||
|
writeln!(self.buf, "for(var {ivar}: i32 = 0; {ivar} <= {count}; {ivar}++) {{")?;
|
||||||
|
let mut loop_local = local.clone();
|
||||||
|
loop_local.local_vars.insert(itervar);
|
||||||
|
let mut last = String::new();
|
||||||
|
for child in &expr.children[..expr.children.len() - 1] {
|
||||||
|
last = self.compile_expr(&mut loop_local, child)?;
|
||||||
|
}
|
||||||
|
writeln!(self.buf, "{itervar_fmt} = {last};\n}}")?;
|
||||||
|
Ok(itervar_fmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_func(&self, name: &str) -> Result<(String, usize), CompileError> {
|
||||||
|
if let Some(argc) = self.global_funcs.get(name) {
|
||||||
|
Ok((format_func(name), *argc))
|
||||||
|
} else if let Some((var, f)) = BUILTIN_FUNCS.with(|c| c.get(name).copied()) {
|
||||||
|
Ok(((*var).to_owned(), f.argc()))
|
||||||
|
} else {
|
||||||
|
Err(format!("use of undeclared function {name}").into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_var(&self, local: &LocalState, name: &str) -> Result<String, CompileError> {
|
||||||
|
if local.local_vars.contains(name) {
|
||||||
|
Ok(format_local(name))
|
||||||
|
} else if self.global_consts.contains(name) {
|
||||||
|
Ok(format_const(name) + "()")
|
||||||
|
} else if let Some(var) = BUILTIN_CONSTS.with(|c| Some(c.get(name)?.0)) {
|
||||||
|
Ok(var.to_owned())
|
||||||
|
} else {
|
||||||
|
Err(format!("use of undeclared variable {name}").into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
libcxgraph/src/language/mod.rs
Normal file
26
libcxgraph/src/language/mod.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use lalrpop_util::lalrpop_mod;
|
||||||
|
|
||||||
|
use crate::language::token::Lexer;
|
||||||
|
|
||||||
|
use self::compiler::Compiler;
|
||||||
|
|
||||||
|
mod token;
|
||||||
|
mod ast;
|
||||||
|
mod compiler;
|
||||||
|
mod builtins;
|
||||||
|
|
||||||
|
lalrpop_mod!(pub syntax, "/language/syntax.rs");
|
||||||
|
|
||||||
|
pub fn compile(src: &str) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let lexer = Lexer::new(src);
|
||||||
|
let result = syntax::ProgramParser::new()
|
||||||
|
.parse(src, lexer)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
let mut wgsl = String::new();
|
||||||
|
let mut cmp = Compiler::new(&mut wgsl);
|
||||||
|
for defn in result {
|
||||||
|
cmp.compile_defn(&defn)?;
|
||||||
|
}
|
||||||
|
cmp.ensure_plot_defined()?;
|
||||||
|
Ok(wgsl)
|
||||||
|
}
|
123
libcxgraph/src/language/syntax.lalrpop
Normal file
123
libcxgraph/src/language/syntax.lalrpop
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
use crate::language::ast::*;
|
||||||
|
use crate::language::token::*;
|
||||||
|
|
||||||
|
grammar<'input>(input: &'input str);
|
||||||
|
|
||||||
|
extern {
|
||||||
|
type Location = usize;
|
||||||
|
type Error = LexerError;
|
||||||
|
|
||||||
|
enum Token<'input> {
|
||||||
|
"(" => Token::LParen,
|
||||||
|
")" => Token::RParen,
|
||||||
|
"{" => Token::LBrace,
|
||||||
|
"}" => Token::RBrace,
|
||||||
|
"+" => Token::Plus,
|
||||||
|
"-" => Token::Minus,
|
||||||
|
"*" => Token::Star,
|
||||||
|
"/" => Token::Slash,
|
||||||
|
"^" => Token::Caret,
|
||||||
|
"," => Token::Comma,
|
||||||
|
"->" => Token::Arrow,
|
||||||
|
"=" => Token::Equal,
|
||||||
|
":" => Token::Colon,
|
||||||
|
"\n" => Token::Newline,
|
||||||
|
"sum" => Token::Sum,
|
||||||
|
"prod" => Token::Prod,
|
||||||
|
"iter" => Token::Iter,
|
||||||
|
Float => Token::Float(<f64>),
|
||||||
|
Int => Token::Int(<i32>),
|
||||||
|
Name => Token::Name(<&'input str>),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Definitions
|
||||||
|
|
||||||
|
pub Program: Vec<Definition<'input>> = Definitions;
|
||||||
|
|
||||||
|
Definitions: Vec<Definition<'input>> = {
|
||||||
|
"\n"* <defs:(<Definition> "\n"+)*> <last:Definition?> => defs.into_iter().chain(last).collect(),
|
||||||
|
}
|
||||||
|
|
||||||
|
Definition: Definition<'input> = {
|
||||||
|
<n:Name> "(" <args:(<Name> ",")*> <last:Name?> ")" "=" <exs:Exprs> => Definition::Function {
|
||||||
|
name: n,
|
||||||
|
args: args.into_iter().chain(last).collect(),
|
||||||
|
value: exs,
|
||||||
|
},
|
||||||
|
<n:Name> "=" <exs:Exprs> => Definition::Constant {
|
||||||
|
name: n,
|
||||||
|
value: exs,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expressions
|
||||||
|
|
||||||
|
Exprs: Vec<Expression<'input>> = {
|
||||||
|
<args:(<Expr> ",")*> <last:Expr?> => args.into_iter().chain(last).collect(),
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr: Expression<'input> = Store;
|
||||||
|
|
||||||
|
Store: Expression<'input> = {
|
||||||
|
<a:Store> "->" <n:Name> => Expression::new_store(a, n),
|
||||||
|
Sum,
|
||||||
|
}
|
||||||
|
|
||||||
|
Sum: Expression<'input> = {
|
||||||
|
<a:Sum> "+" <b:Product> => Expression::new_binary(BinaryOp::Add, a, b),
|
||||||
|
<a:Sum> "-" <b:Product> => Expression::new_binary(BinaryOp::Sub, a, b),
|
||||||
|
Product,
|
||||||
|
}
|
||||||
|
|
||||||
|
Product: Expression<'input> = {
|
||||||
|
<a:Product> "*" <b:Unary> => Expression::new_binary(BinaryOp::Mul, a, b),
|
||||||
|
<a:Product> "/" <b:Unary> => Expression::new_binary(BinaryOp::Div, a, b),
|
||||||
|
Unary,
|
||||||
|
}
|
||||||
|
|
||||||
|
Unary: Expression<'input> = {
|
||||||
|
"+" <a:Unary> => Expression::new_unary(UnaryOp::Pos, a),
|
||||||
|
"-" <a:Unary> => Expression::new_unary(UnaryOp::Neg, a),
|
||||||
|
"*" <a:Unary> => Expression::new_unary(UnaryOp::Conj, a),
|
||||||
|
<a:Juxtapose> <b:Power> => Expression::new_binary(BinaryOp::Mul, a, b),
|
||||||
|
Power,
|
||||||
|
}
|
||||||
|
|
||||||
|
Juxtapose: Expression<'input> = {
|
||||||
|
<a:Juxtapose> <b:PreJuxtapose> => Expression::new_binary(BinaryOp::Mul, a, b),
|
||||||
|
PreJuxtapose,
|
||||||
|
}
|
||||||
|
|
||||||
|
Power: Expression<'input> = {
|
||||||
|
<a:FnCall> "^" <b:Unary> => Expression::new_binary(BinaryOp::Pow, a, b),
|
||||||
|
FnCall,
|
||||||
|
}
|
||||||
|
|
||||||
|
FnCall: Expression<'input> = {
|
||||||
|
<n:Name> "(" <args:Exprs> ")"
|
||||||
|
=> Expression::new_fncall(n, args),
|
||||||
|
<Item>
|
||||||
|
}
|
||||||
|
|
||||||
|
PreJuxtapose: Expression<'input> = {
|
||||||
|
Number,
|
||||||
|
"(" <Expr> ")",
|
||||||
|
}
|
||||||
|
|
||||||
|
Item: Expression<'input> = {
|
||||||
|
Number,
|
||||||
|
<n:Name> => Expression::new_name(n),
|
||||||
|
"(" <Expr> ")",
|
||||||
|
"sum" "(" <name:Name> ":" <min:Int> "," <max:Int> ")" "{" <exs:Exprs> "}"
|
||||||
|
=> Expression::new_sum(name, min, max, exs),
|
||||||
|
"prod" "(" <name:Name> ":" <min:Int> "," <max:Int> ")" "{" <exs:Exprs> "}"
|
||||||
|
=> Expression::new_prod(name, min, max, exs),
|
||||||
|
"iter" "(" <count:Int> "," <name:Name> ":" <init:Expr> ")" "{" <exs:Exprs> "}"
|
||||||
|
=> Expression::new_iter(name, count, init, exs),
|
||||||
|
}
|
||||||
|
|
||||||
|
Number: Expression<'input> = {
|
||||||
|
<n:Float> => Expression::new_number(n),
|
||||||
|
<n:Int> => Expression::new_number(n as f64),
|
||||||
|
}
|
159
libcxgraph/src/language/token.rs
Normal file
159
libcxgraph/src/language/token.rs
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
use std::{str::CharIndices, iter::Peekable, fmt};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum Token<'i> {
|
||||||
|
Float(f64),
|
||||||
|
Int(i32),
|
||||||
|
Name(&'i str),
|
||||||
|
Sum, Prod, Iter,
|
||||||
|
LParen, RParen,
|
||||||
|
LBrace, RBrace,
|
||||||
|
Plus, Minus, Star, Slash, Caret,
|
||||||
|
Comma, Arrow, Equal, Colon,
|
||||||
|
Newline,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i> fmt::Display for Token<'i> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Token::Float(n) => write!(f, "{n}"),
|
||||||
|
Token::Int(n) => write!(f, "{n}"),
|
||||||
|
Token::Name(n) => write!(f, "{n}"),
|
||||||
|
Token::Sum => f.write_str("sum"),
|
||||||
|
Token::Prod => f.write_str("prod"),
|
||||||
|
Token::Iter => f.write_str("iter"),
|
||||||
|
Token::LParen => f.write_str("("),
|
||||||
|
Token::RParen => f.write_str(")"),
|
||||||
|
Token::LBrace => f.write_str("{{"),
|
||||||
|
Token::RBrace => f.write_str("}}"),
|
||||||
|
Token::Plus => f.write_str("+"),
|
||||||
|
Token::Minus => f.write_str("-"),
|
||||||
|
Token::Star => f.write_str("*"),
|
||||||
|
Token::Slash => f.write_str("/"),
|
||||||
|
Token::Caret => f.write_str("^"),
|
||||||
|
Token::Comma => f.write_str(","),
|
||||||
|
Token::Arrow => f.write_str("->"),
|
||||||
|
Token::Equal => f.write_str("="),
|
||||||
|
Token::Colon => f.write_str(":"),
|
||||||
|
Token::Newline => f.write_str("newline")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum LexerError {
|
||||||
|
Unexpected(usize, char),
|
||||||
|
InvalidNumber(usize, usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for LexerError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
LexerError::Unexpected(i, c) => write!(f, "Unexpected character {c:?} at {i}"),
|
||||||
|
LexerError::InvalidNumber(i, j) => write!(f, "Invalid number at {i}:{j}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Spanned<T, L, E> = Result<(L, T, L), E>;
|
||||||
|
|
||||||
|
pub struct Lexer<'i> {
|
||||||
|
src: &'i str,
|
||||||
|
chars: Peekable<CharIndices<'i>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ident_begin(c: char) -> bool {
|
||||||
|
c.is_alphabetic()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ident_middle(c: char) -> bool {
|
||||||
|
c.is_alphanumeric() || c == '_' || c == '\''
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i> Lexer<'i> {
|
||||||
|
pub fn new(src: &'i str) -> Self {
|
||||||
|
Self { src, chars: src.char_indices().peekable() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_number(&mut self, i: usize, mut has_dot: bool) -> Spanned<Token<'i>, usize, LexerError> {
|
||||||
|
let mut j = i;
|
||||||
|
|
||||||
|
while self.chars.peek().is_some_and(|(_, c)| c.is_ascii_digit()) {
|
||||||
|
j = self.chars.next().unwrap().0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !has_dot && matches!(self.chars.peek(), Some((_, '.'))) {
|
||||||
|
j = self.chars.next().unwrap().0;
|
||||||
|
has_dot = true;
|
||||||
|
while self.chars.peek().is_some_and(|(_, c)| c.is_ascii_digit()) {
|
||||||
|
j = self.chars.next().unwrap().0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let s = &self.src[i..j+1];
|
||||||
|
if !has_dot {
|
||||||
|
if let Ok(n) = s.parse::<i32>() {
|
||||||
|
return Ok((i, Token::Int(n), j+1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match s.parse::<f64>() {
|
||||||
|
Ok(n) => Ok((i, Token::Float(n), j+1)),
|
||||||
|
Err(_) => Err(LexerError::InvalidNumber(i, j+1)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_word(&mut self, i: usize, mut j: usize) -> Spanned<Token<'i>, usize, LexerError> {
|
||||||
|
while self.chars.peek().is_some_and(|(_, c)| is_ident_middle(*c)) {
|
||||||
|
j += self.chars.next().unwrap().1.len_utf8();
|
||||||
|
}
|
||||||
|
|
||||||
|
let s = &self.src[i..j];
|
||||||
|
match s {
|
||||||
|
"sum" => Ok((i, Token::Sum, j)),
|
||||||
|
"prod" => Ok((i, Token::Prod, j)),
|
||||||
|
"iter" => Ok((i, Token::Iter, j)),
|
||||||
|
_ => Ok((i, Token::Name(s), j)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_token(&mut self) -> Option<Spanned<Token<'i>, usize, LexerError>> {
|
||||||
|
while matches!(self.chars.peek(), Some((_, ' ' | '\t' | '\r'))) {
|
||||||
|
self.chars.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(match self.chars.next()? {
|
||||||
|
(i, '(') => Ok((i, Token::LParen, i + 1)),
|
||||||
|
(i, ')') => Ok((i, Token::RParen, i + 1)),
|
||||||
|
(i, '{') => Ok((i, Token::LBrace, i + 1)),
|
||||||
|
(i, '}') => Ok((i, Token::RBrace, i + 1)),
|
||||||
|
(i, '+') => Ok((i, Token::Plus, i + 1)),
|
||||||
|
(i, '-') => match self.chars.peek() {
|
||||||
|
Some((_, '>')) => {
|
||||||
|
self.chars.next();
|
||||||
|
Ok((i, Token::Arrow, i + 2))
|
||||||
|
},
|
||||||
|
_ => Ok((i, Token::Minus, i + 1)),
|
||||||
|
}
|
||||||
|
(i, '*') => Ok((i, Token::Star, i + 1)),
|
||||||
|
(i, '/') => Ok((i, Token::Slash, i + 1)),
|
||||||
|
(i, '^') => Ok((i, Token::Caret, i + 1)),
|
||||||
|
(i, ',') => Ok((i, Token::Comma, i + 1)),
|
||||||
|
(i, '=') => Ok((i, Token::Equal, i + 1)),
|
||||||
|
(i, ':') => Ok((i, Token::Colon, i + 1)),
|
||||||
|
(i, '\n') => Ok((i, Token::Newline, i + 1)),
|
||||||
|
(i, '0'..='9') => self.next_number(i, false),
|
||||||
|
(i, '.') => self.next_number(i, true),
|
||||||
|
(i, c) if is_ident_begin(c) => self.next_word(i, i + c.len_utf8()),
|
||||||
|
(i, c) => Err(LexerError::Unexpected(i, c)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i> Iterator for Lexer<'i> {
|
||||||
|
type Item = Spanned<Token<'i>, usize, LexerError>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.next_token()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,2 @@
|
||||||
pub mod language;
|
pub mod language;
|
||||||
pub mod renderer;
|
pub mod renderer;
|
||||||
pub mod complex;
|
|
||||||
pub mod wasm;
|
|
|
@ -26,6 +26,10 @@ fn remap(val: vec2f, a1: vec2f, b1: vec2f, a2: vec2f, b2: vec2f) -> vec2f {
|
||||||
const TAU = 6.283185307179586;
|
const TAU = 6.283185307179586;
|
||||||
const E = 2.718281828459045;
|
const E = 2.718281828459045;
|
||||||
|
|
||||||
|
const C_TAU = vec2f(TAU, 0.0);
|
||||||
|
const C_E = vec2f(E, 0.0);
|
||||||
|
const C_I = vec2f(0.0, 1.0);
|
||||||
|
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
// complex functions //
|
// complex functions //
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
|
@ -122,6 +126,41 @@ fn c_tanh(z: vec2f) -> vec2f {
|
||||||
return vec2(sinh(2.0*z.x), sin(2.0*z.y)) / (cosh(2.0*z.x) + cos(2.0*z.y));
|
return vec2(sinh(2.0*z.x), sin(2.0*z.y)) / (cosh(2.0*z.x) + cos(2.0*z.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn c_asin(z: vec2f) -> vec2f {
|
||||||
|
let u = c_sqrt(vec2f(1.0, 0.0) - c_mul(z, z));
|
||||||
|
let v = c_log(u + vec2(-z.y, z.x));
|
||||||
|
return vec2(v.y, -v.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_acos(z: vec2f) -> vec2f {
|
||||||
|
let u = c_sqrt(vec2f(1.0, 0.0) - c_mul(z, z));
|
||||||
|
let v = c_log(u + vec2f(-z.y, z.x));
|
||||||
|
return vec2f(TAU*0.25 - v.y, v.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_atan(z: vec2f) -> vec2f {
|
||||||
|
let u = vec2f(1.0, 0.0) - vec2f(-z.y, z.x);
|
||||||
|
let v = vec2f(1.0, 0.0) + vec2f(-z.y, z.x);
|
||||||
|
let w = c_log(c_div(u, v));
|
||||||
|
return 0.5 * vec2f(-w.y, w.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_asinh(z: vec2f) -> vec2f {
|
||||||
|
let u = c_sqrt(vec2f(1.0, 0.0) + c_mul(z, z));
|
||||||
|
return c_log(u + z);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_acosh(z: vec2f) -> vec2f {
|
||||||
|
let u = c_sqrt(vec2f(-1.0, 0.0) + c_mul(z, z));
|
||||||
|
return c_log(u + z);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_atanh(z: vec2f) -> vec2f {
|
||||||
|
let u = vec2f(1.0, 0.0) + z;
|
||||||
|
let v = vec2f(1.0, 0.0) - z;
|
||||||
|
return 0.5 * c_log(c_div(u, v));
|
||||||
|
}
|
||||||
|
|
||||||
fn c_gamma(z: vec2f) -> vec2f {
|
fn c_gamma(z: vec2f) -> vec2f {
|
||||||
let reflect = z.x < 0.5;
|
let reflect = z.x < 0.5;
|
||||||
var zp = z;
|
var zp = z;
|
||||||
|
@ -153,46 +192,26 @@ fn c_gamma_inner2(z: vec2f) -> vec2f {
|
||||||
return c_div(w, c_mul(c_mul(z, z + vec2(1.0, 0.0)), c_mul(z + vec2(2.0, 0.0), z + vec2(3.0, 0.0))));
|
return c_div(w, c_mul(c_mul(z, z + vec2(1.0, 0.0)), c_mul(z + vec2(2.0, 0.0), z + vec2(3.0, 0.0))));
|
||||||
}
|
}
|
||||||
|
|
||||||
const D_EPS = 0.01;
|
|
||||||
|
|
||||||
/////////////////
|
/////////////////
|
||||||
// user code //
|
// rendering //
|
||||||
/////////////////
|
/////////////////
|
||||||
|
|
||||||
//INCLUDE//
|
|
||||||
|
|
||||||
//////////////
|
|
||||||
// vertex //
|
|
||||||
//////////////
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4f {
|
|
||||||
var pos = array<vec2f, 4>(
|
|
||||||
vec2(-1.0,-1.0),
|
|
||||||
vec2( 1.0,-1.0),
|
|
||||||
vec2(-1.0, 1.0),
|
|
||||||
vec2( 1.0, 1.0),
|
|
||||||
);
|
|
||||||
|
|
||||||
return vec4f(pos[in_vertex_index], 0.0, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////
|
|
||||||
// fragment //
|
|
||||||
////////////////
|
|
||||||
|
|
||||||
fn hsv2rgb(c: vec3f) -> vec3f {
|
fn hsv2rgb(c: vec3f) -> vec3f {
|
||||||
let p = abs(fract(c.xxx + vec3f(1.0, 2.0/3.0, 1.0/3.0)) * 6.0 - vec3f(3.0));
|
let p = abs(fract(c.xxx + vec3f(1.0, 2.0/3.0, 1.0/3.0)) * 6.0 - vec3f(3.0));
|
||||||
return c.z * mix(vec3f(1.0), clamp(p - vec3f(1.0), vec3f(0.0), vec3f(1.0)), c.y);
|
return c.z * mix(vec3f(1.0), clamp(p - vec3f(1.0), vec3f(0.0), vec3f(1.0)), c.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shademap(r: f32) -> f32 {
|
fn shademap(r: f32) -> f32 {
|
||||||
return r*inverseSqrt(r * r + 0.0625 * uniforms.shading_intensity)*0.9875 + 0.0125;
|
return r*inverseSqrt(r * r + 0.0625 * uniforms.shading_intensity);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn colorfor(w: vec2f) -> vec3f {
|
fn colorfor(w: vec2f) -> vec3f {
|
||||||
let z = func_plot(w);
|
let z = func_plot(w);
|
||||||
|
|
||||||
|
if z.x != z.x || z.y != z.y {
|
||||||
|
return vec3f(0.5, 0.5, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
let r = sqrt(z.x*z.x + z.y*z.y);
|
let r = sqrt(z.x*z.x + z.y*z.y);
|
||||||
let arg = atan2(z.y, z.x);
|
let arg = atan2(z.y, z.x);
|
||||||
let hsv = vec3f(arg / TAU + 1.0, shademap(1.0/r), shademap(r));
|
let hsv = vec3f(arg / TAU + 1.0, shademap(1.0/r), shademap(r));
|
||||||
|
@ -200,10 +219,9 @@ fn colorfor(w: vec2f) -> vec3f {
|
||||||
}
|
}
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fs_main(@builtin(position) in: vec4f) -> @location(0) vec4f {
|
fn main(@builtin(position) in: vec4f) -> @location(0) vec4f {
|
||||||
let pos = vec2(in.x, f32(uniforms.resolution.y) - in.y);
|
let pos = vec2(in.x, f32(uniforms.resolution.y) - in.y);
|
||||||
let z = remap(pos, vec2(0.0), vec2f(uniforms.resolution), uniforms.bounds_min, uniforms.bounds_max);
|
let z = remap(pos, vec2(0.0), vec2f(uniforms.resolution), uniforms.bounds_min, uniforms.bounds_max);
|
||||||
//let dz = z - remap(pos + vec2f(1.0), vec2(0.0), vec2f(uniforms.resolution), uniforms.bounds_min, uniforms.bounds_max);
|
|
||||||
|
|
||||||
let col = colorfor(z);
|
let col = colorfor(z);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::num::NonZeroU64;
|
use std::num::NonZeroU64;
|
||||||
|
|
||||||
use encase::{ShaderType, ShaderSize, UniformBuffer};
|
use encase::{ShaderType, ShaderSize, UniformBuffer};
|
||||||
|
use log::warn;
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
type Vec2u = cgmath::Vector2<u32>;
|
type Vec2u = cgmath::Vector2<u32>;
|
||||||
|
@ -124,23 +125,28 @@ impl WgpuState {
|
||||||
|
|
||||||
pub fn load_shaders(&mut self, userdefs: &str) {
|
pub fn load_shaders(&mut self, userdefs: &str) {
|
||||||
// Shaders //
|
// Shaders //
|
||||||
let src = include_str!("shader.wgsl");
|
let vertex_src = include_str!("vertex.wgsl").to_owned();
|
||||||
let src = src.replace("//INCLUDE//\n", userdefs);
|
let fragment_src = include_str!("fragment.wgsl").to_owned() + userdefs;
|
||||||
|
|
||||||
let shader = self.device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
let vertex_module = self.device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
source: wgpu::ShaderSource::Wgsl(src.into())
|
source: wgpu::ShaderSource::Wgsl(vertex_src.into())
|
||||||
|
});
|
||||||
|
|
||||||
|
let fragment_module = self.device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
|
label: None,
|
||||||
|
source: wgpu::ShaderSource::Wgsl(fragment_src.into())
|
||||||
});
|
});
|
||||||
|
|
||||||
let vertex = wgpu::VertexState {
|
let vertex = wgpu::VertexState {
|
||||||
module: &shader,
|
module: &vertex_module,
|
||||||
entry_point: "vs_main",
|
entry_point: "main",
|
||||||
buffers: &[]
|
buffers: &[]
|
||||||
};
|
};
|
||||||
|
|
||||||
let fragment = wgpu::FragmentState {
|
let fragment = wgpu::FragmentState {
|
||||||
module: &shader,
|
module: &fragment_module,
|
||||||
entry_point: "fs_main",
|
entry_point: "main",
|
||||||
targets: &[Some(wgpu::ColorTargetState {
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
format: self.config.format,
|
format: self.config.format,
|
||||||
blend: Some(wgpu::BlendState {
|
blend: Some(wgpu::BlendState {
|
11
libcxgraph/src/renderer/vertex.wgsl
Normal file
11
libcxgraph/src/renderer/vertex.wgsl
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
@vertex
|
||||||
|
fn main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4f {
|
||||||
|
var pos = array<vec2f, 4>(
|
||||||
|
vec2(-1.0,-1.0),
|
||||||
|
vec2( 1.0,-1.0),
|
||||||
|
vec2(-1.0, 1.0),
|
||||||
|
vec2( 1.0, 1.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
return vec4f(pos[in_vertex_index], 0.0, 1.0);
|
||||||
|
}
|
132
src/complex.rs
132
src/complex.rs
|
@ -1,132 +0,0 @@
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub struct Complex {
|
|
||||||
pub re: f64,
|
|
||||||
pub im: f64
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Complex {
|
|
||||||
pub const fn new(re: f64, im: f64) -> Self {
|
|
||||||
Self { re, im }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<f64> for Complex {
|
|
||||||
fn from(value: f64) -> Self {
|
|
||||||
Self { re: value, im: 0.0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod cxfn {
|
|
||||||
use super::Complex;
|
|
||||||
|
|
||||||
pub type Function = fn(args: &[Complex]) -> Complex;
|
|
||||||
|
|
||||||
pub fn pos(a: &[Complex]) -> Complex {
|
|
||||||
a[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn neg(a: &[Complex]) -> Complex {
|
|
||||||
Complex::new(-a[0].re, -a[0].im)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn recip(a: &[Complex]) -> Complex {
|
|
||||||
let a = a[0];
|
|
||||||
let d = a.re*a.re + a.im*a.im;
|
|
||||||
Complex::new(a.re / d, -a.im / d)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn re(a: &[Complex]) -> Complex {
|
|
||||||
Complex::new(a[0].re, 0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn im(a: &[Complex]) -> Complex {
|
|
||||||
Complex::new(a[0].im, 0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn conj(a: &[Complex]) -> Complex {
|
|
||||||
Complex::new(a[0].re, -a[0].im)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn abs_sq(a: &[Complex]) -> Complex {
|
|
||||||
Complex::new(a[0].re*a[0].re + a[0].im*a[0].im, 0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn abs(a: &[Complex]) -> Complex {
|
|
||||||
Complex::new((a[0].re*a[0].re + a[0].im*a[0].im).sqrt(), 0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn arg(a: &[Complex]) -> Complex {
|
|
||||||
Complex::new(f64::atan2(a[0].im, a[0].re), 0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add(a: &[Complex]) -> Complex {
|
|
||||||
let (a, b) = (a[0], a[1]);
|
|
||||||
Complex::new(a.re + b.re, a.im + b.im)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sub(a: &[Complex]) -> Complex {
|
|
||||||
let (a, b) = (a[0], a[1]);
|
|
||||||
Complex::new(a.re - b.re, a.im - b.im)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mul(a: &[Complex]) -> Complex {
|
|
||||||
let (a, b) = (a[0], a[1]);
|
|
||||||
Complex::new(a.re*b.re - a.im*b.im, a.im*b.re + a.re*b.im)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn div(a: &[Complex]) -> Complex {
|
|
||||||
let (a, b) = (a[0], a[1]);
|
|
||||||
let d = b.re*b.re + b.im*b.im;
|
|
||||||
Complex::new((a.re*b.re + a.im*b.im)/d, (a.im*b.re - a.re*b.im)/d)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exp(a: &[Complex]) -> Complex {
|
|
||||||
let e = a[0].re.exp();
|
|
||||||
Complex::new(e * a[0].im.cos(), e * a[0].im.sin())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn log(a: &[Complex]) -> Complex {
|
|
||||||
let a = a[0];
|
|
||||||
Complex::new(0.5 * (a.re*a.re + a.im*a.im).ln(), f64::atan2(a.im, a.re))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pow(a: &[Complex]) -> Complex {
|
|
||||||
exp(&[mul(&[log(&[a[0]]), a[1]])])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sqrt(a: &[Complex]) -> Complex {
|
|
||||||
pow(&[a[0],Complex::new(0.5, 0.0)])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sin(a: &[Complex]) -> Complex {
|
|
||||||
let a = a[0];
|
|
||||||
Complex::new(a.re.sin()*a.im.cosh(), a.re.cos()*a.im.sinh())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cos(a: &[Complex]) -> Complex {
|
|
||||||
let a = a[0];
|
|
||||||
Complex::new(a.re.cos()*a.im.cosh(), -a.re.sin()*a.re.sinh())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tan(a: &[Complex]) -> Complex {
|
|
||||||
let a = a[0];
|
|
||||||
let d = (2.0*a.re).cos() + (2.0*a.im).cosh();
|
|
||||||
Complex::new((2.0*a.re).sin() / d, (2.0*a.im).sinh() / d)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sinh(a: &[Complex]) -> Complex {
|
|
||||||
let a = a[0];
|
|
||||||
Complex::new(a.re.sinh()*a.im.cos(), a.re.cosh()*a.re.sin())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cosh(a: &[Complex]) -> Complex {
|
|
||||||
let a = a[0];
|
|
||||||
Complex::new(a.re.cosh()*a.im.cos(), a.re.sinh()*a.re.sin())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tanh(a: &[Complex]) -> Complex {
|
|
||||||
let a = a[0];
|
|
||||||
let d = (2.0*a.re).cosh() + (2.0*a.im).cos();
|
|
||||||
Complex::new((2.0*a.re).sinh() / d, (2.0*a.im).sin() / d)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,344 +0,0 @@
|
||||||
use std::{collections::{HashMap, HashSet}, fmt::Write};
|
|
||||||
|
|
||||||
use crate::complex::{Complex, cxfn};
|
|
||||||
|
|
||||||
use super::parser::{Expr, Stmt, UnaryOp, BinaryOp, Defn};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum CompileError<'s> {
|
|
||||||
FmtError,
|
|
||||||
TypeError(&'s str),
|
|
||||||
UndefinedVar(&'s str),
|
|
||||||
Reassignment(&'s str),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'s> From<std::fmt::Error> for CompileError<'s> {
|
|
||||||
fn from(_: std::fmt::Error) -> Self {
|
|
||||||
Self::FmtError
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
thread_local! {
|
|
||||||
pub static BUILTINS: HashMap<&'static str, (&'static str, Type, Option<cxfn::Function>)> = {
|
|
||||||
let mut m: HashMap<&'static str, (&'static str, Type, Option<cxfn::Function>)> = HashMap::new();
|
|
||||||
m.insert("i", ("CONST_I", Type::Number, Some(|_| Complex::new(0.0, 1.0))));
|
|
||||||
m.insert("e", ("CONST_E", Type::Number, Some(|_| Complex::new(std::f64::consts::E, 0.0))));
|
|
||||||
m.insert("tau", ("CONST_TAU",Type::Number, Some(|_| Complex::new(std::f64::consts::TAU, 0.0))));
|
|
||||||
m.insert("re", ("c_re", Type::Function(1), Some(cxfn::re)));
|
|
||||||
m.insert("im", ("c_im", Type::Function(1), Some(cxfn::im)));
|
|
||||||
m.insert("conj", ("c_conj", Type::Function(1), Some(cxfn::conj)));
|
|
||||||
m.insert("abs_sq", ("c_abs_sq", Type::Function(1), Some(cxfn::abs_sq)));
|
|
||||||
m.insert("abs", ("c_abs", Type::Function(1), Some(cxfn::abs)));
|
|
||||||
m.insert("arg", ("c_arg", Type::Function(1), Some(cxfn::arg)));
|
|
||||||
m.insert("pos", ("c_pos", Type::Function(1), Some(cxfn::pos)));
|
|
||||||
m.insert("neg", ("c_neg", Type::Function(1), Some(cxfn::neg)));
|
|
||||||
m.insert("recip", ("c_recip", Type::Function(1), Some(cxfn::recip)));
|
|
||||||
m.insert("add", ("c_add", Type::Function(2), Some(cxfn::add)));
|
|
||||||
m.insert("sub", ("c_sub", Type::Function(2), Some(cxfn::sub)));
|
|
||||||
m.insert("mul", ("c_mul", Type::Function(2), Some(cxfn::mul)));
|
|
||||||
m.insert("div", ("c_div", Type::Function(2), Some(cxfn::div)));
|
|
||||||
m.insert("recip", ("c_recip", Type::Function(1), Some(cxfn::recip)));
|
|
||||||
m.insert("exp", ("c_exp", Type::Function(1), Some(cxfn::exp)));
|
|
||||||
m.insert("log", ("c_log", Type::Function(1), Some(cxfn::log)));
|
|
||||||
m.insert("sqrt", ("c_sqrt", Type::Function(1), Some(cxfn::sqrt)));
|
|
||||||
m.insert("sin", ("c_sin", Type::Function(1), Some(cxfn::sin)));
|
|
||||||
m.insert("cos", ("c_cos", Type::Function(1), Some(cxfn::cos)));
|
|
||||||
m.insert("tan", ("c_tan", Type::Function(1), Some(cxfn::tan)));
|
|
||||||
m.insert("sinh", ("c_sinh", Type::Function(1), Some(cxfn::sinh)));
|
|
||||||
m.insert("cosh", ("c_cosh", Type::Function(1), Some(cxfn::cosh)));
|
|
||||||
m.insert("tanh", ("c_tanh", Type::Function(1), Some(cxfn::tanh)));
|
|
||||||
m.insert("gamma", ("c_gamma", Type::Function(1), None));
|
|
||||||
m
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum Type {
|
|
||||||
Number,
|
|
||||||
Function(u32),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
enum NameScope {
|
|
||||||
Local, Global, Builtin
|
|
||||||
}
|
|
||||||
|
|
||||||
struct NameInfo<'s> {
|
|
||||||
scope: NameScope,
|
|
||||||
name: &'s str,
|
|
||||||
ty: Type
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'s> NameInfo<'s> {
|
|
||||||
pub fn get_cname(&self) -> String {
|
|
||||||
let name = self.name;
|
|
||||||
match (self.scope, self.ty) {
|
|
||||||
(NameScope::Local, _) => format!("arg_{name}"),
|
|
||||||
(NameScope::Global, Type::Number) => format!("VAR_{name}"),
|
|
||||||
(NameScope::Global, Type::Function(_)) => format!("func_{name}"),
|
|
||||||
(NameScope::Builtin, _) => name.to_owned(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type LocalTable<'s> = HashSet<&'s str>;
|
|
||||||
|
|
||||||
type CompileResult<'s,T=()> = Result<T, CompileError<'s>>;
|
|
||||||
|
|
||||||
pub fn compile<'s>(buf: &mut impl Write, defns: &[Defn<'s>]) -> CompileResult<'s> {
|
|
||||||
let mut compiler = Compiler::new(buf);
|
|
||||||
for defn in defns {
|
|
||||||
compiler.compile_defn(defn)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Compiler<'s, 'w, W> where W: Write {
|
|
||||||
buf: &'w mut W,
|
|
||||||
globals: HashMap<&'s str, Type>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'s, 'w, W: Write> Compiler<'s, 'w, W> {
|
|
||||||
fn new(buf: &'w mut W) -> Self {
|
|
||||||
Self {
|
|
||||||
buf,
|
|
||||||
globals: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////
|
|
||||||
// Statements //
|
|
||||||
//////////////////
|
|
||||||
|
|
||||||
fn compile_defn(&mut self, defn: &Defn<'s>) -> CompileResult<'s> {
|
|
||||||
let res = match defn {
|
|
||||||
Defn::Const { name, body } => self.defn_const(name, body),
|
|
||||||
Defn::Func { name, args, body } => self.defn_func(name, args, body),
|
|
||||||
};
|
|
||||||
writeln!(self.buf)?;
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
fn defn_const(&mut self, name: &'s str, body: &Expr<'s>) -> CompileResult<'s> {
|
|
||||||
if self.name_info(name, None).is_some() {
|
|
||||||
return Err(CompileError::Reassignment(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
self.globals.insert(name, Type::Number);
|
|
||||||
write!(self.buf, "const VAR_{name} = ")?;
|
|
||||||
|
|
||||||
let locals = LocalTable::with_capacity(0);
|
|
||||||
self.compile_expr(&locals, body)?;
|
|
||||||
|
|
||||||
write!(self.buf, ";")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn defn_func(&mut self, name: &'s str, args: &[&'s str], body: &(Vec<Stmt<'s>>, Expr<'s>)) -> CompileResult<'s> {
|
|
||||||
if self.name_info(name, None).is_some() {
|
|
||||||
return Err(CompileError::Reassignment(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
self.globals.insert(name, Type::Function(args.len() as u32));
|
|
||||||
write!(self.buf, "fn func_{name}(")?;
|
|
||||||
|
|
||||||
let mut locals = LocalTable::with_capacity(args.len());
|
|
||||||
for arg in args {
|
|
||||||
write!(self.buf, "arg_{arg}:vec2f,")?;
|
|
||||||
locals.insert(arg);
|
|
||||||
}
|
|
||||||
write!(self.buf, ")->vec2f{{return ")?;
|
|
||||||
self.compile_expr(&locals, &body.1)?;
|
|
||||||
write!(self.buf, ";}}")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stmt_deriv(&mut self, name: &'s str, func: &'s str) -> CompileResult<'s> {
|
|
||||||
if self.name_info(name, None).is_some() {
|
|
||||||
return Err(CompileError::Reassignment(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(name_info) = self.name_info(func, None) else {
|
|
||||||
return Err(CompileError::UndefinedVar(name))
|
|
||||||
};
|
|
||||||
let Type::Function(argc) = name_info.ty else {
|
|
||||||
return Err(CompileError::TypeError(name))
|
|
||||||
};
|
|
||||||
|
|
||||||
let func_name = name_info.get_cname();
|
|
||||||
|
|
||||||
self.globals.insert(name, Type::Function(argc));
|
|
||||||
|
|
||||||
write!(self.buf, "fn func_{name}(")?;
|
|
||||||
|
|
||||||
for i in 0..argc {
|
|
||||||
write!(self.buf, "arg_{i}:vec2f,")?;
|
|
||||||
}
|
|
||||||
let args: String = (1..argc).map(|i| format!(",arg_{i}")).collect();
|
|
||||||
|
|
||||||
write!(self.buf, ")->vec2f{{\
|
|
||||||
let a = c_mul({func_name}(arg_0 + vec2( D_EPS, 0.0){args}), vec2( 0.25/D_EPS, 0.0));\
|
|
||||||
let b = c_mul({func_name}(arg_0 + vec2(-D_EPS, 0.0){args}), vec2(-0.25/D_EPS, 0.0));\
|
|
||||||
let c = c_mul({func_name}(arg_0 + vec2(0.0, D_EPS){args}), vec2(0.0, -0.25/D_EPS));\
|
|
||||||
let d = c_mul({func_name}(arg_0 + vec2(0.0, -D_EPS){args}), vec2(0.0, 0.25/D_EPS));\
|
|
||||||
return a + b + c + d;}}\
|
|
||||||
")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stmt_iter(&mut self, name: &'s str, func: &'s str, count: u32) -> CompileResult<'s> {
|
|
||||||
if self.name_info(name, None).is_some() {
|
|
||||||
return Err(CompileError::Reassignment(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(name_info) = self.name_info(func, None) else {
|
|
||||||
return Err(CompileError::UndefinedVar(name))
|
|
||||||
};
|
|
||||||
let Type::Function(argc) = name_info.ty else {
|
|
||||||
return Err(CompileError::TypeError(name))
|
|
||||||
};
|
|
||||||
|
|
||||||
let func_name = name_info.get_cname();
|
|
||||||
|
|
||||||
self.globals.insert(name, Type::Function(argc));
|
|
||||||
|
|
||||||
write!(self.buf, "fn func_{name}(")?;
|
|
||||||
|
|
||||||
for i in 0..argc {
|
|
||||||
write!(self.buf, "arg_{i}:vec2f,")?;
|
|
||||||
}
|
|
||||||
let args: String = (1..argc).map(|i| format!(",arg_{i}")).collect();
|
|
||||||
|
|
||||||
write!(self.buf, ")->vec2f{{\
|
|
||||||
var r=arg_0;\
|
|
||||||
for(var i=0;i<{count};i++){{\
|
|
||||||
r={func_name}(r{args});\
|
|
||||||
}}\
|
|
||||||
return r;}}\
|
|
||||||
")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////
|
|
||||||
// Expressions //
|
|
||||||
///////////////////
|
|
||||||
|
|
||||||
fn compile_expr(&mut self, locals: &LocalTable<'s>, expr: &Expr<'s>) -> CompileResult<'s> {
|
|
||||||
match expr {
|
|
||||||
Expr::Number(z) => self.expr_number(*z),
|
|
||||||
Expr::Name(name) => self.expr_var(locals, name),
|
|
||||||
Expr::Unary(op, arg) => self.expr_unary(locals, *op, arg),
|
|
||||||
Expr::Binary(op, lhs, rhs) => self.expr_binary(locals, *op, lhs, rhs),
|
|
||||||
Expr::FnCall(name, args) => self.expr_fncall(locals, name, args),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expr_number(&mut self, z: Complex) -> CompileResult<'s> {
|
|
||||||
write!(self.buf, "vec2f({:?},{:?})", z.re, z.im)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expr_unary(&mut self, locals: &LocalTable<'s>, op: UnaryOp, arg: &Expr<'s>) -> CompileResult<'s> {
|
|
||||||
let strings = unop_strings(op);
|
|
||||||
write!(self.buf, "{}", strings[0])?;
|
|
||||||
self.compile_expr(locals, arg)?;
|
|
||||||
write!(self.buf, "{}", strings[1])?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expr_binary(&mut self, locals: &LocalTable<'s>, op: BinaryOp, lhs: &Expr<'s>, rhs: &Expr<'s>) -> CompileResult<'s> {
|
|
||||||
let strings = binop_strings(op);
|
|
||||||
write!(self.buf, "{}", strings[0])?;
|
|
||||||
self.compile_expr(locals, lhs)?;
|
|
||||||
write!(self.buf, "{}", strings[1])?;
|
|
||||||
self.compile_expr(locals, rhs)?;
|
|
||||||
write!(self.buf, "{}", strings[2])?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expr_var(&mut self, locals: &LocalTable<'s>, name: &'s str) -> CompileResult<'s> {
|
|
||||||
let Some(name_info) = self.name_info(name, Some(locals)) else {
|
|
||||||
return Err(CompileError::UndefinedVar(name))
|
|
||||||
};
|
|
||||||
if !matches!(name_info.ty, Type::Number) {
|
|
||||||
return Err(CompileError::TypeError(name))
|
|
||||||
}
|
|
||||||
write!(self.buf, "{}", name_info.get_cname())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expr_fncall(&mut self, locals: &LocalTable<'s>, name: &'s str, args: &Vec<Expr<'s>>) -> CompileResult<'s> {
|
|
||||||
let Some(name_info) = self.name_info(name, Some(locals)) else {
|
|
||||||
return Err(CompileError::UndefinedVar(name))
|
|
||||||
};
|
|
||||||
if !matches!(name_info.ty, Type::Function(n) if n as usize == args.len()) {
|
|
||||||
return Err(CompileError::TypeError(name))
|
|
||||||
}
|
|
||||||
write!(self.buf, "{}", name_info.get_cname())?;
|
|
||||||
write!(self.buf, "(")?;
|
|
||||||
for arg in args {
|
|
||||||
self.compile_expr(locals, arg)?;
|
|
||||||
write!(self.buf, ",")?;
|
|
||||||
}
|
|
||||||
write!(self.buf, ")")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////
|
|
||||||
// Names //
|
|
||||||
/////////////
|
|
||||||
|
|
||||||
fn name_info(&self, name: &'s str, locals: Option<&LocalTable<'s>>) -> Option<NameInfo> {
|
|
||||||
if let Some(locals) = locals {
|
|
||||||
if locals.contains(name) {
|
|
||||||
return Some(NameInfo { scope: NameScope::Local, name, ty: Type::Number });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(ty) = self.globals.get(name).copied() {
|
|
||||||
return Some(NameInfo { scope: NameScope::Global, name, ty })
|
|
||||||
}
|
|
||||||
if let Some((bname, ty, _)) = BUILTINS.with(|m| m.get(name).copied()) {
|
|
||||||
return Some(NameInfo { scope: NameScope::Builtin, name: bname, ty })
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
// fn generate_iter(&mut self, argc: u32) -> CompileResult<'s> {
|
|
||||||
// if !self.generate.contains_key(&format!("invoke{argc}")) {
|
|
||||||
// self.generate.insert(format!("invoke{argc}"), Generate::Iter { argc });
|
|
||||||
// self.generate_invoke(argc)?;
|
|
||||||
// writeln!(self.buf)?;
|
|
||||||
// }
|
|
||||||
// write!(self.buf, "fn iter{argc}(func:vec2f,n:vec2f,")?;
|
|
||||||
// for i in 0..argc {
|
|
||||||
// write!(self.buf, "arg_{i}:vec2f")?;
|
|
||||||
// write!(self.buf, ",")?;
|
|
||||||
// }
|
|
||||||
// write!(self.buf, ")->vec2f{{var result=arg_0;")?;
|
|
||||||
// write!(self.buf, "for(var i=0;i<i32(n.x);i++){{result=invoke{argc}(func,result,")?;
|
|
||||||
// for i in 1..argc {
|
|
||||||
// write!(self.buf, "arg_{i},")?;
|
|
||||||
// }
|
|
||||||
// write!(self.buf, ");}}return result;}}")?;
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const fn unop_strings(op: UnaryOp) -> [&'static str; 2] {
|
|
||||||
match op {
|
|
||||||
UnaryOp::Pos => ["+(", ")"],
|
|
||||||
UnaryOp::Neg => ["-(", ")"],
|
|
||||||
UnaryOp::Recip => ["c_recip(", ")"],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn binop_strings(op: BinaryOp) -> [&'static str; 3] {
|
|
||||||
match op {
|
|
||||||
BinaryOp::Add => ["(", ")+(", ")"],
|
|
||||||
BinaryOp::Sub => ["(", ")-(", ")"],
|
|
||||||
BinaryOp::Mul => ["c_mul(", ",", ")"],
|
|
||||||
BinaryOp::Div => ["c_div(", ",", ")"],
|
|
||||||
BinaryOp::Pow => ["c_pow(", ",", ")"],
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
mod scanner;
|
|
||||||
mod parser;
|
|
||||||
mod compiler;
|
|
||||||
mod optimizer;
|
|
||||||
|
|
||||||
pub use parser::ParseError;
|
|
||||||
pub use compiler::CompileError;
|
|
||||||
|
|
||||||
use self::optimizer::optimize;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct Position {
|
|
||||||
pub pos: u32,
|
|
||||||
pub line: u32,
|
|
||||||
pub col: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Error<'s> {
|
|
||||||
Parse(ParseError),
|
|
||||||
Compile(CompileError<'s>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'s> From<CompileError<'s>> for Error<'s> {
|
|
||||||
fn from(value: CompileError<'s>) -> Self {
|
|
||||||
Self::Compile(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'s> From<ParseError> for Error<'s> {
|
|
||||||
fn from(value: ParseError) -> Self {
|
|
||||||
Self::Parse(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compile(src: &str) -> Result<String, Error> {
|
|
||||||
let mut buf = String::new();
|
|
||||||
println!("parsing");
|
|
||||||
let defns = parser::Parser::new(src).parse()?;
|
|
||||||
println!("optimizing");
|
|
||||||
let defns = optimize(defns);
|
|
||||||
println!("compiling");
|
|
||||||
compiler::compile(&mut buf, &defns)?;
|
|
||||||
Ok(buf)
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
use crate::complex::{Complex, cxfn};
|
|
||||||
|
|
||||||
use super::{parser::{Expr, UnaryOp, BinaryOp, Stmt, Defn}, compiler::{BUILTINS, Type}};
|
|
||||||
|
|
||||||
fn apply_unary(op: UnaryOp, arg: Complex) -> Complex {
|
|
||||||
match op {
|
|
||||||
UnaryOp::Pos => cxfn::pos(&[arg]),
|
|
||||||
UnaryOp::Neg => cxfn::neg(&[arg]),
|
|
||||||
UnaryOp::Recip => cxfn::recip(&[arg]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_binary(op: BinaryOp, u: Complex, v: Complex) -> Complex {
|
|
||||||
match op {
|
|
||||||
BinaryOp::Add => cxfn::add(&[u, v]),
|
|
||||||
BinaryOp::Sub => cxfn::sub(&[u, v]),
|
|
||||||
BinaryOp::Mul => cxfn::mul(&[u, v]),
|
|
||||||
BinaryOp::Div => cxfn::div(&[u, v]),
|
|
||||||
BinaryOp::Pow => cxfn::pow(&[u, v]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn optimize(defns: Vec<Defn>) -> Vec<Defn> {
|
|
||||||
defns.into_iter().map(|s| match s {
|
|
||||||
Defn::Const { name, body } => Defn::Const { name, body: optimize_expr(body) },
|
|
||||||
Defn::Func { name, args, body } => {
|
|
||||||
let stmts = body.0.into_iter()
|
|
||||||
.map(optimize_stmt).collect();
|
|
||||||
let expr = optimize_expr(body.1);
|
|
||||||
Defn::Func { name, args, body: (stmts, expr) }
|
|
||||||
}
|
|
||||||
_ => s,
|
|
||||||
}).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn optimize_stmt(stmt: Stmt) -> Stmt {
|
|
||||||
match stmt {
|
|
||||||
Stmt::Expr(e) => Stmt::Expr(optimize_expr(e)),
|
|
||||||
Stmt::Store(v, e) => Stmt::Store(v, optimize_expr(e)),
|
|
||||||
Stmt::Iter(v, min, max, stmts) => Stmt::Iter(v, min, max,
|
|
||||||
stmts.into_iter().map(optimize_stmt).collect()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn optimize_expr<'s>(e: Expr<'s>) -> Expr<'s> {
|
|
||||||
match e {
|
|
||||||
Expr::Unary(op, arg) => {
|
|
||||||
match optimize_expr(*arg) {
|
|
||||||
Expr::Number(z) => Expr::Number(apply_unary(op, z)),
|
|
||||||
e => Expr::Unary(op, Box::new(e)),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Expr::Binary(op, lhs, rhs) => {
|
|
||||||
match (optimize_expr(*lhs), optimize_expr(*rhs)) {
|
|
||||||
(Expr::Number(u), Expr::Number(v)) => Expr::Number(apply_binary(op, u, v)),
|
|
||||||
(u, v) => Expr::Binary(op, Box::new(u), Box::new(v))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Expr::FnCall(name, args) => {
|
|
||||||
let args: Vec<Expr<'s>> = args.into_iter().map(optimize_expr).collect();
|
|
||||||
if let Some((_, Type::Function(argc), Some(func))) = BUILTINS.with(|m| m.get(name).copied()) {
|
|
||||||
if argc as usize == args.len()
|
|
||||||
&& args.iter().all(|e| matches!(e, Expr::Number(_))) {
|
|
||||||
let ns: Vec<Complex> = args.into_iter().map(|a| match a { Expr::Number(n) => n, _ => unreachable!() }).collect();
|
|
||||||
return Expr::Number(func(&ns))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expr::FnCall(name, args)
|
|
||||||
}
|
|
||||||
Expr::Name(name) => {
|
|
||||||
if let Some((_, Type::Number, Some(func))) = BUILTINS.with(|m| m.get(name).copied()) {
|
|
||||||
Expr::Number(func(&[]))
|
|
||||||
} else {
|
|
||||||
e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expr::Number(_) => e,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,272 +0,0 @@
|
||||||
use std::iter::Peekable;
|
|
||||||
|
|
||||||
use crate::complex::Complex;
|
|
||||||
|
|
||||||
use super::{scanner::{Scanner, Token}, Position};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct ParseError {
|
|
||||||
msg: String,
|
|
||||||
pos: Option<Position>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum BinaryOp {
|
|
||||||
Add, Sub, Mul, Div, Pow,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BinaryOp {
|
|
||||||
pub const fn from_token(tok: Token) -> Option<Self> {
|
|
||||||
match tok {
|
|
||||||
Token::Plus => Some(Self::Add),
|
|
||||||
Token::Minus => Some(Self::Sub),
|
|
||||||
Token::Star => Some(Self::Mul),
|
|
||||||
Token::Slash => Some(Self::Div),
|
|
||||||
Token::Caret => Some(Self::Pow),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn precedence(self) -> (u32, u32) {
|
|
||||||
match self {
|
|
||||||
BinaryOp::Add
|
|
||||||
| BinaryOp::Sub => (10, 11),
|
|
||||||
BinaryOp::Mul
|
|
||||||
| BinaryOp::Div => (20, 21),
|
|
||||||
BinaryOp::Pow => (31, 30),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum UnaryOp {
|
|
||||||
Pos, Neg, Recip,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnaryOp {
|
|
||||||
pub const fn from_token(tok: Token) -> Option<Self> {
|
|
||||||
match tok {
|
|
||||||
Token::Plus => Some(Self::Pos),
|
|
||||||
Token::Minus => Some(Self::Neg),
|
|
||||||
Token::Slash => Some(Self::Recip),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn precedence(self) -> u32 {
|
|
||||||
40
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Expr<'s> {
|
|
||||||
Number(Complex),
|
|
||||||
Name(&'s str),
|
|
||||||
Unary(UnaryOp, Box<Expr<'s>>),
|
|
||||||
Binary(BinaryOp, Box<Expr<'s>>, Box<Expr<'s>>),
|
|
||||||
FnCall(&'s str, Vec<Expr<'s>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Stmt<'s> {
|
|
||||||
Expr(Expr<'s>),
|
|
||||||
Store(&'s str, Expr<'s>),
|
|
||||||
Iter(&'s str, u32, u32, Vec<Stmt<'s>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Defn<'s> {
|
|
||||||
Const { name: &'s str, body: Expr<'s> },
|
|
||||||
Func { name: &'s str, args: Vec<&'s str>, body: (Vec<Stmt<'s>>, Expr<'s>) },
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Parser<'s> {
|
|
||||||
scanner: Peekable<Scanner<'s>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'s> Parser<'s> {
|
|
||||||
pub fn new(src: &'s str) -> Self {
|
|
||||||
Self {
|
|
||||||
scanner: Scanner::new(src).peekable()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next(&mut self) -> Result<(Position, Token<'s>), ParseError> {
|
|
||||||
match self.scanner.next() {
|
|
||||||
Some(r) => Ok(r),
|
|
||||||
None => Err(self.err_here("Unexpected EOF")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn peek(&mut self) -> Result<(Position, Token<'s>), ParseError> {
|
|
||||||
match self.scanner.peek() {
|
|
||||||
Some(r) => Ok(*r),
|
|
||||||
None => Err(self.err_here("Unexpected EOF")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn at_end(&mut self) -> bool {
|
|
||||||
self.scanner.peek().is_none()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expect(&mut self, tok: Token<'s>) -> Result<Position, ParseError> {
|
|
||||||
match self.peek()? {
|
|
||||||
(p, t) if t == tok => {
|
|
||||||
self.next()?;
|
|
||||||
Ok(p)
|
|
||||||
},
|
|
||||||
(p, t) => Err(self.err_at(format!("Unexpected token {t:?}, expected {tok:?}"), p)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn err_here<S>(&mut self, msg: S) -> ParseError
|
|
||||||
where S: Into<String> {
|
|
||||||
ParseError { msg: msg.into(), pos: self.peek().map(|(p,_)| p).ok() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn err_at<S>(&mut self, msg: S, pos: Position) -> ParseError
|
|
||||||
where S: Into<String> {
|
|
||||||
ParseError { msg: msg.into(), pos: Some(pos) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expr(&mut self, min_prec: u32) -> Result<Expr<'s>, ParseError> {
|
|
||||||
let (pos, tok) = self.next()?;
|
|
||||||
let mut expr = match tok {
|
|
||||||
Token::Number(n) => Expr::Number(Complex::from(n)),
|
|
||||||
Token::Name(n) => Expr::Name(n),
|
|
||||||
Token::LParen => {
|
|
||||||
let e = self.expr(0)?;
|
|
||||||
self.expect(Token::RParen)?;
|
|
||||||
e
|
|
||||||
}
|
|
||||||
tok => if let Some(op) = UnaryOp::from_token(tok) {
|
|
||||||
Expr::Unary(op, Box::new(self.expr(op.precedence())?))
|
|
||||||
} else {
|
|
||||||
return Err(self.err_at(format!("Unexpected token {:?}", tok), pos))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
while let Ok((_, tok)) = self.peek() {
|
|
||||||
if is_closing(&tok) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
expr = match tok {
|
|
||||||
Token::LParen => {
|
|
||||||
self.next()?;
|
|
||||||
let mut args = Vec::new();
|
|
||||||
while !matches!(self.peek(), Err(_) | Ok((_, Token::RParen))) {
|
|
||||||
args.push(self.expr(0)?);
|
|
||||||
match self.peek()?.1 {
|
|
||||||
Token::Comma => { self.next()?; },
|
|
||||||
Token::RParen => break,
|
|
||||||
_ => return Err(self.err_here(format!("Unexpected token {:?}, expected ',' or ')'", tok)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.expect(Token::RParen)?;
|
|
||||||
match expr {
|
|
||||||
Expr::Name(name) => Expr::FnCall(name, args),
|
|
||||||
_ => return Err(self.err_here("Cannot call this expression"))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tok => if let Some(op) = BinaryOp::from_token(tok) {
|
|
||||||
let (lp, rp) = op.precedence();
|
|
||||||
if lp < min_prec {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
self.next()?;
|
|
||||||
let rhs = self.expr(rp)?;
|
|
||||||
Expr::Binary(op, Box::new(expr), Box::new(rhs))
|
|
||||||
} else {
|
|
||||||
let (lp, rp) = BinaryOp::Mul.precedence();
|
|
||||||
if lp < min_prec {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let rhs = self.expr(rp)?;
|
|
||||||
Expr::Binary(BinaryOp::Mul, Box::new(expr), Box::new(rhs))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(expr)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stmt(&mut self) -> Result<Stmt<'s>, ParseError> {
|
|
||||||
let expr = self.expr(0)?;
|
|
||||||
match self.peek()?.1 {
|
|
||||||
Token::Arrow => {
|
|
||||||
self.next()?;
|
|
||||||
let name = match self.next()? {
|
|
||||||
(_, Token::Name(name)) => name,
|
|
||||||
(p, t) => return Err(self.err_at(format!("Unexpected token {t:?}, expected a name"), p))
|
|
||||||
};
|
|
||||||
Ok(Stmt::Store(name, expr))
|
|
||||||
}
|
|
||||||
_ => Ok(Stmt::Expr(expr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stmts(&mut self) -> Result<Vec<Stmt<'s>>, ParseError> {
|
|
||||||
let mut stmts = Vec::new();
|
|
||||||
loop {
|
|
||||||
stmts.push(self.stmt()?);
|
|
||||||
if !matches!(self.peek(), Ok((_, Token::Comma))) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
self.next()?;
|
|
||||||
}
|
|
||||||
Ok(stmts)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse(&mut self) -> Result<Vec<Defn<'s>>, ParseError> {
|
|
||||||
println!("parse");
|
|
||||||
let mut defns = Vec::new();
|
|
||||||
while self.peek().is_ok() {
|
|
||||||
println!("parse loop");
|
|
||||||
while matches!(self.peek(), Ok((_, Token::Newline))) {
|
|
||||||
self.next()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.peek().is_err() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let lhspos = self.peek()?.0;
|
|
||||||
let lhs = self.expr(0)?;
|
|
||||||
|
|
||||||
self.expect(Token::Equal)?;
|
|
||||||
|
|
||||||
let defn = match lhs {
|
|
||||||
Expr::Name(name) => {
|
|
||||||
let rhs = self.expr(0)?;
|
|
||||||
Defn::Const { name, body: rhs }
|
|
||||||
},
|
|
||||||
Expr::FnCall(name, args) => {
|
|
||||||
let mut rhs = self.stmts()?;
|
|
||||||
let last = rhs.pop().ok_or(self.err_here("Empty function body"))?;
|
|
||||||
let Stmt::Expr(last) = last else {
|
|
||||||
return Err(self.err_here("Last statement in function body must be a plain expression"))
|
|
||||||
};
|
|
||||||
let args = args.iter()
|
|
||||||
.map(|a| match a {
|
|
||||||
Expr::Name(n) => Ok(*n),
|
|
||||||
_ => Err(self.err_at("Invalid function declaration", lhspos))
|
|
||||||
}).collect::<Result<Vec<&str>, ParseError>>()?;
|
|
||||||
Defn::Func { name, args, body: (rhs, last) }
|
|
||||||
},
|
|
||||||
_ => return Err(self.err_at("Invalid lvalue, expected a name or function call", lhspos)),
|
|
||||||
};
|
|
||||||
|
|
||||||
defns.push(defn);
|
|
||||||
|
|
||||||
if self.at_end() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
self.expect(Token::Newline)?;
|
|
||||||
}
|
|
||||||
Ok(defns)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_closing(tok: &Token) -> bool {
|
|
||||||
matches!(tok, Token::Equal | Token::RParen | Token::Newline | Token::Comma | Token::Arrow)
|
|
||||||
}
|
|
|
@ -1,118 +0,0 @@
|
||||||
use std::{iter::Peekable, str::Chars};
|
|
||||||
|
|
||||||
use super::Position;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub enum Token<'s> {
|
|
||||||
Error,
|
|
||||||
Name(&'s str),
|
|
||||||
Number(f64),
|
|
||||||
Plus,
|
|
||||||
Minus,
|
|
||||||
Star,
|
|
||||||
Slash,
|
|
||||||
Caret,
|
|
||||||
Equal,
|
|
||||||
Comma,
|
|
||||||
Arrow,
|
|
||||||
LParen,
|
|
||||||
RParen,
|
|
||||||
Newline,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Scanner<'s> {
|
|
||||||
pub src: &'s str,
|
|
||||||
pub chars: Peekable<Chars<'s>>,
|
|
||||||
pub pos: Position,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'s> Scanner<'s> {
|
|
||||||
pub fn new(src: &'s str) -> Self {
|
|
||||||
Self {
|
|
||||||
src,
|
|
||||||
chars: src.chars().peekable(),
|
|
||||||
pos: Position { pos: 0, line: 1, col: 1 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<char> {
|
|
||||||
match self.chars.next() {
|
|
||||||
Some('\n') => {
|
|
||||||
self.pos.pos += 1;
|
|
||||||
self.pos.line += 1;
|
|
||||||
self.pos.col = 1;
|
|
||||||
Some('\n')
|
|
||||||
},
|
|
||||||
Some(c) => {
|
|
||||||
self.pos.pos += 1;
|
|
||||||
self.pos.col += 1;
|
|
||||||
Some(c)
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn peek(&mut self) -> Option<char> {
|
|
||||||
self.chars.peek().copied()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_number(&mut self, pos: u32) -> Token<'s> {
|
|
||||||
while matches!(self.peek(), Some('0'..='9')) {
|
|
||||||
self.next();
|
|
||||||
}
|
|
||||||
if matches!(self.peek(), Some('.')) {
|
|
||||||
self.next();
|
|
||||||
while matches!(self.peek(), Some('0'..='9')) {
|
|
||||||
self.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let s = &self.src[pos as usize..self.pos.pos as usize];
|
|
||||||
match s.parse() {
|
|
||||||
Ok(x) => Token::Number(x),
|
|
||||||
Err(_) => Token::Error,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_name(&mut self, pos: u32) -> Token<'s> {
|
|
||||||
while matches!(self.peek(), Some('a'..='z' | 'A'..='Z' | '0'..='9' | '_')) {
|
|
||||||
self.next();
|
|
||||||
}
|
|
||||||
let s = &self.src[pos as usize..self.pos.pos as usize];
|
|
||||||
Token::Name(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_token(&mut self) -> Option<(Position, Token<'s>)> {
|
|
||||||
while matches!(self.peek(), Some(' ' | '\t')) {
|
|
||||||
self.next();
|
|
||||||
}
|
|
||||||
self.peek()?;
|
|
||||||
let pos = self.pos;
|
|
||||||
let tok = match self.next().unwrap() {
|
|
||||||
'\n' => Token::Newline,
|
|
||||||
'+' => Token::Plus,
|
|
||||||
'-' => match self.peek().unwrap() {
|
|
||||||
'>' => { self.next(); Token::Arrow },
|
|
||||||
_ => Token::Minus,
|
|
||||||
}
|
|
||||||
'*' => Token::Star,
|
|
||||||
'/' => Token::Slash,
|
|
||||||
'^' => Token::Caret,
|
|
||||||
'=' => Token::Equal,
|
|
||||||
',' => Token::Comma,
|
|
||||||
'(' => Token::LParen,
|
|
||||||
')' => Token::RParen,
|
|
||||||
'0'..='9' => self.next_number(pos.pos),
|
|
||||||
'a'..='z' | 'A'..='Z' => self.next_name(pos.pos),
|
|
||||||
_ => Token::Error,
|
|
||||||
};
|
|
||||||
Some((pos, tok))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'s> Iterator for Scanner<'s> {
|
|
||||||
type Item = (Position, Token<'s>);
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
self.next_token()
|
|
||||||
}
|
|
||||||
}
|
|
49
style.css
49
style.css
|
@ -1,49 +0,0 @@
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
min-height: 100vh;
|
|
||||||
max-height: 100vh;
|
|
||||||
overflow: hidden;
|
|
||||||
--header-height: 50px;
|
|
||||||
--sidebar-width: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main {
|
|
||||||
min-height: 100vh;
|
|
||||||
max-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header {
|
|
||||||
height: var(--header-height);
|
|
||||||
}
|
|
||||||
|
|
||||||
#content {
|
|
||||||
width: 100vw;
|
|
||||||
height: calc(100vh - var(--header-height));
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sidebar {
|
|
||||||
width: var(--sidebar-width);
|
|
||||||
height: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
#canvas_container {
|
|
||||||
width: calc(100vw - var(--sidebar-width));
|
|
||||||
height: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sidebar textarea {
|
|
||||||
width: calc(100% - 24px);
|
|
||||||
margin-left: 10px;
|
|
||||||
height: 99%;
|
|
||||||
resize: none;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas {
|
|
||||||
background-color: black;
|
|
||||||
}
|
|
Loading…
Reference in a new issue