diff --git a/Cargo.lock b/Cargo.lock index 660d357..e0fe4b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,9 +31,9 @@ checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" @@ -41,12 +41,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bitflags" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" - [[package]] name = "bumpalo" version = "3.19.0" @@ -58,24 +52,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "clipboard-win" -version = "5.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" -dependencies = [ - "error-code", -] +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "convert_case" @@ -86,6 +65,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "countme" version = "3.0.1" @@ -94,9 +82,9 @@ checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636" [[package]] name = "cranelift" -version = "0.121.1" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94c4a83217cefee80a63921d524b7c98c4dc0c9913bd876fcdfa76a4fcef9b62" +checksum = "d0849f998d4e04e6dd056a75268636e39a58ffe57a295bc69d351a424343a79e" dependencies = [ "cranelift-codegen", "cranelift-frontend", @@ -105,42 +93,42 @@ dependencies = [ [[package]] name = "cranelift-assembler-x64" -version = "0.121.1" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b7077389885873ffad5d778e8512742580a6e11b0f723072f41f305d3652f" +checksum = "0ae7b60ec3fd7162427d3b3801520a1908bef7c035b52983cd3ca11b8e7deb51" dependencies = [ "cranelift-assembler-x64-meta", ] [[package]] name = "cranelift-assembler-x64-meta" -version = "0.121.1" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9cfeae5a23c8cf9c43381f49211f3ce6dc1da1d46f1c5d06966e6258cc483fa" +checksum = "6511c200fed36452697b4b6b161eae57d917a2044e6333b1c1389ed63ccadeee" dependencies = [ "cranelift-srcgen", ] [[package]] name = "cranelift-bforest" -version = "0.121.1" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c88c577c6af92b550cb83455c331cf8e1bc89fe0ccc3e7eb0fa617ed1d63056" +checksum = "5f7086a645aa58bae979312f64e3029ac760ac1b577f5cd2417844842a2ca07f" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-bitset" -version = "0.121.1" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "370f0aa7f1816bf0f838048d69b72d6cf12ef2fc3b37f6997fe494ffb9feb3ad" +checksum = "5225b4dec45f3f3dbf383f12560fac5ce8d780f399893607e21406e12e77f491" [[package]] name = "cranelift-codegen" -version = "0.121.1" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d1a10a8a2958b68ecd261e565eef285249e242a8447ac959978319eabbb4a55" +checksum = "858fb3331e53492a95979378d6df5208dd1d0d315f19c052be8115f4efc888e0" dependencies = [ "bumpalo", "cranelift-assembler-x64", @@ -152,21 +140,21 @@ dependencies = [ "cranelift-entity", "cranelift-isle", "gimli", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "log", "regalloc2", "rustc-hash 2.1.1", "serde", "smallvec", "target-lexicon", - "wasmtime-math", + "wasmtime-internal-math", ] [[package]] name = "cranelift-codegen-meta" -version = "0.121.1" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f319986d5ae1386cfec625c70f8c01e52dc1f910aa6aaee7740bf8842d4e19c7" +checksum = "456715b9d5f12398f156d5081096e7b5d039f01b9ecc49790a011c8e43e65b5f" dependencies = [ "cranelift-assembler-x64-meta", "cranelift-codegen-shared", @@ -175,33 +163,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" -version = "0.121.1" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed52f5660397039c3c741c3acf18746445f4e20629b7280d9f2ccfe57e2b1efd" +checksum = "0306041099499833f167a0ddb707e1e54100f1a84eab5631bc3dad249708f482" [[package]] name = "cranelift-control" -version = "0.121.1" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79bde8d48e1840702574e28c5d7d4499441435af71e6c47450881f84ce2b60a5" +checksum = "1672945e1f9afc2297f49c92623f5eabc64398e2cb0d824f8f72a2db2df5af23" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.121.1" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0335ac187211ac94c254826b6e78d23b8654ae09ebf0830506a827a2647162f" +checksum = "aa3cd55eb5f3825b9ae5de1530887907360a6334caccdc124c52f6d75246c98a" dependencies = [ "cranelift-bitset", ] [[package]] name = "cranelift-frontend" -version = "0.121.1" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fce5fcf93c1fece95d0175b15fbaf0808b187430bc06c8ecde80db0ed58c5e" +checksum = "781f9905f8139b8de22987b66b522b416fe63eb76d823f0b3a8c02c8fd9500c7" dependencies = [ "cranelift-codegen", "log", @@ -211,15 +199,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.121.1" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13fc8d838a2bf28438dbaf6ccdbc34531b6a972054f43fd23be7f124121ce6e0" +checksum = "a05337a2b02c3df00b4dd9a263a027a07b3dff49f61f7da3b5d195c21eaa633d" [[package]] name = "cranelift-jit" -version = "0.121.1" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e50932cee220b782812b728c0e63adf2b8eef63e823df8e5fea84c18f3fff99" +checksum = "593f8ff2c1a1785d9ab61a4b112ec1c9e8a3b976d8857ed1e70a79d4a07dd5ba" dependencies = [ "anyhow", "cranelift-codegen", @@ -231,15 +219,15 @@ dependencies = [ "log", "region", "target-lexicon", - "wasmtime-jit-icache-coherence", + "wasmtime-internal-jit-icache-coherence", "windows-sys 0.59.0", ] [[package]] name = "cranelift-module" -version = "0.121.1" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2707466bd2c786bd637e6b6375ebb472a158be35b6efbe85d2a744ec82e16356" +checksum = "f9f7a4b804066f3e62d8fc943e25adc135acbb39288aa6c68e67021a9f6a0c58" dependencies = [ "anyhow", "cranelift-codegen", @@ -248,9 +236,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.121.1" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0975ce66adcf2e0729d06b1d3efea0398d793d1f39c2e0a6f52a347537836693" +checksum = "2eee7a496dd66380082c9c5b6f2d5fa149cec0ec383feec5caf079ca2b3671c2" dependencies = [ "cranelift-codegen", "libc", @@ -259,9 +247,9 @@ dependencies = [ [[package]] name = "cranelift-srcgen" -version = "0.121.1" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4493a9b500bb02837ea2fb7d4b58c1c21c37a470ae33c92659f4e637aad14c9" +checksum = "b530783809a55cb68d070e0de60cfbb3db0dc94c8850dd5725411422bedcf6bb" [[package]] name = "derive_more" @@ -278,30 +266,18 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ - "convert_case", + "convert_case 0.7.1", "proc-macro2", "quote", "syn", "unicode-xid", ] -[[package]] -name = "ecow" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54bfbb1708988623190a6c4dbedaeaf0f53c20c6395abd6a01feb327b3146f4b" - [[package]] name = "either" -version = "1.10.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" - -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "equivalent" @@ -309,45 +285,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "errno" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "error-code" -version = "3.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" - [[package]] name = "fallible-iterator" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" -[[package]] -name = "fd-lock" -version = "4.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" -dependencies = [ - "cfg-if", - "rustix", - "windows-sys 0.59.0", -] - -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" - [[package]] name = "foldhash" version = "0.1.5" @@ -367,38 +310,29 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "allocator-api2", "equivalent", "foldhash", ] -[[package]] -name = "home" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.15.3", + "hashbrown 0.15.4", ] [[package]] @@ -412,9 +346,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.172" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libm" @@ -422,27 +356,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" -[[package]] -name = "linux-raw-sys" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" - [[package]] name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" -[[package]] -name = "lru" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f8cc7106155f10bdf99a6f379688f543ad6596a415375b36a59a054ceda1198" -dependencies = [ - "hashbrown 0.15.3", -] - [[package]] name = "mach2" version = "0.4.3" @@ -454,9 +373,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memoffset" @@ -467,70 +386,123 @@ dependencies = [ "autocfg", ] -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags 2.9.0", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "nixjit" -version = "0.0.0" +version = "0.1.0" + +[[package]] +name = "nixjit_context" +version = "0.1.0" dependencies = [ "cranelift", "cranelift-jit", "cranelift-module", "cranelift-native", - "derive_more", - "ecow", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "itertools", - "lru", - "petgraph", - "priority-queue", - "regex", - "replace_with", - "rnix", - "rustyline", + "nixjit_error", + "nixjit_eval", + "nixjit_hir", + "nixjit_ir", + "nixjit_jit", + "nixjit_lir", + "nixjit_value", +] + +[[package]] +name = "nixjit_error" +version = "0.1.0" +dependencies = [ "thiserror", ] [[package]] -name = "petgraph" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54acf3a685220b533e437e264e4d932cfbdc4cc7ec0cd232ed73c08d03b8a7ca" +name = "nixjit_eval" +version = "0.1.0" dependencies = [ - "fixedbitset", - "hashbrown 0.15.3", - "indexmap", - "serde", + "derive_more", + "hashbrown 0.15.4", + "itertools", + "nixjit_error", + "nixjit_ir", + "nixjit_lir", + "nixjit_value", + "replace_with", ] [[package]] -name = "priority-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5676d703dda103cbb035b653a9f11448c0a7216c7926bd35fcb5865475d0c970" +name = "nixjit_hir" +version = "0.1.0" dependencies = [ - "autocfg", - "equivalent", - "indexmap", + "derive_more", + "hashbrown 0.15.4", + "itertools", + "nixjit_error", + "nixjit_ir", + "nixjit_macros", + "nixjit_value", + "rnix", +] + +[[package]] +name = "nixjit_ir" +version = "0.1.0" +dependencies = [ + "derive_more", + "hashbrown 0.15.4", + "nixjit_error", + "nixjit_value", + "rnix", +] + +[[package]] +name = "nixjit_jit" +version = "0.1.0" +dependencies = [ + "cranelift", + "cranelift-jit", + "cranelift-module", + "cranelift-native", + "hashbrown 0.15.4", + "nixjit_error", + "nixjit_eval", + "nixjit_hir", + "nixjit_ir", + "nixjit_lir", + "nixjit_value", +] + +[[package]] +name = "nixjit_lir" +version = "0.1.0" +dependencies = [ + "derive_more", + "hashbrown 0.15.4", + "itertools", + "nixjit_error", + "nixjit_hir", + "nixjit_ir", + "nixjit_macros", + "nixjit_value", + "rnix", +] + +[[package]] +name = "nixjit_macros" +version = "0.1.0" +dependencies = [ + "convert_case 0.8.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "nixjit_value" +version = "0.1.0" +dependencies = [ + "derive_more", + "regex", ] [[package]] @@ -551,16 +523,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - [[package]] name = "regalloc2" version = "0.12.2" @@ -569,7 +531,7 @@ checksum = "5216b1837de2149f8bc8e6d5f88a9326b63b8c836ed58ce4a0a29ec736a59734" dependencies = [ "allocator-api2", "bumpalo", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "log", "rustc-hash 2.1.1", "smallvec", @@ -610,7 +572,7 @@ version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6b6ebd13bc009aef9cd476c1310d49ac354d36e240cf1bd753290f3dc7199a7" dependencies = [ - "bitflags 1.3.2", + "bitflags", "libc", "mach2", "windows-sys 0.52.0", @@ -633,12 +595,12 @@ dependencies = [ [[package]] name = "rowan" -version = "0.15.15" +version = "0.15.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49" +checksum = "d4f1e4a001f863f41ea8d0e6a0c34b356d5b733db50dadab3efef640bafb779b" dependencies = [ "countme", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "memoffset", "rustc-hash 1.1.0", "text-size", @@ -656,41 +618,6 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" -[[package]] -name = "rustix" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" -dependencies = [ - "bitflags 2.9.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustyline" -version = "15.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1e066dc922e513bda599c6ccb5f3bb2b0ea5870a579448f2622993f0a9a2f" -dependencies = [ - "bitflags 2.9.0", - "cfg-if", - "clipboard-win", - "fd-lock", - "home", - "libc", - "log", - "memchr", - "nix", - "radix_trie", - "unicode-segmentation", - "unicode-width", - "utf8parse", - "windows-sys 0.59.0", -] - [[package]] name = "serde" version = "1.0.219" @@ -713,9 +640,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "stable_deref_trait" @@ -725,9 +652,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "syn" -version = "2.0.101" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -778,12 +705,6 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" -[[package]] -name = "unicode-width" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" - [[package]] name = "unicode-xid" version = "0.2.6" @@ -791,16 +712,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] -name = "utf8parse" -version = "0.2.2" +name = "wasmtime-internal-jit-icache-coherence" +version = "35.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "wasmtime-jit-icache-coherence" -version = "34.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c71d64e8ebe132cd45e9d299a4d0daf261d66bd05cf50a204a1bf8cf96ff1f" +checksum = "4417e06b7f80baff87d9770852c757a39b8d7f11d78b2620ca992b8725f16f50" dependencies = [ "anyhow", "cfg-if", @@ -809,10 +724,10 @@ dependencies = [ ] [[package]] -name = "wasmtime-math" -version = "34.0.1" +name = "wasmtime-internal-math" +version = "35.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222bfa4769c6931c985711eb49a92748ea0acc4ca85fcd24e945a2f1bacda0c1" +checksum = "7710d5c4ecdaa772927fd11e5dc30a9a62d1fc8fe933e11ad5576ad596ab6612" dependencies = [ "libm", ] diff --git a/Cargo.toml b/Cargo.toml index 7767052..532aaa7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,38 +1,14 @@ -[package] -name = "nixjit" -version = "0.0.0" -edition = "2024" - -[features] -repl = ["dep:rustyline"] - -[[bin]] -name = "repl" -required-features = ["repl"] - -[profile.perf] -debug = 2 -strip = false -inherits = "release" - -[profile.release] -strip = true - -[dependencies] -rnix = "0.12" -thiserror = "2.0" -itertools = "0.14" -derive_more = { version = "2.0", features = ["full"] } -ecow = "0.2" -regex = "1.11" -hashbrown = "0.15" -petgraph = "0.8" -priority-queue = "2.5" -lru = "0.14" -replace_with = "0.1" -cranelift = "0.121" -cranelift-module = "0.121" -cranelift-jit = "0.121" -cranelift-native = "0.121" - -rustyline = { version = "15.0", optional = true } +[workspace] +resolver = "3" +members = [ + "evaluator/nixjit", + "evaluator/nixjit_context", + "evaluator/nixjit_error", + "evaluator/nixjit_eval", + "evaluator/nixjit_hir", + "evaluator/nixjit_ir", + "evaluator/nixjit_jit", + "evaluator/nixjit_lir", + "evaluator/nixjit_macros", + "evaluator/nixjit_value", +] diff --git a/evaluator/nixjit/Cargo.toml b/evaluator/nixjit/Cargo.toml new file mode 100644 index 0000000..291d821 --- /dev/null +++ b/evaluator/nixjit/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "nixjit" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/evaluator/nixjit/src/lib.rs b/evaluator/nixjit/src/lib.rs new file mode 100644 index 0000000..b93cf3f --- /dev/null +++ b/evaluator/nixjit/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/src/engine/test.rs b/evaluator/nixjit/src/test.rs similarity index 99% rename from src/engine/test.rs rename to evaluator/nixjit/src/test.rs index 14972b4..d04dcb3 100644 --- a/src/engine/test.rs +++ b/evaluator/nixjit/src/test.rs @@ -13,7 +13,7 @@ use super::eval; fn test_expr(expr: &str, expected: Value) { println!("{expr}"); let downgraded = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap(); - println!("{downgraded:?}"); + println!("{downgraded:#?}"); assert_eq!(eval(downgraded).unwrap(), expected); } diff --git a/evaluator/nixjit_context/Cargo.toml b/evaluator/nixjit_context/Cargo.toml new file mode 100644 index 0000000..273e259 --- /dev/null +++ b/evaluator/nixjit_context/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "nixjit_context" +version = "0.1.0" +edition = "2024" + +[dependencies] +hashbrown = "0.15" +itertools = "0.14" + +cranelift = "0.122" +cranelift-module = "0.122" +cranelift-jit = "0.122" +cranelift-native = "0.122" + +nixjit_error = { path = "../nixjit_error" } +nixjit_eval = { path = "../nixjit_eval" } +nixjit_hir = { path = "../nixjit_hir" } +nixjit_ir = { path = "../nixjit_ir" } +nixjit_jit = { path = "../nixjit_jit" } +nixjit_lir = { path = "../nixjit_lir" } +nixjit_value = { path = "../nixjit_value" } diff --git a/evaluator/nixjit_context/src/lib.rs b/evaluator/nixjit_context/src/lib.rs new file mode 100644 index 0000000..634ca31 --- /dev/null +++ b/evaluator/nixjit_context/src/lib.rs @@ -0,0 +1,145 @@ +use core::mem::MaybeUninit; +use std::cell::{OnceCell, RefCell}; + +use hashbrown::{HashMap, HashSet}; +use itertools::Itertools; + +use nixjit_error::Result; +use nixjit_eval::EvalContext; +use nixjit_hir::{DowngradeContext, Hir}; +use nixjit_ir::{ExprId, Param}; +use nixjit_lir::{Lir, ResolveContext}; +use nixjit_value::Value; + +use nixjit_jit::{JITCompiler, JITContext, JITFunc}; + +#[derive(Default)] +pub struct Context { + hirs: Vec>, + lirs: Vec>, + jit: JITCompiler, + compiled: Vec>>, +} + +impl Drop for Context { + fn drop(&mut self) { + unsafe { + self.lirs.iter_mut().for_each(|lir| lir.assume_init_drop()); + } + } +} + +impl Context { + pub fn new() -> Self { + Self::default() + } +} + +impl DowngradeContext for Context { + fn new_expr(&mut self, expr: Hir) -> ExprId { + let id = unsafe { core::mem::transmute(self.hirs.len() + self.lirs.len()) }; + self.hirs.push(expr.into()); + id + } + fn with_expr(&self, id: ExprId, f: impl FnOnce(&Hir, &Self) -> T) -> T { + unsafe { + let idx: usize = core::mem::transmute(id); + f(&self.hirs.get_unchecked(idx).borrow(), self) + } + } + fn with_expr_mut(&mut self, id: ExprId, f: impl FnOnce(&mut Hir, &mut Self) -> T) -> T { + unsafe { + let self_mut = &mut *(self as *mut Context); + let idx: usize = core::mem::transmute(id); + f(&mut self.hirs.get_unchecked_mut(idx).borrow_mut(), self_mut) + } + } +} + +impl ResolveContext for Context { + fn lookup(&self, name: &str) -> nixjit_lir::LookupResult { + todo!() + } + fn new_dep(&mut self, expr: ExprId, dep: ExprId) { + todo!() + } + fn resolve(&mut self, expr: ExprId) -> Result<()> { + todo!() + } + fn new_func(&mut self, body: ExprId, param: Param) { + todo!() + } + fn with_let_env<'a, T>( + &mut self, + bindings: impl IntoIterator, + f: impl FnOnce(&mut Self) -> T, + ) -> T { + todo!() + } + fn with_with_env(&mut self, f: impl FnOnce(&mut Self) -> T) -> (bool, T) { + todo!() + } + fn with_param_env<'a, T>( + &mut self, + ident: Option<&'a str>, + f: impl FnOnce(&mut Self) -> T, + ) -> T { + todo!() + } +} + +impl EvalContext for Context { + fn eval(&mut self, expr: ExprId) -> Result> + where + Self: Sized, + { + todo!() + } + fn pop_frame(&mut self) -> Vec> + where + Self: Sized, + { + todo!() + } + fn lookup_with<'a>(&'a self, ident: &str) -> Option<&'a nixjit_eval::Value> + where + Self: Sized, + { + todo!() + } + fn with_with_env( + &mut self, + namespace: std::rc::Rc>>, + f: impl FnOnce(&mut Self) -> T, + ) -> T + where + Self: Sized, + { + todo!() + } + fn with_args_env( + &mut self, + args: Vec>, + f: impl FnOnce(&mut Self) -> T, + ) -> (Vec>, T) + where + Self: Sized, + { + todo!() + } +} + +impl JITContext for Context { + fn lookup_arg(&self, offset: usize) -> &nixjit_eval::Value { + todo!() + } + fn lookup_stack(&self, offset: usize) -> &nixjit_eval::Value { + todo!() + } + fn enter_with(&mut self, namespace: std::rc::Rc>>) { + todo!() + } + fn exit_with(&mut self) { + todo!() + } +} diff --git a/evaluator/nixjit_error/Cargo.toml b/evaluator/nixjit_error/Cargo.toml new file mode 100644 index 0000000..09a586e --- /dev/null +++ b/evaluator/nixjit_error/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "nixjit_error" +version = "0.1.0" +edition = "2024" + +[dependencies] +thiserror = "2.0" diff --git a/src/error.rs b/evaluator/nixjit_error/src/lib.rs similarity index 76% rename from src/error.rs rename to evaluator/nixjit_error/src/lib.rs index 02e53dc..6b0c551 100644 --- a/src/error.rs +++ b/evaluator/nixjit_error/src/lib.rs @@ -8,10 +8,10 @@ pub enum Error { ParseError(String), #[error("error occurred during downgrade stage: {0}")] DowngradeError(String), + #[error("error occurred during variable resolve stage: {0}")] + ResolutionError(String), #[error("error occurred during evaluation stage: {0}")] EvalError(String), - // #[error("error occurred during JIT compile stage: {0}")] - // CompileError(#[from] inkwell::builder::BuilderError), #[error("unknown error")] Unknown, } diff --git a/evaluator/nixjit_eval/Cargo.toml b/evaluator/nixjit_eval/Cargo.toml new file mode 100644 index 0000000..a3e7e74 --- /dev/null +++ b/evaluator/nixjit_eval/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "nixjit_eval" +version = "0.1.0" +edition = "2024" + +[dependencies] +derive_more = { version = "2.0", features = ["full"] } +hashbrown = "0.15" +itertools = "0.14" +replace_with = "0.1" + +nixjit_error = { path = "../nixjit_error" } +nixjit_ir = { path = "../nixjit_ir" } +nixjit_lir = { path = "../nixjit_lir" } +nixjit_value = { path = "../nixjit_value" } diff --git a/evaluator/nixjit_eval/src/lib.rs b/evaluator/nixjit_eval/src/lib.rs new file mode 100644 index 0000000..8e04f1f --- /dev/null +++ b/evaluator/nixjit_eval/src/lib.rs @@ -0,0 +1,308 @@ +use std::rc::Rc; + +use hashbrown::HashMap; + +use nixjit_error::{Error, Result}; +use nixjit_ir::{self as ir, ExprId}; +use nixjit_lir as lir; +use nixjit_value::{Const, Symbol}; + +pub use crate::value::*; + +mod value; + +pub trait EvalContext: Sized { + fn eval(&mut self, expr: ExprId) -> Result>; + fn with_with_env( + &mut self, + namespace: Rc>>, + f: impl FnOnce(&mut Self) -> T, + ) -> T; + fn with_args_env( + &mut self, + args: Vec>, + f: impl FnOnce(&mut Self) -> T, + ) -> (Vec>, T); + fn lookup_with<'a>(&'a self, ident: &str) -> Option<&'a Value>; + fn pop_frame(&mut self) -> Vec>; +} + +pub trait Evaluate { + fn eval(&self, ctx: &mut Ctx) -> Result>; +} + +impl Evaluate for ExprId { + fn eval(&self, ctx: &mut Ctx) -> Result> { + ctx.eval(*self) + } +} + +impl Evaluate for lir::Lir { + fn eval(&self, ctx: &mut Ctx) -> Result> { + todo!() + } +} + +impl Evaluate for ir::AttrSet { + fn eval(&self, ctx: &mut Ctx) -> Result> { + let mut attrs = AttrSet::new( + self.stcs + .iter() + .map(|(k, v)| { + let eval_result = v.eval(ctx); + Ok((k.clone(), eval_result?)) + }) + .collect::>()?, + ); + for (k, v) in self.dyns.iter() { + let mut k = k.eval(ctx)?; + k.coerce_to_string(); + let v_eval_result = v.eval(ctx)?; + attrs.push_attr(k.unwrap_string(), v_eval_result); + } + let result = Value::AttrSet(attrs.into()).ok(); + Ok(result.unwrap()) + } +} + +impl Evaluate for ir::List { + fn eval(&self, ctx: &mut Ctx) -> Result> { + let items = self + .items + .iter() + .map(|val| val.eval(ctx)) + .collect::>>()?; + let result = Value::List(List::from(items).into()).ok(); + Ok(result.unwrap()) + } +} + +impl Evaluate for ir::HasAttr { + fn eval(&self, ctx: &mut Ctx) -> Result> { + use ir::Attr::*; + let mut val = self.lhs.eval(ctx)?; + val.has_attr(self.rhs.iter().map(|attr| { + Ok(match attr { + Str(ident) => ident.clone(), + Dynamic(expr) => { + let mut val = expr.eval(ctx)?; + val.coerce_to_string(); + val.unwrap_string() + } + }) + }))?; + let result = val.ok(); + Ok(result.unwrap()) + } +} + +impl Evaluate for ir::BinOp { + fn eval(&self, ctx: &mut Ctx) -> Result> { + use ir::BinOpKind::*; + let mut lhs = self.lhs.eval(ctx)?; + let mut rhs = self.rhs.eval(ctx)?; + match self.kind { + Add => lhs.add(rhs), + Sub => { + rhs.neg(); + lhs.add(rhs); + } + Mul => lhs.mul(rhs), + Div => lhs.div(rhs)?, + Eq => Value::eq(&mut lhs, &rhs), + Neq => { + Value::eq(&mut lhs, &rhs); + lhs.not(); + } + Lt => lhs.lt(rhs), + Gt => { + rhs.lt(lhs); + lhs = rhs; + } + Leq => { + rhs.lt(lhs); + rhs.not(); + lhs = rhs; + } + Geq => { + lhs.lt(rhs); + lhs.not(); + } + And => lhs.and(rhs), + Or => lhs.or(rhs), + Impl => { + lhs.not(); + lhs.or(rhs); + } + Con => lhs.concat(rhs), + Upd => lhs.update(rhs), + PipeL => lhs.call(vec![rhs], ctx)?, + PipeR => { + rhs.call(vec![lhs], ctx)?; + lhs = rhs; + } + } + Ok(lhs) + } +} + +impl Evaluate for ir::UnOp { + fn eval(&self, ctx: &mut Ctx) -> Result> { + use ir::UnOpKind::*; + let mut rhs = self.rhs.eval(ctx)?; + match self.kind { + Neg => { + rhs.neg(); + } + Not => { + rhs.not(); + } + }; + Ok(rhs) + } +} + +impl Evaluate for ir::Select { + fn eval(&self, ctx: &mut Ctx) -> Result> { + use ir::Attr::*; + let mut val = self.expr.eval(ctx)?; + if let Some(default) = &self.default { + let default = default.eval(ctx)?; + val.select_with_default( + self.attrpath.iter().map(|attr| { + Ok(match attr { + Str(ident) => ident.clone(), + Dynamic(expr) => { + let mut val = expr.eval(ctx)?; + val.coerce_to_string(); + val.unwrap_string() + } + }) + }), + default, + )?; + } else { + val.select(self.attrpath.iter().map(|attr| { + Ok(match attr { + Str(ident) => ident.clone(), + Dynamic(expr) => { + let mut val = expr.eval(ctx)?; + val.coerce_to_string(); + val.unwrap_string() + } + }) + }))?; + } + let result = val.ok(); + Ok(result.unwrap()) + } +} + +impl Evaluate for ir::If { + fn eval(&self, ctx: &mut Ctx) -> Result> { + // TODO: Error Handling + let cond = self.cond.eval(ctx)?; + let cond = cond + .try_unwrap_bool() + .map_err(|_| Error::EvalError(format!("expected a boolean but found ...")))?; + + if cond { + self.consq.eval(ctx) + } else { + self.alter.eval(ctx) + } + } +} + +impl Evaluate for ir::Call { + fn eval(&self, ctx: &mut Ctx) -> Result> { + let mut func = self.func.eval(ctx)?; + func.call( + self.args + .iter() + .map(|arg| arg.eval(ctx)) + .collect::>()?, + ctx, + )?; + Ok(func.ok().unwrap()) + } +} + +impl Evaluate for ir::With { + fn eval(&self, ctx: &mut Ctx) -> Result> { + let namespace = self.namespace.eval(ctx)?; + ctx.with_with_env( + namespace + .try_unwrap_attr_set() + .map_err(|_| Error::EvalError(format!("expected a set but found ...")))? + .into_inner(), + |ctx| self.expr.eval(ctx), + ) + } +} + +impl Evaluate for ir::Assert { + fn eval(&self, ctx: &mut Ctx) -> Result> { + todo!() + } +} + +impl Evaluate for ir::ConcatStrings { + fn eval(&self, ctx: &mut Ctx) -> Result> { + let mut parts = self + .parts + .iter() + .map(|part| { + let mut part = part.eval(ctx)?; + part.coerce_to_string(); + part.ok() + }) + .collect::>>()? + .into_iter(); + let init = parts.next().unwrap(); + let result = parts.fold(init, |mut a, b| { + a.concat_string(b); + a + }); + Ok(result.ok().unwrap()) + } +} + +impl Evaluate for ir::Str { + fn eval(&self, _: &mut Ctx) -> Result> { + let result = Value::String(self.val.clone()).ok(); + Ok(result.unwrap()) + } +} + +impl Evaluate for ir::Const { + fn eval(&self, _: &mut Ctx) -> Result> { + let result = match self.val { + Const::Null => Value::Null, + Const::Int(x) => Value::Int(x), + Const::Float(x) => Value::Float(x), + Const::Bool(x) => Value::Bool(x), + } + .ok(); + Ok(result.unwrap()) + } +} + +impl Evaluate for ir::Var { + fn eval(&self, ctx: &mut Ctx) -> Result> { + ctx.lookup_with(&self.sym) + .ok_or_else(|| { + Error::EvalError(format!( + "variable {} not found", + Symbol::from(self.sym.clone()) + )) + }) + .map(|val| val.clone()) + } +} + +impl Evaluate for ir::Path { + fn eval(&self, ctx: &mut Ctx) -> Result> { + todo!() + } +} diff --git a/src/ty/internal/attrset.rs b/evaluator/nixjit_eval/src/value/attrset.rs similarity index 54% rename from src/ty/internal/attrset.rs rename to evaluator/nixjit_eval/src/value/attrset.rs index 51abad0..2447f09 100644 --- a/src/ty/internal/attrset.rs +++ b/evaluator/nixjit_eval/src/value/attrset.rs @@ -1,48 +1,72 @@ use core::ops::Deref; use std::rc::Rc; +use std::fmt::Debug; use derive_more::Constructor; use hashbrown::{HashMap, HashSet}; use itertools::Itertools; -use crate::engine::Engine; -use crate::error::{Error, Result}; -use crate::ty::public::Symbol; +use nixjit_error::{Error, Result}; +use nixjit_value as p; +use nixjit_value::Symbol; -use super::super::public as p; use super::Value; +use crate::EvalContext; #[repr(transparent)] -#[derive(Constructor, Clone, PartialEq, Debug)] -pub struct AttrSet { - data: HashMap, +#[derive(Constructor, PartialEq)] +pub struct AttrSet { + data: HashMap>, } -impl From> for AttrSet { - fn from(data: HashMap) -> Self { +impl Debug for AttrSet { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Value::*; + write!(f, "{{ ")?; + for (k, v) in self.data.iter() { + match v { + List(_) => write!(f, "{k:?} = [ ... ]; ")?, + AttrSet(_) => write!(f, "{k:?} = {{ ... }}; ")?, + v => write!(f, "{k:?} = {v:?}; ")?, + } + } + write!(f, "}}") + } +} + +impl Clone for AttrSet { + fn clone(&self) -> Self { + AttrSet { + data: self.data.clone(), + } + } +} + +impl From>> for AttrSet { + fn from(data: HashMap>) -> Self { Self { data } } } -impl Deref for AttrSet { - type Target = HashMap; +impl Deref for AttrSet { + type Target = HashMap>; fn deref(&self) -> &Self::Target { &self.data } } -impl AttrSet { +impl AttrSet { pub fn with_capacity(cap: usize) -> Self { AttrSet { data: HashMap::with_capacity(cap), } } - pub fn push_attr_force(&mut self, sym: String, val: Value) { + pub fn push_attr_force(&mut self, sym: String, val: Value) { self.data.insert(sym, val); } - pub fn push_attr(&mut self, sym: String, val: Value) { + pub fn push_attr(&mut self, sym: String, val: Value) { if self.data.get(&sym).is_some() { todo!() } @@ -52,24 +76,23 @@ impl AttrSet { pub fn select( &self, mut path: impl DoubleEndedIterator>, - ) -> Result { - // .ok_or_else(|| Error::EvalError())), + ) -> Result> { let mut data = &self.data; let last = path.nth_back(0).unwrap(); for item in path { let item = item?; let Some(Value::AttrSet(attrs)) = data.get(&item) else { return Err(Error::EvalError(format!( - "attribute {} not found", + "attribute '{}' not found", Symbol::from(item) ))); }; data = attrs.as_inner(); } let last = last?; - data.get(&last) - .cloned() - .ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(last)))) + data.get(&last).cloned().ok_or_else(|| { + Error::EvalError(format!("attribute '{}' not found", Symbol::from(last))) + }) } pub fn has_attr( @@ -87,38 +110,38 @@ impl AttrSet { Ok(data.get(&last?).is_some()) } - pub fn update(&mut self, other: &AttrSet) { + pub fn update(&mut self, other: &Self) { for (k, v) in other.data.iter() { self.push_attr_force(k.clone(), v.clone()) } } - pub fn as_inner(&self) -> &HashMap { + pub fn as_inner(&self) -> &HashMap> { &self.data } - pub fn into_inner(self: Rc) -> Rc> { + pub fn into_inner(self: Rc) -> Rc>> { unsafe { core::mem::transmute(self) } } - pub fn from_inner(data: HashMap) -> Self { + pub fn from_inner(data: HashMap>) -> Self { Self { data } } - pub fn eq_impl(&self, other: &AttrSet) -> bool { + pub fn eq_impl(&self, other: &Self) -> bool { self.data.iter().len() == other.data.iter().len() && std::iter::zip( self.data.iter().sorted_by(|(a, _), (b, _)| a.cmp(b)), other.data.iter().sorted_by(|(a, _), (b, _)| a.cmp(b)), ) - .all(|((_, v1), (_, v2))| v1.eq_impl(v2)) + .all(|((k1, v1), (k2, v2))| k1 == k2 && v1.eq_impl(v2)) } - pub fn to_public(&self, engine: &Engine, seen: &mut HashSet) -> p::Value { + pub fn to_public(&self, ctx: &Ctx, seen: &mut HashSet>) -> p::Value { p::Value::AttrSet(p::AttrSet::new( self.data .iter() - .map(|(sym, value)| (sym.as_str().into(), value.to_public(engine, seen))) + .map(|(sym, value)| (sym.as_str().into(), value.to_public(ctx, seen))) .collect(), )) } diff --git a/evaluator/nixjit_eval/src/value/func.rs b/evaluator/nixjit_eval/src/value/func.rs new file mode 100644 index 0000000..cf6886f --- /dev/null +++ b/evaluator/nixjit_eval/src/value/func.rs @@ -0,0 +1,47 @@ +use std::rc::Rc; + +use derive_more::Constructor; + +use nixjit_error::Result; +use nixjit_ir::ExprId; + +use super::Value; +use crate::EvalContext; + +#[derive(Debug, Constructor)] +pub struct FuncApp { + pub body: ExprId, + pub args: Vec>, + pub frame: Vec>, +} + +impl Clone for FuncApp { + fn clone(&self) -> Self { + Self { + body: self.body, + args: self.args.clone(), + frame: self.frame.clone(), + } + } +} + +impl FuncApp { + pub fn call( + self: &mut Rc, + new_args: Vec>, + ctx: &mut Ctx, + ) -> Result> { + let FuncApp { body: expr, args, frame } = Rc::make_mut(self); + args.extend(new_args); + let (args, ret) = ctx.with_args_env(core::mem::take(args), |ctx| ctx.eval(*expr)); + let mut ret = ret?; + if let Value::Func(expr) = ret { + let frame = ctx.pop_frame(); + ret = Value::FuncApp(FuncApp::new(expr, args, frame).into()); + } else if let Value::FuncApp(func) = &mut ret { + todo!(); + let func = Rc::make_mut(func); + } + ret.ok() + } +} diff --git a/evaluator/nixjit_eval/src/value/list.rs b/evaluator/nixjit_eval/src/value/list.rs new file mode 100644 index 0000000..7a17937 --- /dev/null +++ b/evaluator/nixjit_eval/src/value/list.rs @@ -0,0 +1,86 @@ +use std::ops::Deref; +use std::fmt::Debug; + +use hashbrown::HashSet; + +use nixjit_value::List as PubList; +use nixjit_value::Value as PubValue; + +use super::Value; +use crate::EvalContext; + +#[derive(Default)] +pub struct List { + data: Vec>, +} + +impl Debug for List { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[ ")?; + for v in self.data.iter() { + write!(f, "{v:?} ")?; + } + write!(f, "]") + } +} + +impl Clone for List { + fn clone(&self) -> Self { + Self { + data: self.data.clone(), + } + } +} + +impl>>> From for List { + fn from(value: T) -> Self { + Self { data: value.into() } + } +} + +impl Deref for List { + type Target = [Value]; + fn deref(&self) -> &Self::Target { + &self.data + } +} + +impl List { + pub fn new() -> Self { + List { data: Vec::new() } + } + + pub fn with_capacity(cap: usize) -> Self { + List { + data: Vec::with_capacity(cap), + } + } + + pub fn push(&mut self, elem: Value) { + self.data.push(elem); + } + + pub fn concat(&mut self, other: &Self) { + for elem in other.data.iter() { + self.data.push(elem.clone()); + } + } + + pub fn into_inner(self) -> Vec> { + self.data + } + + pub fn eq_impl(&self, other: &Self) -> bool { + self.len() == other.len() + && core::iter::zip(self.iter(), other.iter()).all(|(a, b)| a.eq_impl(b)) + } + + pub fn to_public(&self, engine: &Ctx, seen: &mut HashSet>) -> PubValue { + PubValue::List(PubList::new( + self.data + .iter() + .map(|value| value.clone().to_public(engine, seen)) + .collect(), + )) + } +} diff --git a/src/ty/internal/mod.rs b/evaluator/nixjit_eval/src/value/mod.rs similarity index 62% rename from src/ty/internal/mod.rs rename to evaluator/nixjit_eval/src/value/mod.rs index e648d85..7f5ded6 100644 --- a/src/ty/internal/mod.rs +++ b/evaluator/nixjit_eval/src/value/mod.rs @@ -1,17 +1,19 @@ use std::hash::Hash; use std::rc::Rc; +use std::fmt::{write, Debug}; +use derive_more::TryUnwrap; use derive_more::{IsVariant, Unwrap}; -use func::PartialFunc; +use func::FuncApp; use hashbrown::HashSet; +use nixjit_ir::ExprId; use replace_with::replace_with_or_abort; -use super::common::*; -use super::public as p; +use nixjit_error::{Error, Result}; +use nixjit_value::Const; +use nixjit_value::Value as PubValue; -use crate::engine::Engine; -use crate::env::Env; -use crate::error::*; +use crate::EvalContext; mod attrset; mod func; @@ -24,24 +26,66 @@ pub use list::List; pub use primop::*; #[repr(C, u64)] -#[derive(IsVariant, Unwrap, Clone, Debug)] -pub enum Value { - Int(i64) = Self::INT, - Float(f64) = Self::FLOAT, - Bool(bool) = Self::BOOL, - String(String) = Self::STRING, - Null = Self::NULL, - Thunk(usize) = Self::THUNK, - AttrSet(Rc) = Self::ATTRSET, - List(List) = Self::LIST, - Catchable(String) = Self::CATCHABLE, - PrimOp(Rc) = Self::PRIMOP, - PartialPrimOp(Rc) = Self::PARTIAL_PRIMOP, - Func(usize) = Self::FUNC, - PartialFunc(Rc) = Self::PARTIAL_FUNC, +#[derive(IsVariant, TryUnwrap, Unwrap)] +pub enum Value { + Int(i64), + Float(f64), + Bool(bool), + String(String), + Null, + Thunk(usize), + AttrSet(Rc>), + List(Rc>), + Catchable(String), + PrimOp(Rc>), + PrimOpApp(Rc>), + Func(ExprId), + FuncApp(Rc>), } -impl Hash for Value { +impl Debug for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Value::*; + match self { + Int(x) => write!(f, "{x}"), + Float(x) => write!(f, "{x}"), + Bool(x) => write!(f, "{x}"), + Null => write!(f, "null"), + String(x) => write!(f, "{x}"), + AttrSet(x) => write!(f, "{x:?}"), + List(x) => write!(f, "{x:?}"), + Catchable(x) => write!(f, "{x}"), + Thunk(thunk) => write!(f, ""), + Func(func) => write!(f, ""), + FuncApp(func) => write!(f, "", func.body), + PrimOp(primop) => write!(f, "", primop.name), + PrimOpApp(primop) => write!(f, "", primop.name), + } + } +} + +impl Clone for Value { + fn clone(&self) -> Self { + use Value::*; + match self { + AttrSet(attrs) => AttrSet(attrs.clone()), + List(list) => List(list.clone()), + Catchable(catchable) => Catchable(catchable.clone()), + Int(x) => Int(*x), + Float(x) => Float(*x), + Bool(x) => Bool(*x), + String(x) => String(x.clone()), + Null => Null, + Thunk(expr) => Thunk(*expr), + PrimOp(primop) => PrimOp(primop.clone()), + PrimOpApp(primop) => PrimOpApp(primop.clone()), + Func(expr) => Func(*expr), + FuncApp(func) => FuncApp(func.clone()), + } + } +} + +impl Hash for Value { fn hash(&self, state: &mut H) { use Value::*; std::mem::discriminant(self).hash(state); @@ -53,7 +97,7 @@ impl Hash for Value { } } -impl Value { +impl Value { pub const INT: u64 = 0; pub const FLOAT: u64 = 1; pub const BOOL: u64 = 2; @@ -79,13 +123,13 @@ impl Value { (String(a), String(b)) => a.as_str().eq(b.as_str()), (Null, Null) => true, (AttrSet(a), AttrSet(b)) => a.eq_impl(b), - (List(a), List(b)) => a.eq(b), + (List(a), List(b)) => a.eq_impl(b), _ => false, } } } -impl PartialEq for Value { +impl PartialEq for Value { fn eq(&self, other: &Self) -> bool { use Value::*; match (self, other) { @@ -96,27 +140,27 @@ impl PartialEq for Value { } } -impl Eq for Value {} +impl Eq for Value {} #[derive(IsVariant, Unwrap, Clone)] -pub enum ValueAsRef<'v> { +pub enum ValueAsRef<'v, Ctx: EvalContext> { Int(i64), Float(f64), Bool(bool), String(&'v String), Null, Thunk(usize), - AttrSet(&'v AttrSet), - List(&'v List), + AttrSet(&'v AttrSet), + List(&'v List), Catchable(&'v str), - PrimOp(&'v PrimOp), - PartialPrimOp(&'v PartialPrimOp), - Func(usize), - PartialFunc(&'v PartialFunc), + PrimOp(&'v PrimOp), + PartialPrimOp(&'v PrimOpApp), + Func(ExprId), + PartialFunc(&'v FuncApp), } -impl Value { - pub fn as_ref(&self) -> ValueAsRef<'_> { +impl Value { + pub fn as_ref(&self) -> ValueAsRef<'_, Ctx> { use Value::*; use ValueAsRef as R; match self { @@ -130,13 +174,13 @@ impl Value { List(x) => R::List(x), Catchable(x) => R::Catchable(x), PrimOp(x) => R::PrimOp(x), - PartialPrimOp(x) => R::PartialPrimOp(x), + PrimOpApp(x) => R::PartialPrimOp(x), Func(x) => R::Func(*x), - PartialFunc(x) => R::PartialFunc(x), + FuncApp(x) => R::PartialFunc(x), } } } -impl Value { +impl Value { pub fn ok(self) -> Result { Ok(self) } @@ -154,21 +198,21 @@ impl Value { List(_) => "list", Catchable(_) => unreachable!(), PrimOp(_) => "lambda", - PartialPrimOp(_) => "lambda", + PrimOpApp(_) => "lambda", Func(_) => "lambda", - PartialFunc(..) => "lambda", + FuncApp(..) => "lambda", } } pub fn callable(&self) -> bool { match self { - Value::PrimOp(_) | Value::PartialPrimOp(_) | Value::Func(_) => true, + Value::PrimOp(_) | Value::PrimOpApp(_) | Value::Func(_) => true, Value::AttrSet(_) => todo!(), _ => false, } } - pub fn call(&mut self, args: Vec, engine: &mut Engine, env: &mut Env) -> Result<()> { + pub fn call(&mut self, args: Vec, ctx: &mut Ctx) -> Result<()> { use Value::*; for arg in args.iter() { if matches!(arg, Value::Catchable(_)) { @@ -177,95 +221,18 @@ impl Value { } } *self = match self { - PrimOp(func) => func.call(args, engine), - PartialPrimOp(func) => func.call(args, engine), - PartialFunc(func) => { - let self::PartialFunc { - idx, - args: old_args, - frame, - } = Rc::make_mut(func); - let idx = *idx; - let len = args.len() + old_args.len(); - env.reserve_args(len); - env.push_args(core::mem::take(old_args)); - let mut args = args.into_iter().peekable(); - env.push_arg(args.next().unwrap()); - env.resume_stack(core::mem::take(frame)); - engine.eval_func_deps(idx, env)?; - let mut ret = engine.call_func(idx, env)?; - while args.peek().is_some() { - match ret { - Value::Func(func) => { - env.push_arg(args.next().unwrap()); - engine.eval_func_deps(idx, env)?; - ret = engine.call_func(func, env)?; - } - Value::PartialFunc(_) => { - todo!() - } - Value::PrimOp(primop) => { - ret = primop.call(args.collect(), engine)?; - break; - } - Value::PartialPrimOp(mut primop) => { - ret = primop.call(args.collect(), engine)?; - break; - } - _ => todo!(), - } - } - let frame = env.pop_frame(); - if let Value::Func(idx) = ret { - let args = env.pop_args(len); - ret = PartialFunc(self::PartialFunc::new(idx, args, frame).into()); - } else if let Value::PartialFunc(func) = &mut ret { - let args = env.pop_args(len); + PrimOp(func) => func.call(args, ctx), + PrimOpApp(func) => func.call(args, ctx), + FuncApp(func) => func.call(args, ctx), + &mut Func(expr) => { + let (args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(expr)); + let mut ret = ret?; + if let Value::Func(expr) = ret { + let frame = ctx.pop_frame(); + ret = Value::FuncApp(self::FuncApp::new(expr, args, frame).into()); + } else if let Value::FuncApp(func) = &mut ret { + todo!(); let func = Rc::make_mut(func); - func.args.extend(args); - } else { - env.drop_args(len); - } - ret.ok() - } - &mut Func(idx) => { - let len = args.len(); - env.reserve_args(len); - let mut args = args.into_iter().peekable(); - env.push_arg(args.next().unwrap()); - engine.eval_func_deps(idx, env)?; - let mut ret = engine.call_func(idx, env)?; - while args.peek().is_some() { - match ret { - Value::Func(func) => { - env.push_arg(args.next().unwrap()); - engine.eval_func_deps(idx, env)?; - ret = engine.call_func(func, env)?; - } - Value::PartialFunc(_) => { - todo!() - } - Value::PrimOp(primop) => { - ret = primop.call(args.collect(), engine)?; - break; - } - Value::PartialPrimOp(mut primop) => { - ret = primop.call(args.collect(), engine)?; - break; - } - _ => todo!(), - } - } - let frame = env.pop_frame(); - if let Value::Func(idx) = ret { - let args = env.pop_args(len); - ret = PartialFunc(self::PartialFunc::new(idx, args, frame).into()); - } else if let Value::PartialFunc(func) = &mut ret { - let args = env.pop_args(len); - let func = Rc::make_mut(func); - func.args.extend(args); - } else { - env.drop_args(len); } ret.ok() } @@ -404,7 +371,7 @@ impl Value { pub fn push(&mut self, elem: Self) -> &mut Self { use Value::*; if let List(list) = self { - list.push(elem); + Rc::make_mut(list).push(elem); } else if let Catchable(_) = self { } else if let Catchable(_) = elem { *self = elem; @@ -422,7 +389,7 @@ impl Value { } match (self, other) { (List(a), List(b)) => { - a.concat(&b); + Rc::make_mut(a).concat(&b); } (Catchable(_), _) => (), _ => todo!(), @@ -519,39 +486,31 @@ impl Value { self } - pub fn force(&mut self, engine: &mut Engine, env: &mut Env) -> Result<&mut Self> { - if let &mut Value::Thunk(idx) = self { - *self = env.lookup_stack(idx).clone(); - } - Ok(self) - } - - pub fn to_public(&self, engine: &Engine, seen: &mut HashSet) -> p::Value { - use self::Value::*; - use p::Value; + pub fn to_public(&self, ctx: &Ctx, seen: &mut HashSet>) -> PubValue { + use Value::*; if seen.contains(self) { - return Value::Repeated; + return PubValue::Repeated; } match self { AttrSet(attrs) => { seen.insert(self.clone()); - attrs.to_public(engine, seen) + attrs.to_public(ctx, seen) } List(list) => { seen.insert(self.clone()); - list.to_public(engine, seen) + list.to_public(ctx, seen) } - Catchable(catchable) => Value::Catchable(catchable.clone().into()), - Int(x) => Value::Const(Const::Int(*x)), - Float(x) => Value::Const(Const::Float(*x)), - Bool(x) => Value::Const(Const::Bool(*x)), - String(x) => Value::String(x.clone()), - Null => Value::Const(Const::Null), - Thunk(_) => Value::Thunk, - PrimOp(primop) => Value::PrimOp(primop.name), - PartialPrimOp(primop) => Value::PartialPrimOp(primop.name), - Func(_) => Value::Func, - PartialFunc(..) => Value::Func, + Catchable(catchable) => PubValue::Catchable(catchable.clone().into()), + Int(x) => PubValue::Const(Const::Int(*x)), + Float(x) => PubValue::Const(Const::Float(*x)), + Bool(x) => PubValue::Const(Const::Bool(*x)), + String(x) => PubValue::String(x.clone()), + Null => PubValue::Const(Const::Null), + Thunk(_) => PubValue::Thunk, + PrimOp(primop) => PubValue::PrimOp(primop.name), + PrimOpApp(primop) => PubValue::PrimOpApp(primop.name), + Func(_) => PubValue::Func, + FuncApp(..) => PubValue::Func, } } } diff --git a/src/ty/internal/primop.rs b/evaluator/nixjit_eval/src/value/primop.rs similarity index 53% rename from src/ty/internal/primop.rs rename to evaluator/nixjit_eval/src/value/primop.rs index 430d5fc..eed6f83 100644 --- a/src/ty/internal/primop.rs +++ b/evaluator/nixjit_eval/src/value/primop.rs @@ -2,31 +2,25 @@ use std::rc::Rc; use derive_more::Constructor; -use crate::engine::Engine; -use crate::error::Result; +use nixjit_error::Result; use super::Value; +use crate::EvalContext; #[derive(Debug, Clone, Constructor)] -pub struct PrimOp { +pub struct PrimOp { pub name: &'static str, arity: usize, - func: fn(Vec, &Engine) -> Result, + func: fn(Vec>, &Ctx) -> Result>, } -impl PartialEq for PrimOp { - fn eq(&self, _: &Self) -> bool { - false - } -} - -impl PrimOp { - pub fn call(&self, args: Vec, ctx: &Engine) -> Result { +impl PrimOp { + pub fn call(&self, args: Vec>, ctx: &Ctx) -> Result> { if args.len() > self.arity { todo!() } if self.arity > args.len() { - Value::PartialPrimOp(Rc::new(PartialPrimOp { + Value::PrimOpApp(Rc::new(PrimOpApp { name: self.name, arity: self.arity - args.len(), args, @@ -39,22 +33,27 @@ impl PrimOp { } } -#[derive(Clone, Debug)] -pub struct PartialPrimOp { +#[derive(Debug)] +pub struct PrimOpApp { pub name: &'static str, arity: usize, - args: Vec, - func: fn(Vec, &Engine) -> Result, + args: Vec>, + func: fn(Vec>, &Ctx) -> Result>, } -impl PartialEq for PartialPrimOp { - fn eq(&self, _: &Self) -> bool { - false +impl Clone for PrimOpApp { + fn clone(&self) -> Self { + Self { + name: self.name, + arity: self.arity, + args: self.args.clone(), + func: self.func, + } } } -impl PartialPrimOp { - pub fn call(self: &mut Rc, args: Vec, ctx: &Engine) -> Result { +impl PrimOpApp { + pub fn call(self: &mut Rc, args: Vec>, ctx: &Ctx) -> Result> { if self.arity < args.len() { todo!() } @@ -69,7 +68,7 @@ impl PartialPrimOp { None } }) else { - return Value::PartialPrimOp(self.clone()).ok(); + return Value::PrimOpApp(self.clone()).ok(); }; ret } diff --git a/src/ty/internal/string.rs b/evaluator/nixjit_eval/src/value/string.rs similarity index 100% rename from src/ty/internal/string.rs rename to evaluator/nixjit_eval/src/value/string.rs diff --git a/evaluator/nixjit_hir/Cargo.toml b/evaluator/nixjit_hir/Cargo.toml new file mode 100644 index 0000000..980b8c7 --- /dev/null +++ b/evaluator/nixjit_hir/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "nixjit_hir" +description = "The high-level intermediate representation (HIR) for nixjit." +version = "0.1.0" +edition = "2024" + +[dependencies] +derive_more = { version = "2.0", features = ["full"] } +hashbrown = "0.15" +itertools = "0.14" +rnix = "0.12" + +nixjit_error = { path = "../nixjit_error" } +nixjit_ir = { path = "../nixjit_ir" } +nixjit_macros = { path = "../nixjit_macros" } +nixjit_value = { path = "../nixjit_value" } diff --git a/evaluator/nixjit_hir/src/downgrade.rs b/evaluator/nixjit_hir/src/downgrade.rs new file mode 100644 index 0000000..f3b14a4 --- /dev/null +++ b/evaluator/nixjit_hir/src/downgrade.rs @@ -0,0 +1,357 @@ +//! This module handles the "downgrading" of the `rnix` Abstract Syntax Tree (AST) +//! into the High-level Intermediate Representation (HIR). The term "downgrade" is used +//! because the process moves from a concrete syntax tree, which is very detailed about +//! source code structure (like parentheses and whitespace), to a more abstract, +//! semantically-focused representation. +//! +//! The core of this module is the `Downgrade` trait, which defines a standard way to +//! convert different AST node types into their corresponding HIR representations. + +use rnix::ast::{self, Expr}; + +use nixjit_error::{Error, Result}; +use nixjit_ir as ir; + +use super::*; + +/// A trait for converting (downgrading) an `rnix` AST node into an HIR expression. +pub trait Downgrade { + /// Performs the downgrade conversion. + /// + /// # Arguments + /// * `self` - The `rnix` AST node to convert. + /// * `ctx` - The context for the conversion, used for allocating new HIR expressions. + /// + /// # Returns + /// A `Result` containing the `ExprId` of the newly created HIR expression, or an error. + fn downgrade(self, ctx: &mut Ctx) -> Result; +} + +/// The main entry point for downgrading any `rnix` expression. +impl Downgrade for Expr { + fn downgrade(self, ctx: &mut Ctx) -> Result { + use Expr::*; + match self { + // Dispatch to the specific implementation for each expression type. + Apply(apply) => apply.downgrade(ctx), + Assert(assert) => assert.downgrade(ctx), + Error(error) => Err(self::Error::DowngradeError(error.to_string())), + IfElse(ifelse) => ifelse.downgrade(ctx), + Select(select) => select.downgrade(ctx), + Str(str) => str.downgrade(ctx), + Path(path) => path.downgrade(ctx), + Literal(lit) => lit.downgrade(ctx), + Lambda(lambda) => lambda.downgrade(ctx), + LegacyLet(let_) => let_.downgrade(ctx), + LetIn(letin) => letin.downgrade(ctx), + List(list) => list.downgrade(ctx), + BinOp(op) => op.downgrade(ctx), + AttrSet(attrs) => attrs.downgrade(ctx), + UnaryOp(op) => op.downgrade(ctx), + Ident(ident) => ident.downgrade(ctx), + With(with) => with.downgrade(ctx), + HasAttr(has) => has.downgrade(ctx), + // Parentheses and the root node are transparent; we just downgrade their contents. + Paren(paren) => paren.expr().unwrap().downgrade(ctx), + Root(root) => root.expr().unwrap().downgrade(ctx), + } + } +} + +impl Downgrade for ast::Assert { + fn downgrade(self, ctx: &mut Ctx) -> Result { + let assertion = self.condition().unwrap().downgrade(ctx)?; + let expr = self.body().unwrap().downgrade(ctx)?; + Ok(ctx.new_expr(Assert { assertion, expr }.to_hir())) + } +} + +impl Downgrade for ast::IfElse { + fn downgrade(self, ctx: &mut Ctx) -> Result { + let cond = self.condition().unwrap().downgrade(ctx)?; + let consq = self.body().unwrap().downgrade(ctx)?; + let alter = self.else_body().unwrap().downgrade(ctx)?; + Ok(ctx.new_expr(If { cond, consq, alter }.to_hir())) + } +} + +/// Downgrades a path expression. +/// A path can be a simple literal or contain interpolated expressions. +/// If it contains interpolations, it's converted into a `ConcatStrings` HIR node +/// which is then wrapped in a `Path` node. +impl Downgrade for ast::Path { + fn downgrade(self, ctx: &mut Ctx) -> Result { + let parts = self + .parts() + .map(|part| match part { + ast::InterpolPart::Literal(lit) => Ok(ctx.new_expr( + Str { + val: lit.to_string(), + } + .to_hir(), + )), + ast::InterpolPart::Interpolation(interpol) => { + interpol.expr().unwrap().downgrade(ctx) + } + }) + .collect::>>()?; + let expr = if parts.len() == 1 { + // If there's only one part, it's a simple string, no concatenation needed. + parts.into_iter().next().unwrap() + } else { + // Multiple parts (e.g., `./${name}.txt`) require string concatenation. + ctx.new_expr(ConcatStrings { parts }.to_hir()) + }; + Ok(ctx.new_expr(Path { expr }.to_hir())) + } +} + +/// Downgrades a string expression. +/// A string can be a simple literal or contain interpolated expressions. +/// If it contains interpolations, it's converted into a `ConcatStrings` HIR node. +impl Downgrade for ast::Str { + fn downgrade(self, ctx: &mut Ctx) -> Result { + let parts = self + .normalized_parts() + .into_iter() + .map(|part| match part { + ast::InterpolPart::Literal(lit) => Ok(ctx.new_expr(Str { val: lit }.to_hir())), + ast::InterpolPart::Interpolation(interpol) => { + interpol.expr().unwrap().downgrade(ctx) + } + }) + .collect::>>()?; + Ok(if parts.len() == 1 { + // If there's only one part, it's a simple string, no concatenation needed. + parts.into_iter().next().unwrap() + } else { + // Multiple parts (e.g., "hello ${name}") require string concatenation. + ctx.new_expr(ConcatStrings { parts }.to_hir()) + }) + } +} + +impl Downgrade for ast::Literal { + fn downgrade(self, ctx: &mut Ctx) -> Result { + Ok(ctx.new_expr(match self.kind() { + ast::LiteralKind::Integer(int) => Const::from(int.value().unwrap()).to_hir(), + ast::LiteralKind::Float(float) => Const::from(float.value().unwrap()).to_hir(), + ast::LiteralKind::Uri(uri) => Str { + val: uri.to_string(), + } + .to_hir(), + })) + } +} + +impl Downgrade for ast::Ident { + fn downgrade(self, ctx: &mut Ctx) -> Result { + let sym = self.ident_token().unwrap().to_string(); + Ok(ctx.new_expr(Var { sym }.to_hir())) + } +} + +impl Downgrade for ast::AttrSet { + fn downgrade(self, ctx: &mut Ctx) -> Result { + let rec = self.rec_token().is_some(); + let mut attrs = downgrade_attrs(self, ctx)?; + attrs.rec = rec; + Ok(ctx.new_expr(attrs.to_hir())) + } +} + +impl Downgrade for ast::List { + fn downgrade(self, ctx: &mut Ctx) -> Result { + let mut items = Vec::with_capacity(self.items().size_hint().0); + for item in self.items() { + items.push(item.downgrade(ctx)?) + } + Ok(ctx.new_expr(List { items }.to_hir())) + } +} + +impl Downgrade for ast::BinOp { + fn downgrade(self, ctx: &mut Ctx) -> Result { + let lhs = self.lhs().unwrap().downgrade(ctx)?; + let rhs = self.rhs().unwrap().downgrade(ctx)?; + let kind = self.operator().unwrap().into(); + Ok(ctx.new_expr(BinOp { lhs, rhs, kind }.to_hir())) + } +} + +impl Downgrade for ast::HasAttr { + fn downgrade(self, ctx: &mut Ctx) -> Result { + let lhs = self.expr().unwrap().downgrade(ctx)?; + let rhs = downgrade_attrpath(self.attrpath().unwrap(), ctx)?; + Ok(ctx.new_expr(HasAttr { lhs, rhs }.to_hir())) + } +} + +impl Downgrade for ast::UnaryOp { + fn downgrade(self, ctx: &mut Ctx) -> Result { + let rhs = self.expr().unwrap().downgrade(ctx)?; + let kind = self.operator().unwrap().into(); + Ok(ctx.new_expr(UnOp { rhs, kind }.to_hir())) + } +} + +impl Downgrade for ast::Select { + fn downgrade(self, ctx: &mut Ctx) -> Result { + let expr = self.expr().unwrap().downgrade(ctx)?; + let attrpath = downgrade_attrpath(self.attrpath().unwrap(), ctx)?; + let default = if let Some(default) = self.default_expr() { + Some(default.downgrade(ctx)?) + } else { + None + }; + Ok(ctx.new_expr( + Select { + expr, + attrpath, + default, + } + .to_hir(), + )) + } +} + +/// Downgrades a `legacy let`, which is essentially a recursive attribute set. +/// The body of the `let` is accessed via `let.body`. +impl Downgrade for ast::LegacyLet { + fn downgrade(self, ctx: &mut Ctx) -> Result { + let mut attrs = downgrade_attrs(self, ctx)?; + attrs.rec = true; + let expr = ctx.new_expr(attrs.to_hir()); + // The result of a `legacy let` is the `body` attribute of the resulting set. + let attrpath = vec![Attr::Str("body".into())]; + Ok(ctx.new_expr( + Select { + expr, + attrpath, + default: None, + } + .to_hir(), + )) + } +} + +impl Downgrade for ast::LetIn { + fn downgrade(self, ctx: &mut Ctx) -> Result { + let body = self.body().unwrap().downgrade(ctx)?; + let bindings = downgrade_static_attrs(self, ctx)?; + Ok(ctx.new_expr(Let { bindings, body }.to_hir())) + } +} + +impl Downgrade for ast::With { + fn downgrade(self, ctx: &mut Ctx) -> Result { + let namespace = self.namespace().unwrap().downgrade(ctx)?; + let expr = self.body().unwrap().downgrade(ctx)?; + Ok(ctx.new_expr(With { namespace, expr }.to_hir())) + } +} + +impl Downgrade for ast::Lambda { + fn downgrade(self, ctx: &mut Ctx) -> Result { + let param = downgrade_param(self.param().unwrap(), ctx)?; + let mut body = self.body().unwrap().downgrade(ctx)?; + + // Desugar pattern matching in function arguments into a `let` expression. + // For example, `({ a, b ? 2 }): a + b` is desugared into: + // `arg: let a = arg.a; b = arg.b or 2; in a + b` + if let Param::Formals { formals, alias, .. } = ¶m { + // `Arg` represents the raw argument (the attribute set) passed to the function. + let arg = ctx.new_expr(Hir::Arg(Arg)); + let mut bindings: HashMap<_, _> = formals + .iter() + .map(|&(ref k, default)| { + // For each formal parameter, create a `Select` expression to extract it from the argument set. + ( + k.clone(), + ctx.new_expr( + Select { + expr: arg, + attrpath: vec![Attr::Str(k.clone())], + default, + } + .to_hir(), + ), + ) + }) + .collect(); + // If there's an alias (`... }@alias`), bind the alias name to the raw argument set. + if let Some(alias) = alias { + bindings.insert( + alias.clone(), + ctx.new_expr(Var { sym: alias.clone() }.to_hir()), + ); + } + // Wrap the original function body in the new `let` expression. + let let_ = Let { bindings, body }; + body = ctx.new_expr(let_.to_hir()); + } + let ident; + let required; + let allowed; + match param { + Param::Ident(id) => { + ident = Some(id); + required = None; + allowed = None; + } + Param::Formals { + formals, + ellipsis, + alias, + } => { + ident = alias; + required = Some( + formals + .iter() + .cloned() + .filter(|(_, default)| default.is_none()) + .map(|(k, _)| k) + .collect(), + ); + allowed = if ellipsis { + None + } else { + Some( + formals + .into_iter() + .filter(|(_, default)| default.is_some()) + .map(|(k, _)| k) + .collect(), + ) + }; + } + } + let param = ir::Param { + ident, + required, + allowed, + }; + // The function's body and parameters are now stored directly in the `Func` node. + Ok(ctx.new_expr(Func { body, param }.to_hir())) + } +} + +/// Downgrades a function application. +/// The `rnix` AST represents chained function calls as nested `Apply` nodes, +/// e.g., `f a b` is parsed as `(f a) b`. This implementation unnests these +/// calls into a single `Call` HIR node with a list of arguments. +impl Downgrade for ast::Apply { + fn downgrade(self, ctx: &mut Ctx) -> Result { + let mut args = vec![self.argument().unwrap().downgrade(ctx)?]; + let mut func = self.lambda().unwrap(); + // Traverse the chain of nested `Apply` nodes to collect all arguments. + while let ast::Expr::Apply(call) = func { + func = call.lambda().unwrap(); + args.push(call.argument().unwrap().downgrade(ctx)?); + } + let func = func.downgrade(ctx)?; + // The arguments were collected in reverse order, so fix that. + args.reverse(); + Ok(ctx.new_expr(Call { func, args }.to_hir())) + } +} diff --git a/evaluator/nixjit_hir/src/lib.rs b/evaluator/nixjit_hir/src/lib.rs new file mode 100644 index 0000000..cf8f6c9 --- /dev/null +++ b/evaluator/nixjit_hir/src/lib.rs @@ -0,0 +1,202 @@ +//! The high-level intermediate representation (HIR) for nixjit. +//! +//! This module defines the data structures for the HIR, which is a more abstract and +//! semantically rich representation of the original Nix code compared to the raw AST from `rnix`. +//! It's designed to be easily translatable from the AST and serves as a stepping stone +//! towards the lower-level IR (`nixjit_lir`). +//! +//! The key components are: +//! - `Hir`: An enum representing all possible expression types in the HIR. +//! - `Downgrade`: A trait for converting `rnix::ast` nodes into HIR expressions. +//! - `DowngradeContext`: A trait that provides the necessary context for the conversion, +//! such as allocating new expressions and functions. + +use derive_more::{IsVariant, TryUnwrap, Unwrap}; +use hashbrown::HashMap; + +use nixjit_error::{Error, Result}; +use nixjit_ir::{ + Assert, Attr, AttrSet, BinOp, Call, ConcatStrings, Const, ExprId, Func, HasAttr, If, List, + Path, Select, Str, UnOp, Var, With, +}; +use nixjit_macros::ir; +use nixjit_value::format_symbol; + +mod downgrade; +mod utils; +use utils::*; + +pub use downgrade::Downgrade; + +/// A context for the AST-to-HIR downgrading process. +/// +/// This trait abstracts the storage of HIR expressions and functions, allowing the +/// `downgrade` implementations to be generic over the specific context implementation. +pub trait DowngradeContext { + /// Allocates a new HIR expression in the context and returns its ID. + fn new_expr(&mut self, expr: Hir) -> ExprId; + + /// Provides temporary access to an immutable expression for inspection or use. + fn with_expr(&self, id: ExprId, f: impl FnOnce(&Hir, &Self) -> T) -> T; + + /// Provides temporary mutable access to an expression. + fn with_expr_mut(&mut self, id: ExprId, f: impl FnOnce(&mut Hir, &mut Self) -> T) -> T; +} + +ir! { + Hir, + + // Represents an attribute set, e.g., `{ a = 1; b = 2; }`. + AttrSet, + // Represents a list, e.g., `[1 2 3]`. + List, + // Represents a "has attribute" check, e.g., `attrs ? a`. + HasAttr, + // Represents a binary operation, e.g., `a + b`. + BinOp, + // Represents a unary operation, e.g., `-a`. + UnOp, + // Represents an attribute selection, e.g., `attrs.a` or `attrs.a or defaultValue`. + Select, + // Represents an if-then-else expression. + If, + // Represents a function definition (lambda). + Func, + // Represents a function call. + Call, + // Represents a `with` expression, e.g., `with pkgs; stdenv.mkDerivation { ... }`. + With, + // Represents an `assert` expression. + Assert, + // Represents the concatenation of strings, often from interpolated strings. + ConcatStrings, + // Represents a constant value (integer, float, boolean, null). + Const, + // Represents a simple string literal. + Str, + // Represents a variable lookup by its symbol/name. + Var, + // Represents a path expression. + Path, + // Represents a `let ... in ...` binding. + Let { pub bindings: HashMap, pub body: ExprId }, + // Represents a function argument lookup. + Arg, +} + +#[derive(Debug)] +pub struct Arg; + +/// A trait defining operations on attribute sets within the HIR. +trait Attrs { + /// Inserts a value into the attribute set at a given path. + /// + /// # Example + /// `insert([a, b], value)` corresponds to `a.b = value;`. + fn insert( + &mut self, + path: Vec, + value: ExprId, + ctx: &mut impl DowngradeContext, + ) -> Result<()>; + + /// Internal helper for recursively inserting an attribute. + fn _insert( + &mut self, + path: impl Iterator, + name: Attr, + value: ExprId, + ctx: &mut impl DowngradeContext, + ) -> Result<()>; +} + +impl Attrs for AttrSet { + fn _insert( + &mut self, + mut path: impl Iterator, + name: Attr, + value: ExprId, + ctx: &mut impl DowngradeContext, + ) -> Result<()> { + if let Some(attr) = path.next() { + // If the path is not yet exhausted, we need to recurse deeper. + match attr { + Attr::Str(ident) => { + // If the next attribute is a static string. + if let Some(&id) = self.stcs.get(&ident) { + // If a sub-attrset already exists, recurse into it. + ctx.with_expr_mut(id, |expr, ctx| { + expr.as_mut() + .try_unwrap_attr_set() + .map_err(|_| { + // This path segment exists but is not an attrset. + Error::DowngradeError(format!( + r#"attribute '{}' already defined"#, + format_symbol(&ident) + )) + }) + .and_then(|attrs| attrs._insert(path, name, value, ctx)) + })?; + } else { + // Create a new sub-attrset because this path doesn't exist yet. + let mut attrs = AttrSet::default(); + attrs._insert(path, name, value, ctx)?; + let attrs = ctx.new_expr(attrs.to_hir()); + self.stcs.insert(ident, attrs); + } + Ok(()) + } + Attr::Dynamic(dynamic) => { + // If the next attribute is a dynamic expression. + let mut attrs = AttrSet::default(); + attrs._insert(path, name, value, ctx)?; + self.dyns.push((dynamic, ctx.new_expr(attrs.to_hir()))); + Ok(()) + } + } + } else { + // This is the final attribute in the path, so insert the value here. + match name { + Attr::Str(ident) => { + if self.stcs.insert(ident.clone(), value).is_some() { + return Err(Error::DowngradeError(format!( + r#"attribute '{}' already defined"#, + format_symbol(&ident) + ))); + } + } + Attr::Dynamic(dynamic) => { + self.dyns.push((dynamic, value)); + } + } + Ok(()) + } + } + + fn insert( + &mut self, + path: Vec, + value: ExprId, + ctx: &mut impl DowngradeContext, + ) -> Result<()> { + let mut path = path.into_iter(); + // The last part of the path is the name of the attribute to be inserted. + let name = path.next_back().unwrap(); + self._insert(path, name, value, ctx) + } +} + +#[derive(Clone, Debug)] +enum Param { + /// A simple parameter, e.g., `x: ...`. + Ident(String), + /// A pattern-matching parameter (formals), e.g., `{ a, b ? 2, ... }@args: ...`. + Formals { + /// The individual formal parameters, with optional default values. + formals: Vec<(String, Option)>, + /// Whether an ellipsis (`...`) is present, allowing extra arguments. + ellipsis: bool, + /// An optional alias for the entire argument set, e.g., `args @ { ... }`. + alias: Option, + }, +} diff --git a/evaluator/nixjit_hir/src/utils.rs b/evaluator/nixjit_hir/src/utils.rs new file mode 100644 index 0000000..c5004f9 --- /dev/null +++ b/evaluator/nixjit_hir/src/utils.rs @@ -0,0 +1,223 @@ +//! This module provides utility functions for the AST-to-HIR downgrade process. +//! These functions handle common, often complex, patterns in the `rnix` AST, +//! such as parsing parameters, attribute sets, and `inherit` statements. +//! They are helpers to the main `Downgrade` trait implementations. + +use hashbrown::HashMap; + +use rnix::ast; + +use nixjit_error::{Error, Result}; +use nixjit_ir::{Attr, AttrSet, ConcatStrings, ExprId, Select, Str, Var}; + +use super::ToHir; +use super::downgrade::Downgrade; +use super::{Attrs, DowngradeContext, Param}; + +/// Downgrades a function parameter from the AST. +pub fn downgrade_param(param: ast::Param, ctx: &mut impl DowngradeContext) -> Result { + match param { + ast::Param::IdentParam(ident) => Ok(Param::Ident(ident.to_string())), + ast::Param::Pattern(pattern) => downgrade_pattern(pattern, ctx), + } +} + +/// Downgrades a parameter pattern (formals) from the AST. +/// This handles `{ a, b ? 2, ... }@args` style parameters. +pub fn downgrade_pattern(pattern: ast::Pattern, ctx: &mut impl DowngradeContext) -> Result { + // Extract each formal parameter, downgrading its default value if it exists. + let formals = pattern + .pat_entries() + .map(|entry| { + let ident = entry.ident().unwrap().to_string(); + if entry.default().is_none() { + Ok((ident, None)) + } else { + entry + .default() + .unwrap() + .downgrade(ctx) + .map(|ok| (ident, Some(ok))) + } + }) + .collect::>>()?; + let ellipsis = pattern.ellipsis_token().is_some(); + let alias = pattern + .pat_bind() + .map(|alias| alias.ident().unwrap().to_string()); + Ok(Param::Formals { + formals, + ellipsis, + alias, + }) +} + +/// Downgrades the entries of an attribute set. +/// This handles `inherit` and `attrpath = value;` entries. +pub fn downgrade_attrs( + attrs: impl ast::HasEntry, + ctx: &mut impl DowngradeContext, +) -> Result { + let entries = attrs.entries(); + let mut attrs = AttrSet { + stcs: HashMap::new(), + dyns: Vec::new(), + rec: false, + }; + + for entry in entries { + match entry { + ast::Entry::Inherit(inherit) => downgrade_inherit(inherit, &mut attrs.stcs, ctx)?, + ast::Entry::AttrpathValue(value) => downgrade_attrpathvalue(value, &mut attrs, ctx)?, + } + } + + Ok(attrs) +} + +/// Downgrades attribute set entries for a `let...in` expression. +/// This is a stricter version of `downgrade_attrs` that disallows dynamic attributes, +/// as `let` bindings must be statically known. +pub fn downgrade_static_attrs( + attrs: impl ast::HasEntry, + ctx: &mut impl DowngradeContext, +) -> Result> { + let entries = attrs.entries(); + let mut attrs = AttrSet { + stcs: HashMap::new(), + dyns: Vec::new(), + rec: false, + }; + + for entry in entries { + match entry { + ast::Entry::Inherit(inherit) => downgrade_inherit(inherit, &mut attrs.stcs, ctx)?, + ast::Entry::AttrpathValue(value) => { + downgrade_static_attrpathvalue(value, &mut attrs, ctx)? + } + } + } + + Ok(attrs.stcs) +} + +/// Downgrades an `inherit` statement. +/// `inherit (from) a b;` is translated into `a = from.a; b = from.b;`. +/// `inherit a b;` is translated into `a = a; b = b;` (i.e., bringing variables into scope). +pub fn downgrade_inherit( + inherit: ast::Inherit, + stcs: &mut HashMap, + ctx: &mut impl DowngradeContext, +) -> Result<()> { + // Downgrade the `from` expression if it exists. + let from = if let Some(from) = inherit.from() { + Some(from.expr().unwrap().downgrade(ctx)?) + } else { + None + }; + for attr in inherit.attrs() { + let ident = match downgrade_attr(attr, ctx)? { + Attr::Str(ident) => ident, + _ => { + // `inherit` does not allow dynamic attributes. + return Err(Error::DowngradeError( + "dynamic attributes not allowed in inherit".to_string(), + )); + } + }; + let expr = from.map_or_else( + // If `from` is None, `inherit foo;` becomes `foo = foo;`. + || Var { sym: ident.clone() }.to_hir(), + // If `from` is Some, `inherit (from) foo;` becomes `foo = from.foo;`. + |expr| { + Select { + expr, + attrpath: vec![Attr::Str(ident.clone())], + default: None, + } + .to_hir() + }, + ); + if stcs.insert(ident, ctx.new_expr(expr)).is_some() { + // TODO: Handle or error on duplicate attribute definitions. + todo!() + } + } + Ok(()) +} + +/// Downgrades a single attribute key (part of an attribute path). +/// An attribute can be a static identifier, an interpolated string, or a dynamic expression. +pub fn downgrade_attr(attr: ast::Attr, ctx: &mut impl DowngradeContext) -> Result { + use ast::Attr::*; + use ast::InterpolPart::*; + match attr { + Ident(ident) => Ok(Attr::Str(ident.to_string())), + Str(string) => { + let parts = string.normalized_parts(); + if parts.is_empty() { + Ok(Attr::Str("".into())) + } else if parts.len() == 1 { + // If the string has only one part, it's either a literal or a single interpolation. + match parts.into_iter().next().unwrap() { + Literal(ident) => Ok(Attr::Str(ident)), + Interpolation(interpol) => { + Ok(Attr::Dynamic(interpol.expr().unwrap().downgrade(ctx)?)) + } + } + } else { + // If the string has multiple parts, it's an interpolated string that must be concatenated. + let parts = parts + .into_iter() + .map(|part| match part { + Literal(lit) => Ok(ctx.new_expr(self::Str { val: lit }.to_hir())), + Interpolation(interpol) => interpol.expr().unwrap().downgrade(ctx), + }) + .collect::>>()?; + Ok(Attr::Dynamic( + ctx.new_expr(ConcatStrings { parts }.to_hir()), + )) + } + } + Dynamic(dynamic) => Ok(Attr::Dynamic(dynamic.expr().unwrap().downgrade(ctx)?)), + } +} + +/// Downgrades an attribute path (e.g., `a.b."${c}".d`) into a `Vec`. +pub fn downgrade_attrpath( + attrpath: ast::Attrpath, + ctx: &mut impl DowngradeContext, +) -> Result> { + attrpath + .attrs() + .map(|attr| downgrade_attr(attr, ctx)) + .collect::>>() +} + +/// Downgrades an `attrpath = value;` expression and inserts it into an `AttrSet`. +pub fn downgrade_attrpathvalue( + value: ast::AttrpathValue, + attrs: &mut AttrSet, + ctx: &mut impl DowngradeContext, +) -> Result<()> { + let path = downgrade_attrpath(value.attrpath().unwrap(), ctx)?; + let value = value.value().unwrap().downgrade(ctx)?; + attrs.insert(path, value, ctx) +} + +/// A stricter version of `downgrade_attrpathvalue` for `let...in` bindings. +/// It ensures that the attribute path contains no dynamic parts. +pub fn downgrade_static_attrpathvalue( + value: ast::AttrpathValue, + attrs: &mut AttrSet, + ctx: &mut impl DowngradeContext, +) -> Result<()> { + let path = downgrade_attrpath(value.attrpath().unwrap(), ctx)?; + if path.iter().any(|attr| matches!(attr, Attr::Dynamic(_))) { + return Err(Error::DowngradeError( + "dynamic attributes not allowed".to_string(), + )); + } + let value = value.value().unwrap().downgrade(ctx)?; + attrs.insert(path, value, ctx) +} diff --git a/evaluator/nixjit_ir/Cargo.toml b/evaluator/nixjit_ir/Cargo.toml new file mode 100644 index 0000000..0d3d66e --- /dev/null +++ b/evaluator/nixjit_ir/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "nixjit_ir" +description = "The core data structures for the nixjit intermediate representation (IR)." +version = "0.1.0" +edition = "2024" + +[dependencies] +derive_more = { version = "2.0", features = ["full"] } +hashbrown = "0.15" +rnix = "0.12" + +nixjit_error = { path = "../nixjit_error" } +nixjit_value = { path = "../nixjit_value" } diff --git a/evaluator/nixjit_ir/src/lib.rs b/evaluator/nixjit_ir/src/lib.rs new file mode 100644 index 0000000..0684f1c --- /dev/null +++ b/evaluator/nixjit_ir/src/lib.rs @@ -0,0 +1,255 @@ +//! This crate defines the core data structures for the nixjit Intermediate Representation (IR). +//! +//! The IR provides a simplified, language-agnostic representation of Nix expressions, +//! serving as a bridge between the high-level representation (HIR) and the low-level +//! representation (LIR). It defines the fundamental building blocks like expression IDs, +//! function IDs, and structures for various expression types (e.g., binary operations, +//! attribute sets, function calls). +//! +//! These structures are designed to be generic and reusable across different stages of +//! the compiler. + +use rnix::ast; + +use derive_more::TryUnwrap; +use hashbrown::HashMap; + +use nixjit_value::Const as PubConst; + +/// A type-safe wrapper for an index into an expression table. +#[repr(transparent)] +#[derive(Debug, Clone, Copy)] +pub struct ExprId(usize); + +/// A type-safe wrapper for an index into a function table. +#[repr(transparent)] +#[derive(Debug, Clone, Copy)] +pub struct FuncId(usize); + +#[repr(transparent)] +#[derive(Debug, Clone, Copy)] +pub struct ArgIdx(usize); + +/// Represents a Nix attribute set. +#[derive(Debug, Default)] +pub struct AttrSet { + /// Statically known attributes (key is a string). + pub stcs: HashMap, + /// Dynamically computed attributes, where both the key and value are expressions. + pub dyns: Vec<(ExprId, ExprId)>, + /// `true` if this is a recursive attribute set (`rec { ... }`). + pub rec: bool, +} + +/// Represents a key in an attribute path. +#[derive(Clone, Debug, TryUnwrap)] +pub enum Attr { + /// A dynamic attribute key, which is an expression that must evaluate to a string. + Dynamic(ExprId), + /// A static attribute key. + Str(String), +} + +/// Represents a Nix list. +#[derive(Debug)] +pub struct List { + /// The expressions that are elements of the list. + pub items: Vec, +} + +/// Represents a "has attribute" check (`?` operator). +#[derive(Debug)] +pub struct HasAttr { + /// The expression to check for the attribute (the left-hand side). + pub lhs: ExprId, + /// The attribute path to look for (the right-hand side). + pub rhs: Vec, +} + +/// Represents a binary operation. +#[derive(Debug)] +pub struct BinOp { + pub lhs: ExprId, + pub rhs: ExprId, + pub kind: BinOpKind, +} + +/// The kinds of binary operations supported in Nix. +#[derive(Clone, Debug)] +pub enum BinOpKind { + // Arithmetic + Add, + Sub, + Div, + Mul, + + // Comparison + Eq, + Neq, + Lt, + Gt, + Leq, + Geq, + + // Logical + And, + Or, + Impl, + + // Set/String/Path operations + Con, // List concatenation (`++`) + Upd, // AttrSet update (`//`) + + // Not standard, but part of rnix AST + PipeL, + PipeR, +} + +impl From for BinOpKind { + fn from(op: ast::BinOpKind) -> Self { + use BinOpKind::*; + use ast::BinOpKind as kind; + match op { + kind::Concat => Con, + kind::Update => Upd, + kind::Add => Add, + kind::Sub => Sub, + kind::Mul => Mul, + kind::Div => Div, + kind::And => And, + kind::Equal => Eq, + kind::Implication => Impl, + kind::Less => Lt, + kind::LessOrEq => Leq, + kind::More => Gt, + kind::MoreOrEq => Geq, + kind::NotEqual => Neq, + kind::Or => Or, + kind::PipeLeft => PipeL, + kind::PipeRight => PipeR, + } + } +} + +/// Represents a unary operation. +#[derive(Debug)] +pub struct UnOp { + pub rhs: ExprId, + pub kind: UnOpKind, +} + +/// The kinds of unary operations. +#[derive(Clone, Debug)] +pub enum UnOpKind { + Neg, // Negation (`-`) + Not, // Logical not (`!`) +} + +impl From for UnOpKind { + fn from(value: ast::UnaryOpKind) -> Self { + match value { + ast::UnaryOpKind::Invert => UnOpKind::Not, + ast::UnaryOpKind::Negate => UnOpKind::Neg, + } + } +} + +/// Represents an attribute selection from an attribute set. +#[derive(Debug)] +pub struct Select { + /// The expression that should evaluate to an attribute set. + pub expr: ExprId, + /// The path of attributes to select. + pub attrpath: Vec, + /// An optional default value to return if the selection fails. + pub default: Option, +} + +/// Represents an `if-then-else` expression. +#[derive(Debug)] +pub struct If { + pub cond: ExprId, + pub consq: ExprId, // Consequence (then branch) + pub alter: ExprId, // Alternative (else branch) +} + +/// Represents a function value (a lambda). +#[derive(Debug)] +pub struct Func { + /// The body of the function + pub body: ExprId, + pub param: Param, +} + +#[derive(Debug)] +pub struct Param { + pub ident: Option, + pub required: Option>, + pub allowed: Option>, +} + +/// Represents a function call. +#[derive(Debug)] +pub struct Call { + /// The expression that evaluates to the function to be called. + pub func: ExprId, + /// The list of arguments to pass to the function. + pub args: Vec, +} + +/// Represents a `with` expression. +#[derive(Debug)] +pub struct With { + /// The namespace to bring into scope. + pub namespace: ExprId, + /// The expression to be evaluated within the new scope. + pub expr: ExprId, +} + +/// Represents an `assert` expression. +#[derive(Debug)] +pub struct Assert { + /// The condition to assert. + pub assertion: ExprId, + /// The expression to return if the assertion is true. + pub expr: ExprId, +} + +/// Represents the concatenation of multiple string expressions. +/// This is typically the result of downgrading an interpolated string. +#[derive(Debug)] +pub struct ConcatStrings { + pub parts: Vec, +} + +/// Represents a constant value (e.g., integer, float, boolean, null). +#[derive(Clone, Copy, Debug)] +pub struct Const { + pub val: PubConst, +} + +impl> From for Const { + fn from(value: T) -> Self { + Self { val: value.into() } + } +} + +/// Represents a simple, non-interpolated string literal. +#[derive(Debug)] +pub struct Str { + pub val: String, +} + +/// Represents a variable lookup by its name. +#[derive(Debug)] +pub struct Var { + pub sym: String, +} + +/// Represents a path literal. +#[derive(Debug)] +pub struct Path { + /// The expression that evaluates to the string content of the path. + /// This can be a simple `Str` or a `ConcatStrings` for interpolated paths. + pub expr: ExprId, +} diff --git a/evaluator/nixjit_jit/Cargo.toml b/evaluator/nixjit_jit/Cargo.toml new file mode 100644 index 0000000..7fc37e5 --- /dev/null +++ b/evaluator/nixjit_jit/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "nixjit_jit" +version = "0.1.0" +edition = "2024" + +[dependencies] +hashbrown = "0.15" + +cranelift = "0.122" +cranelift-module = "0.122" +cranelift-jit = "0.122" +cranelift-native = "0.122" + +nixjit_error = { path = "../nixjit_error" } +nixjit_eval = { path = "../nixjit_eval" } +nixjit_hir = { path = "../nixjit_hir" } +nixjit_ir = { path = "../nixjit_ir" } +nixjit_lir = { path = "../nixjit_lir" } +nixjit_value = { path = "../nixjit_value" } diff --git a/src/eval/jit/compile.rs b/evaluator/nixjit_jit/src/compile.rs similarity index 78% rename from src/eval/jit/compile.rs rename to evaluator/nixjit_jit/src/compile.rs index 5fbf7d4..a6b6a93 100644 --- a/src/eval/jit/compile.rs +++ b/evaluator/nixjit_jit/src/compile.rs @@ -1,18 +1,30 @@ use cranelift::codegen::ir::{self, StackSlot}; use cranelift::prelude::*; -use crate::ir::*; -use crate::ty::common as c; -use crate::ty::internal::Value; +use nixjit_eval::{EvalContext, Value}; +use nixjit_ir::*; +use nixjit_lir::Lir; -use super::JITContext; +use super::{Context, JITContext}; -pub trait JITCompile { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot; +pub trait JITCompile { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot; } -impl JITCompile for Attrs { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { +impl JITCompile for ExprId { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { + todo!() + } +} + +impl JITCompile for Lir { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { + todo!() + } +} + +impl JITCompile for AttrSet { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { let attrs = ctx.create_attrs(); for (k, v) in self.stcs.iter() { let v = v.compile(ctx, engine, env); @@ -22,8 +34,8 @@ impl JITCompile for Attrs { } } -impl JITCompile for List { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { +impl JITCompile for List { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { let array = ctx.alloc_array(self.items.len()); for (i, item) in self.items.iter().enumerate() { let item = item.compile(ctx, engine, env); @@ -48,19 +60,17 @@ impl JITCompile for List { } } -impl JITCompile for HasAttr { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { +impl JITCompile for HasAttr { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { todo!() } } -impl JITCompile for BinOp { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { +impl JITCompile for BinOp { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { use BinOpKind::*; let lhs = self.lhs.compile(ctx, engine, env); let rhs = self.rhs.compile(ctx, engine, env); - ctx.force(lhs, engine, env); - ctx.force(rhs, engine, env); let lhs_tag = ctx.get_tag(lhs); let rhs_tag = ctx.get_tag(rhs); let eq = ctx.builder.ins().icmp(IntCC::Equal, lhs_tag, rhs_tag); @@ -78,10 +88,10 @@ impl JITCompile for BinOp { let float_block = ctx.builder.create_block(); let float_check_block = ctx.builder.create_block(); - let is_int = ctx - .builder - .ins() - .icmp_imm(IntCC::Equal, lhs_tag, Value::INT as i64); + let is_int = + ctx.builder + .ins() + .icmp_imm(IntCC::Equal, lhs_tag, Value::::INT as i64); ctx.builder .ins() .brif(is_int, int_block, [], float_check_block, []); @@ -99,7 +109,7 @@ impl JITCompile for BinOp { let is_float = ctx.builder .ins() - .icmp_imm(IntCC::Equal, lhs_tag, Value::FLOAT as i64); + .icmp_imm(IntCC::Equal, lhs_tag, Value::::FLOAT as i64); ctx.builder .ins() .brif(is_float, float_block, [], default_block, []); @@ -131,10 +141,10 @@ impl JITCompile for BinOp { let float_block = ctx.builder.create_block(); let float_check_block = ctx.builder.create_block(); - let is_int = ctx - .builder - .ins() - .icmp_imm(IntCC::Equal, lhs_tag, Value::INT as i64); + let is_int = + ctx.builder + .ins() + .icmp_imm(IntCC::Equal, lhs_tag, Value::::INT as i64); ctx.builder .ins() .brif(is_int, int_block, [], float_check_block, []); @@ -152,7 +162,7 @@ impl JITCompile for BinOp { let is_float = ctx.builder .ins() - .icmp_imm(IntCC::Equal, lhs_tag, Value::FLOAT as i64); + .icmp_imm(IntCC::Equal, lhs_tag, Value::::FLOAT as i64); ctx.builder .ins() .brif(is_float, float_block, [], default_block, []); @@ -184,10 +194,10 @@ impl JITCompile for BinOp { let float_block = ctx.builder.create_block(); let float_check_block = ctx.builder.create_block(); - let is_int = ctx - .builder - .ins() - .icmp_imm(IntCC::Equal, lhs_tag, Value::INT as i64); + let is_int = + ctx.builder + .ins() + .icmp_imm(IntCC::Equal, lhs_tag, Value::::INT as i64); ctx.builder .ins() .brif(is_int, int_block, [], float_check_block, []); @@ -205,7 +215,7 @@ impl JITCompile for BinOp { let is_float = ctx.builder .ins() - .icmp_imm(IntCC::Equal, lhs_tag, Value::FLOAT as i64); + .icmp_imm(IntCC::Equal, lhs_tag, Value::::FLOAT as i64); ctx.builder .ins() .brif(is_float, float_block, [], default_block, []); @@ -235,10 +245,10 @@ impl JITCompile for BinOp { let bool_block = ctx.builder.create_block(); let non_bool_block = ctx.builder.create_block(); - let is_bool = ctx - .builder - .ins() - .icmp_imm(IntCC::Equal, lhs_tag, Value::BOOL as i64); + let is_bool = + ctx.builder + .ins() + .icmp_imm(IntCC::Equal, lhs_tag, Value::::BOOL as i64); ctx.builder .ins() .brif(is_bool, bool_block, [], non_bool_block, []); @@ -265,10 +275,10 @@ impl JITCompile for BinOp { let bool_block = ctx.builder.create_block(); let non_bool_block = ctx.builder.create_block(); - let is_bool = ctx - .builder - .ins() - .icmp_imm(IntCC::Equal, lhs_tag, Value::BOOL as i64); + let is_bool = + ctx.builder + .ins() + .icmp_imm(IntCC::Equal, lhs_tag, Value::::BOOL as i64); ctx.builder .ins() .brif(is_bool, bool_block, [], non_bool_block, []); @@ -311,25 +321,24 @@ impl JITCompile for BinOp { } } -impl JITCompile for UnOp { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { +impl JITCompile for UnOp { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { todo!() } } -impl JITCompile for Attr { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { +impl JITCompile for Attr { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { use Attr::*; match self { Str(string) => ctx.create_string(string), Dynamic(ir) => ir.compile(ctx, engine, env), - Strs(strings) => strings.compile(ctx, engine, env), } } } -impl JITCompile for Select { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { +impl JITCompile for Select { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { let val = self.expr.compile(ctx, engine, env); let attrpath = ctx.alloc_array(self.attrpath.len()); for (i, attr) in self.attrpath.iter().enumerate() { @@ -356,10 +365,9 @@ impl JITCompile for Select { } } -impl JITCompile for If { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { +impl JITCompile for If { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { let cond = self.cond.compile(ctx, engine, env); - ctx.force(cond, engine, env); let cond_type = ctx.builder.ins().stack_load(types::I64, cond, 0); let cond_value = ctx.builder.ins().stack_load(types::I64, cond, 8); @@ -370,10 +378,10 @@ impl JITCompile for If { let judge_block = ctx.builder.create_block(); let slot = ctx.alloca(); - let is_bool = ctx - .builder - .ins() - .icmp_imm(IntCC::Equal, cond_type, Value::BOOL as i64); + let is_bool = + ctx.builder + .ins() + .icmp_imm(IntCC::Equal, cond_type, Value::::BOOL as i64); ctx.builder .ins() .brif(is_bool, judge_block, [], error_block, []); @@ -415,21 +423,9 @@ impl JITCompile for If { } } -impl JITCompile for LoadFunc { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { - let slot = ctx.alloca(); - let tag = ctx.builder.ins().iconst(types::I64, Value::FUNC as i64); - let val = ctx.builder.ins().iconst(types::I64, self.idx as i64); - ctx.builder.ins().stack_store(tag, slot, 0); - ctx.builder.ins().stack_store(val, slot, 8); - slot - } -} - -impl JITCompile for Call { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { +impl JITCompile for Call { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { let func = self.func.compile(ctx, engine, env); - ctx.force(func, engine, env); let args = ctx.alloc_array(self.args.len()); for (i, arg) in self.args.iter().enumerate() { let arg = arg.compile(ctx, engine, env); @@ -455,14 +451,8 @@ impl JITCompile for Call { } } -impl JITCompile for Let { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { - unreachable!() - } -} - -impl JITCompile for With { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { +impl JITCompile for With { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { let namespace = self.namespace.compile(ctx, engine, env); ctx.enter_with(env, namespace); let ret = self.expr.compile(ctx, engine, env); @@ -472,43 +462,55 @@ impl JITCompile for With { } } -impl JITCompile for Assert { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { +impl JITCompile for Assert { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { todo!() } } -impl JITCompile for ConcatStrings { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { +impl JITCompile for ConcatStrings { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { todo!() } } -impl JITCompile for Const { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { - use c::Const::*; +impl JITCompile for Const { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { + use nixjit_value::Const::*; let slot = ctx.alloca(); match self.val { Bool(x) => { - let tag = ctx.builder.ins().iconst(types::I64, Value::BOOL as i64); + let tag = ctx + .builder + .ins() + .iconst(types::I64, Value::::BOOL as i64); let val = ctx.builder.ins().iconst(types::I64, x as i64); ctx.builder.ins().stack_store(tag, slot, 0); ctx.builder.ins().stack_store(val, slot, 8); } Int(x) => { - let tag = ctx.builder.ins().iconst(types::I64, Value::INT as i64); + let tag = ctx + .builder + .ins() + .iconst(types::I64, Value::::INT as i64); let val = ctx.builder.ins().iconst(types::I64, x); ctx.builder.ins().stack_store(tag, slot, 0); ctx.builder.ins().stack_store(val, slot, 8); } Float(x) => { - let tag = ctx.builder.ins().iconst(types::I64, Value::FLOAT as i64); + let tag = ctx + .builder + .ins() + .iconst(types::I64, Value::::FLOAT as i64); let val = ctx.builder.ins().f64const(x); ctx.builder.ins().stack_store(tag, slot, 0); ctx.builder.ins().stack_store(val, slot, 8); } Null => { - let tag = ctx.builder.ins().iconst(types::I64, Value::NULL as i64); + let tag = ctx + .builder + .ins() + .iconst(types::I64, Value::::NULL as i64); ctx.builder.ins().stack_store(tag, slot, 0); } } @@ -516,43 +518,20 @@ impl JITCompile for Const { } } -impl JITCompile for Str { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { +impl JITCompile for Str { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { ctx.create_string(&self.val) } } -impl JITCompile for Var { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { +impl JITCompile for Var { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { ctx.lookup(env, &self.sym) } } -impl JITCompile for Arg { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { - ctx.lookup_arg(env, self.offset) - } -} - -impl JITCompile for StackVar { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { - ctx.lookup_stack(env, self.offset) - } -} - -impl JITCompile for Thunk { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { - let slot = ctx.alloca(); - let tag = ctx.builder.ins().iconst(types::I64, Value::THUNK as i64); - let val = ctx.builder.ins().iconst(types::I64, self.idx as i64); - ctx.builder.ins().stack_store(tag, slot, 0); - ctx.builder.ins().stack_store(val, slot, 8); - slot - } -} - -impl JITCompile for Path { - fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { +impl JITCompile for Path { + fn compile(&self, ctx: &mut Context, engine: ir::Value, env: ir::Value) -> StackSlot { todo!() } } diff --git a/evaluator/nixjit_jit/src/helpers.rs b/evaluator/nixjit_jit/src/helpers.rs new file mode 100644 index 0000000..88c0bc3 --- /dev/null +++ b/evaluator/nixjit_jit/src/helpers.rs @@ -0,0 +1,165 @@ +use core::{slice, str}; +use std::alloc::Layout; +use std::alloc::alloc; +use std::mem::MaybeUninit; +use std::ptr::NonNull; + +use hashbrown::HashMap; + +use nixjit_eval::{AttrSet, EvalContext, List, Value}; + +use super::JITContext; + +pub extern "C" fn helper_call( + func: &mut Value, + args_ptr: *mut Value, + args_len: usize, + ctx: &mut Ctx, +) { + // TODO: Error Handling + let args = core::ptr::slice_from_raw_parts_mut(args_ptr, args_len); + let args = unsafe { Box::from_raw(args) }; + func.call(args.into_iter().collect(), ctx).unwrap(); +} + +pub extern "C" fn helper_lookup_stack( + ctx: &Ctx, + offset: usize, + ret: &mut MaybeUninit>, +) { + ret.write(ctx.lookup_stack(offset).clone()); +} + +pub extern "C" fn helper_lookup_arg( + ctx: &Ctx, + offset: usize, + ret: &mut MaybeUninit>, +) { + ret.write(ctx.lookup_arg(offset).clone()); +} + +pub extern "C" fn helper_lookup( + ctx: &Ctx, + sym_ptr: *const u8, + sym_len: usize, + ret: &mut MaybeUninit>, +) { + // TODO: Error Handling + unsafe { + ret.write( + ctx.lookup_with(str::from_utf8_unchecked(slice::from_raw_parts( + sym_ptr, sym_len, + ))) + .unwrap() + .clone(), + ); + } +} + +pub extern "C" fn helper_select( + val: &mut Value, + path_ptr: *mut Value, + path_len: usize, +) { + let path = core::ptr::slice_from_raw_parts_mut(path_ptr, path_len); + let path = unsafe { Box::from_raw(path) }; + val.select(path.into_iter().map(|mut val| { + val.coerce_to_string(); + Ok(val.unwrap_string()) + })) + .unwrap(); +} + +pub extern "C" fn helper_select_with_default( + val: &mut Value, + path_ptr: *mut Value, + path_len: usize, + default: NonNull>, +) { + let path = core::ptr::slice_from_raw_parts_mut(path_ptr, path_len); + let path = unsafe { Box::from_raw(path) }; + val.select_with_default( + path.into_iter().map(|mut val| { + val.coerce_to_string(); + Ok(val.unwrap_string()) + }), + unsafe { default.read() }, + ) + .unwrap(); +} + +pub extern "C" fn helper_eq(lhs: &mut Value, rhs: &Value) { + lhs.eq(rhs); +} + +pub unsafe extern "C" fn helper_create_string( + ptr: *const u8, + len: usize, + ret: &mut MaybeUninit>, +) { + unsafe { + ret.write(Value::String( + str::from_utf8_unchecked(slice::from_raw_parts(ptr, len)).to_owned(), + )); + } +} + +pub unsafe extern "C" fn helper_create_list( + ptr: *mut Value, + len: usize, + ret: &mut MaybeUninit>, +) { + unsafe { + ret.write(Value::List( + List::from(Vec::from_raw_parts(ptr, len, len)).into(), + )); + } +} + +pub unsafe extern "C" fn helper_create_attrs( + ret: &mut MaybeUninit>>, +) { + ret.write(HashMap::new()); +} + +pub unsafe extern "C" fn helper_push_attr( + attrs: &mut HashMap>, + sym_ptr: *const u8, + sym_len: usize, + val: NonNull>, +) { + unsafe { + attrs.insert( + str::from_utf8_unchecked(slice::from_raw_parts(sym_ptr, sym_len)).to_owned(), + val.read(), + ); + } +} + +pub unsafe extern "C" fn helper_finalize_attrs( + attrs: NonNull>>, + ret: &mut MaybeUninit>, +) { + ret.write(Value::AttrSet( + AttrSet::from(unsafe { attrs.read() }).into(), + )); +} + +pub unsafe extern "C" fn helper_enter_with( + ctx: &mut Ctx, + namespace: NonNull>, +) { + ctx.enter_with(unsafe { namespace.read() }.unwrap_attr_set().into_inner()); +} + +pub unsafe extern "C" fn helper_exit_with(ctx: &mut Ctx) { + ctx.exit_with(); +} + +pub unsafe extern "C" fn helper_alloc_array(len: usize) -> *mut u8 { + unsafe { alloc(Layout::array::>(len).unwrap()) } +} + +pub extern "C" fn helper_dbg(value: &Value) { + println!("{value:?}") +} diff --git a/src/eval/jit/mod.rs b/evaluator/nixjit_jit/src/lib.rs similarity index 89% rename from src/eval/jit/mod.rs rename to evaluator/nixjit_jit/src/lib.rs index 1ea6c39..e1ed25e 100644 --- a/src/eval/jit/mod.rs +++ b/evaluator/nixjit_jit/src/lib.rs @@ -1,16 +1,16 @@ +use std::marker::PhantomData; use std::ops::Deref; +use std::rc::Rc; use cranelift::codegen::ir::Function; use cranelift::codegen::ir::{self, ArgumentExtension, ArgumentPurpose, StackSlot}; use cranelift::prelude::*; use cranelift_jit::{JITBuilder, JITModule}; use cranelift_module::{FuncId, Linkage, Module}; -use hashbrown::HashSet; +use hashbrown::{HashMap, HashSet}; -use crate::engine::Engine; -use crate::env::Env; -use crate::ir::Ir; -use crate::ty::internal::Value; +use nixjit_eval::{EvalContext, Value}; +use nixjit_lir::Lir; mod compile; mod helpers; @@ -18,29 +18,36 @@ mod helpers; pub use compile::JITCompile; use helpers::*; -type F = unsafe extern "C" fn(*const Engine, *const Env, *mut Value); +pub trait JITContext: EvalContext + Sized { + fn lookup_stack(&self, offset: usize) -> &Value; + fn lookup_arg(&self, offset: usize) -> &Value; + fn enter_with(&mut self, namespace: Rc>>); + fn exit_with(&mut self); +} -pub struct JITFunc { - func: F, +type F = unsafe extern "C" fn(*const Ctx, *mut Value); + +pub struct JITFunc { + func: F, strings: HashSet, } -impl Deref for JITFunc { - type Target = F; +impl Deref for JITFunc { + type Target = F; fn deref(&self) -> &Self::Target { &self.func } } -pub struct JITContext<'comp, 'ctx> { - pub compiler: &'comp mut JITCompiler, +struct Context<'comp, 'ctx, Ctx: JITContext> { + pub compiler: &'comp mut JITCompiler, pub builder: FunctionBuilder<'ctx>, free_slots: Vec, strings: HashSet, } -impl<'comp, 'ctx> JITContext<'comp, 'ctx> { - fn new(compiler: &'comp mut JITCompiler, builder: FunctionBuilder<'ctx>) -> Self { +impl<'comp, 'ctx, Ctx: JITContext> Context<'comp, 'ctx, Ctx> { + fn new(compiler: &'comp mut JITCompiler, builder: FunctionBuilder<'ctx>) -> Self { Self { compiler, builder, @@ -342,18 +349,6 @@ impl<'comp, 'ctx> JITContext<'comp, 'ctx> { ); } - pub fn force(&mut self, slot: StackSlot, engine: ir::Value, env: ir::Value) { - let force = self - .compiler - .module - .declare_func_in_func(self.compiler.force, self.builder.func); - let ptr = self - .builder - .ins() - .stack_addr(self.compiler.ptr_type, slot, 0); - self.builder.ins().call(force, &[ptr, engine, env]); - } - pub fn eq(&mut self, lhs: StackSlot, rhs: StackSlot) { let lhs = self .builder @@ -379,10 +374,11 @@ impl<'comp, 'ctx> JITContext<'comp, 'ctx> { } } -pub struct JITCompiler { +pub struct JITCompiler { ctx: codegen::Context, module: JITModule, builder_ctx: Option, + _marker: PhantomData, int_type: Type, float_type: Type, @@ -397,7 +393,6 @@ pub struct JITCompiler { lookup: FuncId, select: FuncId, select_with_default: FuncId, - force: FuncId, eq: FuncId, @@ -412,13 +407,13 @@ pub struct JITCompiler { dbg: FuncId, } -impl Default for JITCompiler { +impl Default for JITCompiler { fn default() -> Self { Self::new() } } -impl JITCompiler { +impl JITCompiler { pub fn new() -> Self { let mut flag_builder = settings::builder(); flag_builder.set("use_colocated_libcalls", "false").unwrap(); @@ -431,27 +426,26 @@ impl JITCompiler { .unwrap(); let mut builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); - builder.symbol("helper_call", helper_call as _); - builder.symbol("helper_lookup_stack", helper_lookup_stack as _); - builder.symbol("helper_lookup_arg", helper_lookup_arg as _); - builder.symbol("helper_lookup", helper_lookup as _); - builder.symbol("helper_select", helper_select as _); + builder.symbol("helper_call", helper_call:: as _); + builder.symbol("helper_lookup_stack", helper_lookup_stack:: as _); + builder.symbol("helper_lookup_arg", helper_lookup_arg:: as _); + builder.symbol("helper_lookup", helper_lookup:: as _); + builder.symbol("helper_select", helper_select:: as _); builder.symbol( "helper_select_with_default", - helper_select_with_default as _, + helper_select_with_default:: as _, ); - builder.symbol("helper_force", helper_force as _); - builder.symbol("helper_eq", helper_eq as _); + builder.symbol("helper_eq", helper_eq:: as _); - builder.symbol("helper_alloc_array", helper_alloc_array as _); - builder.symbol("helper_create_string", helper_create_string as _); - builder.symbol("helper_create_list", helper_create_list as _); - builder.symbol("helper_create_attrs", helper_create_attrs as _); - builder.symbol("helper_push_attr", helper_push_attr as _); - builder.symbol("helper_finalize_attrs", helper_finalize_attrs as _); - builder.symbol("helper_enter_with", helper_enter_with as _); - builder.symbol("helper_exit_with", helper_exit_with as _); - builder.symbol("helper_dbg", helper_dbg as _); + builder.symbol("helper_alloc_array", helper_alloc_array:: as _); + builder.symbol("helper_create_string", helper_create_string:: as _); + builder.symbol("helper_create_list", helper_create_list:: as _); + builder.symbol("helper_create_attrs", helper_create_attrs:: as _); + builder.symbol("helper_push_attr", helper_push_attr:: as _); + builder.symbol("helper_finalize_attrs", helper_finalize_attrs:: as _); + builder.symbol("helper_enter_with", helper_enter_with:: as _); + builder.symbol("helper_exit_with", helper_exit_with:: as _); + builder.symbol("helper_dbg", helper_dbg:: as _); let mut module = JITModule::new(builder); let ctx = module.make_context(); @@ -462,7 +456,7 @@ impl JITCompiler { let ptr_type = module.target_config().pointer_type(); let value_type = types::I128; - // fn(*const Engine, *const Env, *mut Value) + // fn(*const Context, *const Env, *mut Value) let mut func_sig = module.make_signature(); func_sig.params.extend( [AbiParam { @@ -472,7 +466,7 @@ impl JITCompiler { }; 3], ); - // fn(func: &mut Value, args_ptr: *mut Value, args_len: usize, engine: &mut Engine, env: + // fn(func: &mut Value, args_ptr: *mut Value, args_len: usize, engine: &mut Context, env: // &mut Env) let mut call_sig = module.make_signature(); call_sig.params.extend( @@ -524,7 +518,7 @@ impl JITCompiler { .declare_function("helper_lookup", Linkage::Import, &lookup_sig) .unwrap(); - // fn(val: &mut Value, path_ptr: *mut Value, path_len: usize, engine: &mut Engine, env: &mut Env) + // fn(val: &mut Value, path_ptr: *mut Value, path_len: usize, engine: &mut Context, env: &mut Env) let mut select_sig = module.make_signature(); select_sig.params.extend( [AbiParam { @@ -537,7 +531,7 @@ impl JITCompiler { .declare_function("helper_select", Linkage::Import, &select_sig) .unwrap(); - // fn(val: &mut Value, path_ptr: *mut Value, path_len: usize, default: NonNull, engine: &mut Engine, env: &mut Env) + // fn(val: &mut Value, path_ptr: *mut Value, path_len: usize, default: NonNull, engine: &mut Context, env: &mut Env) let mut select_with_default_sig = module.make_signature(); select_with_default_sig.params.extend( [AbiParam { @@ -554,19 +548,6 @@ impl JITCompiler { ) .unwrap(); - // fn(thunk: &mut Value, engine: &mut Engine, env: &mut Env) - let mut force_sig = module.make_signature(); - force_sig.params.extend( - [AbiParam { - value_type: ptr_type, - purpose: ArgumentPurpose::Normal, - extension: ArgumentExtension::None, - }; 3], - ); - let force = module - .declare_function("helper_force", Linkage::Import, &force_sig) - .unwrap(); - let mut eq_sig = module.make_signature(); eq_sig.params.extend( [AbiParam { @@ -690,6 +671,7 @@ impl JITCompiler { Self { builder_ctx: None, + _marker: PhantomData, ctx, module, @@ -706,7 +688,6 @@ impl JITCompiler { lookup, select, select_with_default, - force, eq, @@ -722,7 +703,7 @@ impl JITCompiler { } } - pub fn compile(&mut self, ir: &Ir, id: usize) -> JITFunc { + pub fn compile(&mut self, ir: &Lir, id: usize) -> JITFunc { let func_id = self .module .declare_function( @@ -734,7 +715,7 @@ impl JITCompiler { let mut func = Function::new(); func.signature = self.func_sig.clone(); let mut builder_ctx = self.builder_ctx.take().unwrap_or_default(); - let mut ctx = JITContext::new(self, FunctionBuilder::new(&mut func, &mut builder_ctx)); + let mut ctx = Context::new(self, FunctionBuilder::new(&mut func, &mut builder_ctx)); let entry = ctx.builder.create_block(); ctx.builder.append_block_params_for_function_params(entry); @@ -760,7 +741,7 @@ impl JITCompiler { let strings = ctx.strings; if cfg!(debug_assertions) { - println!("{ir:?}"); + println!("{ir:#?}"); println!("{}", func.display()); } self.ctx.func = func; @@ -771,7 +752,7 @@ impl JITCompiler { let _ = self.builder_ctx.insert(builder_ctx); unsafe { JITFunc { - func: std::mem::transmute::<*const u8, F>( + func: std::mem::transmute::<*const u8, F>( self.module.get_finalized_function(func_id), ), strings, diff --git a/evaluator/nixjit_lir/Cargo.toml b/evaluator/nixjit_lir/Cargo.toml new file mode 100644 index 0000000..9c6c529 --- /dev/null +++ b/evaluator/nixjit_lir/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "nixjit_lir" +version = "0.1.0" +edition = "2024" + +[dependencies] +derive_more = { version = "2.0", features = ["full"] } +hashbrown = "0.15" +itertools = "0.14" +rnix = "0.12" + +nixjit_error = { path = "../nixjit_error" } +nixjit_ir = { path = "../nixjit_ir" } +nixjit_hir = { path = "../nixjit_hir" } +nixjit_macros = { path = "../nixjit_macros" } +nixjit_value = { path = "../nixjit_value" } diff --git a/evaluator/nixjit_lir/src/lib.rs b/evaluator/nixjit_lir/src/lib.rs new file mode 100644 index 0000000..a5ddeef --- /dev/null +++ b/evaluator/nixjit_lir/src/lib.rs @@ -0,0 +1,244 @@ +use derive_more::{IsVariant, TryUnwrap, Unwrap}; + +use nixjit_error::{Error, Result}; +use nixjit_hir as hir; +use nixjit_ir::*; +use nixjit_macros::ir; +use nixjit_value::format_symbol; + +ir! { + Lir, + + AttrSet, + List, + HasAttr, + BinOp, + UnOp, + Select, + If, + Call, + With, + Assert, + ConcatStrings, + Const, + Str, + Var, + Path, + ExprRef(ExprId), + FuncRef(ExprId), + ArgRef(ArgIdx), +} + +pub enum LookupResult { + Expr(ExprId), + Arg(ArgIdx), + Unknown, + NotFound, +} + +pub trait ResolveContext { + fn new_dep(&mut self, expr: ExprId, dep: ExprId); + fn new_func(&mut self, body: ExprId, param: Param); + fn resolve(&mut self, expr: ExprId) -> Result<()>; + fn lookup(&self, name: &str) -> LookupResult; + fn with_with_env(&mut self, f: impl FnOnce(&mut Self) -> T) -> (bool, T); + fn with_let_env<'a, T>( + &mut self, + bindings: impl IntoIterator, + f: impl FnOnce(&mut Self) -> T, + ) -> T; + fn with_param_env<'a, T>( + &mut self, + ident: Option<&'a str>, + f: impl FnOnce(&mut Self) -> T, + ) -> T; +} + +pub trait Resolve { + fn resolve(self, ctx: &mut Ctx) -> Result; +} + +impl Resolve for hir::Hir { + fn resolve(self, ctx: &mut Ctx) -> Result { + use hir::Hir::*; + match self { + AttrSet(x) => x.resolve(ctx), + List(x) => x.resolve(ctx), + HasAttr(x) => x.resolve(ctx), + BinOp(x) => x.resolve(ctx), + UnOp(x) => x.resolve(ctx), + Select(x) => x.resolve(ctx), + If(x) => x.resolve(ctx), + Func(x) => x.resolve(ctx), + Call(x) => x.resolve(ctx), + With(x) => x.resolve(ctx), + Assert(x) => x.resolve(ctx), + ConcatStrings(x) => x.resolve(ctx), + Const(x) => Ok(Lir::Const(x)), + Str(x) => Ok(Lir::Str(x)), + Var(x) => x.resolve(ctx), + Path(x) => x.resolve(ctx), + Let(x) => x.resolve(ctx), + Arg(_) => todo!(), + } + } +} + +impl Resolve for AttrSet { + fn resolve(self, ctx: &mut Ctx) -> Result { + if self.rec { + todo!() + } else { + for (_, &v) in self.stcs.iter() { + ctx.resolve(v)?; + } + for &(k, v) in self.dyns.iter() { + ctx.resolve(k)?; + ctx.resolve(v)?; + } + Ok(self.to_lir()) + } + } +} + +impl Resolve for List { + fn resolve(self, ctx: &mut Ctx) -> Result { + for &item in self.items.iter() { + ctx.resolve(item)?; + } + Ok(self.to_lir()) + } +} + +impl Resolve for HasAttr { + fn resolve(self, ctx: &mut Ctx) -> Result { + ctx.resolve(self.lhs)?; + for attr in self.rhs.iter() { + if let &Attr::Dynamic(expr) = attr { + ctx.resolve(expr)?; + } + } + Ok(self.to_lir()) + } +} + +impl Resolve for BinOp { + fn resolve(self, ctx: &mut Ctx) -> Result { + ctx.resolve(self.lhs)?; + ctx.resolve(self.rhs)?; + Ok(self.to_lir()) + } +} + +impl Resolve for UnOp { + fn resolve(self, ctx: &mut Ctx) -> Result { + ctx.resolve(self.rhs)?; + Ok(self.to_lir()) + } +} + +impl Resolve for Select { + fn resolve(self, ctx: &mut Ctx) -> Result { + ctx.resolve(self.expr)?; + for attr in self.attrpath.iter() { + if let &Attr::Dynamic(expr) = attr { + ctx.resolve(expr)?; + } + } + if let Some(expr) = self.default { + ctx.resolve(expr)?; + } + Ok(self.to_lir()) + } +} + +impl Resolve for If { + fn resolve(self, ctx: &mut Ctx) -> Result { + ctx.resolve(self.cond)?; + ctx.resolve(self.consq)?; + ctx.resolve(self.alter)?; + Ok(self.to_lir()) + } +} + +impl Resolve for Func { + fn resolve(self, ctx: &mut Ctx) -> Result { + ctx.with_param_env(self.param.ident.as_deref(), |ctx| ctx.resolve(self.body))?; + ctx.new_func(self.body, self.param); + Ok(Lir::FuncRef(self.body)) + } +} + +impl Resolve for Call { + fn resolve(self, ctx: &mut Ctx) -> Result { + ctx.resolve(self.func)?; + for &arg in self.args.iter() { + ctx.resolve(arg)?; + } + Ok(self.to_lir()) + } +} + +impl Resolve for With { + fn resolve(self, ctx: &mut Ctx) -> Result { + ctx.resolve(self.namespace)?; + let (env_used, res) = ctx.with_with_env(|ctx| ctx.resolve(self.expr)); + res?; + if env_used { + Ok(self.to_lir()) + } else { + Ok(Lir::ExprRef(self.expr)) + } + } +} + +impl Resolve for Assert { + fn resolve(self, ctx: &mut Ctx) -> Result { + ctx.resolve(self.assertion)?; + ctx.resolve(self.expr)?; + Ok(self.to_lir()) + } +} + +impl Resolve for ConcatStrings { + fn resolve(self, ctx: &mut Ctx) -> Result { + for &part in self.parts.iter() { + ctx.resolve(part)?; + } + Ok(self.to_lir()) + } +} + +impl Resolve for Var { + fn resolve(self, ctx: &mut Ctx) -> Result { + use LookupResult::*; + match ctx.lookup(&self.sym) { + Expr(expr) => Ok(Lir::ExprRef(expr)), + Arg(arg) => Ok(Lir::ArgRef(arg)), + Unknown => Ok(self.to_lir()), + NotFound => Err(Error::ResolutionError(format!( + "undefined variable '{}'", + format_symbol(&self.sym) + ))), + } + } +} + +impl Resolve for Path { + fn resolve(self, ctx: &mut Ctx) -> Result { + ctx.resolve(self.expr)?; + Ok(self.to_lir()) + } +} + +impl Resolve for hir::Let { + fn resolve(self, ctx: &mut Ctx) -> Result { + ctx.with_let_env(self.bindings.iter(), |ctx| { + for &id in self.bindings.values() { + ctx.resolve(id)?; + } + ctx.resolve(self.body) + })?; + Ok(Lir::ExprRef(self.body)) + } +} diff --git a/evaluator/nixjit_macros/Cargo.toml b/evaluator/nixjit_macros/Cargo.toml new file mode 100644 index 0000000..6ad9140 --- /dev/null +++ b/evaluator/nixjit_macros/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "nixjit_macros" +version = "0.1.0" +edition = "2024" + +[lib] +proc-macro = true + +[dependencies] +convert_case = "0.8" +proc-macro2 = "1.0" +quote = "1.0" +syn = { version = "2.0", features = ["full"] } diff --git a/evaluator/nixjit_macros/src/ir.rs b/evaluator/nixjit_macros/src/ir.rs new file mode 100644 index 0000000..589bd89 --- /dev/null +++ b/evaluator/nixjit_macros/src/ir.rs @@ -0,0 +1,54 @@ +use syn::{ + FieldsNamed, Ident, Token, Type, parenthesized, + parse::{Parse, ParseStream, Result}, + punctuated::Punctuated, + token, +}; + +pub enum VariantInput { + Unit(Ident), + Tuple(Ident, Type), + Struct(Ident, FieldsNamed), +} + +pub struct MacroInput { + pub base_name: Ident, + pub variants: Punctuated, +} + +impl Parse for VariantInput { + fn parse(input: ParseStream) -> Result { + let name: Ident = input.parse()?; + + if input.peek(token::Paren) { + let content; + parenthesized!(content in input); + let ty: Type = content.parse()?; + + if !content.is_empty() { + return Err(content.error("Expected a single type inside parentheses")); + } + + Ok(VariantInput::Tuple(name, ty)) + } else if input.peek(token::Brace) { + let fields: FieldsNamed = input.parse()?; + + Ok(VariantInput::Struct(name, fields)) + } else { + Ok(VariantInput::Unit(name)) + } + } +} + +impl Parse for MacroInput { + fn parse(input: ParseStream) -> Result { + let base_name = input.parse()?; + input.parse::()?; + let variants = Punctuated::parse_terminated(input)?; + + Ok(MacroInput { + base_name, + variants, + }) + } +} diff --git a/evaluator/nixjit_macros/src/lib.rs b/evaluator/nixjit_macros/src/lib.rs new file mode 100644 index 0000000..bb01254 --- /dev/null +++ b/evaluator/nixjit_macros/src/lib.rs @@ -0,0 +1,121 @@ +use convert_case::{Case, Casing}; +use proc_macro::TokenStream; +use quote::{format_ident, quote}; + +mod ir; + +#[proc_macro] +pub fn ir(input: TokenStream) -> TokenStream { + use ir::*; + let parsed_input = syn::parse_macro_input!(input as MacroInput); + + let base_name = &parsed_input.base_name; + let ref_name = format_ident!("{}Ref", base_name); + let mut_name = format_ident!("{}Mut", base_name); + let to_trait_name = format_ident!("To{}", base_name); + let to_trait_fn_name = format_ident!("to_{}", base_name.to_string().to_case(Case::Snake)); + + let mut enum_variants = Vec::new(); + let mut struct_defs = Vec::new(); + let mut ref_variants = Vec::new(); + let mut mut_variants = Vec::new(); + let mut as_ref_arms = Vec::new(); + let mut as_mut_arms = Vec::new(); + let mut from_impls = Vec::new(); + let mut to_trait_impls = Vec::new(); + + for variant in parsed_input.variants { + match variant { + VariantInput::Unit(name) => { + let inner_type = name.clone(); + enum_variants.push(quote! { #name(#inner_type) }); + ref_variants.push(quote! { #name(&'a #inner_type) }); + mut_variants.push(quote! { #name(&'a mut #inner_type) }); + as_ref_arms.push(quote! { Self::#name(inner) => #ref_name::#name(inner) }); + as_mut_arms.push(quote! { Self::#name(inner) => #mut_name::#name(inner) }); + from_impls.push(quote! { + impl From<#inner_type> for #base_name { + fn from(val: #inner_type) -> Self { #base_name::#name(val) } + } + }); + to_trait_impls.push(quote! { + impl #to_trait_name for #name { + fn #to_trait_fn_name(self) -> #base_name { #base_name::from(self) } + } + }); + } + VariantInput::Tuple(name, ty) => { + enum_variants.push(quote! { #name(#ty) }); + ref_variants.push(quote! { #name(&'a #ty) }); + mut_variants.push(quote! { #name(&'a mut #ty) }); + as_ref_arms.push(quote! { Self::#name(inner) => #ref_name::#name(inner) }); + as_mut_arms.push(quote! { Self::#name(inner) => #mut_name::#name(inner) }); + } + VariantInput::Struct(name, fields) => { + let inner_type = name.clone(); + struct_defs.push(quote! { + #[derive(Debug)] + pub struct #name #fields + }); + enum_variants.push(quote! { #name(#inner_type) }); + ref_variants.push(quote! { #name(&'a #inner_type) }); + mut_variants.push(quote! { #name(&'a mut #inner_type) }); + as_ref_arms.push(quote! { Self::#name(inner) => #ref_name::#name(inner) }); + as_mut_arms.push(quote! { Self::#name(inner) => #mut_name::#name(inner) }); + from_impls.push(quote! { + impl From<#inner_type> for #base_name { + fn from(val: #inner_type) -> Self { #base_name::#name(val) } + } + }); + to_trait_impls.push(quote! { + impl #to_trait_name for #name { + fn #to_trait_fn_name(self) -> #base_name { #base_name::from(self) } + } + }); + } + } + } + + let expanded = quote! { + #[derive(Debug, IsVariant, Unwrap, TryUnwrap)] + pub enum #base_name { + #( #enum_variants ),* + } + + #( #struct_defs )* + + #[derive(Debug, IsVariant, Unwrap, TryUnwrap)] + pub enum #ref_name<'a> { + #( #ref_variants ),* + } + + #[derive(Debug, IsVariant, Unwrap, TryUnwrap)] + pub enum #mut_name<'a> { + #( #mut_variants ),* + } + + impl #base_name { + pub fn as_ref(&self) -> #ref_name<'_> { + match self { + #( #as_ref_arms ),* + } + } + + pub fn as_mut(&mut self) -> #mut_name<'_> { + match self { + #( #as_mut_arms ),* + } + } + } + + #( #from_impls )* + + pub trait #to_trait_name { + fn #to_trait_fn_name(self) -> #base_name; + } + + #( #to_trait_impls )* + }; + + TokenStream::from(expanded) +} diff --git a/evaluator/nixjit_value/Cargo.toml b/evaluator/nixjit_value/Cargo.toml new file mode 100644 index 0000000..051a068 --- /dev/null +++ b/evaluator/nixjit_value/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "nixjit_value" +version = "0.1.0" +edition = "2024" + +[dependencies] +derive_more = { version = "2.0", features = ["full"] } +regex = "1.11" diff --git a/src/ty/public.rs b/evaluator/nixjit_value/src/lib.rs similarity index 66% rename from src/ty/public.rs rename to evaluator/nixjit_value/src/lib.rs index a16795c..376d415 100644 --- a/src/ty/public.rs +++ b/evaluator/nixjit_value/src/lib.rs @@ -1,12 +1,68 @@ +use core::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use core::hash::Hash; +use core::ops::Deref; + +use std::borrow::Cow; use std::collections::BTreeMap; -use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; -use std::ops::Deref; use std::sync::LazyLock; use derive_more::{Constructor, IsVariant, Unwrap}; use regex::Regex; -use super::common::*; +#[derive(Clone, Debug, PartialEq, Constructor, Hash)] +pub struct Catchable { + msg: String, +} + +impl> From for Catchable { + fn from(value: T) -> Self { + Catchable { msg: value.into() } + } +} + +impl Display for Catchable { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "", self.msg) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, IsVariant, Unwrap)] +pub enum Const { + Bool(bool), + Int(i64), + Float(f64), + Null, +} + +impl Display for Const { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + use Const::*; + match self { + Int(x) => write!(f, "{x}"), + Float(x) => write!(f, "{x}"), + Bool(x) => write!(f, "{x}"), + Null => write!(f, "null"), + } + } +} + +impl From for Const { + fn from(value: bool) -> Self { + Const::Bool(value) + } +} + +impl From for Const { + fn from(value: i64) -> Self { + Const::Int(value) + } +} + +impl From for Const { + fn from(value: f64) -> Self { + Const::Float(value) + } +} #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Constructor)] pub struct Symbol(String); @@ -17,12 +73,20 @@ impl> From for Symbol { } } +pub fn format_symbol<'a>(sym: &'a str) -> Cow<'a, str> { + if REGEX.is_match(sym) { + Cow::Borrowed(sym) + } else { + Cow::Owned(format!(r#""{sym}""#)) + } +} + impl Display for Symbol { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { if self.normal() { - write!(f, r#""{}""#, self.0) - } else { write!(f, "{}", self.0) + } else { + write!(f, r#""{}""#, self.0) } } } @@ -31,7 +95,7 @@ static REGEX: LazyLock = LazyLock::new(|| Regex::new(r#"^[a-zA-Z\_][a-zA-Z0-9\_\'\-]*$"#).unwrap()); impl Symbol { fn normal(&self) -> bool { - !REGEX.is_match(self) + REGEX.is_match(self) } } @@ -114,7 +178,7 @@ pub enum Value { Thunk, Func, PrimOp(&'static str), - PartialPrimOp(&'static str), + PrimOpApp(&'static str), Repeated, } @@ -130,7 +194,7 @@ impl Display for Value { Thunk => write!(f, ""), Func => write!(f, ""), PrimOp(x) => write!(f, ""), - PartialPrimOp(x) => write!(f, ""), + PrimOpApp(x) => write!(f, ""), Repeated => write!(f, ""), } } diff --git a/flake.lock b/flake.lock index a2b4e30..48a213a 100644 --- a/flake.lock +++ b/flake.lock @@ -8,11 +8,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1752389012, - "narHash": "sha256-Y9PhEOyV+MrJG0Rgrd1AiX+9MfqRPu7msM2y04t57FY=", + "lastModified": 1754376329, + "narHash": "sha256-Uz90O6qpmXQoNV57bf78yNd+nTxOoV5sjF1MibSdqWg=", "owner": "nix-community", "repo": "fenix", - "rev": "444e34333e224a39ac3acb6d8831bde2d0e2902f", + "rev": "ee7cae7d4cd68f7f2e78493a1f62212640db223c", "type": "github" }, "original": { @@ -23,11 +23,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1751984180, - "narHash": "sha256-LwWRsENAZJKUdD3SpLluwDmdXY9F45ZEgCb0X+xgOL0=", + "lastModified": 1754214453, + "narHash": "sha256-Q/I2xJn/j1wpkGhWkQnm20nShYnG7TI99foDBpXm1SY=", "owner": "nixos", "repo": "nixpkgs", - "rev": "9807714d6944a957c2e036f84b0ff8caf9930bc0", + "rev": "5b09dc45f24cf32316283e62aec81ffee3c3e376", "type": "github" }, "original": { @@ -46,11 +46,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1752262373, - "narHash": "sha256-eRDeo/hVnf958ESWy8qV/jZj4ZRbFXsmMdw1cnI57dE=", + "lastModified": 1754320527, + "narHash": "sha256-5/EHPlvDFb1MPVcnUwpAcc9sHejhDhAj4uloUU4rthk=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "a489123e806ceadfdc5568bf9609b0468f5a2e6a", + "rev": "978393bae86212f867e0c43872989e1658f7690f", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 76114fd..4b1ef86 100644 --- a/flake.nix +++ b/flake.nix @@ -14,7 +14,7 @@ { default = pkgs.mkShell { packages = with pkgs; [ - /* (fenix.packages.${system}.complete.withComponents [ + (fenix.packages.${system}.complete.withComponents [ "cargo" "clippy" "rust-src" @@ -22,7 +22,7 @@ "rustfmt" "rust-analyzer" "miri" - ]) */ + ]) gdb valgrind gemini-cli diff --git a/src/bin/eval.rs b/src/bin/eval.rs deleted file mode 100644 index ea025c7..0000000 --- a/src/bin/eval.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::process::exit; - -use itertools::Itertools; - -use nixjit::engine::eval; -use nixjit::error::Error; -use nixjit::error::Result; -use nixjit::ir::downgrade; - -fn main() -> Result<()> { - let mut args = std::env::args(); - if args.len() != 2 { - eprintln!("Usage: {} expr", args.next().unwrap()); - exit(1); - } - args.next(); - let expr = args.next().unwrap(); - let root = rnix::Root::parse(&expr); - if !root.errors().is_empty() { - return Err(Error::ParseError( - root.errors().iter().map(|err| err.to_string()).join(";"), - )); - } - let expr = root.tree().expr().unwrap(); - let downgraded = downgrade(expr)?; - println!("{}", eval(downgraded)?); - - Ok(()) -} diff --git a/src/bin/repl.rs b/src/bin/repl.rs deleted file mode 100644 index 19c16e8..0000000 --- a/src/bin/repl.rs +++ /dev/null @@ -1,61 +0,0 @@ -use itertools::Itertools; -use rustyline::error::ReadlineError; -use rustyline::{DefaultEditor, Result}; - -use nixjit::engine::eval; -use nixjit::error::Error; -use nixjit::ir::downgrade; - -macro_rules! unwrap { - ($e:expr) => { - match $e { - Ok(ok) => ok, - Err(err) => { - println!("{err}"); - continue; - } - } - }; -} - -fn main() -> Result<()> { - let mut rl = DefaultEditor::new()?; - loop { - let readline = rl.readline("nixjit-repl> "); - match readline { - Ok(expr) => { - if expr.trim().is_empty() { - continue; - } - rl.add_history_entry(expr.as_str())?; - let root = rnix::Root::parse(&expr); - if !root.errors().is_empty() { - println!( - "{}", - Error::ParseError( - root.errors().iter().map(|err| err.to_string()).join(";") - ) - ); - continue; - } - let expr = root.tree().expr().unwrap(); - let downgraded = unwrap!(downgrade(expr)); - println!("{downgraded:?}"); - println!("{}", unwrap!(eval(downgraded))); - } - Err(ReadlineError::Interrupted) => { - println!("CTRL-C"); - break; - } - Err(ReadlineError::Eof) => { - println!("CTRL-D"); - break; - } - Err(err) => { - println!("Error: {err:?}"); - break; - } - } - } - Ok(()) -} diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs deleted file mode 100644 index 980a3de..0000000 --- a/src/builtins/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -use hashbrown::HashMap; - -use crate::ir::{Const, DowngradeContext, Ir}; - -pub fn ir_env(_: &mut DowngradeContext) -> HashMap { - let mut map = HashMap::new(); - map.insert("true".into(), Const::from(true).ir()); - map.insert("false".into(), Const::from(false).ir()); - map -} diff --git a/src/engine/mod.rs b/src/engine/mod.rs deleted file mode 100644 index 82381db..0000000 --- a/src/engine/mod.rs +++ /dev/null @@ -1,136 +0,0 @@ -use core::mem::MaybeUninit; -use std::cell::OnceCell; -use std::rc::Rc; - -use hashbrown::{HashMap, HashSet}; -use itertools::Itertools; -use priority_queue::PriorityQueue; - -use crate::env::Env; -use crate::error::Result; -use crate::eval::Evaluate; -use crate::eval::jit::{JITCompiler, JITFunc}; -use crate::ir::{Downgraded, Ir}; -use crate::ty::internal as i; -use crate::ty::public::Value; - -#[cfg(test)] -mod test; - -type ThunkIdx = usize; -type EnvIdx = usize; - -pub struct Engine { - pub thunks: Box<[Ir]>, - pub funcs: Box<[Ir]>, - pub func_deps: Vec>, - pub func_arg_deps: Vec>, - jit: JITCompiler, - compiled: Box<[OnceCell]>, - tasks: PriorityQueue, -} - -pub fn eval(downgraded: Downgraded) -> Result { - let mut engine = Engine::new( - downgraded.thunks, - downgraded.funcs, - downgraded.func_deps, - downgraded.func_arg_deps, - JITCompiler::new(), - ); - engine.eval(downgraded.graph) -} - -impl Engine { - pub fn new( - thunks: Box<[Ir]>, - funcs: Box<[Ir]>, - func_deps: Vec>, - func_arg_deps: Vec>, - jit: JITCompiler, - ) -> Self { - Self { - compiled: (0..thunks.len() + funcs.len()) - .map(|_| OnceCell::new()) - .collect(), - tasks: PriorityQueue::new(), - thunks, - funcs, - func_deps, - func_arg_deps, - jit, - } - } - - pub fn eval(&mut self, graph: Vec>) -> Result { - let mut env = Env::new(); - let engine = unsafe { &mut *(self as *mut Self) }; - env.new_frame(0); - env.new_frame(0); - for members in graph.into_iter() { - if members.len() == 1 { - let val = self.thunks[members[0]].eval(engine, &mut env)?; - env.push_stack(val); - } else { - todo!(); - for member in members.into_iter() { - let val = self.thunks[member].eval(engine, &mut env)?; - env.push_stack(val); - } - } - } - Ok(env.pop_frame().last_mut().unwrap().force(self, &mut env)?.to_public(self, &mut HashSet::new())) - } - - pub fn eval_thunk(&mut self, idx: usize, env: &mut Env) -> Result { - let engine = self as *const Engine; - let func = self.compiled[idx].get_or_init(|| self.jit.compile(&self.thunks[idx], idx)); - let mut ret: MaybeUninit = MaybeUninit::uninit(); - unsafe { - func(engine, env, ret.as_mut_ptr()); - Ok(ret.assume_init()) - } - } - - pub fn call_func(&mut self, idx: usize, env: &mut Env) -> Result { - let engine = self as *const Engine; - let func = self.compiled[idx + self.thunks.len()] - .get_or_init(|| self.jit.compile(&self.funcs[idx], idx)); - let mut ret: MaybeUninit = MaybeUninit::uninit(); - unsafe { - func(engine, env, ret.as_mut_ptr()); - Ok(ret.assume_init()) - } - } - - pub fn eval_func_deps(&mut self, idx: usize, env: &mut Env) -> Result<()> { - let engine = unsafe { &mut *(self as *mut Self) }; - let engine_mut = unsafe { &mut *(self as *mut Self) }; - env.new_frame(self.func_deps[idx].len()); - for (&dep, _) in engine.func_deps[idx].iter().sorted_by_key(|&(_, &k)| k) { - let val = self.thunks[dep].eval(engine_mut, env)?; - env.push_stack(val); - } - for &arg in engine.func_arg_deps[idx].iter() { - env.force_arg(arg); - } - Ok(()) - } -} - -enum Thunk { - Expr(Ir), - Compiling, - Compiled(fn(Rc) -> Value), -} - -impl Thunk { - fn new(ir: Ir) -> Thunk { - Thunk::Expr(ir) - } -} - -#[derive(Hash, PartialEq, Eq)] -struct CompileTask { - idx: usize, -} diff --git a/src/env.rs b/src/env.rs deleted file mode 100644 index 0d7a56a..0000000 --- a/src/env.rs +++ /dev/null @@ -1,95 +0,0 @@ -use core::fmt::Debug; -use std::rc::Rc; - -use hashbrown::HashMap; - -use crate::ty::internal::Value; - -#[derive(Clone, Debug, Default)] -pub struct Env { - stack: Vec, - args: Vec, - frame_offsets: Vec, - with: Vec>>, -} - -impl Env { - pub fn new() -> Self { - Self::default() - } - - pub fn reserve_args(&mut self, size: usize) { - self.args.reserve(size); - } - - pub fn push_arg(&mut self, value: Value) { - self.args.push(value); - } - - pub fn push_args(&mut self, values: impl IntoIterator) { - self.args.extend(values); - } - - pub fn lookup_arg(&self, offset: usize) -> &Value { - &self.args[self.args.len() - offset - 1] - } - - pub fn pop_args(&mut self, len: usize) -> Vec { - self.args.split_off(self.args.len() - len) - } - - pub fn drop_args(&mut self, len: usize) { - self.args.truncate(self.args.len() - len); - } - - pub fn force_arg(&mut self, offset: usize) { - let env = unsafe { &mut *(self as *mut Env) }; - if let thunk @ Value::Thunk(_) = &mut self.args[offset] { - let &mut Value::Thunk(offset) = thunk else { unreachable!() }; - *thunk = env.lookup_stack(offset).clone() - } - } - - pub fn new_frame(&mut self, size: usize) { - self.stack.reserve(size); - self.frame_offsets.push(self.stack.len()); - } - - pub fn push_stack(&mut self, value: Value) { - self.stack.push(value); - } - - pub fn resume_stack(&mut self, iter: impl IntoIterator) { - self.stack.extend(iter); - } - - pub fn lookup_stack(&self, offset: usize) -> &Value { - dbg!(self.frame_offsets[self.frame_offsets.len() - 2], offset); - &self.stack[self.frame_offsets[self.frame_offsets.len() - 2] + offset] - } - - pub fn pop_frame(&mut self) -> Vec { - self.stack.split_off(self.frame_offsets.pop().unwrap()) - } - - pub fn drop_frame(&mut self) { - self.stack.truncate(self.frame_offsets.pop().unwrap()); - } - - pub fn lookup_with(&self, symbol: &str) -> Option { - for with in self.with.iter().rev() { - if let Some(ret) = with.get(symbol) { - return Some(ret.clone()); - } - } - None - } - - pub fn enter_with(&mut self, map: Rc>) { - self.with.push(map) - } - - pub fn exit_with(&mut self) { - self.with.pop(); - } -} diff --git a/src/eval/jit/helpers.rs b/src/eval/jit/helpers.rs deleted file mode 100644 index cd8c2f9..0000000 --- a/src/eval/jit/helpers.rs +++ /dev/null @@ -1,163 +0,0 @@ -use core::{slice, str}; -use std::alloc::Layout; -use std::alloc::alloc; -use std::mem::MaybeUninit; -use std::ptr::NonNull; -use std::rc::Rc; - -use hashbrown::HashMap; - -use crate::env::Env; -use crate::eval::Engine; -use crate::ty::internal::Value; - -pub extern "C" fn helper_call( - func: &mut Value, - args_ptr: *mut Value, - args_len: usize, - engine: &mut Engine, - env: &mut Env, -) { - // TODO: Error Handling - let args = core::ptr::slice_from_raw_parts_mut(args_ptr, args_len); - let args = unsafe { Box::from_raw(args) }; - func.call(args.into_iter().collect(), engine, env) - .unwrap(); -} - -pub extern "C" fn helper_lookup_stack(env: &Env, offset: usize, ret: &mut MaybeUninit) { - ret.write(env.lookup_stack(offset).clone()); -} - -pub extern "C" fn helper_lookup_arg(env: &Env, offset: usize, ret: &mut MaybeUninit) { - ret.write(env.lookup_arg(offset).clone()); -} - -pub extern "C" fn helper_lookup( - env: &Env, - sym_ptr: *const u8, - sym_len: usize, - ret: &mut MaybeUninit, -) { - // TODO: Error Handling - unsafe { - ret.write( - env.lookup_with(str::from_utf8_unchecked(slice::from_raw_parts( - sym_ptr, sym_len, - ))) - .unwrap(), - ); - } -} - -pub extern "C" fn helper_select( - val: &mut Value, - path_ptr: *mut Value, - path_len: usize, - engine: &mut Engine, - env: &mut Env, -) { - let path = core::ptr::slice_from_raw_parts_mut(path_ptr, path_len); - let path = unsafe { Box::from_raw(path) }; - val.select(path.into_iter().map(|mut val| { - val.force(engine, env)?.coerce_to_string(); - Ok(val.unwrap_string()) - })) - .unwrap(); -} - -pub extern "C" fn helper_select_with_default( - val: &mut Value, - path_ptr: *mut Value, - path_len: usize, - default: NonNull, - engine: &mut Engine, - env: &mut Env, -) { - let path = core::ptr::slice_from_raw_parts_mut(path_ptr, path_len); - let path = unsafe { Box::from_raw(path) }; - val.select_with_default( - path.into_iter().map(|mut val| { - val.force(engine, env)?.coerce_to_string(); - Ok(val.unwrap_string()) - }), - unsafe { default.read() }, - ) - .unwrap(); -} - -pub extern "C" fn helper_force(thunk: &mut Value, engine: &mut Engine, env: &mut Env) { - thunk.force(engine, env).unwrap(); -} - -pub extern "C" fn helper_eq(lhs: &mut Value, rhs: &Value) { - lhs.eq(rhs); -} - -pub unsafe extern "C" fn helper_create_string( - ptr: *const u8, - len: usize, - ret: &mut MaybeUninit, -) { - unsafe { - ret.write(Value::String( - str::from_utf8_unchecked(slice::from_raw_parts(ptr, len)).to_owned(), - )); - } -} - -pub unsafe extern "C" fn helper_create_list( - ptr: *mut Value, - len: usize, - ret: &mut MaybeUninit, -) { - unsafe { - ret.write(Value::List(Vec::from_raw_parts(ptr, len, len).into())); - } -} - -pub unsafe extern "C" fn helper_create_attrs(ret: &mut MaybeUninit>) { - ret.write(HashMap::new()); -} - -pub unsafe extern "C" fn helper_push_attr( - attrs: &mut HashMap, - sym_ptr: *const u8, - sym_len: usize, - val: NonNull, -) { - unsafe { - attrs.insert( - str::from_utf8_unchecked(slice::from_raw_parts(sym_ptr, sym_len)).to_owned(), - val.read(), - ); - } -} - -pub unsafe extern "C" fn helper_finalize_attrs( - attrs: NonNull>, - ret: &mut MaybeUninit, -) { - ret.write(Value::AttrSet(Rc::new(unsafe { attrs.read() }.into()))); -} - -pub unsafe extern "C" fn helper_enter_with( - env: &mut Env, - namespace: NonNull, -) { - env.enter_with(unsafe { namespace.read() }.unwrap_attr_set().into_inner()); -} - -pub unsafe extern "C" fn helper_exit_with( - env: &mut Env, -) { - env.exit_with(); -} - -pub unsafe extern "C" fn helper_alloc_array(len: usize) -> *mut u8 { - unsafe { alloc(Layout::array::(len).unwrap()) } -} - -pub extern "C" fn helper_dbg(value: &Value) { - println!("{value:?}") -} diff --git a/src/eval/mod.rs b/src/eval/mod.rs deleted file mode 100644 index b071f32..0000000 --- a/src/eval/mod.rs +++ /dev/null @@ -1,328 +0,0 @@ -use ecow::EcoVec; - -use crate::engine::Engine; -use crate::env::Env; -use crate::error::{Error, Result}; -use crate::ir::{self, DynAttr}; -use crate::ty::common::Const; -use crate::ty::internal::{AttrSet, List, Value}; -use crate::ty::public::Symbol; - -pub mod jit; - -pub trait Evaluate { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result; -} - -impl Evaluate for ir::Attrs { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - let mut attrs = AttrSet::new( - self.stcs - .iter() - .map(|(k, v)| { - let eval_result = v.eval(engine, env); - Ok((k.clone(), eval_result?)) - }) - .collect::>()?, - ); - for DynAttr(k, v) in self.dyns.iter() { - let mut k = k.eval(engine, env)?; - k.force(engine, env)?.coerce_to_string(); - let v_eval_result = v.eval(engine, env)?; - attrs.push_attr(k.unwrap_string(), v_eval_result); - } - let result = Value::AttrSet(attrs.into()).ok(); - Ok(result.unwrap()) - } -} - -impl Evaluate for ir::List { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - let items = self - .items - .iter() - .map(|val| { - - val.eval(engine, env) - }) - .collect::>>()?; - let result = Value::List(List::from(items)).ok(); - Ok(result.unwrap()) - } -} - -impl Evaluate for ir::HasAttr { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - use ir::Attr::*; - let mut val = self.lhs.eval(engine, env)?; - val.force(engine, env)? - .has_attr(self.rhs.iter().map(|attr| { - Ok(match attr { - Str(ident) => ident.clone(), - Strs(expr) => expr.eval(engine, env)?.unwrap_string(), - Dynamic(expr) => { - let mut val = expr.eval(engine, env)?; - val.force(engine, env)?.coerce_to_string(); - val.unwrap_string() - } - }) - }))?; - let result = val.ok(); - Ok(result.unwrap()) - } -} - -impl Evaluate for ir::BinOp { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - use ir::BinOpKind::*; - let mut lhs = self.lhs.eval(engine, env)?; - let mut rhs = self.rhs.eval(engine, env)?; - lhs.force(engine, env)?; - rhs.force(engine, env)?; - match self.kind { - Add => lhs.add(rhs), - Sub => { - rhs.neg(); - lhs.add(rhs); - } - Mul => lhs.mul(rhs), - Div => lhs.div(rhs)?, - Eq => Value::eq(&mut lhs, &rhs), - Neq => { - Value::eq(&mut lhs, &rhs); - lhs.not(); - } - Lt => lhs.lt(rhs), - Gt => { - rhs.lt(lhs); - lhs = rhs; - } - Leq => { - rhs.lt(lhs); - rhs.not(); - lhs = rhs; - } - Geq => { - lhs.lt(rhs); - lhs.not(); - } - And => lhs.and(rhs), - Or => lhs.or(rhs), - Impl => { - lhs.not(); - lhs.or(rhs); - } - Con => lhs.concat(rhs), - Upd => lhs.update(rhs), - PipeL => lhs.call(vec![rhs], engine, env)?, - PipeR => { - rhs.call(vec![lhs], engine, env)?; - lhs = rhs; - } - } - Ok(lhs) - } -} - -impl Evaluate for ir::UnOp { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - use ir::UnOpKind::*; - let mut rhs = self.rhs.eval(engine, env)?; - rhs.force(engine, env)?; - match self.kind { - Neg => { - rhs.neg(); - } - Not => { - rhs.not(); - } - }; - Ok(rhs) - } -} - -impl Evaluate for ir::Select { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - use ir::Attr::*; - let mut val = self.expr.eval(engine, env)?; - if let Some(default) = &self.default { - let default = default.eval(engine, env)?; - val.force(engine, env)?.select_with_default( - self.attrpath.iter().map(|attr| { - Ok(match attr { - Str(ident) => ident.clone(), - Strs(expr) => expr.eval(engine, env)?.unwrap_string(), - Dynamic(expr) => { - let mut val = expr.eval(engine, env)?; - val.force(engine, env)?.coerce_to_string(); - val.unwrap_string() - } - }) - }), - default, - )?; - } else { - val.force(engine, env)? - .select(self.attrpath.iter().map(|attr| { - Ok(match attr { - Str(ident) => ident.clone(), - Strs(expr) => expr.eval(engine, env)?.unwrap_string(), - Dynamic(expr) => { - let mut val = expr.eval(engine, env)?; - val.force(engine, env)?.coerce_to_string(); - val.unwrap_string() - } - }) - }))?; - } - let result = val.ok(); - Ok(result.unwrap()) - } -} - -impl Evaluate for ir::If { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - // TODO: Error Handling - let mut cond = self.cond.eval(engine, env)?; - cond.force(engine, env)?; - let cond = cond.unwrap_bool(); - - if cond { - self.consq.eval(engine, env) - } else { - self.alter.eval(engine, env) - } - } -} - -impl Evaluate for ir::LoadFunc { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - Value::Func(self.idx).ok() - } -} - -impl Evaluate for ir::Call { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - let mut func = self.func.eval(engine, env)?; - func.force(engine, env)?; - func.call( - self.args - .iter() - .map(|arg| arg.eval(engine, env)) - .collect::>()?, - engine, - env, - )?; - Ok(func.ok().unwrap()) - } -} - -impl Evaluate for ir::Let { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - unreachable!() - } -} - -impl Evaluate for ir::With { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - let mut namespace = self.namespace.eval(engine, env)?; - namespace.force(engine, env)?; - // TODO: Error Handling - env.enter_with(namespace.unwrap_attr_set().into_inner()); - let ret = self.expr.eval(engine, env); - env.exit_with(); - ret - } -} - -impl Evaluate for ir::Assert { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - todo!() - } -} - -impl Evaluate for ir::ConcatStrings { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - let mut parts = self - .parts - .iter() - .map(|part| { - let mut part = part.eval(engine, env)?; - part.force(engine, env)?.coerce_to_string(); - part.ok() - }) - .collect::>>()? - .into_iter(); - let init = parts.next().unwrap(); - let result = parts.fold(init, |mut a, b| { - a.concat_string(b); - a - }); - Ok(result.ok().unwrap()) - } -} - -impl Evaluate for ir::Str { - fn eval(&self, _: &mut Engine, _: &mut Env) -> Result { - let result = Value::String(self.val.clone()).ok(); - Ok(result.unwrap()) - } -} - -impl Evaluate for ir::Const { - fn eval(&self, _: &mut Engine, _: &mut Env) -> Result { - let result = match self.val { - Const::Null => Value::Null, - Const::Int(x) => Value::Int(x), - Const::Float(x) => Value::Float(x), - Const::Bool(x) => Value::Bool(x), - } - .ok(); - Ok(result.unwrap()) - } -} - -impl Evaluate for ir::Var { - fn eval(&self, _: &mut Engine, env: &mut Env) -> Result { - env.lookup_with(&self.sym).ok_or_else(|| { - Error::EvalError(format!( - "variable {} not found", - Symbol::from(self.sym.clone()) - )) - }) - } -} - -impl Evaluate for ir::Arg { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - env.lookup_arg(self.offset).clone().ok() - } -} - -impl Evaluate for ir::StackVar { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - env.lookup_stack(self.offset).clone().ok() - } -} - -impl Evaluate for ir::Thunk { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - unreachable!() - } -} - -impl Evaluate for ir::MaybeThunk { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - match self { - ir::MaybeThunk::Const(cnst) => cnst.eval(engine, env), - ir::MaybeThunk::String(string) => string.eval(engine, env), - ir::MaybeThunk::Thunk(thunk) => thunk.eval(engine, env), - } - } -} - -impl Evaluate for ir::Path { - fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - todo!() - } -} diff --git a/src/ir/ctx.rs b/src/ir/ctx.rs deleted file mode 100644 index 8c05fad..0000000 --- a/src/ir/ctx.rs +++ /dev/null @@ -1,282 +0,0 @@ -use derive_more::Unwrap; -use hashbrown::HashMap; - -use crate::{error::Result, ir::sort_dependencies}; -use crate::ty::common::Const; - -use super::{Func, Ir, LoadFunc, MaybeThunk, Thunk}; - -#[derive(Clone, Copy, Unwrap, Debug)] -pub enum Index { - Thunk(usize), - Func(usize), -} - -pub struct DowngradeContext { - pub thunks: Vec<(Ir, bool)>, - pub thunk_deps: Vec>, - pub func_deps: Vec>, - pub func_arg_deps: Vec>, - pub funcs: Vec, -} - -pub struct Env<'a, 'env> { - env: EnvNode<'a>, - prev: Option<&'env Env<'a, 'env>>, -} - -enum EnvNode<'a> { - Builtins(&'a HashMap), - Let(&'a Vec<(String, MaybeThunk)>), - SingleArg(String), - MultiArg(HashMap>, Option), - With, -} - -pub enum LookupResult { - Builtin(Ir), - MaybeThunk(MaybeThunk), - SingleArg { idx: usize }, - MultiArg { idx: usize, default: Option }, - With, -} - -impl<'a, 'env> Env<'a, 'env> { - pub fn new(builtins: &'a HashMap) -> Self { - Self { - env: EnvNode::Builtins(builtins), - prev: None, - } - } - - pub fn enter_let(&'env self, map: &'a Vec<(String, MaybeThunk)>) -> Self { - Self { - env: EnvNode::Let(map), - prev: Some(self), - } - } - - pub fn enter_single_arg(&'env self, ident: String) -> Self { - Self { - env: EnvNode::SingleArg(ident), - prev: Some(self), - } - } - - pub fn enter_multi_arg( - &'env self, - map: HashMap>, - alias: Option, - ) -> Self { - Self { - env: EnvNode::MultiArg(map, alias), - prev: Some(self), - } - } - - pub fn enter_with(&'env self) -> Self { - Self { - env: EnvNode::With, - prev: Some(self), - } - } - - fn _lookup( - &self, - ident: &String, - mut arg_idx: usize, - has_with: bool, - ) -> core::result::Result { - use EnvNode::*; - let mut has_with = has_with; - match &self.env { - Builtins(map) => { - return if let Some(ir) = map.get(ident) { - Ok(LookupResult::Builtin(ir.clone())) - } else if has_with { - Ok(LookupResult::With) - } else { - Err(()) - }; - } - Let(map) => { - if let Ok(idx) = map.binary_search_by(|(k, _)| k.cmp(ident)) { - return Ok(LookupResult::MaybeThunk(map[idx].1.clone())); - } - } - SingleArg(arg) => { - if arg == ident { - return Ok(LookupResult::SingleArg { idx: arg_idx }); - } else { - arg_idx += 1; - } - } - MultiArg(set, alias) => { - if let Some(default) = set.get(ident) { - return Ok(LookupResult::MultiArg { - idx: arg_idx, - default: default.clone(), - }); - } else if alias.as_ref() == Some(ident) { - return Ok(LookupResult::SingleArg { idx: arg_idx }); - } else { - arg_idx += 1; - } - } - With => has_with = true, - } - self.prev - .map(|prev| prev._lookup(ident, arg_idx, has_with)) - .map_or_else(|| unreachable!(), |x| x) - } - - pub fn lookup(&self, ident: &String) -> core::result::Result { - self._lookup(ident, 0, false) - } -} - -impl Default for DowngradeContext { - fn default() -> Self { - Self::new() - } -} - -impl DowngradeContext { - pub fn new() -> Self { - DowngradeContext { - thunks: Vec::new(), - thunk_deps: Vec::new(), - func_deps: Vec::new(), - func_arg_deps: Vec::new(), - funcs: Vec::new(), - } - } -} - -impl DowngradeContext { - pub fn new_thunk(&mut self, thunk: Ir) -> Thunk { - let idx = self.thunks.len(); - self.thunks.push((thunk, false)); - self.thunk_deps.push(HashMap::new()); - Thunk { idx } - } - - pub fn maybe_thunk(&mut self, ir: Ir) -> MaybeThunk { - match ir { - Ir::Const(cnst) => MaybeThunk::Const(cnst), - Ir::Str(string) => MaybeThunk::String(string), - Ir::Thunk(thunk) => MaybeThunk::Thunk(thunk), - ir => MaybeThunk::Thunk(self.new_thunk(ir)), - } - } - - pub fn new_thunk_dep(&mut self, this: Index, thunk: usize) -> usize { - match this { - Index::Thunk(idx) => { - let len = self.thunk_deps[idx].len(); - *self.thunk_deps[idx] - .entry(thunk) - .or_insert(len) - } - Index::Func(idx) => { - let len = self.func_deps[idx].len(); - *self.func_deps[idx].entry(thunk).or_insert(len) - } - } - } - - pub fn new_arg_dep(&mut self, this: Index, arg: usize) { - match this { - Index::Func(idx) => { - self.func_arg_deps[idx].push(arg); - } - Index::Thunk(idx) => { - return; - panic!("unexpected dependency from Thunk({idx}) to Arg({arg})") - } - } - } - - pub fn new_func(&mut self, func: Func) -> LoadFunc { - let idx = self.funcs.len(); - self.funcs.push(func); - self.func_deps.push(HashMap::new()); - self.func_arg_deps.push(Vec::new()); - LoadFunc { idx } - } - - pub fn resolve_func(&mut self, idx: usize, env: &Env) -> Result<()> { - let self_ptr = self as *mut Self; - self.funcs.get_mut(idx).map_or_else( - || unreachable!(), - |func| { - unsafe { - let old = core::ptr::read(func); - match old.resolve(Index::Func(idx), self_ptr.as_mut().unwrap(), env) { - Ok(ok) => core::ptr::write(func, ok), - Err(err) => { - core::ptr::write( - func, - Func { - param: crate::ir::Param::Ident(String::new()), - body: super::Const { val: Const::Null }.ir().boxed(), - }, - ); - return Err(err); - } - } - } - Ok(()) - }, - ) - } - - pub fn resolve_thunk(&mut self, idx: usize, env: &Env) -> Result<()> { - if self.thunks[idx].1 { - return Ok(()); - } - self.thunks[idx].1 = true; - let self_ptr = self as *mut Self; - self.thunks.get_mut(idx).map_or_else( - || unreachable!(), - |(thunk, _)| { - unsafe { - let old = core::ptr::read(thunk); - match old.resolve(Index::Thunk(idx), self_ptr.as_mut().unwrap(), env) { - Ok(ok) => core::ptr::write(thunk, ok), - Err(err) => { - core::ptr::write(thunk, Ir::Const(super::Const { val: Const::Null })); - return Err(err); - } - } - } - Ok(()) - }, - ) - } -} - -#[derive(Debug)] -pub struct Downgraded { - pub thunks: Box<[Ir]>, - pub funcs: Box<[Ir]>, - pub func_deps: Vec>, - pub func_arg_deps: Vec>, - pub graph: Vec>, -} - -impl Downgraded { - pub fn new(ctx: DowngradeContext) -> Self { - Self { - graph: sort_dependencies(&ctx), - func_deps: ctx.func_deps, - func_arg_deps: ctx.func_arg_deps, - thunks: ctx.thunks.into_iter().map(|(ir, _)| ir).collect(), - funcs: ctx - .funcs - .into_iter() - .map(|Func { body, .. }| *body) - .collect(), - } - } -} diff --git a/src/ir/graph.rs b/src/ir/graph.rs deleted file mode 100644 index ad7b6c2..0000000 --- a/src/ir/graph.rs +++ /dev/null @@ -1,37 +0,0 @@ -use hashbrown::HashMap; -use petgraph::graph::{DiGraph, NodeIndex}; -use petgraph::algo::{condensation, toposort}; - -use super::*; - -struct GraphBuilder<'ctx> { - ctx: &'ctx DowngradeContext, - graph: DiGraph, - nodes: HashMap -} - -impl GraphBuilder<'_> { - fn connect(&mut self, idx: usize) { - let node = self.graph.add_node(idx); - self.nodes.insert(idx, node); - let deps = self.ctx.thunk_deps.get(idx).unwrap(); - for (&refee, _) in deps { - if !self.nodes.contains_key(&refee) { - self.connect(refee); - } - self.graph.add_edge(node, *self.nodes.get(&refee).unwrap(), ()); - } - } -} - -pub fn sort_dependencies(ctx: &DowngradeContext) -> Vec> { - let mut builder = GraphBuilder { - ctx, - graph: DiGraph::new(), - nodes: HashMap::new(), - }; - builder.connect(ctx.thunks.len() - 1); - let mut graph = condensation(builder.graph, false); - let sorted = toposort(&graph, None).unwrap(); - sorted.into_iter().map(|idx| graph.remove_node(idx).unwrap()).collect_vec() -} diff --git a/src/ir/mod.rs b/src/ir/mod.rs deleted file mode 100644 index cc8b6aa..0000000 --- a/src/ir/mod.rs +++ /dev/null @@ -1,1162 +0,0 @@ -use derive_more::{IsVariant, TryUnwrap, Unwrap}; -use hashbrown::HashMap; -use itertools::Itertools; -use rnix::ast::HasEntry; -use rnix::ast::{self, Expr}; - -use crate::builtins::ir_env; -use crate::engine::Engine; -use crate::env::Env as VmEnv; -use crate::error::*; -use crate::eval::Evaluate; -use crate::eval::jit::{JITCompile, JITContext}; -use crate::ty::common as c; -use crate::ty::internal::Value; -use crate::ty::public::Symbol; - -mod ctx; -mod scc; -mod utils; -use ctx::*; -use utils::*; - -pub use ctx::{DowngradeContext, Downgraded}; -pub use scc::*; - -pub fn downgrade(expr: Expr) -> Result { - let mut ctx = DowngradeContext::new(); - let builtins = ir_env(&mut ctx); - let env = Env::new(&builtins); - let top_level = expr.downgrade(&mut ctx)?; - let Thunk { idx } = ctx.new_thunk(top_level); - ctx.resolve_thunk(idx, &env)?; - Ok(Downgraded::new(ctx)) -} - -macro_rules! ir { - ( - $( - $(#[$($x:tt)*])* - $ty:ident - => - {$($name:ident : $elemtype:ty),*$(,)?} - ) - ,*$(,)? - ) => { - #[derive(Clone, Debug, IsVariant, Unwrap)] - pub enum Ir { - $( - $ty($ty), - )* - } - - #[derive(Debug, IsVariant, TryUnwrap)] - pub enum IrRef<'a> { - $( - $ty(&'a $ty), - )* - } - - #[derive(Debug, IsVariant, TryUnwrap)] - pub enum IrMut<'a> { - $( - $ty(&'a mut $ty), - )* - } - - impl Ir { - #[inline] - fn as_ref(&self) -> IrRef<'_> { - match self { - $(Ir::$ty(ir) => IrRef::$ty(ir),)* - } - } - - #[inline] - fn as_mut(&mut self) -> IrMut<'_> { - match self { - $(Ir::$ty(ir) => IrMut::$ty(ir),)* - } - } - - #[inline] - fn resolve<'a, 'env>(self, self_idx: Index, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result { - match self { - $(Ir::$ty(ir) => ir.resolve(self_idx, ctx, env),)* - } - } - } - - impl JITCompile for Ir { - fn compile(&self, ctx: &mut JITContext, engine: cranelift::prelude::Value, env: cranelift::prelude::Value) -> cranelift::codegen::ir::StackSlot { - match self { - $(Ir::$ty(ir) => ir.compile(ctx, engine, env),)* - } - } - } - - impl Evaluate for Ir { - fn eval(&self, engine: &mut Engine, env: &mut VmEnv) -> Result { - match self { - $(Ir::$ty(ir) => ir.eval(engine, env),)* - } - } - } - - $( - $( - #[$($x)*] - )* - #[derive(Clone, Debug)] - pub struct $ty { - $( - pub $name : $elemtype, - )* - } - - impl $ty { - pub fn ir(self) -> Ir { - Ir::$ty(self) - } - } - )* - } -} - -ir! { - Attrs => { stcs: HashMap, dyns: Vec }, - List => { items: Vec }, - HasAttr => { lhs: Box, rhs: Vec }, - BinOp => { lhs: Box, rhs: Box, kind: BinOpKind }, - UnOp => { rhs: Box, kind: UnOpKind }, - Select => { expr: Box, attrpath: Vec, default: Option> }, - If => { cond: Box, consq: Box, alter: Box }, - LoadFunc => { idx: usize }, - Call => { func: Box, args: Vec }, - - Let => { bindings: Vec<(String, MaybeThunk)>, expr: Box }, - With => { namespace: Box, expr: Box }, - Assert => { assertion: Box, expr: Box }, - ConcatStrings => { parts: Vec }, - #[derive(Copy)] - Const => { val: c::Const }, - Str => { val: String }, - Var => { sym: String }, - #[derive(Copy)] - Arg => { offset: usize }, - #[derive(Copy)] - StackVar => { offset: usize }, - #[derive(Copy)] - Thunk => { idx: usize }, - Path => { expr: Box }, -} - -impl Ir { - #[inline] - fn boxed(self) -> Box { - Box::new(self) - } - - #[inline] - fn ok(self) -> Result { - Ok(self) - } -} - -#[derive(Debug, Clone)] -pub enum MaybeThunk { - Const(Const), - String(Str), - Thunk(Thunk), -} - -impl MaybeThunk { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - match self { - MaybeThunk::Thunk(thunk) => thunk.resolve(self_idx, ctx, env), - MaybeThunk::Const(cnst) => cnst.ir().ok(), - MaybeThunk::String(string) => string.ir().ok(), - } - } -} - -impl> From for Const { - fn from(value: T) -> Self { - Const { val: value.into() } - } -} - -#[derive(Clone, Debug)] -pub struct DynAttr(pub Ir, pub Ir); - -impl Attrs { - fn _insert( - &mut self, - mut path: impl Iterator, - name: Attr, - value: Ir, - ) -> Result<()> { - if let Some(attr) = path.next() { - match attr { - Attr::Str(ident) => self - .stcs - .entry(ident.clone()) - .or_insert_with(|| { - Attrs { - stcs: HashMap::new(), - dyns: Vec::new(), - } - .ir() - }) - .as_mut() - .try_unwrap_attrs() - .map_err(|_| { - Error::DowngradeError(format!( - r#"attribute '{}' already defined"#, - Symbol::from(ident) - )) - }) - .and_then(|attrs: &mut Attrs| attrs._insert(path, name, value)), - Attr::Strs(string) => { - let mut attrs = Attrs { - stcs: HashMap::new(), - dyns: Vec::new(), - }; - attrs._insert(path, name, value)?; - self.dyns.push(DynAttr(string.ir(), attrs.ir())); - Ok(()) - } - Attr::Dynamic(dynamic) => { - let mut attrs = Attrs { - stcs: HashMap::new(), - dyns: Vec::new(), - }; - attrs._insert(path, name, value)?; - self.dyns.push(DynAttr(dynamic, attrs.ir())); - Ok(()) - } - } - } else { - match name { - Attr::Str(ident) => { - if self.stcs.insert(ident.clone(), value).is_some() { - return Err(Error::DowngradeError(format!( - r#"attribute '{}' already defined"#, - Symbol::from(ident) - ))); - } - } - Attr::Strs(string) => { - self.dyns.push(DynAttr(string.ir(), value)); - } - Attr::Dynamic(dynamic) => { - self.dyns.push(DynAttr(dynamic, value)); - } - } - Ok(()) - } - } - - pub fn insert(&mut self, path: Vec, value: Ir) -> Result<()> { - let mut path = path.into_iter(); - let name = path.next_back().unwrap(); - self._insert(path, name, value) - } - - fn _has_attr(&self, mut path: std::slice::Iter, name: Attr) -> Option { - match path.next() { - Some(Attr::Str(ident)) => self - .stcs - .get(ident) - .and_then(|attrs| attrs.as_ref().try_unwrap_attrs().ok()) - .map_or(Some(false), |attrs: &Attrs| attrs._has_attr(path, name)), - None => match name { - Attr::Str(ident) => Some(self.stcs.get(&ident).is_some()), - _ => None, - }, - _ => None, - } - } - - pub fn has_attr(&self, path: &[Attr]) -> Option { - let mut path = path.iter(); - let name = path.next_back().unwrap().clone(); - self._has_attr(path, name) - } - - fn _select(&self, mut path: std::slice::Iter, name: Attr) -> Result> { - match path.next() { - Some(Attr::Str(ident)) => self - .stcs - .get(ident) - .and_then(|attrs| attrs.as_ref().try_unwrap_attrs().ok()) - .ok_or_else(|| { - Error::DowngradeError(format!( - "attribute {} not found", - Symbol::from(ident.clone()) - )) - })? - ._select(path, name), - None => match name { - Attr::Str(ident) => self - .stcs - .get(&ident) - .map(|res| Some(res.clone())) - .map_or_else( - || { - if !self.dyns.is_empty() { - Ok(None) - } else { - Err(Error::DowngradeError(format!( - "attribute {} not found", - Symbol::from(ident.clone()) - ))) - } - }, - Ok, - ), - _ => Ok(None), - }, - _ => Ok(None), - } - } - - pub fn select(&self, path: &[Attr]) -> Result> { - let mut path = path.iter(); - let name = path.next_back().unwrap().clone(); - self._select(path, name) - } -} - -#[derive(Clone, Debug, TryUnwrap)] -pub enum Attr { - Dynamic(Ir), - Strs(ConcatStrings), - Str(String), -} - -impl Attr { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - use Attr::*; - Ok(match self { - Dynamic(ir) => Dynamic(ir.resolve(self_idx, ctx, env)?), - other => other, - }) - } -} - -impl Thunk { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - let idx = ctx.new_thunk_dep(self_idx, self.idx); - ctx.resolve_thunk(self.idx, env)?; - StackVar { offset: idx }.ir().ok() - } -} - -impl Arg { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - unreachable!() - } -} - -impl StackVar { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - unreachable!() - } -} - -#[derive(Clone, Debug)] -pub enum BinOpKind { - Add, - Sub, - Div, - Mul, - Eq, - Neq, - Lt, - Gt, - Leq, - Geq, - And, - Or, - Impl, - - Con, - Upd, - - PipeL, - PipeR, -} - -impl From for BinOpKind { - fn from(op: ast::BinOpKind) -> Self { - use BinOpKind::*; - use ast::BinOpKind as astkind; - match op { - astkind::Concat => Con, - astkind::Update => Upd, - astkind::Add => Add, - astkind::Sub => Sub, - astkind::Mul => Mul, - astkind::Div => Div, - astkind::And => And, - astkind::Equal => Eq, - astkind::Implication => Impl, - astkind::Less => Lt, - astkind::LessOrEq => Leq, - astkind::More => Gt, - astkind::MoreOrEq => Geq, - astkind::NotEqual => Neq, - astkind::Or => Or, - astkind::PipeLeft => PipeL, - astkind::PipeRight => PipeR, - } - } -} - -#[derive(Clone, Debug)] -pub enum UnOpKind { - Neg, - Not, -} - -impl From for UnOpKind { - fn from(value: ast::UnaryOpKind) -> Self { - match value { - ast::UnaryOpKind::Invert => UnOpKind::Not, - ast::UnaryOpKind::Negate => UnOpKind::Neg, - } - } -} - -pub struct Func { - pub param: Param, - pub body: Box, -} - -#[derive(Clone, Debug)] -pub enum Param { - Ident(String), - Formals { - formals: Vec<(String, Option)>, - ellipsis: bool, - alias: Option, - }, -} - -impl LoadFunc { - fn resolve<'a, 'env>( - self, - _: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - ctx.resolve_func(self.idx, env)?; - self.ir().ok() - } -} - -trait Downgrade -where - Self: Sized, -{ - fn downgrade(self, ctx: &mut DowngradeContext) -> Result; -} - -impl Downgrade for Expr { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { - use Expr::*; - match self { - Apply(apply) => apply.downgrade(ctx), - Assert(assert) => assert.downgrade(ctx), - Error(error) => Err(self::Error::DowngradeError(error.to_string())), - IfElse(ifelse) => ifelse.downgrade(ctx), - Select(select) => select.downgrade(ctx), - Str(str) => str.downgrade(ctx), - Path(path) => path.downgrade(ctx), - Literal(lit) => lit.downgrade(ctx), - Lambda(lambda) => lambda.downgrade(ctx), - LegacyLet(let_) => let_.downgrade(ctx), - LetIn(letin) => letin.downgrade(ctx), - List(list) => list.downgrade(ctx), - BinOp(op) => op.downgrade(ctx), - Paren(paren) => paren.expr().unwrap().downgrade(ctx), - Root(root) => root.expr().unwrap().downgrade(ctx), - AttrSet(attrs) => attrs.downgrade(ctx), - UnaryOp(op) => op.downgrade(ctx), - Ident(ident) => ident.downgrade(ctx), - With(with) => with.downgrade(ctx), - HasAttr(has) => has.downgrade(ctx), - } - } -} - -impl Downgrade for ast::Assert { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { - Assert { - assertion: self.condition().unwrap().downgrade(ctx)?.boxed(), - expr: self.body().unwrap().downgrade(ctx)?.boxed(), - } - .ir() - .ok() - } -} - -impl Assert { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - Self { - assertion: self.assertion.resolve(self_idx, ctx, env)?.boxed(), - expr: self.expr.resolve(self_idx, ctx, env)?.boxed(), - } - .ir() - .ok() - } -} - -impl Downgrade for ast::IfElse { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { - If { - cond: self.condition().unwrap().downgrade(ctx)?.boxed(), - consq: self.body().unwrap().downgrade(ctx)?.boxed(), - alter: self.else_body().unwrap().downgrade(ctx)?.boxed(), - } - .ir() - .ok() - } -} - -impl If { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - If { - cond: self.cond.resolve(self_idx, ctx, env)?.boxed(), - consq: self.consq.resolve(self_idx, ctx, env)?.boxed(), - alter: self.alter.resolve(self_idx, ctx, env)?.boxed(), - } - .ir() - .ok() - } -} - -impl Downgrade for ast::Path { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { - let parts = self - .parts() - .map(|part| match part { - ast::InterpolPart::Literal(lit) => Str { - val: lit.to_string(), - } - .ir() - .ok(), - ast::InterpolPart::Interpolation(interpol) => { - interpol.expr().unwrap().downgrade(ctx) - } - }) - .collect::>>()?; - if parts.len() == 1 { - Path { - expr: parts.into_iter().next().unwrap().boxed(), - } - } else { - Path { - expr: ConcatStrings { parts }.ir().boxed(), - } - } - .ir() - .ok() - } -} - -impl Path { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - Self { - expr: self.expr.resolve(self_idx, ctx, env)?.boxed(), - } - .ir() - .ok() - } -} - -impl Downgrade for ast::Str { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { - let parts = self - .normalized_parts() - .into_iter() - .map(|part| match part { - ast::InterpolPart::Literal(lit) => Str { val: lit }.ir().ok(), - ast::InterpolPart::Interpolation(interpol) => { - interpol.expr().unwrap().downgrade(ctx) - } - }) - .collect::>>()?; - if parts.len() == 1 { - Ok(parts.into_iter().next().unwrap()) - } else { - ConcatStrings { parts }.ir().ok() - } - } -} - -impl ConcatStrings { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - Self { - parts: self - .parts - .into_iter() - .map(|ir| ir.resolve(self_idx, ctx, env)) - .collect::>>()?, - } - .ir() - .ok() - } -} - -impl Downgrade for ast::Literal { - fn downgrade(self, _: &mut DowngradeContext) -> Result { - match self.kind() { - ast::LiteralKind::Integer(int) => Const::from(int.value().unwrap()).ir(), - ast::LiteralKind::Float(float) => Const::from(float.value().unwrap()).ir(), - ast::LiteralKind::Uri(uri) => Str { - val: uri.to_string(), - } - .ir(), - } - .ok() - } -} - -impl Const { - fn resolve<'a, 'env>( - self, - _: Index, - _: &mut DowngradeContext, - _: &Env<'a, 'env>, - ) -> Result { - self.ir().ok() - } -} - -impl Str { - fn resolve<'a, 'env>( - self, - _: Index, - _: &mut DowngradeContext, - _: &Env<'a, 'env>, - ) -> Result { - self.ir().ok() - } -} - -impl Downgrade for ast::Ident { - fn downgrade(self, _: &mut DowngradeContext) -> Result { - let sym = self.ident_token().unwrap().to_string(); - Var { sym }.ir().ok() - } -} - -impl Var { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - use LookupResult::*; - let Ok(res) = env.lookup(&self.sym) else { - return Err(Error::DowngradeError(format!( - "variable {} not found", - Symbol::from(self.sym) - ))); - }; - match res { - Builtin(ir) => ir, - SingleArg { idx } => { - ctx.new_arg_dep(self_idx, idx); - Arg { offset: idx }.ir() - }, - MultiArg { - idx, - default, - } => { - ctx.new_thunk_dep(self_idx, idx); - Select { - expr: Arg { offset: idx }.ir().boxed(), - attrpath: vec![Attr::Str(self.sym)], - default: default.map(Box::new), - } - .ir() - } - MaybeThunk(thunk) => thunk.resolve(self_idx, ctx, env)?, - With => self.ir(), - } - .ok() - } -} - -impl Downgrade for ast::AttrSet { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { - let rec = self.rec_token().is_some(); - let attrs = downgrade_attrs(self, ctx)?; - if rec { - let bindings = attrs - .stcs - .into_iter() - .sorted_by(|(a, _), (b, _)| a.cmp(b)) - .map(|(k, v)| (k, ctx.maybe_thunk(v))) - .collect::>(); - let stcs = bindings - .iter() - .map(|(sym, _)| (sym.clone(), Var { sym: sym.clone() }.ir())) - .collect(); - Let { - bindings, - expr: Attrs { stcs, ..attrs }.ir().boxed(), - } - .ir() - } else { - attrs.ir() - } - .ok() - } -} - -impl Attrs { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - Self { - stcs: self - .stcs - .into_iter() - .map(|(k, v)| Ok((k, v.resolve(self_idx, ctx, env)?))) - .collect::>()?, - dyns: self - .dyns - .into_iter() - .map(|DynAttr(k, v)| { - Ok(DynAttr( - k.resolve(self_idx, ctx, env)?, - v.resolve(self_idx, ctx, env)?, - )) - }) - .collect::>()?, - } - .ir() - .ok() - } -} - -impl Downgrade for ast::List { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { - let mut items = Vec::with_capacity(self.items().size_hint().0); - for item in self.items() { - items.push(item.downgrade(ctx)?) - } - List { items }.ir().ok() - } -} - -impl List { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - Self { - items: self - .items - .into_iter() - .map(|item| item.resolve(self_idx, ctx, env)) - .collect::>()?, - } - .ir() - .ok() - } -} - -impl Downgrade for ast::BinOp { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { - BinOp { - lhs: self.lhs().unwrap().downgrade(ctx)?.boxed(), - rhs: self.rhs().unwrap().downgrade(ctx)?.boxed(), - kind: self.operator().unwrap().into(), - } - .ir() - .ok() - } -} - -impl BinOp { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - Self { - lhs: self.lhs.resolve(self_idx, ctx, env)?.boxed(), - rhs: self.rhs.resolve(self_idx, ctx, env)?.boxed(), - ..self - } - .ir() - .ok() - } -} - -impl Downgrade for ast::HasAttr { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { - let attrs = self.expr().unwrap().downgrade(ctx)?; - let path = downgrade_attrpath(self.attrpath().unwrap(), ctx)?; - HasAttr { - lhs: attrs.boxed(), - rhs: path, - } - .ir() - .ok() - } -} - -impl HasAttr { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - Self { - lhs: self.lhs.resolve(self_idx, ctx, env)?.boxed(), - rhs: self - .rhs - .into_iter() - .map(|attr| attr.resolve(self_idx, ctx, env)) - .collect::>()?, - } - .ir() - .ok() - } -} - -impl Downgrade for ast::UnaryOp { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { - UnOp { - rhs: self.expr().unwrap().downgrade(ctx)?.boxed(), - kind: self.operator().unwrap().into(), - } - .ir() - .ok() - } -} - -impl UnOp { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - Self { - rhs: self.rhs.resolve(self_idx, ctx, env)?.boxed(), - ..self - } - .ir() - .ok() - } -} - -impl Downgrade for ast::Select { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { - Select { - expr: self.expr().unwrap().downgrade(ctx)?.boxed(), - attrpath: downgrade_attrpath(self.attrpath().unwrap(), ctx)?, - default: match self.default_expr() { - Some(default) => Some(default.downgrade(ctx)?.boxed()), - None => None, - }, - } - .ir() - .ok() - } -} - -impl Select { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - let expr = self.expr.resolve(self_idx, ctx, env)?; - let attrpath = self - .attrpath - .into_iter() - .map(|attr| attr.resolve(self_idx, ctx, env)) - .collect::>>()?; - let res = match &expr { - Ir::Attrs(attrs) => attrs.select(&attrpath), - &Ir::Thunk(Thunk { idx }) => { - match ctx.thunks[idx].0.as_ref() { - IrRef::Attrs(attrs) => attrs.select(&attrpath), - _ => Ok(None) - } - } - _ => Ok(None), - }; - let res = match res { - Err(err) => { - if let Some(default) = self.default.clone() { - Ok(Some(default.resolve(self_idx, ctx, env)?)) - } else { - Err(err) - } - } - ok => ok, - }?; - if let Some(res) = res { - res.ok() - } else { - Select { - expr: expr.boxed(), - attrpath, - default: if let Some(default) = self.default { - Some(default.resolve(self_idx, ctx, env)?.boxed()) - } else { - None - }, - } - .ir() - .ok() - } - } -} - -impl Downgrade for ast::LegacyLet { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { - let attrs = downgrade_attrs(self, ctx)?; - Select { - expr: attrs.ir().boxed(), - attrpath: vec![Attr::Str("body".into())], - default: None, - } - .ir() - .ok() - } -} - -impl Downgrade for ast::LetIn { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { - let expr = self.body().unwrap().downgrade(ctx)?.boxed(); - let mut bindings = HashMap::new(); - for entry in self.entries() { - match entry { - ast::Entry::Inherit(inherit) => downgrade_inherit(inherit, &mut bindings, ctx)?, - ast::Entry::AttrpathValue(value) => { - let mut path = downgrade_attrpath(value.attrpath().unwrap(), ctx)?.into_iter(); - let value = value.value().unwrap().downgrade(ctx)?; - let unwrap_ident = |ident: Attr| { - ident.try_unwrap_str().map_err(|_| { - Error::DowngradeError("dynamic attributes not allowed in let".into()) - }) - }; - let ident = unwrap_ident(path.next().unwrap())?; - if path.len() > 1 { - let mut attrs = bindings - .entry(ident.clone()) - .or_insert_with(|| { - Attrs { - stcs: HashMap::new(), - dyns: Vec::new(), - } - .ir() - }) - .as_mut() - .try_unwrap_attrs() - .map_err(|_| { - Error::DowngradeError(format!( - r#"attribute '{}' already defined"#, - Symbol::from(ident) - )) - })?; - while path.len() > 1 { - attrs = attrs - .stcs - .entry(unwrap_ident(path.next().unwrap())?) - .or_insert_with(|| { - Attrs { - stcs: HashMap::new(), - dyns: Vec::new(), - } - .ir() - }) - .as_mut() - .try_unwrap_attrs() - .unwrap(); - } - attrs - .stcs - .insert(unwrap_ident(path.next().unwrap())?, value); - } else { - bindings.insert(ident, value); - } - } - } - } - let bindings = bindings - .into_iter() - .sorted_by(|(a, _), (b, _)| a.cmp(b)) - .map(|(k, v)| (k, ctx.maybe_thunk(v))) - .collect(); - Let { bindings, expr }.ir().ok() - } -} - -impl Let { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - let map = self.bindings.clone(); - let env = env.enter_let(&map); - self.bindings.into_iter().try_for_each(|(_, ir)| { - ir.resolve(self_idx, ctx, &env)?; - Ok(()) - })?; - self.expr.resolve(self_idx, ctx, &env)?.ok() - } -} - -impl Downgrade for ast::With { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { - let namespace = self.namespace().unwrap().downgrade(ctx)?.boxed(); - let expr = self.body().unwrap().downgrade(ctx)?.boxed(); - With { namespace, expr }.ir().ok() - } -} - -impl With { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - let namespace = self.namespace.resolve(self_idx, ctx, env)?.boxed(); - let expr = self.expr.resolve(self_idx, ctx, &env.enter_with())?.boxed(); - Self { namespace, expr }.ir().ok() - } -} - -impl Downgrade for ast::Lambda { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { - let body = self.body().unwrap(); - let param = downgrade_param(self.param().unwrap(), ctx)?; - let body = body.downgrade(ctx)?.boxed(); - ctx.new_func(Func { param, body }).ir().ok() - } -} - -impl Func { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - let env = match self.param.clone() { - Param::Ident(ident) => env.enter_single_arg(ident), - Param::Formals { formals, alias, .. } => env.enter_multi_arg( - formals - .iter() - .cloned() - .map(|(ident, default)| (ident, default.map(Ir::Thunk))) - .collect(), - alias, - ), - }; - let body = self.body.resolve(self_idx, ctx, &env)?.boxed(); - Ok(Self { body, ..self }) - } -} - -impl Downgrade for ast::Apply { - fn downgrade(self, ctx: &mut DowngradeContext) -> Result { - let mut args = vec![self.argument().unwrap().downgrade(ctx)?]; - let mut func = self.lambda().unwrap(); - while let ast::Expr::Apply(call) = func { - func = call.lambda().unwrap(); - args.push(call.argument().unwrap().downgrade(ctx)?); - } - let func = func.downgrade(ctx)?.boxed(); - args.reverse(); - Call { func, args }.ir().ok() - } -} - -impl Call { - fn resolve<'a, 'env>( - self, - self_idx: Index, - ctx: &mut DowngradeContext, - env: &Env<'a, 'env>, - ) -> Result { - Self { - func: self.func.resolve(self_idx, ctx, env)?.boxed(), - args: self - .args - .into_iter() - .map(|arg| arg.resolve(self_idx, ctx, env)) - .collect::>()?, - } - .ir() - .ok() - } -} diff --git a/src/ir/utils.rs b/src/ir/utils.rs deleted file mode 100644 index 3303251..0000000 --- a/src/ir/utils.rs +++ /dev/null @@ -1,146 +0,0 @@ -use rnix::ast; - -use super::*; - -pub fn downgrade_param(param: ast::Param, ctx: &mut DowngradeContext) -> Result { - match param { - ast::Param::IdentParam(ident) => Ok(Param::Ident(ident.to_string())), - ast::Param::Pattern(pattern) => downgrade_pattern(pattern, ctx), - } -} - -pub fn downgrade_pattern(pattern: ast::Pattern, ctx: &mut DowngradeContext) -> Result { - let formals = pattern - .pat_entries() - .map(|entry| { - let ident = entry.ident().unwrap().to_string(); - if entry.default().is_none() { - Ok((ident, None)) - } else { - entry - .default() - .unwrap() - .downgrade(ctx) - .map(|ok| (ident, Some(ctx.new_thunk(ok)))) - } - }) - .collect::>>()?; - let ellipsis = pattern.ellipsis_token().is_some(); - let alias = pattern - .pat_bind() - .map(|alias| alias.ident().unwrap().to_string()); - Ok(Param::Formals { - formals, - ellipsis, - alias, - }) -} - -pub fn downgrade_attrs(has_entry: impl ast::HasEntry, ctx: &mut DowngradeContext) -> Result { - let entires = has_entry.entries(); - let mut attrs = Attrs { - stcs: HashMap::new(), - dyns: Vec::new(), - }; - - for entry in entires { - match entry { - ast::Entry::Inherit(inherit) => downgrade_inherit(inherit, &mut attrs.stcs, ctx)?, - ast::Entry::AttrpathValue(value) => downgrade_attrpathvalue(value, &mut attrs, ctx)?, - } - } - - Ok(attrs) -} - -pub fn downgrade_inherit( - inherit: ast::Inherit, - stcs: &mut HashMap, - ctx: &mut DowngradeContext, -) -> Result<()> { - let from = if let Some(from) = inherit.from() { - let from = from.expr().unwrap().downgrade(ctx)?; - Some(ctx.new_thunk(from)) - } else { - None - }; - for attr in inherit.attrs() { - let ident = match downgrade_attr(attr, ctx)? { - Attr::Str(ident) => ident, - _ => { - return Err(Error::DowngradeError( - "dynamic attributes not allowed in inherit".to_string(), - )); - } - }; - let expr = from.map_or_else( - || Var { sym: ident.clone() }.ir().ok(), - |from| { - Ok(Select { - expr: from.ir().boxed(), - attrpath: vec![Attr::Str(ident.clone())], - default: None, - } - .ir()) - }, - )?; - // TODO: Error Handling - assert!(stcs.insert(ident, expr).is_none()); - } - Ok(()) -} - -pub fn downgrade_attr(attr: ast::Attr, ctx: &mut DowngradeContext) -> Result { - use ast::Attr::*; - use ast::InterpolPart::*; - match attr { - Ident(ident) => Ok(Attr::Str(ident.to_string())), - Str(string) => { - let parts = string.normalized_parts(); - if parts.is_empty() { - Ok(Attr::Str("".into())) - } else if parts.len() == 1 { - match parts.into_iter().next().unwrap() { - Literal(ident) => Ok(Attr::Str(ident)), - Interpolation(interpol) => { - Ok(Attr::Dynamic(interpol.expr().unwrap().downgrade(ctx)?)) - } - } - } else { - let parts = parts - .into_iter() - .map(|part| match part { - Literal(lit) => self::Str { val: lit }.ir().ok(), - Interpolation(interpol) => interpol.expr().unwrap().downgrade(ctx), - }) - .collect::>>()?; - Ok(Attr::Strs(ConcatStrings { parts })) - } - } - Dynamic(dynamic) => Ok(Attr::Dynamic(dynamic.expr().unwrap().downgrade(ctx)?)), - } -} - -pub fn downgrade_attrpath( - attrpath: ast::Attrpath, - ctx: &mut DowngradeContext, -) -> Result> { - attrpath - .attrs() - .map(|attr| downgrade_attr(attr, ctx)) - .collect::>>() -} - -pub fn downgrade_attrpathvalue( - value: ast::AttrpathValue, - attrs: &mut Attrs, - ctx: &mut DowngradeContext, -) -> Result<()> { - let path = downgrade_attrpath(value.attrpath().unwrap(), ctx)?; - let value = value.value().unwrap().downgrade(ctx)?; - let value = match value { - x @ Ir::Const(_) => x, - x => ctx.new_thunk(x).ir(), - }; - attrs.insert(path, value) -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 9c7d064..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![allow(dead_code)] - -mod builtins; -mod env; -mod stack; -mod ty; - -pub mod engine; -pub mod error; -pub mod eval; -pub mod ir; -pub use ty::public::Value; diff --git a/src/stack.rs b/src/stack.rs deleted file mode 100644 index a79cfb0..0000000 --- a/src/stack.rs +++ /dev/null @@ -1,98 +0,0 @@ -use core::mem::{MaybeUninit, replace, transmute}; -use core::ops::{Deref, DerefMut}; - -use crate::error::*; - -macro_rules! into { - ($e:expr) => { - // SAFETY: This macro is used to transmute `MaybeUninit>` to `Value<'vm>` - // or `&MaybeUninit>` to `&Value<'vm>`. - // This is safe because the `Stack` ensures that only initialized values are accessed - // within the `0..top` range. - unsafe { transmute($e) } - }; -} - -pub struct Stack { - items: [MaybeUninit; CAP], - top: usize, -} - -impl Default for Stack { - fn default() -> Self { - Self::new() - } -} - -impl Clone for Stack { - fn clone(&self) -> Self { - let mut items = [const { MaybeUninit::uninit() }; CAP]; - for (i, item) in self.items.iter().take(self.top).enumerate() { - items[i].write(unsafe { item.assume_init_ref() }.clone()); - } - Self { - items, - top: self.top, - } - } -} - -impl Stack { - pub fn new() -> Self { - Stack { - items: [const { MaybeUninit::uninit() }; CAP], - top: 0, - } - } - - pub fn push(&mut self, item: T) -> Result<()> { - self.items - .get_mut(self.top) - .map_or_else(|| Err(Error::EvalError("stack overflow".to_string())), Ok)? - .write(item); - self.top += 1; - Ok(()) - } - - pub fn pop(&mut self) -> T { - let item = self.items.get_mut(self.top - 1).unwrap(); - self.top -= 1; - - // SAFETY: `item` at `self.top` was previously written and is initialized. - // We replace it with `MaybeUninit::uninit()` and then `assume_init` - // on the original value, which is safe as it was initialized. - unsafe { replace(item, MaybeUninit::uninit()).assume_init() } - } - - pub fn tos(&self) -> &T { - into!(&self.items[self.top - 1]) - } - - pub fn tos_mut(&mut self) -> &mut T { - into!(&mut self.items[self.top - 1]) - } -} - -impl Deref for Stack { - type Target = [T]; - fn deref(&self) -> &Self::Target { - into!(&self.items[0..self.top]) - } -} - -impl DerefMut for Stack { - fn deref_mut(&mut self) -> &mut Self::Target { - into!(&mut self.items[0..self.top]) - } -} - -impl Drop for Stack { - fn drop(&mut self) { - self.items.as_mut_slice()[0..self.top] - .iter_mut() - // SAFETY: Items in the range `0..self.top` are guaranteed to be initialized. - // `assume_init_drop` is called to correctly drop these initialized `Value`s. - .map(|item| unsafe { item.assume_init_drop() }) - .for_each(drop) - } -} diff --git a/src/ty/common.rs b/src/ty/common.rs deleted file mode 100644 index 9ffad6c..0000000 --- a/src/ty/common.rs +++ /dev/null @@ -1,88 +0,0 @@ -use core::fmt::{Display, Formatter, Result as FmtResult}; -use core::hash::Hash; - -use derive_more::{Constructor, IsVariant, Unwrap}; - -#[derive(Clone, Debug, PartialEq, Constructor, Hash)] -pub struct Catchable { - msg: String, -} - -impl> From for Catchable { - fn from(value: T) -> Self { - Catchable { msg: value.into() } - } -} - -impl Display for Catchable { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "", self.msg) - } -} - -#[derive(Debug, Clone, Copy, IsVariant, Unwrap)] -pub enum Const { - Bool(bool), - Int(i64), - Float(f64), - Null, -} - -impl Hash for Const { - fn hash(&self, state: &mut H) { - use Const::*; - core::mem::discriminant(self).hash(state); - match self { - Int(x) => x.hash(state), - Float(x) => x.to_bits().hash(state), - Bool(x) => x.hash(state), - Null => (), - } - } -} - -impl Display for Const { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - use Const::*; - match self { - Int(x) => write!(f, "{x}"), - Float(x) => write!(f, "{x}"), - Bool(x) => write!(f, "{x}"), - Null => write!(f, "null"), - } - } -} - -impl From for Const { - fn from(value: bool) -> Self { - Const::Bool(value) - } -} - -impl From for Const { - fn from(value: i64) -> Self { - Const::Int(value) - } -} - -impl From for Const { - fn from(value: f64) -> Self { - Const::Float(value) - } -} - -impl PartialEq for Const { - fn eq(&self, other: &Self) -> bool { - use Const::*; - match (self, other) { - (Bool(a), Bool(b)) => a == b, - (Int(a), Int(b)) => a == b, - (Float(a), Float(b)) => a == b, - (Int(a), Float(b)) => *a as f64 == *b, - (Float(a), Int(b)) => *b as f64 == *a, - _ => false, - } - } -} - -impl Eq for Const {} diff --git a/src/ty/internal/func.rs b/src/ty/internal/func.rs deleted file mode 100644 index 212944f..0000000 --- a/src/ty/internal/func.rs +++ /dev/null @@ -1,11 +0,0 @@ -use derive_more::Constructor; -use hashbrown::HashMap; - -use super::Value; - -#[derive(Debug, Clone, Constructor)] -pub struct PartialFunc { - pub idx: usize, - pub args: Vec, - pub frame: Vec, -} diff --git a/src/ty/internal/list.rs b/src/ty/internal/list.rs deleted file mode 100644 index 08e568a..0000000 --- a/src/ty/internal/list.rs +++ /dev/null @@ -1,70 +0,0 @@ -use std::ops::Deref; - -use ecow::EcoVec; -use hashbrown::HashSet; - -use crate::engine::Engine; -use crate::ty::public as p; - -use super::Value; - -#[derive(Clone, PartialEq, Debug)] -pub struct List { - data: EcoVec, -} - -impl Default for List { - fn default() -> Self { - Self::new() - } -} - -impl>> From for List { - fn from(value: T) -> Self { - Self { data: value.into() } - } -} - -impl Deref for List { - type Target = [Value]; - fn deref(&self) -> &Self::Target { - &self.data - } -} - -impl List { - pub fn new() -> Self { - List { - data: EcoVec::new(), - } - } - - pub fn with_capacity(cap: usize) -> Self { - List { - data: EcoVec::with_capacity(cap), - } - } - - pub fn push(&mut self, elem: Value) { - self.data.push(elem); - } - - pub fn concat(&mut self, other: &List) { - for elem in other.data.iter() { - self.data.push(elem.clone()); - } - } - - pub fn into_inner(self) -> EcoVec { - self.data - } - - pub fn to_public(&self, engine: &Engine, seen: &mut HashSet) -> p::Value { - p::Value::List(p::List::new( - self.data - .iter() - .map(|value| value.clone().to_public(engine, seen)) - .collect(), - )) - } -} diff --git a/src/ty/mod.rs b/src/ty/mod.rs deleted file mode 100644 index 179e099..0000000 --- a/src/ty/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod common; -pub mod internal; -pub mod public;