diff --git a/Cargo.lock b/Cargo.lock index 9885d2c..f98ce02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,12 +3,30 @@ version = 3 [[package]] -name = "aho-corasick" -version = "1.1.3" +name = "addr2line" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ - "memchr", + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", ] [[package]] @@ -18,54 +36,92 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] -name = "anstream" -version = "0.6.15" +name = "async-trait" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "anstyle" -version = "1.0.8" +name = "axum" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" - -[[package]] -name = "anstyle-parse" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ - "utf8parse", + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", ] [[package]] -name = "anstyle-query" -version = "1.1.1" +name = "axum-core" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ - "windows-sys", + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.1", + "tower-layer", + "tower-service", + "tracing", ] [[package]] -name = "anstyle-wincon" -version = "3.0.4" +name = "backtrace" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ - "anstyle", - "windows-sys", + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", ] +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "bumpalo" version = "3.16.0" @@ -76,39 +132,99 @@ dependencies = [ ] [[package]] -name = "colorchoice" -version = "1.0.2" +name = "bytes" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] -name = "env_filter" -version = "0.1.2" +name = "cfg-if" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "depell" +version = "0.1.0" dependencies = [ - "log", - "regex", + "axum", + "rusqlite", ] [[package]] -name = "env_logger" -version = "0.11.5" +name = "fallible-iterator" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "anstream", - "anstyle", - "env_filter", - "humantime", - "log", + "percent-encoding", ] +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] [[package]] name = "hashbrown" @@ -116,6 +232,15 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + [[package]] name = "hbbytecode" version = "0.1.0" @@ -128,7 +253,6 @@ version = "0.1.0" name = "hblang" version = "0.1.0" dependencies = [ - "env_logger", "hashbrown 0.15.0", "hbbytecode", "hbvm", @@ -152,16 +276,101 @@ dependencies = [ ] [[package]] -name = "humantime" -version = "2.1.0" +name = "hermit-abi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] -name = "is_terminal_polyfill" -version = "1.70.1" +name = "html-macro" +version = "0.1.0" + +[[package]] +name = "http" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "libc" @@ -169,12 +378,28 @@ version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "pkg-config", + "vcpkg", +] + [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.7.4" @@ -190,6 +415,99 @@ dependencies = [ "libc", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + [[package]] name = "regalloc2" version = "0.10.2" @@ -204,33 +522,24 @@ dependencies = [ ] [[package]] -name = "regex" -version = "1.10.6" +name = "rusqlite" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", ] [[package]] -name = "regex-automata" -version = "0.4.7" +name = "rustc-demangle" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -238,6 +547,72 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -245,10 +620,135 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] -name = "utf8parse" -version = "0.2.2" +name = "socket2" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "windows-sys" @@ -326,3 +826,23 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "xtask" version = "0.1.0" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 6b5f6ce..4ffe95b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["hbbytecode", "hbvm", "hbxrt", "xtask", "hblang", "hbjit"] +members = ["hbbytecode", "hbvm", "hbxrt", "xtask", "hblang", "hbjit", "depell", "depell/html-macro"] [profile.release] lto = true diff --git a/depell/Cargo.toml b/depell/Cargo.toml new file mode 100644 index 0000000..e1fef58 --- /dev/null +++ b/depell/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "depell" +version = "0.1.0" +edition = "2021" + +[dependencies] +axum = "0.7.7" +rusqlite = "0.32.1" diff --git a/depell/html-macro/Cargo.toml b/depell/html-macro/Cargo.toml new file mode 100644 index 0000000..8d16ce2 --- /dev/null +++ b/depell/html-macro/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "html-macro" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true diff --git a/depell/html-macro/src/lib.rs b/depell/html-macro/src/lib.rs new file mode 100644 index 0000000..11c9499 --- /dev/null +++ b/depell/html-macro/src/lib.rs @@ -0,0 +1,530 @@ +#![feature(proc_macro_diagnostic)] +#![feature(proc_macro_def_site)] + +use std::fmt::Write; +use std::str::FromStr; + +macro_rules! diag { + ($level:ident: $($tt:tt)*) => {diag!($level(proc_macro::Span::call_site()): $($tt)*)}; + ($level:ident[$span:expr]: $($tt:tt)*) => {diag!($level($span.span()): $($tt)*)}; + ($level:ident($span:expr): $($tt:tt)*) => {{ + proc_macro::Diagnostic::spanned($span, proc_macro::Level::$level, format!($($tt)*)).emit(); + }}; +} + +macro_rules! fatal_diag { + ($($tt:tt)*) => {{ + diag!($($tt)*); + return None; + }}; +} + +#[proc_macro] +pub fn html(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let mut trees = input.into_iter().peekable(); + let mut st = State { + final_code: "Html({ let mut ___out = String::new(); ".to_string(), + write_target: "___out".to_string(), + error_handler: ".unwrap()", + ..State::default() + }; + + if st.output_fmt(&mut trees).is_none() { + return proc_macro::TokenStream::default(); + }; + + st.final_code.push_str("___out })"); + + if !st.tag_stack.is_empty() { + diag!(Error(proc_macro::Span::def_site()): "unclosed tags: {}", st.tag_stack); + } + + proc_macro::TokenStream::from_str(&st.final_code).unwrap() +} + +#[proc_macro] +pub fn write_html(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let mut trees = input.into_iter().peekable(); + let mut st = State { + write_target: trees.next().expect("expected first token").to_string(), + error_handler: ".unwrap()", + ..State::default() + }; + + if st.output_fmt(&mut trees).is_none() { + return proc_macro::TokenStream::default(); + }; + + if !st.tag_stack.is_empty() { + diag!(Error(proc_macro::Span::def_site()): "unclosed tags: {}", st.tag_stack); + } + + proc_macro::TokenStream::from_str(&st.final_code).unwrap() +} + +type Trees = std::iter::Peekable; + +#[derive(Default)] +struct State { + final_code: String, + template: String, + tstr: String, + tag_stack: String, + write_target: String, + no_escaping: bool, + error_handler: &'static str, +} + +impl State { + fn flush_template(&mut self) { + if self.template.is_empty() { + return; + } + write!( + self.final_code, + "write!({}, {:?}){};", + self.write_target, self.template, self.error_handler + ) + .unwrap(); + self.template.clear(); + } + + fn display_expr(&mut self, expr: impl std::fmt::Display) { + self.flush_template(); + if std::mem::take(&mut self.no_escaping) { + write!( + self.final_code, + "write!({}, \"{{}}\", {expr}){};", + self.write_target, self.error_handler + ) + .unwrap(); + } else { + write!( + self.final_code, + "write!({}, \"{{}}\", &HtmlEscaped(&{expr})){};", + self.write_target, self.error_handler, + ) + .unwrap() + } + } + + fn expect_group( + &mut self, + trees: &mut Trees, + delim: proc_macro::Delimiter, + ) -> Option { + match trees.next() { + Some(proc_macro::TokenTree::Group(g)) if g.delimiter() == delim => Some(g.stream()), + Some(c) => fatal_diag!(Error[c]: "expected {delim:?} group"), + None => fatal_diag!(Error: "expected {delim:?} group, got eof"), + } + } + + fn expect_punct(&mut self, trees: &mut Trees, c: char) -> Option<()> { + match trees.next() { + Some(proc_macro::TokenTree::Punct(p)) if p.as_char() == c => Some(()), + Some(c) => fatal_diag!(Error[c]: "expected {c}"), + None => fatal_diag!(Error: "expected {c}, got eof"), + } + } + + fn tag(&mut self, p: proc_macro::Punct, trees: &mut Trees) -> Option<()> { + let temp = match trees.next() { + Some(proc_macro::TokenTree::Ident(id)) => { + let temp = temp_str(&id, &mut self.tstr); + if !is_html_tag(temp) { + diag!(Error[p]: "expected html5 tag name"); + } + temp + } + Some(proc_macro::TokenTree::Literal(lit)) => { + let temp = temp_str(&lit, &mut self.tstr).trim_matches('"'); + if is_html_tag(temp) { + if !temp.contains('!') { + diag!(Warning[p]: "unnescessary string escaping"); + } + } else if !is_valid_webcomponent(temp) { + diag!(Error[p]: "invalid web component identifier"); + } + temp + } + Some(proc_macro::TokenTree::Punct(p)) if p.as_char() == '/' => { + let Some((_, top)) = self.tag_stack.rsplit_once(',') else { + fatal_diag!(Error[p]: "no tag to close"); + }; + + let new_tag_stack_len = self.tag_stack.len() - top.len() - 1; + + let (temp, span) = match trees.next() { + Some(proc_macro::TokenTree::Ident(id)) => { + (temp_str(&id, &mut self.tstr), id.span()) + } + Some(proc_macro::TokenTree::Literal(lit)) => { + (temp_str(&lit, &mut self.tstr).trim_matches('"'), lit.span()) + } + Some(proc_macro::TokenTree::Punct(p)) if p.as_char() == '>' => { + // easter egg + write!(&mut self.template, "").unwrap(); + self.tag_stack.truncate(new_tag_stack_len); + return Some(()); + } + Some(c) => fatal_diag!(Error[c]: "unexpected token in closing tag"), + None => { + fatal_diag!(Error[p]: "expected tag ident or string or '>'") + } + }; + + if temp != top { + diag!(Error(span): "expected closing '{top}' tag"); + } + + write!(&mut self.template, "").unwrap(); + self.expect_punct(trees, '>')?; + self.tag_stack.truncate(new_tag_stack_len); + return Some(()); + } + _ => fatal_diag!(Error[p]: "expected tag ident or string literal"), + }; + + write!(&mut self.template, "<{temp}").unwrap(); + if !is_self_closing(temp) { + write!(self.tag_stack, ",{temp}").unwrap(); + } + + let mut has_attr = false; + while let Some(c) = trees.next() { + let mut has_attr_tmp = false; + match c { + proc_macro::TokenTree::Punct(p) if p.as_char() == '=' && has_attr => loop { + match trees.next() { + Some(proc_macro::TokenTree::Punct(p)) if p.as_char() == '!' => { + self.no_escaping = true; + continue; + } + Some(proc_macro::TokenTree::Literal(lit)) => { + write!(&mut self.template, "={lit}").unwrap(); + } + Some(proc_macro::TokenTree::Ident(id)) => { + write!(&mut self.template, "=\"").unwrap(); + self.display_expr(id); + write!(&mut self.template, "\"").unwrap(); + } + Some(proc_macro::TokenTree::Group(g)) + if g.delimiter() == proc_macro::Delimiter::Brace => + { + write!(&mut self.template, "=\"").unwrap(); + self.display_expr(g.stream()); + write!(&mut self.template, "\"").unwrap(); + } + Some(c) => { + diag!(Error[c]: "unexpected token in attr value") + } + None => diag!(Error[p]: "expected attribute value"), + } + break; + }, + proc_macro::TokenTree::Punct(p) if p.as_char() == '>' => { + write!(&mut self.template, ">").unwrap(); + break; + } + proc_macro::TokenTree::Ident(id) => { + write!(&mut self.template, " {id}").unwrap(); + has_attr_tmp = true; + } + proc_macro::TokenTree::Literal(lit) => { + let temp = temp_str(&lit, &mut self.tstr).trim_matches('"'); + if !is_valid_html_attt_name(temp) { + diag!(Error[p]: "invalid attribute name"); + } + write!(&mut self.template, " {temp}").unwrap(); + has_attr_tmp = true; + } + c => diag!(Error[c]: "unexpected token in attribute list"), + } + has_attr = has_attr_tmp; + } + + Some(()) + } + + fn matches_char(t: &proc_macro::TokenTree, ch: char, spacing: proc_macro::Spacing) -> bool { + matches!(t, proc_macro::TokenTree::Punct(p) if p.as_char() == ch && p.spacing() == spacing) + } + + fn match_expr(&mut self, trees: &mut Trees) -> Option<()> { + let expr = self.expect_group(trees, proc_macro::Delimiter::Parenthesis)?; + let mut body = self + .expect_group(trees, proc_macro::Delimiter::Brace)? + .into_iter() + .peekable(); + self.flush_template(); + + write!(&mut self.final_code, "match {expr} {{").unwrap(); + + loop { + let mut pattern = proc_macro::TokenStream::new(); + let mut looped = false; + + while let Some(c) = body.next() { + if !Self::matches_char(&c, '=', proc_macro::Spacing::Joint) { + pattern.extend([c]); + looped = true; + continue; + } + + let nc = body.next().expect("haaaaa"); + if Self::matches_char(&nc, '>', proc_macro::Spacing::Alone) { + break; + } + pattern.extend([c, nc]); + looped = true; + } + + if !looped { + break; + } + + let body = self.expect_group(&mut body, proc_macro::Delimiter::Brace)?; + + write!(&mut self.final_code, "{pattern} => {{").unwrap(); + self.output_fmt(&mut body.into_iter().peekable())?; + write!(&mut self.final_code, "}}").unwrap(); + } + + write!(&mut self.final_code, "}}").unwrap(); + + Some(()) + } + + fn for_expr(&mut self, trees: &mut Trees) -> Option<()> { + let mut ink = proc_macro::Span::call_site(); + let loop_var = trees + .by_ref() + .take_while(|t| { + ink = t.span(); + !matches!(t, proc_macro::TokenTree::Ident(id) if temp_str(id, &mut self.tstr) == "in") + }) + .collect::(); + + let iter = self.expect_group(trees, proc_macro::Delimiter::Parenthesis)?; + let body = self.expect_group(trees, proc_macro::Delimiter::Brace)?; + let else_body = match trees.peek() { + Some(proc_macro::TokenTree::Ident(id)) if temp_str(id, &mut self.tstr) == "else" => { + trees.next(); + self.expect_group(trees, proc_macro::Delimiter::Brace)? + } + _ => proc_macro::TokenStream::new(), + }; + + self.tstr.clear(); + + self.flush_template(); + + if else_body.is_empty() { + write!(&mut self.final_code, "for {loop_var} in {iter} {{").unwrap(); + } else { + write!( + &mut self.final_code, + "let mut looped = false;\ + for {loop_var} in {iter} {{\ + looped = true;" + ) + .unwrap(); + } + self.output_fmt(&mut body.into_iter().peekable())?; + write!(&mut self.final_code, "}}").unwrap(); + if !else_body.is_empty() { + write!(&mut self.final_code, "if !looped {{").unwrap(); + self.output_fmt(&mut else_body.into_iter().peekable())?; + write!(&mut self.final_code, "}}").unwrap(); + } + Some(()) + } + + fn output_fmt(&mut self, trees: &mut Trees) -> Option<()> { + while let Some(c) = trees.next() { + match c { + proc_macro::TokenTree::Punct(p) if p.as_char() == '!' => self.no_escaping = true, + proc_macro::TokenTree::Punct(p) if p.as_char() == '<' => self.tag(p, trees)?, + proc_macro::TokenTree::Literal(lit) => self + .template + .push_str(temp_str(&lit, &mut self.tstr).trim_matches('"')), + proc_macro::TokenTree::Ident(id) => match temp_str(&id, &mut self.tstr) { + "match" => self.match_expr(trees)?, + "for" => self.for_expr(trees)?, + _ => self.display_expr(id), + }, + proc_macro::TokenTree::Group(g) + if g.delimiter() == proc_macro::Delimiter::Brace => + { + self.display_expr(g.stream()); + } + c => fatal_diag!(Error[c]: "unexpected token"), + } + } + self.flush_template(); + + Some(()) + } +} + +fn temp_str(i: impl std::fmt::Display, buf: &mut String) -> &str { + buf.clear(); + write!(buf, "{i}").unwrap(); + buf +} + +fn is_valid_html_attt_name(tag: &str) -> bool { + tag.bytes() + .all(|c| matches!(c, b'a'..=b'z' | b'_' | b'-' | b'A'..=b'Z' | 128..=u8::MAX)) +} + +fn is_valid_webcomponent(tag: &str) -> bool { + let mut seen_dash = false; + tag.bytes() + .inspect(|c| seen_dash |= *c == b'-') + .all(|c| matches!(c, b'a'..=b'z' | b'_' | b'-' | b'.' | 128..=u8::MAX)) + && seen_dash +} + +fn is_html_tag(tag: &str) -> bool { + matches!( + tag, + "!DOCTYPE" + | "a" + | "abbr" + | "acronym" + | "address" + | "area" + | "article" + | "aside" + | "audio" + | "b" + | "base" + | "basefont" + | "bdi" + | "bdo" + | "big" + | "blockquote" + | "body" + | "br" + | "button" + | "canvas" + | "caption" + | "center" + | "cite" + | "code" + | "col" + | "colgroup" + | "data" + | "datalist" + | "dd" + | "del" + | "details" + | "dfn" + | "dialog" + | "div" + | "dl" + | "dt" + | "em" + | "embed" + | "fieldset" + | "figcaption" + | "figure" + | "footer" + | "form" + | "h1" + | "h2" + | "h3" + | "h4" + | "h5" + | "h6" + | "head" + | "header" + | "hr" + | "html" + | "i" + | "iframe" + | "img" + | "input" + | "ins" + | "kbd" + | "label" + | "legend" + | "li" + | "link" + | "main" + | "map" + | "mark" + | "meta" + | "meter" + | "nav" + | "noscript" + | "object" + | "ol" + | "optgroup" + | "option" + | "output" + | "p" + | "param" + | "picture" + | "pre" + | "progress" + | "q" + | "rp" + | "rt" + | "ruby" + | "s" + | "samp" + | "script" + | "section" + | "select" + | "small" + | "source" + | "span" + | "strong" + | "style" + | "sub" + | "summary" + | "sup" + | "svg" + | "table" + | "tbody" + | "td" + | "template" + | "textarea" + | "tfoot" + | "th" + | "thead" + | "time" + | "title" + | "tr" + | "track" + | "u" + | "ul" + | "var" + | "video" + | "wbr" + ) +} + +fn is_self_closing(tag: &str) -> bool { + matches!( + tag, + "!DOCTYPE" + | "area" + | "base" + | "br" + | "col" + | "embed" + | "hr" + | "img" + | "input" + | "link" + | "meta" + | "param" + | "source" + | "track" + | "wbr" + ) +} diff --git a/depell/src/main.rs b/depell/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/depell/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/fooll-orig.txt b/fooll-orig.txt deleted file mode 100644 index 53d5539..0000000 --- a/fooll-orig.txt +++ /dev/null @@ -1,47 +0,0 @@ -sb0[]-[]: -0: iCall { func: 1 }:[Def: v3i fixed(p1i)] -1: iCInt { value: 0 }:[Def: v6i reg] -2: iCInt { value: 30 }:[Def: v5i reg] -3: iCInt { value: 100 }:[Def: v4i reg] -4: iLoop:[] -eb0[VReg(vreg = 6, class = Int), VReg(vreg = 6, class = Int), VReg(vreg = 6, class = Int)]-[Block(1)]: -sb1[VReg(vreg = 18, class = Int), VReg(vreg = 8, class = Int), VReg(vreg = 15, class = Int)]-[Block(0), Block(11)]: -5: iIf:[Use: v8i reg, Use: v5i reg] -eb1[]-[Block(2), Block(10)]: -sb2[]-[Block(1)]: -6: iBinOp { op: Add }:[Def: v19i reg, Use: v18i reg] -7: iCall { func: 2 }:[Def: v20i fixed(p1i), Use: v6i fixed(p2i), Use: v19i fixed(p3i), Use: v5i fixed(p4i)] -8: iIf:[Use: v20i reg, Use: v15i reg] -eb2[]-[Block(3), Block(4)]: -sb3[]-[Block(2)]: -9: iReturn:[Use: v6i fixed(p1i)] -eb3[]-[]: -sb4[]-[Block(2)]: -10: iIf:[Use: v19i reg, Use: v4i reg] -eb4[]-[Block(5), Block(9)]: -sb5[]-[Block(4)]: -11: iLoop:[] -eb5[VReg(vreg = 19, class = Int)]-[Block(6)]: -sb6[VReg(vreg = 36, class = Int)]-[Block(5), Block(7)]: -12: iIf:[Use: v4i reg, Use: v36i reg] -eb6[]-[Block(7), Block(8)]: -sb7[]-[Block(6)]: -13: iBinOp { op: Add }:[Def: v41i reg, Use: v36i reg] -14: iLoop:[] -eb7[VReg(vreg = 41, class = Int)]-[Block(6)]: -sb8[]-[Block(6)]: -15: iReturn:[Use: v15i fixed(p1i)] -eb8[]-[]: -sb9[]-[Block(4)]: -16: iRegion:[] -eb9[VReg(vreg = 6, class = Int), VReg(vreg = 19, class = Int), VReg(vreg = 15, class = Int)]-[Block(11)]: -sb10[]-[Block(1)]: -17: iBinOp { op: Add }:[Def: v16i reg, Use: v15i reg] -18: iBinOp { op: Add }:[Def: v14i reg, Use: v8i reg] -19: iRegion:[] -eb10[VReg(vreg = 14, class = Int), VReg(vreg = 18, class = Int), VReg(vreg = 16, class = Int)]-[Block(11)]: -sb11[VReg(vreg = 32, class = Int), VReg(vreg = 33, class = Int), VReg(vreg = 34, class = Int)]-[Block(10), Block(9)]: -20: iLoop:[] -eb11[VReg(vreg = 33, class = Int), VReg(vreg = 32, class = Int), VReg(vreg = 34, class = Int)]-[Block(1)]: - - diff --git a/fooll.txt b/fooll.txt deleted file mode 100644 index 4a27e0f..0000000 --- a/fooll.txt +++ /dev/null @@ -1,48 +0,0 @@ -sb0[]-[]: -0: iCall { func: 1 }:[Def: v3i fixed(p1i)] -1: iCInt { value: 0 }:[Def: v6i reg] -2: iCInt { value: 30 }:[Def: v5i reg] -3: iCInt { value: 100 }:[Def: v4i reg] -4: iLoop:[] -eb0[VReg(vreg = 4, class = Int), VReg(vreg = 6, class = Int), VReg(vreg = 6, class = Int), VReg(vreg = 6, class = Int)]-[Block(1)]: -sb1[VReg(vreg = 26, class = Int), VReg(vreg = 8, class = Int), VReg(vreg = 15, class = Int), VReg(vreg = 18, class = Int)]-[Block(0), Block(11)]: -5: iIf:[Use: v8i reg, Use: v5i reg] -eb1[]-[Block(2), Block(10)]: -sb2[]-[Block(1)]: -6: iBinOp { op: Add }:[Def: v19i reg, Use: v18i reg] -7: iCall { func: 2 }:[Def: v20i fixed(p1i), Use: v6i fixed(p2i), Use: v19i fixed(p3i), Use: v5i fixed(p4i)] -8: iIf:[Use: v20i reg, Use: v15i reg] -eb2[]-[Block(3), Block(4)]: -sb3[]-[Block(2)]: -9: iReturn:[Use: v6i fixed(p1i)] -eb3[]-[]: -sb4[]-[Block(2)]: -10: iIf:[Use: v19i reg, Use: v4i reg] -eb4[]-[Block(5), Block(9)]: -sb5[]-[Block(4)]: -11: iBinOp { op: Add }:[Def: v35i reg, Use: v26i reg] -12: iLoop:[] -eb5[VReg(vreg = 19, class = Int)]-[Block(6)]: -sb6[VReg(vreg = 38, class = Int)]-[Block(5), Block(7)]: -13: iIf:[Use: v35i reg, Use: v38i reg] -eb6[]-[Block(7), Block(8)]: -sb7[]-[Block(6)]: -14: iBinOp { op: Add }:[Def: v43i reg, Use: v38i reg] -15: iLoop:[] -eb7[VReg(vreg = 43, class = Int)]-[Block(6)]: -sb8[]-[Block(6)]: -16: iReturn:[Use: v15i fixed(p1i)] -eb8[]-[]: -sb9[]-[Block(4)]: -17: iRegion:[] -eb9[VReg(vreg = 6, class = Int), VReg(vreg = 19, class = Int), VReg(vreg = 15, class = Int)]-[Block(11)]: -sb10[]-[Block(1)]: -18: iBinOp { op: Add }:[Def: v16i reg, Use: v15i reg] -19: iBinOp { op: Add }:[Def: v14i reg, Use: v8i reg] -20: iRegion:[] -eb10[VReg(vreg = 14, class = Int), VReg(vreg = 18, class = Int), VReg(vreg = 16, class = Int)]-[Block(11)]: -sb11[VReg(vreg = 32, class = Int), VReg(vreg = 33, class = Int), VReg(vreg = 34, class = Int)]-[Block(10), Block(9)]: -21: iLoop:[] -eb11[VReg(vreg = 0, class = Int), VReg(vreg = 32, class = Int), VReg(vreg = 34, class = Int), VReg(vreg = 33, class = Int)]-[Block(1)]: - - diff --git a/hblang/Cargo.toml b/hblang/Cargo.toml index 284e397..9df928a 100644 --- a/hblang/Cargo.toml +++ b/hblang/Cargo.toml @@ -14,9 +14,6 @@ hbvm = { path = "../hbvm", features = ["nightly"] } log = { version = "0.4.22", features = ["release_max_level_error"] } regalloc2 = { git = "https://github.com/jakubDoka/regalloc2", branch = "reuse-allocations", features = [] } -[dev-dependencies] -env_logger = "0.11.5" - [features] default = ["std"] std = [] diff --git a/hblang/README.md b/hblang/README.md index c4ec734..2de607d 100644 --- a/hblang/README.md +++ b/hblang/README.md @@ -178,8 +178,7 @@ main := fn(): int { } pass := fn(t: ^Ty): int { - .{a, b} := *t - return a - b + return t.a - t.b } odher_pass := fn(t: Ty2): Ty2 { diff --git a/hblang/src/codegen.rs b/hblang/src/codegen.rs index 7a158ca..13f4a6b 100644 --- a/hblang/src/codegen.rs +++ b/hblang/src/codegen.rs @@ -2150,7 +2150,7 @@ impl Codegen { self.ci.emit(jala(ZERO, RET_ADDR, 0)); self.ci.regs.free(core::mem::take(&mut self.ci.ret_reg)); self.tys.ins.funcs[id as usize].code.append(&mut self.ci.code); - self.tys.ins.funcs[id as usize].relocs.append(&mut self.ci.relocs); + self.tys.ins.funcs[id as usize].relocs = self.ci.relocs.drain(..).collect(); self.pool.cis.push(core::mem::replace(&mut self.ci, prev_ci)); self.ct.vm.write_reg(reg::STACK_PTR, ct_stack_base); } diff --git a/hblang/src/fmt.rs b/hblang/src/fmt.rs new file mode 100644 index 0000000..5f85545 --- /dev/null +++ b/hblang/src/fmt.rs @@ -0,0 +1,442 @@ +use { + crate::{ + lexer::{self, TokenKind}, + parser::{self, CommentOr, CtorField, Expr, Poser, Radix, StructField}, + }, + alloc::string::String, + core::fmt, +}; + +pub fn minify(source: &mut str) -> Option<&str> { + fn needs_space(c: u8) -> bool { + matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | 127..) + } + + let mut writer = source.as_mut_ptr(); + let mut reader = &source[..]; + let mut prev_needs_whitecpace = false; + loop { + let mut token = lexer::Lexer::new(reader).next(); + match token.kind { + TokenKind::Eof => break, + TokenKind::CtIdent | TokenKind::Directive => token.start -= 1, + _ => {} + } + + let mut suffix = 0; + if token.kind == TokenKind::Comment && reader.as_bytes()[token.end as usize - 1] != b'/' { + token.end = token.start + reader[token.range()].trim_end().len() as u32; + suffix = b'\n'; + } + + let mut prefix = 0; + if prev_needs_whitecpace && needs_space(reader.as_bytes()[token.start as usize]) { + prefix = b' '; + } + + prev_needs_whitecpace = needs_space(reader.as_bytes()[token.end as usize - 1]); + let sstr = reader[token.start as usize..].as_ptr(); + reader = &reader[token.end as usize..]; + unsafe { + if prefix != 0 { + writer.write(prefix); + writer = writer.add(1); + } + writer.copy_from(sstr, token.range().len()); + writer = writer.add(token.range().len()); + if suffix != 0 { + writer.write(suffix); + writer = writer.add(1); + } + } + } + + None +} + +pub struct Formatter<'a> { + source: &'a str, + depth: usize, + disp_buff: String, +} + +impl<'a> Formatter<'a> { + pub fn new(source: &'a str) -> Self { + Self { source, depth: 0, disp_buff: Default::default() } + } + + fn fmt_list( + &mut self, + f: &mut F, + trailing: bool, + end: &str, + sep: &str, + list: &[T], + fmt: impl Fn(&mut Self, &T, &mut F) -> fmt::Result, + ) -> fmt::Result { + self.fmt_list_low(f, trailing, end, sep, list, |s, v, f| { + fmt(s, v, f)?; + Ok(true) + }) + } + + fn fmt_list_low( + &mut self, + f: &mut F, + trailing: bool, + end: &str, + sep: &str, + list: &[T], + fmt: impl Fn(&mut Self, &T, &mut F) -> Result, + ) -> fmt::Result { + if !trailing { + let mut first = true; + for expr in list { + if !core::mem::take(&mut first) { + write!(f, "{sep} ")?; + } + first = !fmt(self, expr, f)?; + } + return write!(f, "{end}"); + } + + writeln!(f)?; + self.depth += 1; + let res = (|| { + for (i, stmt) in list.iter().enumerate() { + for _ in 0..self.depth { + write!(f, "\t")?; + } + let add_sep = fmt(self, stmt, f)?; + if add_sep { + write!(f, "{sep}")?; + } + if let Some(expr) = list.get(i + 1) + && let Some(rest) = self.source.get(expr.posi() as usize..) + { + if insert_needed_semicolon(rest) { + write!(f, ";")?; + } + if preserve_newlines(&self.source[..expr.posi() as usize]) > 1 { + writeln!(f)?; + } + } + if add_sep { + writeln!(f)?; + } + } + Ok(()) + })(); + self.depth -= 1; + + for _ in 0..self.depth { + write!(f, "\t")?; + } + write!(f, "{end}")?; + res + } + + fn fmt_paren( + &mut self, + expr: &Expr, + f: &mut F, + cond: impl FnOnce(&Expr) -> bool, + ) -> fmt::Result { + if cond(expr) { + write!(f, "(")?; + self.fmt(expr, f)?; + write!(f, ")") + } else { + self.fmt(expr, f) + } + } + + pub fn fmt(&mut self, expr: &Expr, f: &mut F) -> fmt::Result { + macro_rules! impl_parenter { + ($($name:ident => $pat:pat,)*) => { + $( + let $name = |e: &Expr| matches!(e, $pat); + )* + }; + } + + impl_parenter! { + unary => Expr::BinOp { .. }, + postfix => Expr::UnOp { .. } | Expr::BinOp { .. }, + consecutive => Expr::UnOp { .. }, + } + + match *expr { + Expr::Ct { value, .. } => { + write!(f, "$: ")?; + self.fmt(value, f) + } + Expr::String { literal, .. } => write!(f, "{literal}"), + Expr::Comment { literal, .. } => write!(f, "{}", literal.trim_end()), + Expr::Mod { path, .. } => write!(f, "@use(\"{path}\")"), + Expr::Field { target, name: field, .. } => { + self.fmt_paren(target, f, postfix)?; + write!(f, ".{field}") + } + Expr::Directive { name, args, .. } => { + write!(f, "@{name}(")?; + self.fmt_list(f, false, ")", ",", args, Self::fmt) + } + Expr::Struct { fields, trailing_comma, packed, .. } => { + if packed { + write!(f, "packed ")?; + } + + write!(f, "struct {{")?; + self.fmt_list_low(f, trailing_comma, "}", ",", fields, |s, field, f| { + match field { + CommentOr::Or(StructField { name, ty, .. }) => { + write!(f, "{name}: ")?; + s.fmt(ty, f)? + } + CommentOr::Comment { literal, .. } => write!(f, "{literal}")?, + } + Ok(field.or().is_some()) + }) + } + Expr::Ctor { ty, fields, trailing_comma, .. } => { + if let Some(ty) = ty { + self.fmt_paren(ty, f, unary)?; + } + write!(f, ".{{")?; + self.fmt_list( + f, + trailing_comma, + "}", + ",", + fields, + |s: &mut Self, CtorField { name, value, .. }: &_, f| { + if matches!(value, Expr::Ident { name: n, .. } if name == n) { + write!(f, "{name}") + } else { + write!(f, "{name}: ")?; + s.fmt(value, f) + } + }, + ) + } + Expr::Tupl { ty, fields, trailing_comma, .. } => { + if let Some(ty) = ty { + self.fmt_paren(ty, f, unary)?; + } + write!(f, ".(")?; + self.fmt_list(f, trailing_comma, ")", ",", fields, Self::fmt) + } + Expr::Slice { item, size, .. } => { + write!(f, "[")?; + self.fmt(item, f)?; + if let Some(size) = size { + write!(f, "; ")?; + self.fmt(size, f)?; + } + write!(f, "]") + } + Expr::Index { base, index } => { + self.fmt(base, f)?; + write!(f, "[")?; + self.fmt(index, f)?; + write!(f, "]") + } + Expr::UnOp { op, val, .. } => { + write!(f, "{op}")?; + self.fmt_paren(val, f, unary) + } + Expr::Break { .. } => write!(f, "break"), + Expr::Continue { .. } => write!(f, "continue"), + Expr::If { cond, then, else_, .. } => { + write!(f, "if ")?; + self.fmt(cond, f)?; + write!(f, " ")?; + self.fmt_paren(then, f, consecutive)?; + if let Some(e) = else_ { + write!(f, " else ")?; + self.fmt(e, f)?; + } + Ok(()) + } + Expr::Loop { body, .. } => { + write!(f, "loop ")?; + self.fmt(body, f) + } + Expr::Closure { ret, body, args, .. } => { + write!(f, "fn(")?; + self.fmt_list(f, false, "", ",", args, |s, arg, f| { + if arg.is_ct { + write!(f, "$")?; + } + write!(f, "{}: ", arg.name)?; + s.fmt(&arg.ty, f) + })?; + write!(f, "): ")?; + self.fmt(ret, f)?; + write!(f, " ")?; + self.fmt_paren(body, f, consecutive)?; + Ok(()) + } + Expr::Call { func, args, trailing_comma } => { + self.fmt_paren(func, f, postfix)?; + write!(f, "(")?; + self.fmt_list(f, trailing_comma, ")", ",", args, Self::fmt) + } + Expr::Return { val: Some(val), .. } => { + write!(f, "return ")?; + self.fmt(val, f) + } + Expr::Return { val: None, .. } => write!(f, "return"), + Expr::Ident { name, is_ct: true, .. } => write!(f, "${name}"), + Expr::Ident { name, is_ct: false, .. } => write!(f, "{name}"), + Expr::Block { stmts, .. } => { + write!(f, "{{")?; + self.fmt_list(f, true, "}", "", stmts, Self::fmt) + } + Expr::Number { value, radix, .. } => match radix { + Radix::Decimal => write!(f, "{value}"), + Radix::Hex => write!(f, "{value:#X}"), + Radix::Octal => write!(f, "{value:#o}"), + Radix::Binary => write!(f, "{value:#b}"), + }, + Expr::Bool { value, .. } => write!(f, "{value}"), + Expr::Idk { .. } => write!(f, "idk"), + Expr::BinOp { + left, + op: TokenKind::Assign, + right: Expr::BinOp { left: lleft, op, right }, + } if { + let mut b = core::mem::take(&mut self.disp_buff); + self.fmt(lleft, &mut b)?; + let len = b.len(); + self.fmt(left, &mut b)?; + let (lleft, left) = b.split_at(len); + let res = lleft == left; + b.clear(); + self.disp_buff = b; + res + } => + { + self.fmt(left, f)?; + write!(f, " {op}= ")?; + self.fmt(right, f) + } + Expr::BinOp { right, op, left } => { + let pec_miss = |e: &Expr| { + matches!( + e, Expr::BinOp { op: lop, .. } if op.precedence() > lop.precedence() + ) + }; + + self.fmt_paren(left, f, pec_miss)?; + if let Some(mut prev) = self.source.get(..right.pos() as usize) { + prev = prev.trim_end(); + let estimate_bound = + prev.rfind(|c: char| c.is_ascii_whitespace()).map_or(prev.len(), |i| i + 1); + let exact_bound = lexer::Lexer::new(&prev[estimate_bound..]).last().start; + prev = &prev[..exact_bound as usize + estimate_bound]; + if preserve_newlines(prev) > 0 { + writeln!(f)?; + for _ in 0..self.depth + 1 { + write!(f, "\t")?; + } + write!(f, "{op} ")?; + } else { + write!(f, " {op} ")?; + } + } else { + write!(f, " {op} ")?; + } + self.fmt_paren(right, f, pec_miss) + } + } + } +} + +pub fn preserve_newlines(source: &str) -> usize { + source[source.trim_end().len()..].chars().filter(|&c| c == '\n').count() +} + +pub fn insert_needed_semicolon(source: &str) -> bool { + let kind = lexer::Lexer::new(source).next().kind; + kind.precedence().is_some() || matches!(kind, TokenKind::Ctor | TokenKind::Tupl) +} + +impl core::fmt::Display for parser::Ast { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for (i, expr) in self.exprs().iter().enumerate() { + Formatter::new(&self.file).fmt(expr, f)?; + if let Some(expr) = self.exprs().get(i + 1) + && let Some(rest) = self.file.get(expr.pos() as usize..) + { + if insert_needed_semicolon(rest) { + write!(f, ";")?; + } + + if preserve_newlines(&self.file[..expr.pos() as usize]) > 1 { + writeln!(f)?; + } + } + + if i + 1 != self.exprs().len() { + writeln!(f)?; + } + } + Ok(()) + } +} + +#[cfg(test)] +pub mod test { + use { + crate::parser::{self, StackAlloc}, + alloc::borrow::ToOwned, + std::{fmt::Write, string::String}, + }; + + pub fn format(ident: &str, input: &str) { + let ast = + parser::Ast::new(ident, input.to_owned(), &mut StackAlloc::default(), &|_, _| Ok(0)); + let mut output = String::new(); + write!(output, "{ast}").unwrap(); + + let input_path = format!("formatter_{ident}.expected"); + let output_path = format!("formatter_{ident}.actual"); + std::fs::write(&input_path, input).unwrap(); + std::fs::write(&output_path, output).unwrap(); + + let success = std::process::Command::new("diff") + .arg("-u") + .arg("--color") + .arg(&input_path) + .arg(&output_path) + .status() + .unwrap() + .success(); + std::fs::remove_file(&input_path).unwrap(); + std::fs::remove_file(&output_path).unwrap(); + assert!(success, "test failed"); + } + + macro_rules! test { + ($($name:ident => $input:expr;)*) => {$( + #[test] + fn $name() { + format(stringify!($name), $input); + } + )*}; + } + + test! { + comments => "// comment\n// comment\n\n// comment\n\n\ + /* comment */\n/* comment */\n\n/* comment */"; + some_ordinary_code => "loft := fn(): int return loft(1, 2, 3)"; + some_arg_per_line_code => "loft := fn(): int return loft(\ + \n\t1,\n\t2,\n\t3,\n)"; + some_ordinary_struct => "loft := fn(): int return loft.{a: 1, b: 2}"; + some_ordinary_fild_per_lin_struct => "loft := fn(): int return loft.{\ + \n\ta: 1,\n\tb: 2,\n}"; + code_block => "loft := fn(): int {\n\tloft()\n\treturn 1\n}"; + } +} diff --git a/hblang/src/fs.rs b/hblang/src/fs.rs index c2a4ab2..49a5d93 100644 --- a/hblang/src/fs.rs +++ b/hblang/src/fs.rs @@ -10,7 +10,7 @@ use { collections::VecDeque, eprintln, ffi::OsStr, - io, + io::{self, Write as _}, path::{Path, PathBuf}, string::ToString, sync::Mutex, @@ -68,35 +68,12 @@ impl Options { } } -pub fn format_to(ast: &parser::Ast, source: &str, out: &mut String) -> core::fmt::Result { - for (i, expr) in ast.exprs().iter().enumerate() { - parser::Formatter::new(&ast.file).fmt(expr, out)?; - if let Some(expr) = ast.exprs().get(i + 1) - && let Some(rest) = source.get(expr.pos() as usize..) - { - if parser::insert_needed_semicolon(rest) { - write!(out, ";")?; - } - if parser::preserve_newlines(&source[..expr.pos() as usize]) > 1 { - writeln!(out)?; - } - } - - if i + 1 != ast.exprs().len() { - writeln!(out)?; - } - } - - Ok(()) -} - pub fn run_compiler(root_file: &str, options: Options, out: &mut Vec) -> std::io::Result<()> { let parsed = parse_from_fs(options.extra_threads, root_file)?; fn format_ast(ast: parser::Ast) -> std::io::Result<()> { let mut output = String::new(); - let source = std::fs::read_to_string(&*ast.path)?; - format_to(&ast, &source, &mut output).unwrap(); + write!(output, "{ast}").unwrap(); std::fs::write(&*ast.path, output)?; Ok(()) } @@ -107,9 +84,7 @@ pub fn run_compiler(root_file: &str, options: Options, out: &mut Vec) -> std } } else if options.fmt_stdout { let ast = parsed.into_iter().next().unwrap(); - let source = std::fs::read_to_string(&*ast.path)?; - format_to(&ast, &source, unsafe { std::mem::transmute::<&mut Vec, &mut String>(out) }) - .unwrap(); + write!(out, "{ast}").unwrap(); } else { let mut codegen = codegen::Codegen::default(); codegen.files = parsed; diff --git a/hblang/src/lib.rs b/hblang/src/lib.rs index 39fcd29..403c8eb 100644 --- a/hblang/src/lib.rs +++ b/hblang/src/lib.rs @@ -61,6 +61,7 @@ macro_rules! run_tests { } pub mod codegen; +pub mod fmt; #[cfg(any(feature = "std", test))] pub mod fs; pub mod parser; @@ -1316,13 +1317,13 @@ fn test_parse_files(ident: &'static str, input: &'static str) -> Vec { } } -trait Poser { +pub trait Poser { fn posi(&self) -> Pos; } @@ -945,6 +946,7 @@ pub struct Display<'a> { source: &'a str, expr: &'a Expr<'a>, } + impl<'a> Display<'a> { pub fn new(source: &'a str, expr: &'a Expr<'a>) -> Self { Self { source, expr } @@ -957,315 +959,6 @@ impl core::fmt::Display for Display<'_> { } } -pub struct Formatter<'a> { - source: &'a str, - depth: usize, - disp_buff: String, -} - -impl<'a> Formatter<'a> { - pub fn new(source: &'a str) -> Self { - Self { source, depth: 0, disp_buff: Default::default() } - } - - fn fmt_list( - &mut self, - f: &mut F, - trailing: bool, - end: &str, - sep: &str, - list: &[T], - fmt: impl Fn(&mut Self, &T, &mut F) -> fmt::Result, - ) -> fmt::Result { - self.fmt_list_low(f, trailing, end, sep, list, |s, v, f| { - fmt(s, v, f)?; - Ok(true) - }) - } - - fn fmt_list_low( - &mut self, - f: &mut F, - trailing: bool, - end: &str, - sep: &str, - list: &[T], - fmt: impl Fn(&mut Self, &T, &mut F) -> Result, - ) -> fmt::Result { - if !trailing { - let mut first = true; - for expr in list { - if !core::mem::take(&mut first) { - write!(f, "{sep} ")?; - } - first = !fmt(self, expr, f)?; - } - return write!(f, "{end}"); - } - - writeln!(f)?; - self.depth += 1; - let res = (|| { - for (i, stmt) in list.iter().enumerate() { - for _ in 0..self.depth { - write!(f, "\t")?; - } - let add_sep = fmt(self, stmt, f)?; - if add_sep { - write!(f, "{sep}")?; - } - if let Some(expr) = list.get(i + 1) - && let Some(rest) = self.source.get(expr.posi() as usize..) - { - if insert_needed_semicolon(rest) { - write!(f, ";")?; - } - if preserve_newlines(&self.source[..expr.posi() as usize]) > 1 { - writeln!(f)?; - } - } - if add_sep { - writeln!(f)?; - } - } - Ok(()) - })(); - self.depth -= 1; - - for _ in 0..self.depth { - write!(f, "\t")?; - } - write!(f, "{end}")?; - res - } - - fn fmt_paren( - &mut self, - expr: &Expr, - f: &mut F, - cond: impl FnOnce(&Expr) -> bool, - ) -> fmt::Result { - if cond(expr) { - write!(f, "(")?; - self.fmt(expr, f)?; - write!(f, ")") - } else { - self.fmt(expr, f) - } - } - - pub fn fmt(&mut self, expr: &Expr, f: &mut F) -> fmt::Result { - macro_rules! impl_parenter { - ($($name:ident => $pat:pat,)*) => { - $( - let $name = |e: &Expr| matches!(e, $pat); - )* - }; - } - - impl_parenter! { - unary => Expr::BinOp { .. }, - postfix => Expr::UnOp { .. } | Expr::BinOp { .. }, - consecutive => Expr::UnOp { .. }, - } - - match *expr { - Expr::Ct { value, .. } => { - write!(f, "$: ")?; - self.fmt(value, f) - } - Expr::String { literal, .. } => write!(f, "{literal}"), - Expr::Comment { literal, .. } => write!(f, "{}", literal.trim_end()), - Expr::Mod { path, .. } => write!(f, "@use(\"{path}\")"), - Expr::Field { target, name: field, .. } => { - self.fmt_paren(target, f, postfix)?; - write!(f, ".{field}") - } - Expr::Directive { name, args, .. } => { - write!(f, "@{name}(")?; - self.fmt_list(f, false, ")", ",", args, Self::fmt) - } - Expr::Struct { fields, trailing_comma, packed, .. } => { - if packed { - write!(f, "packed ")?; - } - - write!(f, "struct {{")?; - self.fmt_list_low(f, trailing_comma, "}", ",", fields, |s, field, f| { - match field { - CommentOr::Or(StructField { name, ty, .. }) => { - write!(f, "{name}: ")?; - s.fmt(ty, f)? - } - CommentOr::Comment { literal, .. } => write!(f, "{literal}")?, - } - Ok(field.or().is_some()) - }) - } - Expr::Ctor { ty, fields, trailing_comma, .. } => { - if let Some(ty) = ty { - self.fmt_paren(ty, f, unary)?; - } - write!(f, ".{{")?; - self.fmt_list( - f, - trailing_comma, - "}", - ",", - fields, - |s: &mut Self, CtorField { name, value, .. }: &_, f| { - if matches!(value, Expr::Ident { name: n, .. } if name == n) { - write!(f, "{name}") - } else { - write!(f, "{name}: ")?; - s.fmt(value, f) - } - }, - ) - } - Expr::Tupl { ty, fields, trailing_comma, .. } => { - if let Some(ty) = ty { - self.fmt_paren(ty, f, unary)?; - } - write!(f, ".(")?; - self.fmt_list(f, trailing_comma, ")", ",", fields, Self::fmt) - } - Expr::Slice { item, size, .. } => { - write!(f, "[")?; - self.fmt(item, f)?; - if let Some(size) = size { - write!(f, "; ")?; - self.fmt(size, f)?; - } - write!(f, "]") - } - Expr::Index { base, index } => { - self.fmt(base, f)?; - write!(f, "[")?; - self.fmt(index, f)?; - write!(f, "]") - } - Expr::UnOp { op, val, .. } => { - write!(f, "{op}")?; - self.fmt_paren(val, f, unary) - } - Expr::Break { .. } => write!(f, "break"), - Expr::Continue { .. } => write!(f, "continue"), - Expr::If { cond, then, else_, .. } => { - write!(f, "if ")?; - self.fmt(cond, f)?; - write!(f, " ")?; - self.fmt_paren(then, f, consecutive)?; - if let Some(e) = else_ { - write!(f, " else ")?; - self.fmt(e, f)?; - } - Ok(()) - } - Expr::Loop { body, .. } => { - write!(f, "loop ")?; - self.fmt(body, f) - } - Expr::Closure { ret, body, args, .. } => { - write!(f, "fn(")?; - self.fmt_list(f, false, "", ",", args, |s, arg, f| { - if arg.is_ct { - write!(f, "$")?; - } - write!(f, "{}: ", arg.name)?; - s.fmt(&arg.ty, f) - })?; - write!(f, "): ")?; - self.fmt(ret, f)?; - write!(f, " ")?; - self.fmt_paren(body, f, consecutive)?; - Ok(()) - } - Expr::Call { func, args, trailing_comma } => { - self.fmt_paren(func, f, postfix)?; - write!(f, "(")?; - self.fmt_list(f, trailing_comma, ")", ",", args, Self::fmt) - } - Expr::Return { val: Some(val), .. } => { - write!(f, "return ")?; - self.fmt(val, f) - } - Expr::Return { val: None, .. } => write!(f, "return"), - Expr::Ident { name, is_ct: true, .. } => write!(f, "${name}"), - Expr::Ident { name, is_ct: false, .. } => write!(f, "{name}"), - Expr::Block { stmts, .. } => { - write!(f, "{{")?; - self.fmt_list(f, true, "}", "", stmts, Self::fmt) - } - Expr::Number { value, radix, .. } => match radix { - Radix::Decimal => write!(f, "{value}"), - Radix::Hex => write!(f, "{value:#X}"), - Radix::Octal => write!(f, "{value:#o}"), - Radix::Binary => write!(f, "{value:#b}"), - }, - Expr::Bool { value, .. } => write!(f, "{value}"), - Expr::Idk { .. } => write!(f, "idk"), - Expr::BinOp { - left, - op: TokenKind::Assign, - right: Expr::BinOp { left: lleft, op, right }, - } if { - let mut b = core::mem::take(&mut self.disp_buff); - self.fmt(lleft, &mut b)?; - let len = b.len(); - self.fmt(left, &mut b)?; - let (lleft, left) = b.split_at(len); - let res = lleft == left; - b.clear(); - self.disp_buff = b; - res - } => - { - self.fmt(left, f)?; - write!(f, " {op}= ")?; - self.fmt(right, f) - } - Expr::BinOp { right, op, left } => { - let pec_miss = |e: &Expr| { - matches!( - e, Expr::BinOp { op: lop, .. } if op.precedence() > lop.precedence() - ) - }; - - self.fmt_paren(left, f, pec_miss)?; - if let Some(mut prev) = self.source.get(..right.pos() as usize) { - prev = prev.trim_end(); - let estimate_bound = - prev.rfind(|c: char| c.is_ascii_whitespace()).map_or(prev.len(), |i| i + 1); - let exact_bound = lexer::Lexer::new(&prev[estimate_bound..]).last().start; - prev = &prev[..exact_bound as usize + estimate_bound]; - if preserve_newlines(prev) > 0 { - writeln!(f)?; - for _ in 0..self.depth + 1 { - write!(f, "\t")?; - } - write!(f, "{op} ")?; - } else { - write!(f, " {op} ")?; - } - } else { - write!(f, " {op} ")?; - } - self.fmt_paren(right, f, pec_miss) - } - } - } -} - -pub fn preserve_newlines(source: &str) -> usize { - source[source.trim_end().len()..].chars().filter(|&c| c == '\n').count() -} - -pub fn insert_needed_semicolon(source: &str) -> bool { - let kind = lexer::Lexer::new(source).next().kind; - kind.precedence().is_some() || matches!(kind, TokenKind::Ctor | TokenKind::Tupl) -} - #[repr(C)] pub struct AstInner { ref_count: AtomicUsize, @@ -1627,53 +1320,3 @@ impl Drop for ArenaChunk { } } } - -#[cfg(test)] -pub mod test { - use {crate::parser::StackAlloc, alloc::borrow::ToOwned, std::string::String}; - - pub fn format(ident: &str, input: &str) { - let ast = - super::Ast::new(ident, input.to_owned(), &mut StackAlloc::default(), &|_, _| Ok(0)); - let mut output = String::new(); - crate::fs::format_to(&ast, input, &mut output).unwrap(); - - let input_path = format!("formatter_{ident}.expected"); - let output_path = format!("formatter_{ident}.actual"); - std::fs::write(&input_path, input).unwrap(); - std::fs::write(&output_path, output).unwrap(); - - let success = std::process::Command::new("diff") - .arg("-u") - .arg("--color") - .arg(&input_path) - .arg(&output_path) - .status() - .unwrap() - .success(); - std::fs::remove_file(&input_path).unwrap(); - std::fs::remove_file(&output_path).unwrap(); - assert!(success, "test failed"); - } - - macro_rules! test { - ($($name:ident => $input:expr;)*) => {$( - #[test] - fn $name() { - format(stringify!($name), $input); - } - )*}; - } - - test! { - comments => "// comment\n// comment\n\n// comment\n\n\ - /* comment */\n/* comment */\n\n/* comment */"; - some_ordinary_code => "loft := fn(): int return loft(1, 2, 3)"; - some_arg_per_line_code => "loft := fn(): int return loft(\ - \n\t1,\n\t2,\n\t3,\n)"; - some_ordinary_struct => "loft := fn(): int return loft.{a: 1, b: 2}"; - some_ordinary_fild_per_lin_struct => "loft := fn(): int return loft.{\ - \n\ta: 1,\n\tb: 2,\n}"; - code_block => "loft := fn(): int {\n\tloft()\n\treturn 1\n}"; - } -} diff --git a/hblang/src/son.rs b/hblang/src/son.rs index d5cb0d0..7d2ee50 100644 --- a/hblang/src/son.rs +++ b/hblang/src/son.rs @@ -12,7 +12,7 @@ use { task, ty::{self}, vc::{BitSet, Vc}, - Func, HashMap, Offset, OffsetIter, Reloc, Sig, SymKey, TypedReloc, Types, + Func, HashMap, Offset, OffsetIter, Reloc, Sig, Size, SymKey, TypedReloc, Types, }, alloc::{borrow::ToOwned, string::String, vec::Vec}, core::{ @@ -20,7 +20,8 @@ use { cell::RefCell, convert::identity, fmt::{self, Debug, Display, Write}, - format_args as fa, mem, ops, + format_args as fa, mem, + ops::{self, Range}, }, hashbrown::hash_map, regalloc2::VReg, @@ -303,10 +304,17 @@ impl Nodes { } K::Stre { offset } => { let parent = self[target].inputs[2]; + let value = self[target].inputs[1]; if self[parent].kind == (K::Stre { offset }) && self[parent].outputs.len() == 1 { return Some(self.modify_input(parent, 1, self[target].inputs[1])); } + + if self[value].kind == K::Stck { + for str in self[value].outputs.clone() { + assert!(self[str].outputs.is_empty(), "TODO: this is lost cause"); + } + } } K::Load { offset } => { let parent = self[target].inputs[1]; @@ -381,26 +389,6 @@ impl Nodes { self.values.iter().enumerate().filter_map(|(i, s)| Some((i as _, s.as_ref().ok()?))) } - fn graphviz_low(&self, out: &mut String) -> core::fmt::Result { - use core::fmt::Write; - - for (i, node) in self.iter() { - let color = if self.is_cfg(i) { "yellow" } else { "white" }; - writeln!(out, "node{i}[label=\"{}\" color={color}]", node.kind)?; - for (j, &o) in node.outputs.iter().enumerate() { - let color = if self.is_cfg(i) && self.is_cfg(o) { "red" } else { "lightgray" }; - let index = self[o].inputs.iter().position(|&inp| i == inp).unwrap(); - let style = if index == 0 && !self.is_cfg(o) { "style=dotted" } else { "" }; - writeln!( - out, - "node{o} -> node{i}[color={color} taillabel={index} headlabel={j} {style}]", - )?; - } - } - - Ok(()) - } - #[allow(clippy::format_in_format_args)] fn basic_blocks_instr(&mut self, out: &mut String, node: Nid) -> core::fmt::Result { if self[node].kind != Kind::Loop && self[node].kind != Kind::Region { @@ -539,13 +527,6 @@ impl Nodes { log::info!("{out}"); } - #[allow(dead_code)] - fn graphviz(&self) { - let out = &mut String::new(); - _ = self.graphviz_low(out); - log::info!("{out}"); - } - fn is_cfg(&self, o: Nid) -> bool { self[o].kind.is_cfg() } @@ -882,6 +863,7 @@ struct ItemCtx { loops: Vec, vars: Vec, memories: Vec, + clobbered: Vec, ret_relocs: Vec, relocs: Vec, jump_relocs: Vec<(Nid, Reloc)>, @@ -960,53 +942,90 @@ pub struct Codegen { } impl Codegen { - fn mem_op( - &mut self, - mut region: Nid, - offset: Offset, - kind: Kind, - mut ty: ty::Id, - mut inps: Vc, - ) -> Nid { - region = self.ci.nodes.trace_mem(region); + fn graphviz_low(&self, out: &mut String) -> core::fmt::Result { + use core::fmt::Write; - let size = self.tys.size_of(ty); - let insert_start = self + for (i, node) in self.ci.nodes.iter() { + let color = if self.ci.nodes.is_cfg(i) { "yellow" } else { "white" }; + writeln!( + out, + "node{i}[label=\"{} {}\" color={color}]", + node.kind, + self.ty_display(node.ty) + )?; + for (j, &o) in node.outputs.iter().enumerate() { + let color = if self.ci.nodes.is_cfg(i) && self.ci.nodes.is_cfg(o) { + "red" + } else { + "lightgray" + }; + let index = self.ci.nodes[o].inputs.iter().position(|&inp| i == inp).unwrap(); + let style = + if index == 0 && !self.ci.nodes.is_cfg(o) { "style=dotted" } else { "" }; + writeln!( + out, + "node{o} -> node{i}[color={color} taillabel={index} headlabel={j} {style}]", + )?; + } + } + + Ok(()) + } + + #[allow(dead_code)] + fn graphviz(&self) { + let out = &mut String::new(); + _ = self.graphviz_low(out); + log::info!("{out}"); + } + + fn region_range(&self, region: Nid, offset: Offset, size: Size) -> Range { + let start = self .ci .memories .binary_search_by_key(&(region, offset), |k| (k.region, k.offset)) .unwrap_or_else(identity); - let insert_end = self + let end = self .ci .memories .binary_search_by(|k| (k.region, k.offset).cmp(&(region, offset + size))) .unwrap_or_else(identity); - for mk in &self.ci.memories[insert_start..insert_end] { + start..end + } + + fn mem_op( + &mut self, + mut region: Nid, + offset: Offset, + kind: Kind, + ty: ty::Id, + mut inps: Vc, + ) -> Nid { + region = self.ci.nodes.trace_mem(region); + let size = self.tys.size_of(ty); + let range = self.region_range(region, offset, size); + + for mk in &self.ci.memories[range.clone()] { debug_assert_eq!(mk.region, region); debug_assert!(mk.offset >= offset); debug_assert!(mk.offset < offset + size); inps.push(mk.node); } - if insert_start == insert_end { + if range.is_empty() { inps.push(region); } - if matches!(kind, Kind::Ptr { .. }) { - ty = self.tys.make_ptr(ty); - } - let (new_op, peeped) = self.ci.nodes.new_node_low(ty, kind, inps); - if !peeped && !matches!(kind, Kind::Ptr { .. }) { - for mk in &self.ci.memories[insert_start..insert_end] { + if !peeped { + for mk in &self.ci.memories[range.clone()] { self.ci.nodes.unlock(mk.node); } - self.ci.memories.splice( - insert_start..insert_end, - core::iter::once(MemKey { node: new_op, region, offset }), - ); + self.ci + .memories + .splice(range, core::iter::once(MemKey { node: new_op, region, offset })); self.ci.nodes.lock(new_op); } new_op @@ -1020,8 +1039,20 @@ impl Codegen { self.mem_op(region, offset, Kind::Load { offset }, ty, [VOID].into()) } - fn ptr_mem(&mut self, region: Nid, offset: Offset, ty: ty::Id) -> Nid { - self.mem_op(region, offset, Kind::Ptr { offset }, ty, [VOID].into()) + fn ptr_mem(&mut self, on: Nid, offset: Offset, ty: ty::Id, derefed: bool) -> Nid { + let offset = match self.ci.nodes[on].kind { + Kind::Ptr { offset } => offset, + _ => 0, + } + offset; + let region = self.ci.nodes.trace_mem(on); + if region != on { + self.ci.nodes.remove(on); + } + let n = self.ci.nodes.new_node_nop(ty, Kind::Ptr { offset }, [VOID, region]); + if derefed { + self.ci.nodes[n].offset = u32::MAX; + } + n } pub fn generate(&mut self) { @@ -1038,11 +1069,11 @@ impl Codegen { } } - fn expr(&mut self, expr: &Expr) -> Option { - self.expr_ctx(expr, Ctx::default()) + fn raw_expr(&mut self, expr: &Expr) -> Option { + self.raw_expr_ctx(expr, Ctx::default()) } - fn expr_ctx(&mut self, expr: &Expr, ctx: Ctx) -> Option { + fn raw_expr_ctx(&mut self, expr: &Expr, ctx: Ctx) -> Option { let msg = "i know nothing about this name, gal, which is vired \ because we parsed succesfully"; // ordered by complexity of the expression @@ -1092,7 +1123,7 @@ impl Codegen { None } Expr::Field { target, name, pos } => { - let vtarget = self.expr(target)?; + let vtarget = self.raw_expr(target)?; let tty = self.tof(vtarget); let ty::Kind::Struct(s) = self.tys.base_of(tty).unwrap_or(tty).expand() else { @@ -1107,7 +1138,7 @@ impl Codegen { return Some(NEVER); }; - let Some((ty, offset)) = OffsetIter::offset_of(&self.tys, s, name) else { + let Some((offset, ty)) = OffsetIter::offset_of(&self.tys, s, name) else { let field_list = self .tys .struct_fields(s) @@ -1126,24 +1157,25 @@ impl Codegen { return Some(NEVER); }; - Some(self.load_mem(vtarget, ty, offset)) + Some(self.ptr_mem(vtarget, offset, ty, true)) } Expr::UnOp { op: TokenKind::Band, val, .. } => { let ctx = Ctx { ty: ctx.ty.and_then(|ty| self.tys.base_of(ty)) }; - let mut val = self.expr_ctx(val, ctx)?; + let mut val = self.raw_expr_ctx(val, ctx)?; let ty = self.tof(val); - if !matches!(self.ci.nodes[val].kind, Kind::Stck) { + if !matches!(self.ci.nodes[self.ci.nodes.trace_mem(val)].kind, Kind::Stck) { let stck = self.ci.nodes.new_node_nop(ty, Kind::Stck, [VOID, MEM]); self.store_mem(stck, 0, val); val = stck; } - Some(self.ptr_mem(val, 0, ty)) + let ptr = self.tys.make_ptr(ty); + Some(self.ptr_mem(val, 0, ptr, false)) } Expr::UnOp { op: TokenKind::Mul, val, pos } => { let ctx = Ctx { ty: ctx.ty.map(|ty| self.tys.make_ptr(ty)) }; - let val = self.expr_ctx(val, ctx)?; + let val = self.raw_expr_ctx(val, ctx)?; let Some(base) = self.get_load_type(val) else { self.report( pos, @@ -1151,7 +1183,7 @@ impl Codegen { ); return Some(NEVER); }; - Some(self.load_mem(val, 0, base)) + Some(self.ptr_mem(val, 0, base, true)) } Expr::UnOp { pos, op: op @ TokenKind::Sub, val } => { let val = self.expr_ctx(val, ctx)?; @@ -1261,7 +1293,7 @@ impl Codegen { if self.tys.size_of(ty) == 0 { continue; } - let mut value = self.expr_ctx(arg, Ctx::default().with_ty(ty))?; + let value = self.expr_ctx(arg, Ctx::default().with_ty(ty))?; _ = self.assert_ty( arg.pos(), self.tof(value), @@ -1269,19 +1301,24 @@ impl Codegen { true, fa!("argument {}", carg.name), ); - if ty.is_pointer() { - value = self.ci.nodes.trace_mem(value); - value = self - .ci - .memories - .binary_search_by_key(&(value, 0), |k| (k.region, k.offset)) - .map_or(value, |i| self.ci.memories[i].node); - // mark the read as clobbed since function can store - self.ci.nodes[value].offset = u32::MAX; + if let Some(base) = self.tys.base_of(ty) { + let Kind::Ptr { offset } = self.ci.nodes[value].kind else { + unreachable!() + }; + let reg = self.ci.nodes.trace_mem(value); + let size = self.tys.size_of(base); + for mk in &self.ci.memories[self.region_range(reg, offset, size)] { + self.ci.nodes[mk.node].offset = u32::MAX; + self.ci.clobbered.push(mk.node); + } } inps.push(value); } self.ci.ctrl = self.ci.nodes.new_node(sig.ret, Kind::Call { func }, inps); + for c in self.ci.clobbered.drain(..) { + self.ci.nodes[self.ci.ctrl].inputs.push(c); + self.ci.nodes[c].outputs.push(self.ci.ctrl); + } Some(self.ci.ctrl) } @@ -1559,6 +1596,22 @@ impl Codegen { } } + fn expr_ctx(&mut self, expr: &Expr, ctx: Ctx) -> Option { + let n = self.raw_expr_ctx(expr, ctx)?; + if let Kind::Ptr { offset } = self.ci.nodes[n].kind + && self.ci.nodes[n].offset == u32::MAX + { + let r = Some(self.load_mem(n, offset, self.tof(n))); + self.ci.nodes.remove(n); + return r; + } + Some(n) + } + + fn expr(&mut self, expr: &Expr) -> Option { + self.expr_ctx(expr, Default::default()) + } + fn jump_to(&mut self, pos: Pos, id: usize) -> Option { let Some(mut loob) = self.ci.loops.last_mut() else { self.report(pos, "break outside a loop"); @@ -1713,7 +1766,7 @@ impl Codegen { } if self.errors.borrow().is_empty() { - self.ci.nodes.graphviz(); + self.graphviz(); self.gcm(); #[cfg(debug_assertions)] @@ -1727,7 +1780,7 @@ impl Codegen { } //self.ci.nodes.basic_blocks(); - self.ci.nodes.graphviz(); + self.graphviz(); let mut stack_size = 0; '_compute_stack: { @@ -1941,10 +1994,12 @@ impl Codegen { debug_assert_eq!(size, 8, "TODO"); let (base, offset) = match func.nodes[region].kind { Kind::Stck => (reg::STACK_PTR, func.nodes[region].offset + offset), + Kind::Arg { .. } => { + (atr(allocs[1]), func.nodes[region].offset + offset) + } k => unreachable!("{k:?}"), }; - let &[dst] = allocs else { unreachable!() }; - self.ci.emit(instrs::ld(atr(dst), base, offset as _, size as _)); + self.ci.emit(instrs::ld(atr(allocs[0]), base, offset as _, size as _)); } Kind::Stre { offset } => { let region = func.nodes.trace_mem(node.inputs[2]); @@ -2484,15 +2539,12 @@ impl<'a> Function<'a> { } } } - Kind::Phi | Kind::Arg { .. } | Kind::Mem => {} - Kind::Stck => { - let ops = vec![self.drg(nid)]; - self.add_instr(nid, ops); - } + Kind::Stck | Kind::Phi | Kind::Arg { .. } | Kind::Mem => {} Kind::Ptr { .. } => { let region = self.nodes.trace_mem(node.inputs[1]); let ops = match self.nodes[region].kind { Kind::Stck => vec![self.drg(nid)], + Kind::Arg { .. } => vec![self.drg(nid), self.urg(region)], k => unreachable!("{k:?}"), }; self.add_instr(nid, ops); @@ -2501,6 +2553,7 @@ impl<'a> Function<'a> { let region = self.nodes.trace_mem(node.inputs[1]); let ops = match self.nodes[region].kind { Kind::Stck => vec![self.drg(nid)], + Kind::Arg { .. } => vec![self.drg(nid), self.urg(region)], k => unreachable!("{k:?}"), }; self.add_instr(nid, ops); @@ -2833,7 +2886,8 @@ mod tests { }; fn generate(ident: &'static str, input: &'static str, output: &mut String) { - _ = env_logger::builder().is_test(true).try_init(); + _ = log::set_logger(&crate::fs::Logger); + log::set_max_level(log::LevelFilter::Info); let mut codegen = super::Codegen { files: crate::test_parse_files(ident, input), ..Default::default() }; diff --git a/main.hb b/main.hb deleted file mode 100644 index 0506b7b..0000000 --- a/main.hb +++ /dev/null @@ -1,3 +0,0 @@ -main := fn(): int { - return 0; -} diff --git a/pointers.hb b/pointers.hb new file mode 100644 index 0000000..eafa8c2 --- /dev/null +++ b/pointers.hb @@ -0,0 +1,51 @@ +Point := struct { + x: int, + y: int, +} + +Rect := struct { + min: Point, + max: Point, +} + +main := fn(): void { + rect := Rect.(.(0, 0), .(0, 0)) + // eliminates initila 0 + rect.min.x = 1 + // here as well + rect.min.y = 2 + // eliminates previous 2 lines, intermidiate stack slot is created, and stores are + // delegated to the rect + rect.min = .(3, 4) + + // encompasses the previous two loads + ptr := &rect.min + // pointer escapes to a function -> rect.min now has unknown values + clobber(ptr) + + // this can not be folded but load can be reused + rect.max.x = rect.min.x * rect.min.x + + // this should invalidate the previous loads + clobber(ptr) + // now all stores are clobbered + clobber(&rect.max) + + // conslusion: pointers fundamentally dont do anything and are not registered anywhere, + // thay are just bound to the base memory and when you interact with them (store, load) + // they modity the memory state, they are literally a view trought which we look at the + // memory and remotely modify it, so in summary, pointers are not bound to a specific load + // or store, but they can invalidate them. + // + // The fact pointers are bound to the base memory also makes it easy to tell how aliasing works + // for the pointer, we prohibit pointer arithmetic on these pointers, instead this is delegated + // to special pointer type that can only be created when compiler can prove ist safe or explicitly + // with a directive + + return +} + +clobber := fn(p: ^Point): void { + *p = .(5, 6) + return +}