diff --git a/.github/logo.png b/.github/logo.png
deleted file mode 100644
index 46e7e472cc5655f578d3cd2e873d230107999375..0000000000000000000000000000000000000000
Binary files a/.github/logo.png and /dev/null differ
diff --git a/.gitignore b/.gitignore
index 4143657114f43375f7bb260327bd203355dcd140..6c08354510e655ecc6f55e468a65aba4c006d11e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,10 @@
-target
-*.js
+# If you have OS-specific or editor-specific files to ignore,
+# such as *.swp or .DS_Store, put those in your global
+# ~/.gitignore and put this in your ~/.gitconfig:
+#
+# [core]
+# excludesfile = ~/.gitignore
+#
+
+zig-out
+zig-cache
diff --git a/Cargo.lock b/Cargo.lock
deleted file mode 100644
index bc884459d74ada74428241bd211efc2c04a4dda8..0000000000000000000000000000000000000000
--- a/Cargo.lock
+++ /dev/null
@@ -1,759 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "addr2line"
-version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
-dependencies = [
- "gimli",
-]
-
-[[package]]
-name = "adler"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
-
-[[package]]
-name = "anstream"
-version = "0.6.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6"
-dependencies = [
- "anstyle",
- "anstyle-parse",
- "anstyle-query",
- "anstyle-wincon",
- "colorchoice",
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
-
-[[package]]
-name = "anstyle-parse"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
-dependencies = [
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle-query"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
-dependencies = [
- "windows-sys",
-]
-
-[[package]]
-name = "anstyle-wincon"
-version = "3.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
-dependencies = [
- "anstyle",
- "windows-sys",
-]
-
-[[package]]
-name = "ast"
-version = "0.1.0"
-dependencies = [
- "log",
- "miette",
- "num",
- "thiserror",
- "tokenizer",
-]
-
-[[package]]
-name = "autocfg"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
-
-[[package]]
-name = "backtrace"
-version = "0.3.69"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
-dependencies = [
- "addr2line",
- "cc",
- "cfg-if",
- "libc",
- "miniz_oxide",
- "object",
- "rustc-demangle",
-]
-
-[[package]]
-name = "backtrace-ext"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50"
-dependencies = [
- "backtrace",
-]
-
-[[package]]
-name = "bitflags"
-version = "2.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
-
-[[package]]
-name = "cc"
-version = "1.0.83"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "clap"
-version = "4.4.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33e92c5c1a78c62968ec57dbc2440366a2d6e5a23faf829970ff1585dc6b18e2"
-dependencies = [
- "clap_builder",
- "clap_derive",
-]
-
-[[package]]
-name = "clap_builder"
-version = "4.4.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4323769dc8a61e2c39ad7dc26f6f2800524691a44d74fe3d1071a5c24db6370"
-dependencies = [
- "anstream",
- "anstyle",
- "clap_lex",
- "strsim",
-]
-
-[[package]]
-name = "clap_derive"
-version = "4.4.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
-dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "clap_lex"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
-
-[[package]]
-name = "cli"
-version = "0.1.0"
-dependencies = [
- "ast",
- "clap",
- "js_ast",
- "js_printer",
- "log",
- "miette",
- "parser",
- "sema",
- "thiserror",
- "tokenizer",
-]
-
-[[package]]
-name = "colorchoice"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
-
-[[package]]
-name = "errno"
-version = "0.3.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
-dependencies = [
- "libc",
- "windows-sys",
-]
-
-[[package]]
-name = "form_urlencoded"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
-dependencies = [
- "percent-encoding",
-]
-
-[[package]]
-name = "gimli"
-version = "0.28.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
-
-[[package]]
-name = "heck"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
-
-[[package]]
-name = "hermit-abi"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
-
-[[package]]
-name = "idna"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
-dependencies = [
- "unicode-bidi",
- "unicode-normalization",
-]
-
-[[package]]
-name = "is-terminal"
-version = "0.4.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455"
-dependencies = [
- "hermit-abi",
- "rustix",
- "windows-sys",
-]
-
-[[package]]
-name = "is_ci"
-version = "1.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
-
-[[package]]
-name = "js_ast"
-version = "0.1.0"
-dependencies = [
- "ast",
- "log",
- "miette",
- "num",
- "thiserror",
-]
-
-[[package]]
-name = "js_printer"
-version = "0.1.0"
-dependencies = [
- "js_ast",
- "miette",
- "num",
- "thiserror",
-]
-
-[[package]]
-name = "libc"
-version = "0.2.152"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
-
-[[package]]
-name = "linux-raw-sys"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
-
-[[package]]
-name = "log"
-version = "0.0.0"
-dependencies = [
- "miette",
- "thiserror",
- "url",
-]
-
-[[package]]
-name = "memchr"
-version = "2.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
-
-[[package]]
-name = "miette"
-version = "5.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e"
-dependencies = [
- "backtrace",
- "backtrace-ext",
- "is-terminal",
- "miette-derive",
- "once_cell",
- "owo-colors",
- "supports-color",
- "supports-hyperlinks",
- "supports-unicode",
- "terminal_size",
- "textwrap",
- "thiserror",
- "unicode-width",
-]
-
-[[package]]
-name = "miette-derive"
-version = "5.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "miniz_oxide"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
-dependencies = [
- "adler",
-]
-
-[[package]]
-name = "num"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
-dependencies = [
- "num-bigint",
- "num-complex",
- "num-integer",
- "num-iter",
- "num-rational",
- "num-traits",
-]
-
-[[package]]
-name = "num-bigint"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
-dependencies = [
- "autocfg",
- "num-integer",
- "num-traits",
-]
-
-[[package]]
-name = "num-complex"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
-dependencies = [
- "num-traits",
-]
-
-[[package]]
-name = "num-integer"
-version = "0.1.45"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
-dependencies = [
- "autocfg",
- "num-traits",
-]
-
-[[package]]
-name = "num-iter"
-version = "0.1.43"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
-dependencies = [
- "autocfg",
- "num-integer",
- "num-traits",
-]
-
-[[package]]
-name = "num-rational"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
-dependencies = [
- "autocfg",
- "num-bigint",
- "num-integer",
- "num-traits",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "object"
-version = "0.32.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "once_cell"
-version = "1.19.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
-
-[[package]]
-name = "owo-colors"
-version = "3.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
-
-[[package]]
-name = "parser"
-version = "0.1.0"
-dependencies = [
- "ast",
- "log",
- "miette",
- "num",
- "thiserror",
- "tokenizer",
-]
-
-[[package]]
-name = "percent-encoding"
-version = "2.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.76"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "rustc-demangle"
-version = "0.1.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
-
-[[package]]
-name = "rustix"
-version = "0.38.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
-dependencies = [
- "bitflags",
- "errno",
- "libc",
- "linux-raw-sys",
- "windows-sys",
-]
-
-[[package]]
-name = "sema"
-version = "0.1.0"
-dependencies = [
- "ast",
- "miette",
- "num",
- "thiserror",
-]
-
-[[package]]
-name = "smawk"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
-
-[[package]]
-name = "strsim"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
-
-[[package]]
-name = "supports-color"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89"
-dependencies = [
- "is-terminal",
- "is_ci",
-]
-
-[[package]]
-name = "supports-hyperlinks"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f84231692eb0d4d41e4cdd0cabfdd2e6cd9e255e65f80c9aa7c98dd502b4233d"
-dependencies = [
- "is-terminal",
-]
-
-[[package]]
-name = "supports-unicode"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b6c2cb240ab5dd21ed4906895ee23fe5a48acdbd15a3ce388e7b62a9b66baf7"
-dependencies = [
- "is-terminal",
-]
-
-[[package]]
-name = "syn"
-version = "2.0.48"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "terminal_size"
-version = "0.1.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
-dependencies = [
- "libc",
- "winapi",
-]
-
-[[package]]
-name = "textwrap"
-version = "0.15.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d"
-dependencies = [
- "smawk",
- "unicode-linebreak",
- "unicode-width",
-]
-
-[[package]]
-name = "thiserror"
-version = "1.0.56"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
-dependencies = [
- "thiserror-impl",
-]
-
-[[package]]
-name = "thiserror-impl"
-version = "1.0.56"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "tinyvec"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
-dependencies = [
- "tinyvec_macros",
-]
-
-[[package]]
-name = "tinyvec_macros"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
-
-[[package]]
-name = "tokenizer"
-version = "0.1.0"
-dependencies = [
- "log",
- "miette",
- "thiserror",
-]
-
-[[package]]
-name = "unicode-bidi"
-version = "0.3.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
-
-[[package]]
-name = "unicode-linebreak"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
-
-[[package]]
-name = "unicode-normalization"
-version = "0.1.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
-dependencies = [
- "tinyvec",
-]
-
-[[package]]
-name = "unicode-width"
-version = "0.1.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
-
-[[package]]
-name = "url"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
-dependencies = [
- "form_urlencoded",
- "idna",
- "percent-encoding",
-]
-
-[[package]]
-name = "utf8parse"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
-
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-
-[[package]]
-name = "windows-sys"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
-dependencies = [
- "windows-targets",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
-dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
-]
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
diff --git a/Cargo.toml b/Cargo.toml
deleted file mode 100644
index 51b88c742a378171aa0093edefae5b956af2eb22..0000000000000000000000000000000000000000
--- a/Cargo.toml
+++ /dev/null
@@ -1,26 +0,0 @@
-[workspace.package]
-version = "0.1.0"
-edition = "2021"
-license = "MIT"
-authors = ["Lino Le Van", "Dave Caruso"]
-repository = "https://github.com/lino-levan/blight"
-
-[workspace]
-resolver = "2"
-members = [
- "crates/ast",
- "crates/cli",
- "crates/js_ast",
- "crates/js_printer",
- "crates/log",
- "crates/parser",
- "crates/sema",
- "crates/tokenizer",
-]
-
-[workspace.dependencies]
-clap = { version = "4.4.12", features = ["derive"] }
-miette = { version = "5.10.0", features = ["fancy"] }
-thiserror = "1.0.53"
-num = "0.4.1"
-url = "2.2.2"
diff --git a/README.md b/README.md
index 25323d8ec84c7b31e32fb7e35de3bc157de8d9de..753fb38ee29536347d8cc9a7a7bea9b702706c0e 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,79 @@
-
+# Compiler
-# Blight
+This language is a mix of features from JavaScript, Rust, and Zig. Some notable features carried over from each include:
-Blight sure is a programming language.
+- **Rust** - Expressive control flow that is easy to write.
+- **Zig** - Execute any code at compile time, everything is an expression.
+- **JavaScript** - Garbage collected values with a versitile type system.
-## Developing
+## Standard Library
-Compiling a file:
+Currently, there is no standard library. One will be added later.
-```bash
-cargo run compile --stdout ./examples/basics/fib.blight
+## Hello World
+
+```
+let print = @js("console").log;
+print("Hello, world!");
+```
+
+The `print` function is implemented by getting an external reference to the `console` JavaScript global, and then retriving the `log` property from it.
+
+## Primative Data Types
+
+| name | ts equivilent | description |
+| ---------- | --------------------- | ---------------------------------------------- |
+| `int` | `number` | a number in signed 32-bit range |
+| `float` | `number` | a 64-bit floating point number |
+| `bool` | `boolean` | either `true` or `false` |
+| `string` | `string` | a sequence of text characters |
+| `void` | `undefined` or `null` | detectable lack of a value |
+| `noreturn` | `never` | control flow prevents this value from existing |
+
+> **note**: `bigint` is planned but not added
+
+## Variables
+
+There are two types of variables, `let` and `var`.
+
+`let` bindings cannot be overridden, and should be thought of as aliases of the expression within. Referring to these identifiers is equivilent to inlining it's where it was used, aside from side effect duplication.
+
+```
+let hello: int = 1;
+let world = hello + 2;
+let hello = hello * world;
+
+@js("console").log(hello);
```
-Running the compiler on a file:
+`var` bindings create mutable memory locations at runtime. This means they can be re-assigned to in arbitrary ways.
-```bash
-cargo run run ./examples/basics/fib.blight
+```
+var message = "first message";
+
+if @js("Math").random() > 0.5 {
+ message = "second message"
+}
+
+@js("console").log(message);
+```
+
+`let` is preferred whenever possible, as it will lead to more readable code, and the compiler is able to optimize it better. The above can be rewritten as:
+
+```
+let message = if @js("Math").random() > 0.5 {
+ "second message"
+} else {
+ "first message"
+};
+
+@js("console").log(message);
+```
+
+The initializer for a `let` and `var` are always required, but the type annotations can be omitted when the data type can be inferred. For `let`, this is always the case. `var`, being runtime-mutable memory, only supports runtime representable data types. This means for numerical literals, an explicit type annotation is needed to determine weather to store an integer or a floating point number.
+
+```
+var a = 5; // error
+var b: int = 5; // ok
+var c: float = 5; // ok
```
diff --git a/build.zig b/build.zig
new file mode 100644
index 0000000000000000000000000000000000000000..4901620fb6c1ec93c4316653b2689d44456b7adc
--- /dev/null
+++ b/build.zig
@@ -0,0 +1,96 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) void {
+ b.reference_trace = 20;
+
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const dep_opts = .{ .target = target, .optimize = optimize };
+
+ const zg = b.dependency("zg", dep_opts);
+ const tracer = b.dependency("tracer", dep_opts);
+ // const ada = b.dependency("ada", dep_opts);
+
+ const exe = b.addExecutable(.{
+ .name = "compiler",
+ .root_source_file = b.path("src/compiler/main.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+ b.installArtifact(exe);
+
+ exe.root_module.addImport("zg:PropsData", zg.module("PropsData"));
+ exe.root_module.addImport("tracer", tracer.module("tracer"));
+ // if (!target.result.isWasm()) {
+ // exe.root_module.addImport("ada", ada.module("ada"));
+ // }
+
+ {
+ const run_cmd = b.addRunArtifact(exe);
+ run_cmd.step.dependOn(b.getInstallStep());
+ if (b.args) |args| {
+ run_cmd.addArgs(args);
+ }
+ const run_step = b.step("compiler", "Run compiler CLI");
+ run_step.dependOn(&run_cmd.step);
+ }
+
+ // const wasm_api = b.addExecutable(.{
+ // .name = "compiler",
+ // .root_source_file = b.path("src/wasm_api.zig"),
+ // .target = b.resolveTargetQuery(.{
+ // .cpu_arch = .wasm32,
+ // .os_tag = .freestanding,
+ // }),
+ // .optimize = optimize,
+ // });
+ // wasm_api.root_module.export_symbol_names = &[_][]const u8{
+ // "iterateTokens",
+ // };
+ // wasm_api.entry = .disabled;
+ // const wasm_step = b.step("wasm", "Build wasm api");
+ // b.installArtifact(wasm_api);
+ // wasm_step.dependOn(&wasm_api.step);
+ // wasm_api.root_module.addImport("zg:PropsData", zg.module("PropsData"));
+
+ const release = b.step("release", "Package releases");
+ inline for (.{
+ b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl }),
+ b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .musl }),
+ b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .macos }),
+ b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .macos }),
+ b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .windows }),
+ b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .windows }),
+ }) |cross_target| {
+ const name = std.fmt.allocPrint(
+ b.allocator,
+ "{s}-{s}",
+ .{
+ @tagName(cross_target.result.cpu.arch),
+ @tagName(cross_target.result.os.tag),
+ },
+ ) catch @panic("OOM");
+
+ const cross_exe = b.addExecutable(.{
+ .name = "compiler",
+ .root_source_file = b.path("src/compiler/main.zig"),
+ .target = cross_target,
+ .optimize = .ReleaseSafe,
+ .strip = true,
+ });
+ cross_exe.root_module.addImport("zg:PropsData", zg.module("PropsData"));
+ cross_exe.root_module.addImport("tracer", tracer.module("tracer"));
+
+ release.dependOn(&b.addInstallFile(
+ cross_exe.getEmittedBin(),
+ b.pathJoin(&.{
+ name,
+ std.mem.concat(b.allocator, u8, &.{
+ "compiler",
+ cross_target.result.exeFileExt(),
+ }) catch @panic("OOM"),
+ }),
+ ).step);
+ }
+}
diff --git a/build.zig.zon b/build.zig.zon
new file mode 100644
index 0000000000000000000000000000000000000000..3c75338bb1c8bba9e1d92569fc1c914bd19e7d8e
--- /dev/null
+++ b/build.zig.zon
@@ -0,0 +1,22 @@
+.{
+ .name = "",
+ .version = "0.0.0",
+
+ .minimum_zig_version = "0.12.0",
+
+ .dependencies = .{
+ .zg = .{
+ .url = "https://codeberg.org/dude_the_builder/zg/archive/16735685fcc3410de361ba3411788ad1fb4fe188.tar.gz",
+ .hash = "1220fe9ac5cdb41833d327a78745614e67d472469f8666567bd8cf9f5847a52b1c51",
+ },
+ .tracer = .{
+ .url = "https://github.com/paperdave/zig-tracer/archive/d4c2eff040e9e69b9f08d2175b315f3055ad7ba3.tar.gz",
+ .hash = "12202d60766a46ceb4074ce5651a58a11f2f6b32927476a55c6889805073e8e3d261",
+ },
+ },
+ .paths = .{
+ "build.zig",
+ "build.zig.zon",
+ "src",
+ },
+}
diff --git a/crates/ast/Cargo.toml b/crates/ast/Cargo.toml
deleted file mode 100644
index fe49e948853cee5c92a9a0ca399f13ed8b2ec4ed..0000000000000000000000000000000000000000
--- a/crates/ast/Cargo.toml
+++ /dev/null
@@ -1,15 +0,0 @@
-[package]
-name = "ast"
-version = "0.1.0"
-edition = "2021"
-
-[lib]
-path = "./lib.rs"
-
-[dependencies]
-miette.workspace = true
-thiserror.workspace = true
-num.workspace = true
-
-tokenizer = { path = "../tokenizer" } # Do not depend on the tokenizer
-log = { path = "../log" }
diff --git a/crates/ast/ast.rs b/crates/ast/ast.rs
deleted file mode 100644
index 6a30596e334101be04882142df1432fa7ff1795c..0000000000000000000000000000000000000000
--- a/crates/ast/ast.rs
+++ /dev/null
@@ -1,211 +0,0 @@
-use log::Range;
-
-use crate::types::{Literal, Type};
-use crate::{BinaryOperator, UnaryOperator};
-
-#[derive(Debug, Clone)]
-pub enum BuiltinId {
- Raw,
-}
-
-#[derive(Debug, Clone)]
-pub struct Module {
- pub comment: Option,
- pub statements: Vec,
-}
-
-#[derive(Debug, Clone)]
-pub struct Block {
- pub statements: Vec,
- pub has_implicit_return: bool,
-}
-
-#[derive(Debug, Clone)]
-pub struct Param {
- pub name: String,
- pub param_type: Type,
-}
-
-#[derive(Debug, Clone)]
-
-pub struct VarDecl {
- pub name: String,
- pub data_type: Type,
- pub init: Expr,
- pub is_constant: bool,
- pub is_pub: bool,
- pub range: Range,
- pub comment: Option,
-}
-
-#[derive(Debug, Clone)]
-pub struct FnDecl {
- pub name: String,
- pub return_type: Type,
- pub params: Vec,
- pub body: Expr,
- pub is_pub: bool,
- pub range: Range,
- pub comment: Option,
-}
-
-#[derive(Debug, Clone)]
-pub struct Expr {
- pub data: ExprData,
- pub range: Range,
- pub comment: Option,
-}
-
-impl Expr {
- pub fn new(data: ExprData, range: Range, comment: Option) -> Self {
- Self {
- data,
- range,
- comment,
- }
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct DocComment {
- pub data: String,
- pub range: Range,
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct Comment {
- pub data: String,
-}
-
-impl Comment {
- pub fn format(&self) -> String {
- let mut lines = self.data.lines();
- let first_line = lines.next().unwrap();
- let mut result = format!("// {}", first_line);
- for line in lines {
- result.push_str(&format!("\n// {}", line));
- }
- result
- }
-
- pub fn to_doc_comment(self, range: Range) -> DocComment {
- DocComment {
- data: self.data,
- range,
- }
- }
-}
-
-impl Into for DocComment {
- fn into(self) -> Comment {
- Comment { data: self.data }
- }
-}
-
-impl DocComment {
- pub fn format(&self) -> String {
- let mut lines = self.data.lines();
- let first_line = lines.next().unwrap();
- let mut result = format!("/// {}", first_line);
- for line in lines {
- result.push_str(&format!("\n/// {}", line));
- }
- result
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct BlockWithRange {
- pub block: Block,
- pub range: Range,
-}
-
-#[derive(Debug, Clone)]
-pub struct If {
- pub condition: Box,
- pub yes: BlockWithRange,
- pub no: Option,
-}
-
-#[derive(Debug, Clone)]
-pub enum BlockOrIf {
- Block(BlockWithRange),
- If(Box),
-}
-
-#[derive(Debug, Clone)]
-pub enum ExprData {
- Block(Block),
- Binary {
- op: BinaryOperator,
- left: Box,
- right: Box,
- },
- Unary {
- op: UnaryOperator,
- value: Box,
- },
- Fn {
- name: String,
- params: Vec,
- return_type: Type,
- body: Box,
- },
- Call {
- base: Box,
- arguments: Vec,
- },
- Field {
- base: Box,
- field: Box,
- },
- Builtin {
- id: BuiltinId,
- arguments: Vec,
- },
- If(If),
- Return(Option>),
- Literal(Literal),
- Identifier(String),
-}
-
-#[derive(Debug, Clone)]
-pub enum Statement {
- Expr(Expr),
- VarDecl(VarDecl),
- FnDecl(FnDecl),
- Comment(Comment),
-}
-
-impl BuiltinId {
- pub fn display_name(&self) -> &'static str {
- match self {
- Self::Raw => "@raw",
- }
- }
-
- pub fn from_str(name: &str) -> Option {
- match name {
- "@raw" => Some(Self::Raw),
- _ => None,
- }
- }
-}
-
-impl Statement {
- /// This is stupid function im sorry
- pub fn require_semicolon(&self, semicolon_hint: bool) -> bool {
- match &self {
- Statement::Expr(_) => !semicolon_hint,
- Statement::VarDecl(_) => true,
- _ => false,
- }
- }
-
- pub fn is_expression(&self) -> bool {
- match &self {
- Statement::Expr(_) => true,
- _ => false,
- }
- }
-}
diff --git a/crates/ast/lib.rs b/crates/ast/lib.rs
deleted file mode 100644
index c35262a7f11a1125135720a5efee113eb1ec8c29..0000000000000000000000000000000000000000
--- a/crates/ast/lib.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-mod ast;
-mod operators;
-mod types;
-
-pub use ast::*;
-pub use operators::*;
-pub use types::*;
diff --git a/crates/ast/operators.rs b/crates/ast/operators.rs
deleted file mode 100644
index 27cf0b1a837d2f1f40b45af57026cc4700ed50e2..0000000000000000000000000000000000000000
--- a/crates/ast/operators.rs
+++ /dev/null
@@ -1,316 +0,0 @@
-use tokenizer::TokenType;
-
-use crate::Literal;
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum BinaryOperator {
- Add,
- Sub,
- Mul,
- Div,
- Pow,
- Eq,
- Gt,
- Lt,
- Gte,
- Lte,
- Lshift,
- Rshift,
- RshiftUnsigned,
- BitAnd,
- BitOr,
- BitXor,
- Mod,
- NotEq,
- And,
- Or,
-
- Assign,
- AssignAdd,
- AssignSub,
- AssignMul,
- AssignDiv,
- AssignPow,
- AssignLshift,
- AssignRshift,
- AssignRshiftUnsigned,
- AssignBitAnd,
- AssignBitOr,
- AssignBitXor,
- AssignMod,
- AssignAnd,
- AssignOr,
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum UnaryOperator {
- Neg,
- Not,
- BitNot,
-}
-
-impl BinaryOperator {
- pub fn to_str(&self) -> &'static str {
- match self {
- Self::Add => "+",
- Self::Sub => "-",
- Self::Div => "/",
- Self::Mul => "*",
- Self::Pow => "**",
- Self::Eq => "==",
- Self::Gt => ">",
- Self::Lt => "<",
- Self::Gte => ">=",
- Self::Lte => "<=",
- Self::Lshift => "<<",
- Self::Rshift => ">>",
- Self::RshiftUnsigned => ">>>",
- Self::BitAnd => "&",
- Self::BitOr => "|",
- Self::BitXor => "^",
- Self::Mod => "%",
- Self::NotEq => "!=",
- Self::And => "&&",
- Self::Or => "||",
- Self::AssignAdd => "+=",
- Self::AssignSub => "-=",
- Self::AssignDiv => "/=",
- Self::AssignMul => "*=",
- Self::AssignPow => "**=",
- Self::Assign => "=",
- Self::AssignLshift => "<<=",
- Self::AssignRshift => ">>=",
- Self::AssignRshiftUnsigned => ">>>=",
- Self::AssignBitAnd => "&=",
- Self::AssignBitOr => "|=",
- Self::AssignBitXor => "^=",
- Self::AssignMod => "%=",
- Self::AssignAnd => "&&=",
- Self::AssignOr => "||=",
- }
- }
-
- pub fn to_string(&self) -> String {
- String::from(self.to_str())
- }
-
- pub fn from_token(token: &TokenType) -> Option {
- match &token {
- TokenType::And => Some(Self::BitAnd),
- TokenType::AndAnd => Some(Self::And),
- TokenType::BangEqual => Some(Self::NotEq),
- TokenType::Caret => Some(Self::BitXor),
- TokenType::EqualEqual => Some(Self::Eq),
- TokenType::Gt => Some(Self::Gt),
- TokenType::GtEqual => Some(Self::Gte),
- TokenType::GtGt => Some(Self::Rshift),
- TokenType::GtGtGt => Some(Self::RshiftUnsigned),
- TokenType::Lt => Some(Self::Lt),
- TokenType::LtEqual => Some(Self::Lte),
- TokenType::LtLt => Some(Self::Lshift),
- TokenType::Minus => Some(Self::Sub),
- TokenType::Or => Some(Self::BitOr),
- TokenType::OrOr => Some(Self::Or),
- TokenType::Percent => Some(Self::Mod),
- TokenType::Plus => Some(Self::Add),
- TokenType::Slash => Some(Self::Div),
- TokenType::Star => Some(Self::Mul),
- TokenType::StarStar => Some(Self::Pow),
- _ => None,
- }
- }
-
- pub fn level(&self) -> OperatorLevel {
- OperatorLevel::from(self)
- }
-
- /// Error case of this means 'types do not allow this operation'
- pub fn perform(&self, left: Literal, right: Literal) -> Result {
- match self {
- BinaryOperator::Eq => Ok(Literal::Bool(left == right)),
- BinaryOperator::NotEq => Ok(Literal::Bool(left != right)),
-
- BinaryOperator::Or | BinaryOperator::And => match (left, right) {
- (Literal::Bool(a), Literal::Bool(b)) => Ok(Literal::Bool(match self {
- BinaryOperator::Or => a || b,
- BinaryOperator::And => a && b,
- _ => unreachable!(),
- })),
- _ => Err(()),
- },
-
- BinaryOperator::Add
- | BinaryOperator::Sub
- | BinaryOperator::Mul
- | BinaryOperator::Div
- | BinaryOperator::Pow
- | BinaryOperator::Gt
- | BinaryOperator::Lt
- | BinaryOperator::Gte
- | BinaryOperator::Lte
- | BinaryOperator::Lshift
- | BinaryOperator::Rshift
- | BinaryOperator::RshiftUnsigned
- | BinaryOperator::BitAnd
- | BinaryOperator::BitOr
- | BinaryOperator::BitXor
- | BinaryOperator::Mod => match (left, right) {
- (Literal::Number(a), Literal::Number(b)) => Ok(match self {
- BinaryOperator::Add => Literal::Number(a + b),
- BinaryOperator::Sub => Literal::Number(a - b),
- BinaryOperator::Mul => Literal::Number(a * b),
- BinaryOperator::Div => Literal::Number(a / b),
- BinaryOperator::Pow => Literal::Number(a.powf(b)),
- BinaryOperator::Gt => Literal::Bool(a > b),
- BinaryOperator::Lt => Literal::Bool(a < b),
- BinaryOperator::Gte => Literal::Bool(a >= b),
- BinaryOperator::Lte => Literal::Bool(a <= b),
- BinaryOperator::Lshift => Literal::Number(((a as i32) << (b as i32)).into()),
- BinaryOperator::Rshift => Literal::Number(((a as i32) >> (b as i32)).into()),
- BinaryOperator::RshiftUnsigned => {
- Literal::Number(((a as u32) >> (b as u32)).into())
- }
- BinaryOperator::BitAnd => Literal::Number(((a as i32) & (b as i32)).into()),
- BinaryOperator::BitOr => Literal::Number(((a as i32) | (b as i32)).into()),
- BinaryOperator::BitXor => Literal::Number(((a as i32) ^ (b as i32)).into()),
- BinaryOperator::Mod => Literal::Number(a % b),
- _ => unreachable!(),
- }),
- _ => Err(()),
- },
-
- // Impossible condition doing assign-add to a literal
- _ => Err(()),
- }
- }
-}
-
-impl UnaryOperator {
- pub fn to_str(&self) -> &'static str {
- match self {
- Self::Neg => "-",
- Self::Not => "!",
- Self::BitNot => "~",
- }
- }
-
- pub fn to_string(&self) -> String {
- String::from(self.to_str())
- }
-
- pub fn perform(self, value: Literal) -> Result {
- match self {
- UnaryOperator::Neg => match value {
- Literal::Number(a) => Ok(Literal::Number(-a)),
- _ => Err(()),
- },
- UnaryOperator::Not => match value {
- Literal::Bool(a) => Ok(Literal::Bool(!a)),
- _ => Err(()),
- },
- UnaryOperator::BitNot => match value {
- Literal::Number(a) => Ok(Literal::Number((!(a as i32)).into())),
- _ => Err(()),
- },
- }
- }
-}
-
-/// Taken from from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence#table
-///
-/// Blight doesn't have all of these operators, but the
-/// printer uses it to determine where to put parens.
-#[derive(PartialEq, PartialOrd, Debug)]
-pub enum OperatorLevel {
- Accessor, // e. e[] e()
- New, // new
- Postfix, // expr++ expr--
- Prefix, // ++e, --e, !e, ~e, +e, -e, typeof, void, delete
-
- // these are the binary operators
- Exponentiation, // **
- Multiplicative, // *, /, %
- Additive, // +, -
- BitwiseShift, // <<, >>, >>>
- Relational, // <, <=, >, >=
- Equality, // ==, !=
- BitwiseAnd, // &
- BitwiseXor, // ^
- BitwiseOr, // |
- LogicalAnd, // &&
- LogicalOr, // ||, ??
-
- AssignmentAndOther, // =, +=, -=, *=, /=, %=, / <<=, >>=, >>>=, &=, ^=, |=
- Comma, // ,
-}
-
-#[derive(PartialEq)]
-pub enum Associativity {
- LeftToRight,
- RightToLeft,
-}
-
-impl OperatorLevel {
- pub fn associativity(&self) -> Associativity {
- use Associativity::*;
- use OperatorLevel::*;
-
- match self {
- Accessor => LeftToRight,
- Exponentiation => RightToLeft,
- Multiplicative => LeftToRight,
- Additive => LeftToRight,
- BitwiseShift => LeftToRight,
- Relational => LeftToRight,
- Equality => LeftToRight,
- BitwiseAnd => LeftToRight,
- BitwiseXor => LeftToRight,
- BitwiseOr => LeftToRight,
- LogicalAnd => LeftToRight,
- LogicalOr => LeftToRight,
- AssignmentAndOther => RightToLeft,
- Comma => LeftToRight,
-
- // These do not make sense to have an associativity
- // You will likely not run into this if you do
- // BinaryOperator::level(o).associativity()
- New | Postfix | Prefix => unreachable!("{:?} does not have an associativity", self),
- }
- }
-}
-
-impl From<&BinaryOperator> for OperatorLevel {
- fn from(operator: &BinaryOperator) -> Self {
- use BinaryOperator::*;
- use OperatorLevel::*;
-
- match operator {
- Add | Sub => Additive,
- Mul | Div | Mod => Multiplicative,
- Pow => Exponentiation,
- Gt | Lt | Gte | Lte => Relational,
- Lshift | Rshift | RshiftUnsigned => BitwiseShift,
- BitAnd => BitwiseAnd,
- BitOr => BitwiseOr,
- BitXor => BitwiseXor,
- Eq | NotEq => Equality,
- And => LogicalAnd,
- Or => LogicalOr,
-
- Assign | AssignAdd | AssignSub | AssignMul | AssignDiv | AssignPow | AssignLshift
- | AssignRshift | AssignRshiftUnsigned | AssignBitAnd | AssignBitOr | AssignBitXor
- | AssignMod | AssignAnd | AssignOr => AssignmentAndOther,
- }
- }
-}
-
-impl From for OperatorLevel {
- fn from(operator: UnaryOperator) -> Self {
- use OperatorLevel::*;
- use UnaryOperator::*;
-
- match operator {
- Neg | Not | BitNot => Prefix,
- }
- }
-}
diff --git a/crates/ast/types.rs b/crates/ast/types.rs
deleted file mode 100644
index d1afb35a3848d2331616da58398410b440a2af6c..0000000000000000000000000000000000000000
--- a/crates/ast/types.rs
+++ /dev/null
@@ -1,134 +0,0 @@
-use miette::{Diagnostic, Result};
-use thiserror::Error;
-
-/// A value that is known at compile-time
-#[derive(Debug, Clone, PartialEq)]
-pub enum Literal {
- Number(f64),
- Bool(bool),
- String(String),
- Void,
-}
-
-impl std::fmt::Display for Literal {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Literal::Number(value) => write!(f, "{}", value),
- Literal::Bool(value) => write!(f, "{}", value),
- Literal::String(value) => write!(f, "{}", value),
- Literal::Void => write!(f, "void"),
- }
- }
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum Type {
- /// Variables without an explicit type are assigned this until
- /// the type inferencer figures out what this is. There is not a
- /// keyword for this.
- Infer,
- /// The type assigned to literals. This allows integer literals
- /// to coerce to integer or float depending on the context, but
- /// prevents implicit casting of runtime values.
- Literal(Literal),
- /// Return value of `@raw`. All type checks are disabled.
- Any,
- /// The type of `void`. This is the return type of functions
- /// that do not return a value.
- Void,
- /// noreturn is the type of 'return', aka a value that never exists.
- /// unlike void, this includes functions that do not return at all.
- NoReturn,
- /// 32 signed integer
- Int,
- /// 64 signed JS float
- Float,
- /// Boolean
- Bool,
- /// JS BigInt
- BigInt,
- /// JS String
- String,
- /// A union of multiple possible types. Might be removed
- Union(Vec),
-}
-
-pub trait TypeCoerceable {
- fn to_type(&self) -> Option;
-}
-
-impl TypeCoerceable for Literal {
- fn to_type(&self) -> Option {
- Some(match self {
- Literal::Number(_) => Type::Float,
- Literal::Bool(_) => Type::Bool,
- Literal::String(_) => Type::String,
- Literal::Void => Type::Void,
- })
- }
-}
-
-impl TypeCoerceable for Type {
- fn to_type(&self) -> Option {
- Some(self.clone())
- }
-}
-
-impl std::fmt::Display for Type {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Type::Infer => write!(f, "infered type"),
- Type::Any => write!(f, "opaque js value"),
- Type::Literal(value) => write!(f, "{}", value),
- Type::Int => write!(f, "int"),
- Type::Float => write!(f, "float"),
- Type::BigInt => write!(f, "bigint"),
- Type::Bool => write!(f, "bool"),
- Type::String => write!(f, "string"),
- Type::NoReturn => write!(f, "noreturn"),
- Type::Void => write!(f, "void"),
- Type::Union(of) => {
- for (i, t) in of.iter().enumerate() {
- if i != 0 {
- write!(f, " | ")?;
- }
- write!(f, "{}", t)?;
- }
- Ok(())
- }
- }
- }
-}
-
-pub fn merge_types(a: Type, b: Type) -> Result {
- match a {
- Type::Infer => return Ok(b),
- Type::Any => return Ok(Type::Any),
- _ => {}
- }
-
- match b {
- Type::Infer => return Ok(a),
- Type::Any => return Ok(Type::Any),
- _ => {}
- }
-
- if a == b {
- return Ok(a);
- }
-
- Err(TypeError::new(format!("TODO: Implement type merging for {} and {}", a, b)).into())
-}
-
-#[derive(Error, Diagnostic, Debug)]
-#[error("TypeError")]
-pub struct TypeError {
- #[help]
- error: String,
-}
-
-impl TypeError {
- pub fn new(error: String) -> Self {
- Self { error }
- }
-}
diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml
deleted file mode 100644
index 1bd4c0904e6ee693867b2cde0353948f1fbb46fe..0000000000000000000000000000000000000000
--- a/crates/cli/Cargo.toml
+++ /dev/null
@@ -1,21 +0,0 @@
-[package]
-name = "cli"
-version = "0.1.0"
-edition = "2021"
-
-[[bin]]
-name = "blight"
-path = "./main.rs"
-
-[dependencies]
-clap.workspace = true
-miette.workspace = true
-thiserror.workspace = true
-
-tokenizer = { path = "../tokenizer" }
-ast = { path = "../ast" }
-parser = { path = "../parser" }
-js_ast = { path = "../js_ast" }
-js_printer = { path = "../js_printer" }
-sema = { path = "../sema" }
-log = { path = "../log" }
diff --git a/crates/cli/main.rs b/crates/cli/main.rs
deleted file mode 100644
index dfcb80febf985e7247541887ad7a59bdd65d364f..0000000000000000000000000000000000000000
--- a/crates/cli/main.rs
+++ /dev/null
@@ -1,172 +0,0 @@
-use std::{io::Write, path::PathBuf};
-
-use clap::{Parser, Subcommand};
-use miette::{diagnostic, Result};
-
-use js_printer;
-use tokenizer;
-
-#[derive(Parser)]
-#[command(author, version, about, long_about = None)]
-struct Cli {
- /// Turn debugging information on
- #[arg(short, long, action = clap::ArgAction::Count)]
- debug: u8,
-
- #[command(subcommand)]
- command: Commands,
-}
-
-#[derive(Subcommand)]
-enum Commands {
- /// Compiles a file into javascript
- Compile {
- file: PathBuf,
- // Whether to output to stdout instead of a file
- #[arg(long, default_value_t = false)]
- stdout: bool,
- /// What javascript runtime to compile to
- #[arg(short, long, default_value = "deno")]
- target: String,
- /// Format the output javascript?
- #[arg(long, default_value_t = true)]
- format: bool,
- },
- Run {
- file: PathBuf,
- /// What javascript runtime to compile to and use
- #[arg(short, long, default_value = "deno")]
- target: String,
- },
- Astdump {
- file: PathBuf,
- },
-}
-
-fn get_runtime(runtime: &String) -> String {
- match std::env::var("USER") {
- Ok(user) if user == "dave" => String::from("bun"),
- _ => runtime.clone(),
- }
-}
-
-pub fn transpile(content: String, writer: &mut dyn Write) -> Result<()> {
- // 1. Tokenize the input
- let tokens = tokenizer::tokenize(content)?;
- // 2. Parse the tokens into an AST
- let mut blight_ast = parser::parse(tokens)?;
- // 3. Semantic analysis.
- sema::analyze_module(&mut blight_ast)?;
- // 4. Optimize the AST
- // optimize::optimize_module(&mut blight_ast);
- // 5. Translate the AST into javascript
- let js_ast = js_ast::blight_to_js(blight_ast);
- // 6. Print the JS AST
- // TODO(@paperdave): use std::io::Write instead of a string
- js_printer::Print::print(&js_ast, writer)
- .map_err(|e| diagnostic!("Error while printing: {}", e))?;
- Ok(())
-}
-
-pub fn transpile_to_string(content: String) -> Result {
- let mut buffer = Vec::new();
- transpile(content, &mut buffer)?;
- Ok(String::from_utf8(buffer).unwrap())
-}
-
-pub fn astdump(content: String) -> Result {
- // 1. Tokenize the input
- let tokens = tokenizer::tokenize(content)?;
- // 2. Parse the tokens into an AST
- let blight_ast = parser::parse(tokens)?;
- return Ok(format!("{:#?}", blight_ast));
-}
-
-fn main() -> Result<()> {
- let cli = Cli::parse();
-
- match cli.command {
- Commands::Compile {
- file,
- stdout,
- target: _,
- format,
- } => {
- let contents = match std::fs::read_to_string(file.clone()) {
- Ok(contents) => contents,
- Err(e) => {
- eprintln!("Failed to read {}: {}", file.display(), e);
- std::process::exit(1);
- }
- };
-
- let mut result = transpile_to_string(contents.clone())?;
-
- if format {
- let mut formatter = std::process::Command::new("deno")
- .stdin(std::process::Stdio::piped())
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::inherit())
- .arg("fmt")
- .arg("-")
- .spawn()
- .expect("Failed to execute 'deno fmt' on output");
- let mut stdin = formatter.stdin.take().expect("Failed to open stdin");
- stdin
- .write_all(result.as_bytes())
- .expect("Failed to write to stdin");
- stdin.flush().expect("Failed to flush stdin");
- drop(stdin);
- let out = formatter
- .wait_with_output()
- .expect("Failed to execute 'deno fmt' on output");
- result = String::from_utf8(out.stdout).unwrap();
- }
-
- if stdout {
- println!("{}", result);
- } else {
- std::fs::write(file.with_extension("js"), result).unwrap();
- }
- }
- Commands::Run { file, target } => {
- let contents = std::fs::read_to_string(file.clone()).unwrap();
- let result = transpile_to_string(contents.clone())?;
- let mut temp_file = std::env::temp_dir();
- temp_file.push("temp.js");
- std::fs::write(temp_file.clone(), result).unwrap();
-
- let rt = get_runtime(&target);
-
- match rt.as_str() {
- "deno" => std::process::Command::new("deno")
- .arg("run")
- .arg("-A")
- .arg(temp_file)
- .spawn()
- .expect("Failed to execute process"),
- "node" => std::process::Command::new("node")
- .arg(temp_file)
- .spawn()
- .expect("Failed to execute process"),
- "bun" => std::process::Command::new("bun")
- .arg("run")
- .arg(temp_file)
- .spawn()
- .expect("Failed to execute process"),
- _ => {
- panic!("Unknown runtime: {}", target);
- }
- }
- .wait()
- .unwrap();
- }
- Commands::Astdump { file } => {
- let contents = std::fs::read_to_string(file.clone()).unwrap();
- let result = astdump(contents.clone())?;
- println!("{}", result);
- }
- }
-
- Ok(())
-}
diff --git a/crates/js_ast/Cargo.toml b/crates/js_ast/Cargo.toml
deleted file mode 100644
index 586042092d15e767b3355095c3459c3f209ad1ce..0000000000000000000000000000000000000000
--- a/crates/js_ast/Cargo.toml
+++ /dev/null
@@ -1,15 +0,0 @@
-[package]
-name = "js_ast"
-version = "0.1.0"
-edition = "2021"
-
-[lib]
-path = "./lib.rs"
-
-[dependencies]
-miette.workspace = true
-thiserror.workspace = true
-num.workspace = true
-
-ast = { path = "../ast" }
-log = { path = "../log" }
diff --git a/crates/js_ast/convert/block_convert_context.rs b/crates/js_ast/convert/block_convert_context.rs
deleted file mode 100644
index 01abce4a2631eabad654d1e31c99a6c8e37d4e30..0000000000000000000000000000000000000000
--- a/crates/js_ast/convert/block_convert_context.rs
+++ /dev/null
@@ -1,244 +0,0 @@
-use crate::js_ast::*;
-use ast::*;
-use log::Range;
-use std::collections::{HashMap, HashSet};
-use std::vec;
-
-use super::tree_shake_statement_list;
-
-/// A block convert context keeps track of state while converting non-valid JS expressions to JS
-pub struct BlockConvertContext {
- /// Statements in this block context
- /// This may be the merged contents of multiple blocks
- statements: Vec,
- ids: HashSet,
- next_id: u32,
- scopes: Vec,
- /// If the last item in `statements` is actually valid
- has_last_statement: bool,
-}
-
-struct VirtualScope {
- renames: HashMap,
-}
-
-pub struct TempId {
- unmapped_name: String,
-}
-
-pub struct StatementHandle {
- index: Option,
-}
-
-impl StatementHandle {
- pub fn delete(self, ctx: &mut BlockConvertContext) {
- if let Some(index) = self.index {
- ctx.statements.remove(index as usize);
- }
- }
-}
-
-impl TempId {
- pub fn get(&self, hoist_context: &BlockConvertContext) -> String {
- hoist_context.remap_identifier(&self.unmapped_name)
- }
-}
-
-impl BlockConvertContext {
- pub fn new() -> Self {
- Self {
- statements: Vec::new(),
- ids: HashSet::new(),
- next_id: 0,
- scopes: vec![VirtualScope {
- renames: HashMap::new(),
- }],
- has_last_statement: false,
- }
- }
-
- pub fn new_child(&self) -> Self {
- Self {
- statements: Vec::new(),
- ids: self.ids.clone(),
- next_id: 0,
- scopes: vec![VirtualScope {
- renames: HashMap::new(),
- }],
- has_last_statement: false,
- }
- }
-
- /// A virtual scope is a scope that exists in blight syntax
- /// but may or may not exist in blight. With these it is important to
- pub fn push_virtual_scope(&mut self) {
- self.scopes.push(VirtualScope {
- renames: HashMap::new(),
- });
- }
-
- /// See `push_virtual_scope` for more information on what this is.
- pub fn pop_virtual_scope(&mut self) {
- self.scopes.pop();
- debug_assert!(self.scopes.len() > 0);
- }
-
- pub fn generate_temp_id(&mut self, basename: &str) -> TempId {
- self.next_id += 1;
- loop {
- let name = format!("{}{}", basename, self.next_id);
- if !self.ids.contains(&name) {
- self.ids.insert(name.clone());
- return TempId {
- unmapped_name: name,
- };
- }
- self.next_id += 1;
- }
- }
-
- /// This does some extra optimizations:
- /// - `let a = b;` will not be emitted but instad write a variable alias
- pub fn add_statement(&mut self, statement: JsStatement) -> StatementHandle {
- match statement.data {
- JsStatementData::Declaration(JSDeclaration {
- name,
- init:
- Some(JsExpr {
- data: JsExprData::Identifier(init_name),
- ..
- }),
- is_constant: true,
- }) => {
- self.scopes
- .last_mut()
- .unwrap()
- .renames
- .insert(name, init_name);
- self.has_last_statement = false;
- return StatementHandle { index: None };
- }
- _ => {}
- }
- self.statements.push(statement);
- self.has_last_statement = true;
- StatementHandle {
- index: Some(self.statements.len() as u32 - 1),
- }
- }
-
- /// Get the last statement if it exists.
- /// If the statement was optimized away, this returns None
- pub fn last_statement(&self) -> Option<&JsStatement> {
- if self.has_last_statement {
- Some(self.statements.last().unwrap())
- } else {
- None
- }
- }
-
- pub fn generate_id(&mut self, basename: &String) -> TempId {
- let mut id = 0u32;
- loop {
- let name = match id {
- 0 => basename.clone(),
- _ => format!("{}{}", basename, id),
- };
- if !self.ids.contains(&name) {
- self.ids.insert(name.clone());
- return TempId {
- unmapped_name: name,
- };
- }
- id += 1;
- }
- }
-
- /// Given a blight identifier, remap it to a new identifier.
- pub fn remap_identifier(&self, name: &String) -> String {
- for scope in self.scopes.iter().rev() {
- if let Some(remapped) = scope.renames.get(name) {
- return remapped.clone();
- }
- }
- name.clone()
- }
-
- /// Declare an identifier in the current scope.
- pub fn declare_identifier(&mut self, name: String) -> TempId {
- let remapped = self.generate_id(&name);
- let scope = self.scopes.last_mut().unwrap();
- self.ids.insert(remapped.unmapped_name.clone());
- scope.renames.insert(name, remapped.unmapped_name.clone());
- remapped
- }
-
- pub fn take_last_statement(&mut self) -> Option {
- if !self.has_last_statement {
- return None;
- }
- self.has_last_statement = false;
- Some(self.statements.pop().unwrap())
- }
-
- pub fn take_last_statement_if_expr(&mut self) -> Option {
- if !self.has_last_statement {
- return None;
- }
- match self.statements.last().unwrap().data {
- JsStatementData::Expression(_) => {
- self.has_last_statement = false;
- match self.statements.pop().unwrap().data {
- JsStatementData::Expression(expr) => Some(expr),
- _ => unreachable!(),
- }
- }
- _ => return None,
- }
- }
-}
-
-pub fn new_js_assignment_statement(expr: JsExpr, target: String, range: Range) -> JsStatement {
- JsStatement::new(
- JsStatementData::Expression(new_js_assignment_expr(expr, target, range.clone())),
- range,
- None,
- )
-}
-
-pub fn new_js_assignment_expr(expr: JsExpr, target: String, range: Range) -> JsExpr {
- JsExpr::new(
- JsExprData::Binary {
- op: BinaryOperator::Assign,
- left: Box::new(JsExpr::new(
- JsExprData::Identifier(target),
- range.clone(),
- None,
- )),
- right: Box::new(expr),
- },
- range,
- None,
- )
-}
-
-impl Into for BlockConvertContext {
- fn into(self) -> JsBlock {
- JsBlock::new(self.into(), Range::EMPTY)
- }
-}
-
-impl Into for BlockConvertContext {
- fn into(self) -> JsModule {
- JsModule {
- statements: self.into(),
- }
- }
-}
-
-impl Into> for BlockConvertContext {
- fn into(mut self) -> Vec {
- tree_shake_statement_list(&mut self.statements);
- self.statements
- }
-}
diff --git a/crates/js_ast/convert/convert_valid_block.rs b/crates/js_ast/convert/convert_valid_block.rs
deleted file mode 100644
index 29ef59c7ccf294682f376ea3afe77d113592ed4f..0000000000000000000000000000000000000000
--- a/crates/js_ast/convert/convert_valid_block.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-use super::convert_valid_statement_to_expr;
-use super::BlockConvertContext;
-use super::IsValidJSExpression;
-use crate::js_ast::*;
-use ast::*;
-use log::Range;
-
-pub fn convert_valid_block(ctx: &mut BlockConvertContext, block: Block, range: Range) -> JsExpr {
- debug_assert!(
- block.is_valid_js_expression(),
- "must call convert_valid_block with valid js expression"
- );
-
- let len = block.statements.len();
- let range = range;
- let block_expr = match len {
- 0 => JsExpr::new(JsExprData::Null, range.clone(), None),
- // a block expression with a single expression is just that expression
- 1 => convert_valid_statement_to_expr(ctx, block.statements.into_iter().next().unwrap()),
- // Since blocks are not valid expressions in JS, we create a
- // comma expression from a block with more than one statement.
- 2.. => {
- let mut items = Vec::new();
- for statement in block.statements.into_iter() {
- items.push(convert_valid_statement_to_expr(ctx, statement));
- }
- JsExpr::new(JsExprData::Comma { items }, range.clone(), None)
- }
- };
-
- // a block without implicit return is to be type void, so we wrap in a void expression
- if !block.has_implicit_return && len > 0 {
- JsExpr::new(JsExprData::Void(Box::new(block_expr)), range, None)
- } else {
- block_expr
- }
-}
diff --git a/crates/js_ast/convert/convert_valid_expr.rs b/crates/js_ast/convert/convert_valid_expr.rs
deleted file mode 100644
index 3e6c29e1d557d6ec835854d34225a21979192e0a..0000000000000000000000000000000000000000
--- a/crates/js_ast/convert/convert_valid_expr.rs
+++ /dev/null
@@ -1,115 +0,0 @@
-use super::convert_valid_block;
-use super::BlockConvertContext;
-use crate::convert::convert_valid_if;
-use crate::convert::process_function::process_function_to_arrow;
-use crate::convert::IsValidJSExpression;
-use crate::js_ast::*;
-use ast::*;
-
-/// Translate a blight ast expression that is a valid JS expression, to a JS expression
-/// Precondition: it is a valid JS expression, check this with `is_valid_js_expression`
-pub fn convert_valid_expr(ctx: &mut BlockConvertContext, expr: Expr) -> JsExpr {
- // Assert may be skipped in release builds as all failures are
- // annotated with a release-checked panic
- debug_assert!(
- expr.is_valid_js_expression(),
- "must call convert_valid_expr with valid js expression (use hoist instead)"
- );
-
- let range = expr.range;
- let comment = expr.comment;
-
- match expr.data {
- ExprData::Identifier(str) => JsExpr::new(
- JsExprData::Identifier(ctx.remap_identifier(&str)),
- range,
- comment,
- ),
-
- ExprData::Literal(Literal::Bool(value)) => {
- JsExpr::new(JsExprData::Bool(value), range, comment)
- }
- ExprData::Literal(Literal::Number(value)) => {
- JsExpr::new(JsExprData::Number(value), range, comment)
- }
- ExprData::Literal(Literal::String(value)) => {
- JsExpr::new(JsExprData::String(value), range, comment)
- }
- ExprData::Literal(Literal::Void) => JsExpr::new(JsExprData::Null, range, comment),
-
- ExprData::Block(block) => convert_valid_block(ctx, block, range),
-
- ExprData::Binary { op, left, right } => {
- let left = convert_valid_expr(ctx, *left);
- let right = convert_valid_expr(ctx, *right);
- JsExpr::new(
- JsExprData::Binary {
- op,
- left: Box::new(left),
- right: Box::new(right),
- },
- range,
- comment,
- )
- }
- ExprData::Unary { op, value } => {
- let value = convert_valid_expr(ctx, *value);
- JsExpr::new(
- JsExprData::Unary {
- op,
- value: Box::new(value),
- },
- range,
- comment,
- )
- }
- ExprData::Fn { body, params, .. } => {
- process_function_to_arrow(ctx, *body, params, range, comment)
- }
- ExprData::Builtin { id, arguments } => match id {
- BuiltinId::Raw => {
- let first_arg = arguments.iter().next().unwrap();
- // TODO(@paperdave): this string clone is stupid
- let str = match first_arg.data.clone() {
- ExprData::Literal(Literal::String(s)) => s,
- _ => panic!("raw expects a string"),
- };
- JsExpr::new(JsExprData::Raw(str), range, comment)
- }
- },
- ExprData::If(expr) => convert_valid_if(ctx, expr, range, comment),
-
- ExprData::Call { base, arguments } => {
- let base = convert_valid_expr(ctx, *base);
- let arguments = arguments
- .into_iter()
- .map(|x| convert_valid_expr(ctx, x))
- .collect();
- JsExpr::new(
- JsExprData::Call {
- base: Box::new(base),
- arguments,
- },
- range,
- comment,
- )
- }
-
- ExprData::Field { base, field } => JsExpr::new(
- match field.data {
- ExprData::Literal(Literal::String(name)) => JsExprData::Dot {
- base: Box::new(convert_valid_expr(ctx, *base)),
- name,
- },
- _ => JsExprData::Index {
- base: Box::new(convert_valid_expr(ctx, *base)),
- index: Box::new(convert_valid_expr(ctx, *field)),
- },
- },
- range,
- comment,
- ),
-
- ExprData::Return(_) => unreachable!("convert_valid_expr called on return"),
- }
-}
diff --git a/crates/js_ast/convert/convert_valid_if.rs b/crates/js_ast/convert/convert_valid_if.rs
deleted file mode 100644
index 0b294194266e2b189be8d7398a7bcce60a337283..0000000000000000000000000000000000000000
--- a/crates/js_ast/convert/convert_valid_if.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-use super::convert_valid_block;
-use super::convert_valid_expr;
-use super::BlockConvertContext;
-use crate::convert::IsValidJSExpression;
-use crate::js_ast::*;
-use ast::*;
-use log::Range;
-
-/// Convert a valid blight if statement to a JS expression
-pub fn convert_valid_if(
- ctx: &mut BlockConvertContext,
- expr: If,
- range: Range,
- comment: Option,
-) -> JsExpr {
- debug_assert!(
- expr.is_valid_js_expression(),
- "convert_valid_if on an invalid expression (use hoist instead)"
- );
- let condition = convert_valid_expr(ctx, *expr.condition);
-
- let yes = convert_valid_block(ctx, expr.yes.block, expr.yes.range);
- let no = match expr.no {
- Some(BlockOrIf::Block(b)) => Some(convert_valid_block(ctx, b.block, b.range)),
- Some(BlockOrIf::If(else_if)) => Some(convert_valid_if(ctx, *else_if, Range::EMPTY, None)),
- None => None,
- };
-
- JsExpr::new(
- JsExprData::Ternary {
- condition: Box::new(condition),
- yes: Box::new(yes),
- no: no.map(Box::new),
- },
- range,
- comment,
- )
-}
diff --git a/crates/js_ast/convert/convert_valid_statement.rs b/crates/js_ast/convert/convert_valid_statement.rs
deleted file mode 100644
index ef0d6b5fb48c8cbe532a76df797c346fb4b915ac..0000000000000000000000000000000000000000
--- a/crates/js_ast/convert/convert_valid_statement.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use super::convert_valid_expr;
-use super::is_valid_js_expression::*;
-use super::BlockConvertContext;
-use crate::js_ast::*;
-use ast::*;
-
-/// Translate a blight ast statement that is a valid JS expression, to a JS expression
-/// Precondition: it is a valid JS expression, check this with `is_valid_js_expression`
-pub fn convert_valid_statement_to_expr(
- ctx: &mut BlockConvertContext,
- statement: Statement,
-) -> JsExpr {
- debug_assert!(
- statement.is_valid_js_expression(),
- "convert_valid_statement_to_expr on an invalid expression (use hoist instead)"
- );
-
- match statement {
- ast::Statement::Expr(expr) => convert_valid_expr(ctx, expr),
- _ => unreachable!("attempt to convert statement to js without hoisting"),
- }
-}
-
-/// Translate a blight ast statement that is a valid JS expression, to a JS statement
-/// Precondition: it is a valid JS expression, check this with `is_valid_js_expression`
-///
-/// TODO(@paperdave): Replace this with Into for JsExpr
-pub fn convert_valid_statement(ctx: &mut BlockConvertContext, statement: Statement) -> JsStatement {
- let expr = convert_valid_statement_to_expr(ctx, statement);
- let range = expr.range.clone();
- JsStatement::new(JsStatementData::Expression(expr), range, None)
-}
diff --git a/crates/js_ast/convert/hoist_block_to_expr.rs b/crates/js_ast/convert/hoist_block_to_expr.rs
deleted file mode 100644
index 16ea9fab3f641f8b3c5c3dc8e571810e8fa47f90..0000000000000000000000000000000000000000
--- a/crates/js_ast/convert/hoist_block_to_expr.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-use super::is_valid_js_expression::*;
-use super::process_statement;
-use super::BlockConvertContext;
-use crate::js_ast::*;
-use ast::*;
-use log::Range;
-
-/// This block hoist is used when we have a block in an expression context.
-///
-/// All the expressions in the block are lifted above the currently
-/// processed expression, and then a temporary variable is returned.
-/// (or the last expression might get inlined as the return value)
-///
-/// A "virtual scope" is created for this block, which handles renaming
-/// situations like:
-///
-/// let x = 1;
-/// {
-/// let x = 1;
-/// }
-/// call(x)
-///
-/// The output JS of the above would be:
-///
-/// const x = 1;
-/// const x2 = 1;
-/// call(x)
-///
-/// For expressions that have branches, use `hoist_block_to_js_block`
-pub fn hoist_block_to_expr(
- ctx: &mut BlockConvertContext,
- block: Block,
- range: Range,
- comment: Option,
-) -> Option {
- debug_assert!(
- !block.is_valid_js_expression(),
- "hoist_block on a valid expression, should call convert_valid_block instead"
- );
-
- let temporary = ctx.generate_temp_id("_block");
-
- ctx.push_virtual_scope();
-
- for statement in block.statements {
- process_statement(ctx, statement);
- }
-
- ctx.pop_virtual_scope();
-
- // If the last statement is an expression, we need
- // to assign to the temporary variable.
- let result = match ctx.last_statement().map(|x| &x.data) {
- Some(JsStatementData::Expression(_)) if block.has_implicit_return => {
- match ctx.take_last_statement().unwrap().data {
- JsStatementData::Expression(expr) => expr,
- _ => unreachable!(),
- }
- }
- _ => JsExpr::new(JsExprData::Null, range.clone(), None),
- };
-
- ctx.add_statement(JsStatement::new(
- JsStatementData::Declaration(JSDeclaration {
- name: temporary.get(&ctx),
- init: Some(result),
- is_constant: false,
- }),
- range.clone(),
- comment,
- ));
-
- Some(JsExpr::new(
- JsExprData::Identifier(temporary.get(&ctx)),
- range,
- None,
- ))
-}
diff --git a/crates/js_ast/convert/hoist_block_to_js_block.rs b/crates/js_ast/convert/hoist_block_to_js_block.rs
deleted file mode 100644
index 50fe974c9b851a8d49aca0fe6fe07d550d33b8ef..0000000000000000000000000000000000000000
--- a/crates/js_ast/convert/hoist_block_to_js_block.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-use crate::js_ast::*;
-use ast::*;
-use log::Range;
-
-use super::*;
-
-/// This block hoist is used when we actually need to create a JsBlock, which
-/// is needed if there is an invalid expression within a branch of code.
-///
-/// For example, `if` has two branches, the 'yes' and 'no' paths.
-/// When we cannot lower it as a ternary, we have to create two JsBlocks
-///
-/// The `target` param is a shared temporary variable that will be assigned
-/// to the result of the block.
-///
-/// Since blocks can be void or noreturn, this target is not always
-/// assigned to. The `did_assign` flag is used to indicate that.
-///
-/// For expressions that do not have branches, use `hoist_block_to_expr`
-///
-/// TODO(@paperdave): move `did_assign` into the return value
-pub fn hoist_block_to_js_block(
- ctx: &mut BlockConvertContext,
- block: Block,
- range: Range,
- target: &TempId,
- did_assign: &mut bool,
-) -> JsBlock {
- // A non-block will become a block with a single expression:
- // _temp = expr;
- //
- // We will also apply this transformation to blocks that are valid
- // expressions, because that is more concise.
- if block.is_valid_js_expression() {
- return JsBlock::new(
- vec![new_js_assignment_statement(
- convert_valid_block(ctx, block, range.clone()),
- target.get(ctx),
- range.clone(),
- )],
- range,
- );
- }
-
- let mut child = ctx.new_child();
-
- for statement in block.statements {
- if let None = process_statement(&mut child, statement) {
- return child.into();
- };
- }
-
- // If the last statement is an expression, we need
- // to assign to the temporary variable.
- if block.has_implicit_return {
- match child.take_last_statement_if_expr() {
- Some(expr) => {
- *did_assign = true;
- child.add_statement(new_js_assignment_statement(
- expr,
- target.get(ctx),
- range.clone(),
- ));
- }
- _ => {}
- }
- }
-
- child.into()
-}
diff --git a/crates/js_ast/convert/hoist_expr.rs b/crates/js_ast/convert/hoist_expr.rs
deleted file mode 100644
index 376e1072961ddfc90a8713a3cae5bb7d1d8925f2..0000000000000000000000000000000000000000
--- a/crates/js_ast/convert/hoist_expr.rs
+++ /dev/null
@@ -1,139 +0,0 @@
-use super::convert_valid_expr;
-use super::hoist_block_to_expr;
-use super::hoist_if_expr;
-use super::is_valid_js_expression::*;
-use super::BlockConvertContext;
-// use super::HasSideEffects;
-use crate::js_ast::*;
-use crate::util::is_js_identifier;
-use ast::*;
-
-/// Pushes a decl with a generated identifier to the hoisted result
-/// and returns an identifier expression for it.
-pub fn hoist_expr_if_needed(ctx: &mut BlockConvertContext, expr: Expr) -> Option {
- // if !expr.has_side_effects() {
- // return Some(ctx.hoist_expression_contents(expr)?);
- // }
-
- let init = hoist_expr_contents(ctx, expr)?;
-
- let id = ctx.generate_temp_id("_");
- let range = init.range.clone();
-
- ctx.add_statement(JsStatement::new(
- JsStatementData::Declaration(JSDeclaration {
- name: id.get(&ctx),
- init: Some(init),
- is_constant: true,
- }),
- range.clone(),
- None,
- ));
-
- Some(JsExpr::new(
- JsExprData::Identifier(id.get(&ctx)),
- range,
- None,
- ))
-}
-
-/// Returning `None` from this means that the expression is noreturn, and that
-/// the rest of the statement never occurs and can be stripped.
-pub fn hoist_expr_contents(ctx: &mut BlockConvertContext, expr: Expr) -> Option {
- if expr.is_valid_js_expression() {
- return Some(convert_valid_expr(ctx, expr));
- }
-
- match expr.data {
- ExprData::Binary { op, left, right } => {
- let left = hoist_expr_if_needed(ctx, *left)?;
- let right = hoist_expr_if_needed(ctx, *right)?;
-
- Some(JsExpr::new(
- JsExprData::Binary {
- op,
- left: Box::new(left),
- right: Box::new(right),
- },
- expr.range,
- expr.comment,
- ))
- }
-
- ExprData::Unary { op, value } => {
- let value = hoist_expr_if_needed(ctx, *value)?;
- Some(JsExpr::new(
- JsExprData::Unary {
- op,
- value: Box::new(value),
- },
- expr.range,
- expr.comment,
- ))
- }
-
- ExprData::Field { base, field } => {
- let base = hoist_expr_if_needed(ctx, *base)?;
- let field = hoist_expr_if_needed(ctx, *field)?;
-
- Some(JsExpr::new(
- match field.data {
- JsExprData::String(name) if is_js_identifier(name.as_str()) => {
- JsExprData::Dot {
- base: Box::new(base),
- name,
- }
- }
- _ => JsExprData::Index {
- base: Box::new(base),
- index: Box::new(field),
- },
- },
- expr.range,
- expr.comment,
- ))
- }
-
- ExprData::Block(block) => hoist_block_to_expr(ctx, block, expr.range, expr.comment),
-
- ExprData::If(_) => hoist_if_expr(ctx, expr),
-
- ExprData::Return(maybe_expr) => {
- let range = expr.range.clone();
- let return_value = match maybe_expr {
- Some(expr) => Some(hoist_expr_if_needed(ctx, *expr)?),
- None => None,
- };
- ctx.add_statement(JsStatement::new(
- JsStatementData::Return(return_value),
- range,
- None,
- ));
- None
- }
-
- ExprData::Call { base, arguments } => {
- let base = hoist_expr_if_needed(ctx, *base)?;
- let arguments = arguments
- .into_iter()
- .map(|x| hoist_expr_if_needed(ctx, x))
- .collect::