refactor: reduce coupling
This commit is contained in:
439
Cargo.lock
generated
439
Cargo.lock
generated
@@ -31,9 +31,9 @@ checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
@@ -41,12 +41,6 @@ version = "1.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "2.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.19.0"
|
version = "3.19.0"
|
||||||
@@ -58,24 +52,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||||
|
|
||||||
[[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",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "convert_case"
|
name = "convert_case"
|
||||||
@@ -86,6 +65,15 @@ dependencies = [
|
|||||||
"unicode-segmentation",
|
"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]]
|
[[package]]
|
||||||
name = "countme"
|
name = "countme"
|
||||||
version = "3.0.1"
|
version = "3.0.1"
|
||||||
@@ -94,9 +82,9 @@ checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cranelift"
|
name = "cranelift"
|
||||||
version = "0.121.1"
|
version = "0.122.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94c4a83217cefee80a63921d524b7c98c4dc0c9913bd876fcdfa76a4fcef9b62"
|
checksum = "d0849f998d4e04e6dd056a75268636e39a58ffe57a295bc69d351a424343a79e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cranelift-codegen",
|
"cranelift-codegen",
|
||||||
"cranelift-frontend",
|
"cranelift-frontend",
|
||||||
@@ -105,42 +93,42 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cranelift-assembler-x64"
|
name = "cranelift-assembler-x64"
|
||||||
version = "0.121.1"
|
version = "0.122.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "226b7077389885873ffad5d778e8512742580a6e11b0f723072f41f305d3652f"
|
checksum = "0ae7b60ec3fd7162427d3b3801520a1908bef7c035b52983cd3ca11b8e7deb51"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cranelift-assembler-x64-meta",
|
"cranelift-assembler-x64-meta",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cranelift-assembler-x64-meta"
|
name = "cranelift-assembler-x64-meta"
|
||||||
version = "0.121.1"
|
version = "0.122.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e9cfeae5a23c8cf9c43381f49211f3ce6dc1da1d46f1c5d06966e6258cc483fa"
|
checksum = "6511c200fed36452697b4b6b161eae57d917a2044e6333b1c1389ed63ccadeee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cranelift-srcgen",
|
"cranelift-srcgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cranelift-bforest"
|
name = "cranelift-bforest"
|
||||||
version = "0.121.1"
|
version = "0.122.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c88c577c6af92b550cb83455c331cf8e1bc89fe0ccc3e7eb0fa617ed1d63056"
|
checksum = "5f7086a645aa58bae979312f64e3029ac760ac1b577f5cd2417844842a2ca07f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cranelift-entity",
|
"cranelift-entity",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cranelift-bitset"
|
name = "cranelift-bitset"
|
||||||
version = "0.121.1"
|
version = "0.122.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "370f0aa7f1816bf0f838048d69b72d6cf12ef2fc3b37f6997fe494ffb9feb3ad"
|
checksum = "5225b4dec45f3f3dbf383f12560fac5ce8d780f399893607e21406e12e77f491"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cranelift-codegen"
|
name = "cranelift-codegen"
|
||||||
version = "0.121.1"
|
version = "0.122.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7d1a10a8a2958b68ecd261e565eef285249e242a8447ac959978319eabbb4a55"
|
checksum = "858fb3331e53492a95979378d6df5208dd1d0d315f19c052be8115f4efc888e0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"cranelift-assembler-x64",
|
"cranelift-assembler-x64",
|
||||||
@@ -152,21 +140,21 @@ dependencies = [
|
|||||||
"cranelift-entity",
|
"cranelift-entity",
|
||||||
"cranelift-isle",
|
"cranelift-isle",
|
||||||
"gimli",
|
"gimli",
|
||||||
"hashbrown 0.15.3",
|
"hashbrown 0.15.4",
|
||||||
"log",
|
"log",
|
||||||
"regalloc2",
|
"regalloc2",
|
||||||
"rustc-hash 2.1.1",
|
"rustc-hash 2.1.1",
|
||||||
"serde",
|
"serde",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"target-lexicon",
|
"target-lexicon",
|
||||||
"wasmtime-math",
|
"wasmtime-internal-math",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cranelift-codegen-meta"
|
name = "cranelift-codegen-meta"
|
||||||
version = "0.121.1"
|
version = "0.122.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f319986d5ae1386cfec625c70f8c01e52dc1f910aa6aaee7740bf8842d4e19c7"
|
checksum = "456715b9d5f12398f156d5081096e7b5d039f01b9ecc49790a011c8e43e65b5f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cranelift-assembler-x64-meta",
|
"cranelift-assembler-x64-meta",
|
||||||
"cranelift-codegen-shared",
|
"cranelift-codegen-shared",
|
||||||
@@ -175,33 +163,33 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cranelift-codegen-shared"
|
name = "cranelift-codegen-shared"
|
||||||
version = "0.121.1"
|
version = "0.122.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed52f5660397039c3c741c3acf18746445f4e20629b7280d9f2ccfe57e2b1efd"
|
checksum = "0306041099499833f167a0ddb707e1e54100f1a84eab5631bc3dad249708f482"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cranelift-control"
|
name = "cranelift-control"
|
||||||
version = "0.121.1"
|
version = "0.122.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79bde8d48e1840702574e28c5d7d4499441435af71e6c47450881f84ce2b60a5"
|
checksum = "1672945e1f9afc2297f49c92623f5eabc64398e2cb0d824f8f72a2db2df5af23"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arbitrary",
|
"arbitrary",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cranelift-entity"
|
name = "cranelift-entity"
|
||||||
version = "0.121.1"
|
version = "0.122.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e0335ac187211ac94c254826b6e78d23b8654ae09ebf0830506a827a2647162f"
|
checksum = "aa3cd55eb5f3825b9ae5de1530887907360a6334caccdc124c52f6d75246c98a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cranelift-bitset",
|
"cranelift-bitset",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cranelift-frontend"
|
name = "cranelift-frontend"
|
||||||
version = "0.121.1"
|
version = "0.122.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f4fce5fcf93c1fece95d0175b15fbaf0808b187430bc06c8ecde80db0ed58c5e"
|
checksum = "781f9905f8139b8de22987b66b522b416fe63eb76d823f0b3a8c02c8fd9500c7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cranelift-codegen",
|
"cranelift-codegen",
|
||||||
"log",
|
"log",
|
||||||
@@ -211,15 +199,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cranelift-isle"
|
name = "cranelift-isle"
|
||||||
version = "0.121.1"
|
version = "0.122.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13fc8d838a2bf28438dbaf6ccdbc34531b6a972054f43fd23be7f124121ce6e0"
|
checksum = "a05337a2b02c3df00b4dd9a263a027a07b3dff49f61f7da3b5d195c21eaa633d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cranelift-jit"
|
name = "cranelift-jit"
|
||||||
version = "0.121.1"
|
version = "0.122.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e50932cee220b782812b728c0e63adf2b8eef63e823df8e5fea84c18f3fff99"
|
checksum = "593f8ff2c1a1785d9ab61a4b112ec1c9e8a3b976d8857ed1e70a79d4a07dd5ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cranelift-codegen",
|
"cranelift-codegen",
|
||||||
@@ -231,15 +219,15 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"region",
|
"region",
|
||||||
"target-lexicon",
|
"target-lexicon",
|
||||||
"wasmtime-jit-icache-coherence",
|
"wasmtime-internal-jit-icache-coherence",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cranelift-module"
|
name = "cranelift-module"
|
||||||
version = "0.121.1"
|
version = "0.122.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2707466bd2c786bd637e6b6375ebb472a158be35b6efbe85d2a744ec82e16356"
|
checksum = "f9f7a4b804066f3e62d8fc943e25adc135acbb39288aa6c68e67021a9f6a0c58"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cranelift-codegen",
|
"cranelift-codegen",
|
||||||
@@ -248,9 +236,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cranelift-native"
|
name = "cranelift-native"
|
||||||
version = "0.121.1"
|
version = "0.122.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0975ce66adcf2e0729d06b1d3efea0398d793d1f39c2e0a6f52a347537836693"
|
checksum = "2eee7a496dd66380082c9c5b6f2d5fa149cec0ec383feec5caf079ca2b3671c2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cranelift-codegen",
|
"cranelift-codegen",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -259,9 +247,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cranelift-srcgen"
|
name = "cranelift-srcgen"
|
||||||
version = "0.121.1"
|
version = "0.122.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4493a9b500bb02837ea2fb7d4b58c1c21c37a470ae33c92659f4e637aad14c9"
|
checksum = "b530783809a55cb68d070e0de60cfbb3db0dc94c8850dd5725411422bedcf6bb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
@@ -278,30 +266,18 @@ version = "2.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"convert_case",
|
"convert_case 0.7.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ecow"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "54bfbb1708988623190a6c4dbedaeaf0f53c20c6395abd6a01feb327b3146f4b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.10.0"
|
version = "1.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "endian-type"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
@@ -309,45 +285,12 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
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]]
|
[[package]]
|
||||||
name = "fallible-iterator"
|
name = "fallible-iterator"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
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]]
|
[[package]]
|
||||||
name = "foldhash"
|
name = "foldhash"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -367,38 +310,29 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.3"
|
version = "0.14.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.3"
|
version = "0.15.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
|
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"allocator-api2",
|
"allocator-api2",
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"foldhash",
|
"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]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.9.0"
|
version = "2.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.15.3",
|
"hashbrown 0.15.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -412,9 +346,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.172"
|
version = "0.2.174"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libm"
|
name = "libm"
|
||||||
@@ -422,27 +356,12 @@ version = "0.2.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "linux-raw-sys"
|
|
||||||
version = "0.9.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.27"
|
version = "0.4.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
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]]
|
[[package]]
|
||||||
name = "mach2"
|
name = "mach2"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@@ -454,9 +373,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.4"
|
version = "2.7.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memoffset"
|
name = "memoffset"
|
||||||
@@ -467,70 +386,123 @@ dependencies = [
|
|||||||
"autocfg",
|
"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]]
|
[[package]]
|
||||||
name = "nixjit"
|
name = "nixjit"
|
||||||
version = "0.0.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nixjit_context"
|
||||||
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cranelift",
|
"cranelift",
|
||||||
"cranelift-jit",
|
"cranelift-jit",
|
||||||
"cranelift-module",
|
"cranelift-module",
|
||||||
"cranelift-native",
|
"cranelift-native",
|
||||||
"derive_more",
|
"hashbrown 0.15.4",
|
||||||
"ecow",
|
|
||||||
"hashbrown 0.15.3",
|
|
||||||
"itertools",
|
"itertools",
|
||||||
"lru",
|
"nixjit_error",
|
||||||
"petgraph",
|
"nixjit_eval",
|
||||||
"priority-queue",
|
"nixjit_hir",
|
||||||
"regex",
|
"nixjit_ir",
|
||||||
"replace_with",
|
"nixjit_jit",
|
||||||
"rnix",
|
"nixjit_lir",
|
||||||
"rustyline",
|
"nixjit_value",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nixjit_error"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "petgraph"
|
name = "nixjit_eval"
|
||||||
version = "0.8.2"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "54acf3a685220b533e437e264e4d932cfbdc4cc7ec0cd232ed73c08d03b8a7ca"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fixedbitset",
|
"derive_more",
|
||||||
"hashbrown 0.15.3",
|
"hashbrown 0.15.4",
|
||||||
"indexmap",
|
"itertools",
|
||||||
"serde",
|
"nixjit_error",
|
||||||
|
"nixjit_ir",
|
||||||
|
"nixjit_lir",
|
||||||
|
"nixjit_value",
|
||||||
|
"replace_with",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "priority-queue"
|
name = "nixjit_hir"
|
||||||
version = "2.5.0"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5676d703dda103cbb035b653a9f11448c0a7216c7926bd35fcb5865475d0c970"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"derive_more",
|
||||||
"equivalent",
|
"hashbrown 0.15.4",
|
||||||
"indexmap",
|
"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]]
|
[[package]]
|
||||||
@@ -551,16 +523,6 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"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]]
|
[[package]]
|
||||||
name = "regalloc2"
|
name = "regalloc2"
|
||||||
version = "0.12.2"
|
version = "0.12.2"
|
||||||
@@ -569,7 +531,7 @@ checksum = "5216b1837de2149f8bc8e6d5f88a9326b63b8c836ed58ce4a0a29ec736a59734"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"allocator-api2",
|
"allocator-api2",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"hashbrown 0.15.3",
|
"hashbrown 0.15.4",
|
||||||
"log",
|
"log",
|
||||||
"rustc-hash 2.1.1",
|
"rustc-hash 2.1.1",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
@@ -610,7 +572,7 @@ version = "3.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6b6ebd13bc009aef9cd476c1310d49ac354d36e240cf1bd753290f3dc7199a7"
|
checksum = "e6b6ebd13bc009aef9cd476c1310d49ac354d36e240cf1bd753290f3dc7199a7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags",
|
||||||
"libc",
|
"libc",
|
||||||
"mach2",
|
"mach2",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
@@ -633,12 +595,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rowan"
|
name = "rowan"
|
||||||
version = "0.15.15"
|
version = "0.15.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49"
|
checksum = "d4f1e4a001f863f41ea8d0e6a0c34b356d5b733db50dadab3efef640bafb779b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"countme",
|
"countme",
|
||||||
"hashbrown 0.14.3",
|
"hashbrown 0.14.5",
|
||||||
"memoffset",
|
"memoffset",
|
||||||
"rustc-hash 1.1.0",
|
"rustc-hash 1.1.0",
|
||||||
"text-size",
|
"text-size",
|
||||||
@@ -656,41 +618,6 @@ version = "2.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
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]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.219"
|
version = "1.0.219"
|
||||||
@@ -713,9 +640,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.13.2"
|
version = "1.15.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stable_deref_trait"
|
name = "stable_deref_trait"
|
||||||
@@ -725,9 +652,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.101"
|
version = "2.0.104"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -778,12 +705,6 @@ version = "1.12.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-width"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
@@ -791,16 +712,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "wasmtime-internal-jit-icache-coherence"
|
||||||
version = "0.2.2"
|
version = "35.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
checksum = "4417e06b7f80baff87d9770852c757a39b8d7f11d78b2620ca992b8725f16f50"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasmtime-jit-icache-coherence"
|
|
||||||
version = "34.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "44c71d64e8ebe132cd45e9d299a4d0daf261d66bd05cf50a204a1bf8cf96ff1f"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@@ -809,10 +724,10 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmtime-math"
|
name = "wasmtime-internal-math"
|
||||||
version = "34.0.1"
|
version = "35.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "222bfa4769c6931c985711eb49a92748ea0acc4ca85fcd24e945a2f1bacda0c1"
|
checksum = "7710d5c4ecdaa772927fd11e5dc30a9a62d1fc8fe933e11ad5576ad596ab6612"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libm",
|
"libm",
|
||||||
]
|
]
|
||||||
|
|||||||
52
Cargo.toml
52
Cargo.toml
@@ -1,38 +1,14 @@
|
|||||||
[package]
|
[workspace]
|
||||||
name = "nixjit"
|
resolver = "3"
|
||||||
version = "0.0.0"
|
members = [
|
||||||
edition = "2024"
|
"evaluator/nixjit",
|
||||||
|
"evaluator/nixjit_context",
|
||||||
[features]
|
"evaluator/nixjit_error",
|
||||||
repl = ["dep:rustyline"]
|
"evaluator/nixjit_eval",
|
||||||
|
"evaluator/nixjit_hir",
|
||||||
[[bin]]
|
"evaluator/nixjit_ir",
|
||||||
name = "repl"
|
"evaluator/nixjit_jit",
|
||||||
required-features = ["repl"]
|
"evaluator/nixjit_lir",
|
||||||
|
"evaluator/nixjit_macros",
|
||||||
[profile.perf]
|
"evaluator/nixjit_value",
|
||||||
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 }
|
|
||||||
|
|||||||
6
evaluator/nixjit/Cargo.toml
Normal file
6
evaluator/nixjit/Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "nixjit"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
14
evaluator/nixjit/src/lib.rs
Normal file
14
evaluator/nixjit/src/lib.rs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ use super::eval;
|
|||||||
fn test_expr(expr: &str, expected: Value) {
|
fn test_expr(expr: &str, expected: Value) {
|
||||||
println!("{expr}");
|
println!("{expr}");
|
||||||
let downgraded = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap();
|
let downgraded = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap();
|
||||||
println!("{downgraded:?}");
|
println!("{downgraded:#?}");
|
||||||
assert_eq!(eval(downgraded).unwrap(), expected);
|
assert_eq!(eval(downgraded).unwrap(), expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
21
evaluator/nixjit_context/Cargo.toml
Normal file
21
evaluator/nixjit_context/Cargo.toml
Normal file
@@ -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" }
|
||||||
145
evaluator/nixjit_context/src/lib.rs
Normal file
145
evaluator/nixjit_context/src/lib.rs
Normal file
@@ -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<RefCell<Hir>>,
|
||||||
|
lirs: Vec<MaybeUninit<Lir>>,
|
||||||
|
jit: JITCompiler<Self>,
|
||||||
|
compiled: Vec<OnceCell<JITFunc<Self>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<T>(&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<T>(&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<Item = (&'a String, &'a ExprId)>,
|
||||||
|
f: impl FnOnce(&mut Self) -> T,
|
||||||
|
) -> T {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn with_with_env<T>(&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<nixjit_eval::Value<Self>>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn pop_frame(&mut self) -> Vec<nixjit_eval::Value<Self>>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn lookup_with<'a>(&'a self, ident: &str) -> Option<&'a nixjit_eval::Value<Self>>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn with_with_env<T>(
|
||||||
|
&mut self,
|
||||||
|
namespace: std::rc::Rc<HashMap<String, nixjit_eval::Value<Self>>>,
|
||||||
|
f: impl FnOnce(&mut Self) -> T,
|
||||||
|
) -> T
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn with_args_env<T>(
|
||||||
|
&mut self,
|
||||||
|
args: Vec<nixjit_eval::Value<Self>>,
|
||||||
|
f: impl FnOnce(&mut Self) -> T,
|
||||||
|
) -> (Vec<nixjit_eval::Value<Self>>, T)
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JITContext for Context {
|
||||||
|
fn lookup_arg(&self, offset: usize) -> &nixjit_eval::Value<Self> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn lookup_stack(&self, offset: usize) -> &nixjit_eval::Value<Self> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn enter_with(&mut self, namespace: std::rc::Rc<HashMap<String, nixjit_eval::Value<Self>>>) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn exit_with(&mut self) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
7
evaluator/nixjit_error/Cargo.toml
Normal file
7
evaluator/nixjit_error/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "nixjit_error"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
thiserror = "2.0"
|
||||||
@@ -8,10 +8,10 @@ pub enum Error {
|
|||||||
ParseError(String),
|
ParseError(String),
|
||||||
#[error("error occurred during downgrade stage: {0}")]
|
#[error("error occurred during downgrade stage: {0}")]
|
||||||
DowngradeError(String),
|
DowngradeError(String),
|
||||||
|
#[error("error occurred during variable resolve stage: {0}")]
|
||||||
|
ResolutionError(String),
|
||||||
#[error("error occurred during evaluation stage: {0}")]
|
#[error("error occurred during evaluation stage: {0}")]
|
||||||
EvalError(String),
|
EvalError(String),
|
||||||
// #[error("error occurred during JIT compile stage: {0}")]
|
|
||||||
// CompileError(#[from] inkwell::builder::BuilderError),
|
|
||||||
#[error("unknown error")]
|
#[error("unknown error")]
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
15
evaluator/nixjit_eval/Cargo.toml
Normal file
15
evaluator/nixjit_eval/Cargo.toml
Normal file
@@ -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" }
|
||||||
308
evaluator/nixjit_eval/src/lib.rs
Normal file
308
evaluator/nixjit_eval/src/lib.rs
Normal file
@@ -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<Value<Self>>;
|
||||||
|
fn with_with_env<T>(
|
||||||
|
&mut self,
|
||||||
|
namespace: Rc<HashMap<String, Value<Self>>>,
|
||||||
|
f: impl FnOnce(&mut Self) -> T,
|
||||||
|
) -> T;
|
||||||
|
fn with_args_env<T>(
|
||||||
|
&mut self,
|
||||||
|
args: Vec<Value<Self>>,
|
||||||
|
f: impl FnOnce(&mut Self) -> T,
|
||||||
|
) -> (Vec<Value<Self>>, T);
|
||||||
|
fn lookup_with<'a>(&'a self, ident: &str) -> Option<&'a Value<Self>>;
|
||||||
|
fn pop_frame(&mut self) -> Vec<Value<Self>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Evaluate<Ctx: EvalContext> {
|
||||||
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ExprId {
|
||||||
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
||||||
|
ctx.eval(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: EvalContext> Evaluate<Ctx> for lir::Lir {
|
||||||
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::AttrSet {
|
||||||
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
||||||
|
let mut attrs = AttrSet::new(
|
||||||
|
self.stcs
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
let eval_result = v.eval(ctx);
|
||||||
|
Ok((k.clone(), eval_result?))
|
||||||
|
})
|
||||||
|
.collect::<Result<_>>()?,
|
||||||
|
);
|
||||||
|
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<Ctx: EvalContext> Evaluate<Ctx> for ir::List {
|
||||||
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
||||||
|
let items = self
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.map(|val| val.eval(ctx))
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
let result = Value::List(List::from(items).into()).ok();
|
||||||
|
Ok(result.unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::HasAttr {
|
||||||
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
||||||
|
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<Ctx: EvalContext> Evaluate<Ctx> for ir::BinOp {
|
||||||
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
||||||
|
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<Ctx: EvalContext> Evaluate<Ctx> for ir::UnOp {
|
||||||
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
||||||
|
use ir::UnOpKind::*;
|
||||||
|
let mut rhs = self.rhs.eval(ctx)?;
|
||||||
|
match self.kind {
|
||||||
|
Neg => {
|
||||||
|
rhs.neg();
|
||||||
|
}
|
||||||
|
Not => {
|
||||||
|
rhs.not();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Select {
|
||||||
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
||||||
|
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<Ctx: EvalContext> Evaluate<Ctx> for ir::If {
|
||||||
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
||||||
|
// 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<Ctx: EvalContext> Evaluate<Ctx> for ir::Call {
|
||||||
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
||||||
|
let mut func = self.func.eval(ctx)?;
|
||||||
|
func.call(
|
||||||
|
self.args
|
||||||
|
.iter()
|
||||||
|
.map(|arg| arg.eval(ctx))
|
||||||
|
.collect::<Result<_>>()?,
|
||||||
|
ctx,
|
||||||
|
)?;
|
||||||
|
Ok(func.ok().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::With {
|
||||||
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
||||||
|
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<Ctx: EvalContext> Evaluate<Ctx> for ir::Assert {
|
||||||
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::ConcatStrings {
|
||||||
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
||||||
|
let mut parts = self
|
||||||
|
.parts
|
||||||
|
.iter()
|
||||||
|
.map(|part| {
|
||||||
|
let mut part = part.eval(ctx)?;
|
||||||
|
part.coerce_to_string();
|
||||||
|
part.ok()
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>>>()?
|
||||||
|
.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<Ctx: EvalContext> Evaluate<Ctx> for ir::Str {
|
||||||
|
fn eval(&self, _: &mut Ctx) -> Result<Value<Ctx>> {
|
||||||
|
let result = Value::String(self.val.clone()).ok();
|
||||||
|
Ok(result.unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Const {
|
||||||
|
fn eval(&self, _: &mut Ctx) -> Result<Value<Ctx>> {
|
||||||
|
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<Ctx: EvalContext> Evaluate<Ctx> for ir::Var {
|
||||||
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
||||||
|
ctx.lookup_with(&self.sym)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
Error::EvalError(format!(
|
||||||
|
"variable {} not found",
|
||||||
|
Symbol::from(self.sym.clone())
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.map(|val| val.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: EvalContext> Evaluate<Ctx> for ir::Path {
|
||||||
|
fn eval(&self, ctx: &mut Ctx) -> Result<Value<Ctx>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,48 +1,72 @@
|
|||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use derive_more::Constructor;
|
use derive_more::Constructor;
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::engine::Engine;
|
use nixjit_error::{Error, Result};
|
||||||
use crate::error::{Error, Result};
|
use nixjit_value as p;
|
||||||
use crate::ty::public::Symbol;
|
use nixjit_value::Symbol;
|
||||||
|
|
||||||
use super::super::public as p;
|
|
||||||
use super::Value;
|
use super::Value;
|
||||||
|
use crate::EvalContext;
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Constructor, Clone, PartialEq, Debug)]
|
#[derive(Constructor, PartialEq)]
|
||||||
pub struct AttrSet {
|
pub struct AttrSet<Ctx: EvalContext> {
|
||||||
data: HashMap<String, Value>,
|
data: HashMap<String, Value<Ctx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<HashMap<String, Value>> for AttrSet {
|
impl<Ctx: EvalContext> Debug for AttrSet<Ctx> {
|
||||||
fn from(data: HashMap<String, Value>) -> Self {
|
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<Ctx: EvalContext> Clone for AttrSet<Ctx> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
AttrSet {
|
||||||
|
data: self.data.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: EvalContext> From<HashMap<String, Value<Ctx>>> for AttrSet<Ctx> {
|
||||||
|
fn from(data: HashMap<String, Value<Ctx>>) -> Self {
|
||||||
Self { data }
|
Self { data }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for AttrSet {
|
impl<Ctx: EvalContext> Deref for AttrSet<Ctx> {
|
||||||
type Target = HashMap<String, Value>;
|
type Target = HashMap<String, Value<Ctx>>;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.data
|
&self.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttrSet {
|
impl<Ctx: EvalContext> AttrSet<Ctx> {
|
||||||
pub fn with_capacity(cap: usize) -> Self {
|
pub fn with_capacity(cap: usize) -> Self {
|
||||||
AttrSet {
|
AttrSet {
|
||||||
data: HashMap::with_capacity(cap),
|
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<Ctx>) {
|
||||||
self.data.insert(sym, val);
|
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<Ctx>) {
|
||||||
if self.data.get(&sym).is_some() {
|
if self.data.get(&sym).is_some() {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
@@ -52,24 +76,23 @@ impl AttrSet {
|
|||||||
pub fn select(
|
pub fn select(
|
||||||
&self,
|
&self,
|
||||||
mut path: impl DoubleEndedIterator<Item = Result<String>>,
|
mut path: impl DoubleEndedIterator<Item = Result<String>>,
|
||||||
) -> Result<Value> {
|
) -> Result<Value<Ctx>> {
|
||||||
// .ok_or_else(|| Error::EvalError())),
|
|
||||||
let mut data = &self.data;
|
let mut data = &self.data;
|
||||||
let last = path.nth_back(0).unwrap();
|
let last = path.nth_back(0).unwrap();
|
||||||
for item in path {
|
for item in path {
|
||||||
let item = item?;
|
let item = item?;
|
||||||
let Some(Value::AttrSet(attrs)) = data.get(&item) else {
|
let Some(Value::AttrSet(attrs)) = data.get(&item) else {
|
||||||
return Err(Error::EvalError(format!(
|
return Err(Error::EvalError(format!(
|
||||||
"attribute {} not found",
|
"attribute '{}' not found",
|
||||||
Symbol::from(item)
|
Symbol::from(item)
|
||||||
)));
|
)));
|
||||||
};
|
};
|
||||||
data = attrs.as_inner();
|
data = attrs.as_inner();
|
||||||
}
|
}
|
||||||
let last = last?;
|
let last = last?;
|
||||||
data.get(&last)
|
data.get(&last).cloned().ok_or_else(|| {
|
||||||
.cloned()
|
Error::EvalError(format!("attribute '{}' not found", Symbol::from(last)))
|
||||||
.ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(last))))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_attr(
|
pub fn has_attr(
|
||||||
@@ -87,38 +110,38 @@ impl AttrSet {
|
|||||||
Ok(data.get(&last?).is_some())
|
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() {
|
for (k, v) in other.data.iter() {
|
||||||
self.push_attr_force(k.clone(), v.clone())
|
self.push_attr_force(k.clone(), v.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_inner(&self) -> &HashMap<String, Value> {
|
pub fn as_inner(&self) -> &HashMap<String, Value<Ctx>> {
|
||||||
&self.data
|
&self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_inner(self: Rc<Self>) -> Rc<HashMap<String, Value>> {
|
pub fn into_inner(self: Rc<Self>) -> Rc<HashMap<String, Value<Ctx>>> {
|
||||||
unsafe { core::mem::transmute(self) }
|
unsafe { core::mem::transmute(self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_inner(data: HashMap<String, Value>) -> Self {
|
pub fn from_inner(data: HashMap<String, Value<Ctx>>) -> Self {
|
||||||
Self { data }
|
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()
|
self.data.iter().len() == other.data.iter().len()
|
||||||
&& std::iter::zip(
|
&& std::iter::zip(
|
||||||
self.data.iter().sorted_by(|(a, _), (b, _)| a.cmp(b)),
|
self.data.iter().sorted_by(|(a, _), (b, _)| a.cmp(b)),
|
||||||
other.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<Value>) -> p::Value {
|
pub fn to_public(&self, ctx: &Ctx, seen: &mut HashSet<Value<Ctx>>) -> p::Value {
|
||||||
p::Value::AttrSet(p::AttrSet::new(
|
p::Value::AttrSet(p::AttrSet::new(
|
||||||
self.data
|
self.data
|
||||||
.iter()
|
.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(),
|
.collect(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
47
evaluator/nixjit_eval/src/value/func.rs
Normal file
47
evaluator/nixjit_eval/src/value/func.rs
Normal file
@@ -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<Ctx: EvalContext> {
|
||||||
|
pub body: ExprId,
|
||||||
|
pub args: Vec<Value<Ctx>>,
|
||||||
|
pub frame: Vec<Value<Ctx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: EvalContext> Clone for FuncApp<Ctx> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
body: self.body,
|
||||||
|
args: self.args.clone(),
|
||||||
|
frame: self.frame.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: EvalContext> FuncApp<Ctx> {
|
||||||
|
pub fn call(
|
||||||
|
self: &mut Rc<Self>,
|
||||||
|
new_args: Vec<Value<Ctx>>,
|
||||||
|
ctx: &mut Ctx,
|
||||||
|
) -> Result<Value<Ctx>> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
86
evaluator/nixjit_eval/src/value/list.rs
Normal file
86
evaluator/nixjit_eval/src/value/list.rs
Normal file
@@ -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<Ctx: EvalContext> {
|
||||||
|
data: Vec<Value<Ctx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: EvalContext> Debug for List<Ctx> {
|
||||||
|
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<Ctx: EvalContext> Clone for List<Ctx> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
data: self.data.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: EvalContext, T: Into<Vec<Value<Ctx>>>> From<T> for List<Ctx> {
|
||||||
|
fn from(value: T) -> Self {
|
||||||
|
Self { data: value.into() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: EvalContext> Deref for List<Ctx> {
|
||||||
|
type Target = [Value<Ctx>];
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: EvalContext> List<Ctx> {
|
||||||
|
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<Ctx>) {
|
||||||
|
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<Value<Ctx>> {
|
||||||
|
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<Value<Ctx>>) -> PubValue {
|
||||||
|
PubValue::List(PubList::new(
|
||||||
|
self.data
|
||||||
|
.iter()
|
||||||
|
.map(|value| value.clone().to_public(engine, seen))
|
||||||
|
.collect(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::fmt::{write, Debug};
|
||||||
|
|
||||||
|
use derive_more::TryUnwrap;
|
||||||
use derive_more::{IsVariant, Unwrap};
|
use derive_more::{IsVariant, Unwrap};
|
||||||
use func::PartialFunc;
|
use func::FuncApp;
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
|
use nixjit_ir::ExprId;
|
||||||
use replace_with::replace_with_or_abort;
|
use replace_with::replace_with_or_abort;
|
||||||
|
|
||||||
use super::common::*;
|
use nixjit_error::{Error, Result};
|
||||||
use super::public as p;
|
use nixjit_value::Const;
|
||||||
|
use nixjit_value::Value as PubValue;
|
||||||
|
|
||||||
use crate::engine::Engine;
|
use crate::EvalContext;
|
||||||
use crate::env::Env;
|
|
||||||
use crate::error::*;
|
|
||||||
|
|
||||||
mod attrset;
|
mod attrset;
|
||||||
mod func;
|
mod func;
|
||||||
@@ -24,24 +26,66 @@ pub use list::List;
|
|||||||
pub use primop::*;
|
pub use primop::*;
|
||||||
|
|
||||||
#[repr(C, u64)]
|
#[repr(C, u64)]
|
||||||
#[derive(IsVariant, Unwrap, Clone, Debug)]
|
#[derive(IsVariant, TryUnwrap, Unwrap)]
|
||||||
pub enum Value {
|
pub enum Value<Ctx: EvalContext> {
|
||||||
Int(i64) = Self::INT,
|
Int(i64),
|
||||||
Float(f64) = Self::FLOAT,
|
Float(f64),
|
||||||
Bool(bool) = Self::BOOL,
|
Bool(bool),
|
||||||
String(String) = Self::STRING,
|
String(String),
|
||||||
Null = Self::NULL,
|
Null,
|
||||||
Thunk(usize) = Self::THUNK,
|
Thunk(usize),
|
||||||
AttrSet(Rc<AttrSet>) = Self::ATTRSET,
|
AttrSet(Rc<AttrSet<Ctx>>),
|
||||||
List(List) = Self::LIST,
|
List(Rc<List<Ctx>>),
|
||||||
Catchable(String) = Self::CATCHABLE,
|
Catchable(String),
|
||||||
PrimOp(Rc<PrimOp>) = Self::PRIMOP,
|
PrimOp(Rc<PrimOp<Ctx>>),
|
||||||
PartialPrimOp(Rc<PartialPrimOp>) = Self::PARTIAL_PRIMOP,
|
PrimOpApp(Rc<PrimOpApp<Ctx>>),
|
||||||
Func(usize) = Self::FUNC,
|
Func(ExprId),
|
||||||
PartialFunc(Rc<PartialFunc>) = Self::PARTIAL_FUNC,
|
FuncApp(Rc<FuncApp<Ctx>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for Value {
|
impl<Ctx: EvalContext> Debug for Value<Ctx> {
|
||||||
|
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, "<THUNK {thunk:?}>"),
|
||||||
|
Func(func) => write!(f, "<LAMBDA {func:?}>"),
|
||||||
|
FuncApp(func) => write!(f, "<LAMBDA-APP {:?}>", func.body),
|
||||||
|
PrimOp(primop) => write!(f, "<PRIMOP {}>", primop.name),
|
||||||
|
PrimOpApp(primop) => write!(f, "<PRIMOP-APP {}>", primop.name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: EvalContext> Clone for Value<Ctx> {
|
||||||
|
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<Ctx: EvalContext> Hash for Value<Ctx> {
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
std::mem::discriminant(self).hash(state);
|
std::mem::discriminant(self).hash(state);
|
||||||
@@ -53,7 +97,7 @@ impl Hash for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl<Ctx: EvalContext> Value<Ctx> {
|
||||||
pub const INT: u64 = 0;
|
pub const INT: u64 = 0;
|
||||||
pub const FLOAT: u64 = 1;
|
pub const FLOAT: u64 = 1;
|
||||||
pub const BOOL: u64 = 2;
|
pub const BOOL: u64 = 2;
|
||||||
@@ -79,13 +123,13 @@ impl Value {
|
|||||||
(String(a), String(b)) => a.as_str().eq(b.as_str()),
|
(String(a), String(b)) => a.as_str().eq(b.as_str()),
|
||||||
(Null, Null) => true,
|
(Null, Null) => true,
|
||||||
(AttrSet(a), AttrSet(b)) => a.eq_impl(b),
|
(AttrSet(a), AttrSet(b)) => a.eq_impl(b),
|
||||||
(List(a), List(b)) => a.eq(b),
|
(List(a), List(b)) => a.eq_impl(b),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Value {
|
impl<Ctx: EvalContext> PartialEq for Value<Ctx> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
@@ -96,27 +140,27 @@ impl PartialEq for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for Value {}
|
impl<Ctx: EvalContext> Eq for Value<Ctx> {}
|
||||||
|
|
||||||
#[derive(IsVariant, Unwrap, Clone)]
|
#[derive(IsVariant, Unwrap, Clone)]
|
||||||
pub enum ValueAsRef<'v> {
|
pub enum ValueAsRef<'v, Ctx: EvalContext> {
|
||||||
Int(i64),
|
Int(i64),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
String(&'v String),
|
String(&'v String),
|
||||||
Null,
|
Null,
|
||||||
Thunk(usize),
|
Thunk(usize),
|
||||||
AttrSet(&'v AttrSet),
|
AttrSet(&'v AttrSet<Ctx>),
|
||||||
List(&'v List),
|
List(&'v List<Ctx>),
|
||||||
Catchable(&'v str),
|
Catchable(&'v str),
|
||||||
PrimOp(&'v PrimOp),
|
PrimOp(&'v PrimOp<Ctx>),
|
||||||
PartialPrimOp(&'v PartialPrimOp),
|
PartialPrimOp(&'v PrimOpApp<Ctx>),
|
||||||
Func(usize),
|
Func(ExprId),
|
||||||
PartialFunc(&'v PartialFunc),
|
PartialFunc(&'v FuncApp<Ctx>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl<Ctx: EvalContext> Value<Ctx> {
|
||||||
pub fn as_ref(&self) -> ValueAsRef<'_> {
|
pub fn as_ref(&self) -> ValueAsRef<'_, Ctx> {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
use ValueAsRef as R;
|
use ValueAsRef as R;
|
||||||
match self {
|
match self {
|
||||||
@@ -130,13 +174,13 @@ impl Value {
|
|||||||
List(x) => R::List(x),
|
List(x) => R::List(x),
|
||||||
Catchable(x) => R::Catchable(x),
|
Catchable(x) => R::Catchable(x),
|
||||||
PrimOp(x) => R::PrimOp(x),
|
PrimOp(x) => R::PrimOp(x),
|
||||||
PartialPrimOp(x) => R::PartialPrimOp(x),
|
PrimOpApp(x) => R::PartialPrimOp(x),
|
||||||
Func(x) => R::Func(*x),
|
Func(x) => R::Func(*x),
|
||||||
PartialFunc(x) => R::PartialFunc(x),
|
FuncApp(x) => R::PartialFunc(x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Value {
|
impl<Ctx: EvalContext> Value<Ctx> {
|
||||||
pub fn ok(self) -> Result<Self> {
|
pub fn ok(self) -> Result<Self> {
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
@@ -154,21 +198,21 @@ impl Value {
|
|||||||
List(_) => "list",
|
List(_) => "list",
|
||||||
Catchable(_) => unreachable!(),
|
Catchable(_) => unreachable!(),
|
||||||
PrimOp(_) => "lambda",
|
PrimOp(_) => "lambda",
|
||||||
PartialPrimOp(_) => "lambda",
|
PrimOpApp(_) => "lambda",
|
||||||
Func(_) => "lambda",
|
Func(_) => "lambda",
|
||||||
PartialFunc(..) => "lambda",
|
FuncApp(..) => "lambda",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn callable(&self) -> bool {
|
pub fn callable(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Value::PrimOp(_) | Value::PartialPrimOp(_) | Value::Func(_) => true,
|
Value::PrimOp(_) | Value::PrimOpApp(_) | Value::Func(_) => true,
|
||||||
Value::AttrSet(_) => todo!(),
|
Value::AttrSet(_) => todo!(),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(&mut self, args: Vec<Self>, engine: &mut Engine, env: &mut Env) -> Result<()> {
|
pub fn call(&mut self, args: Vec<Self>, ctx: &mut Ctx) -> Result<()> {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
for arg in args.iter() {
|
for arg in args.iter() {
|
||||||
if matches!(arg, Value::Catchable(_)) {
|
if matches!(arg, Value::Catchable(_)) {
|
||||||
@@ -177,95 +221,18 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*self = match self {
|
*self = match self {
|
||||||
PrimOp(func) => func.call(args, engine),
|
PrimOp(func) => func.call(args, ctx),
|
||||||
PartialPrimOp(func) => func.call(args, engine),
|
PrimOpApp(func) => func.call(args, ctx),
|
||||||
PartialFunc(func) => {
|
FuncApp(func) => func.call(args, ctx),
|
||||||
let self::PartialFunc {
|
&mut Func(expr) => {
|
||||||
idx,
|
let (args, ret) = ctx.with_args_env(args, |ctx| ctx.eval(expr));
|
||||||
args: old_args,
|
let mut ret = ret?;
|
||||||
frame,
|
if let Value::Func(expr) = ret {
|
||||||
} = Rc::make_mut(func);
|
let frame = ctx.pop_frame();
|
||||||
let idx = *idx;
|
ret = Value::FuncApp(self::FuncApp::new(expr, args, frame).into());
|
||||||
let len = args.len() + old_args.len();
|
} else if let Value::FuncApp(func) = &mut ret {
|
||||||
env.reserve_args(len);
|
todo!();
|
||||||
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);
|
|
||||||
let func = Rc::make_mut(func);
|
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()
|
ret.ok()
|
||||||
}
|
}
|
||||||
@@ -404,7 +371,7 @@ impl Value {
|
|||||||
pub fn push(&mut self, elem: Self) -> &mut Self {
|
pub fn push(&mut self, elem: Self) -> &mut Self {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
if let List(list) = self {
|
if let List(list) = self {
|
||||||
list.push(elem);
|
Rc::make_mut(list).push(elem);
|
||||||
} else if let Catchable(_) = self {
|
} else if let Catchable(_) = self {
|
||||||
} else if let Catchable(_) = elem {
|
} else if let Catchable(_) = elem {
|
||||||
*self = elem;
|
*self = elem;
|
||||||
@@ -422,7 +389,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(List(a), List(b)) => {
|
(List(a), List(b)) => {
|
||||||
a.concat(&b);
|
Rc::make_mut(a).concat(&b);
|
||||||
}
|
}
|
||||||
(Catchable(_), _) => (),
|
(Catchable(_), _) => (),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
@@ -519,39 +486,31 @@ impl Value {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn force(&mut self, engine: &mut Engine, env: &mut Env) -> Result<&mut Self> {
|
pub fn to_public(&self, ctx: &Ctx, seen: &mut HashSet<Value<Ctx>>) -> PubValue {
|
||||||
if let &mut Value::Thunk(idx) = self {
|
use Value::*;
|
||||||
*self = env.lookup_stack(idx).clone();
|
|
||||||
}
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_public(&self, engine: &Engine, seen: &mut HashSet<Value>) -> p::Value {
|
|
||||||
use self::Value::*;
|
|
||||||
use p::Value;
|
|
||||||
if seen.contains(self) {
|
if seen.contains(self) {
|
||||||
return Value::Repeated;
|
return PubValue::Repeated;
|
||||||
}
|
}
|
||||||
match self {
|
match self {
|
||||||
AttrSet(attrs) => {
|
AttrSet(attrs) => {
|
||||||
seen.insert(self.clone());
|
seen.insert(self.clone());
|
||||||
attrs.to_public(engine, seen)
|
attrs.to_public(ctx, seen)
|
||||||
}
|
}
|
||||||
List(list) => {
|
List(list) => {
|
||||||
seen.insert(self.clone());
|
seen.insert(self.clone());
|
||||||
list.to_public(engine, seen)
|
list.to_public(ctx, seen)
|
||||||
}
|
}
|
||||||
Catchable(catchable) => Value::Catchable(catchable.clone().into()),
|
Catchable(catchable) => PubValue::Catchable(catchable.clone().into()),
|
||||||
Int(x) => Value::Const(Const::Int(*x)),
|
Int(x) => PubValue::Const(Const::Int(*x)),
|
||||||
Float(x) => Value::Const(Const::Float(*x)),
|
Float(x) => PubValue::Const(Const::Float(*x)),
|
||||||
Bool(x) => Value::Const(Const::Bool(*x)),
|
Bool(x) => PubValue::Const(Const::Bool(*x)),
|
||||||
String(x) => Value::String(x.clone()),
|
String(x) => PubValue::String(x.clone()),
|
||||||
Null => Value::Const(Const::Null),
|
Null => PubValue::Const(Const::Null),
|
||||||
Thunk(_) => Value::Thunk,
|
Thunk(_) => PubValue::Thunk,
|
||||||
PrimOp(primop) => Value::PrimOp(primop.name),
|
PrimOp(primop) => PubValue::PrimOp(primop.name),
|
||||||
PartialPrimOp(primop) => Value::PartialPrimOp(primop.name),
|
PrimOpApp(primop) => PubValue::PrimOpApp(primop.name),
|
||||||
Func(_) => Value::Func,
|
Func(_) => PubValue::Func,
|
||||||
PartialFunc(..) => Value::Func,
|
FuncApp(..) => PubValue::Func,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,31 +2,25 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use derive_more::Constructor;
|
use derive_more::Constructor;
|
||||||
|
|
||||||
use crate::engine::Engine;
|
use nixjit_error::Result;
|
||||||
use crate::error::Result;
|
|
||||||
|
|
||||||
use super::Value;
|
use super::Value;
|
||||||
|
use crate::EvalContext;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Constructor)]
|
#[derive(Debug, Clone, Constructor)]
|
||||||
pub struct PrimOp {
|
pub struct PrimOp<Ctx: EvalContext> {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
arity: usize,
|
arity: usize,
|
||||||
func: fn(Vec<Value>, &Engine) -> Result<Value>,
|
func: fn(Vec<Value<Ctx>>, &Ctx) -> Result<Value<Ctx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for PrimOp {
|
impl<Ctx: EvalContext> PrimOp<Ctx> {
|
||||||
fn eq(&self, _: &Self) -> bool {
|
pub fn call(&self, args: Vec<Value<Ctx>>, ctx: &Ctx) -> Result<Value<Ctx>> {
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PrimOp {
|
|
||||||
pub fn call(&self, args: Vec<Value>, ctx: &Engine) -> Result<Value> {
|
|
||||||
if args.len() > self.arity {
|
if args.len() > self.arity {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
if self.arity > args.len() {
|
if self.arity > args.len() {
|
||||||
Value::PartialPrimOp(Rc::new(PartialPrimOp {
|
Value::PrimOpApp(Rc::new(PrimOpApp {
|
||||||
name: self.name,
|
name: self.name,
|
||||||
arity: self.arity - args.len(),
|
arity: self.arity - args.len(),
|
||||||
args,
|
args,
|
||||||
@@ -39,22 +33,27 @@ impl PrimOp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PartialPrimOp {
|
pub struct PrimOpApp<Ctx: EvalContext> {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
arity: usize,
|
arity: usize,
|
||||||
args: Vec<Value>,
|
args: Vec<Value<Ctx>>,
|
||||||
func: fn(Vec<Value>, &Engine) -> Result<Value>,
|
func: fn(Vec<Value<Ctx>>, &Ctx) -> Result<Value<Ctx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for PartialPrimOp {
|
impl<Ctx: EvalContext> Clone for PrimOpApp<Ctx> {
|
||||||
fn eq(&self, _: &Self) -> bool {
|
fn clone(&self) -> Self {
|
||||||
false
|
Self {
|
||||||
|
name: self.name,
|
||||||
|
arity: self.arity,
|
||||||
|
args: self.args.clone(),
|
||||||
|
func: self.func,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialPrimOp {
|
impl<Ctx: EvalContext> PrimOpApp<Ctx> {
|
||||||
pub fn call(self: &mut Rc<Self>, args: Vec<Value>, ctx: &Engine) -> Result<Value> {
|
pub fn call(self: &mut Rc<Self>, args: Vec<Value<Ctx>>, ctx: &Ctx) -> Result<Value<Ctx>> {
|
||||||
if self.arity < args.len() {
|
if self.arity < args.len() {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
@@ -69,7 +68,7 @@ impl PartialPrimOp {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}) else {
|
}) else {
|
||||||
return Value::PartialPrimOp(self.clone()).ok();
|
return Value::PrimOpApp(self.clone()).ok();
|
||||||
};
|
};
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
16
evaluator/nixjit_hir/Cargo.toml
Normal file
16
evaluator/nixjit_hir/Cargo.toml
Normal file
@@ -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" }
|
||||||
357
evaluator/nixjit_hir/src/downgrade.rs
Normal file
357
evaluator/nixjit_hir/src/downgrade.rs
Normal file
@@ -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<Ctx: DowngradeContext> {
|
||||||
|
/// 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<ExprId>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The main entry point for downgrading any `rnix` expression.
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for Expr {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
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<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Assert {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let assertion = self.condition().unwrap().downgrade(ctx)?;
|
||||||
|
let expr = self.body().unwrap().downgrade(ctx)?;
|
||||||
|
Ok(ctx.new_expr(Assert { assertion, expr }.to_hir()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::IfElse {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
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<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Path {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
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::<Result<Vec<_>>>()?;
|
||||||
|
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<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Str {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
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::<Result<Vec<_>>>()?;
|
||||||
|
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<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Literal {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
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<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Ident {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let sym = self.ident_token().unwrap().to_string();
|
||||||
|
Ok(ctx.new_expr(Var { sym }.to_hir()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::AttrSet {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
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<Ctx: DowngradeContext> Downgrade<Ctx> for ast::List {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
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<Ctx: DowngradeContext> Downgrade<Ctx> for ast::BinOp {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
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<Ctx: DowngradeContext> Downgrade<Ctx> for ast::HasAttr {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
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<Ctx: DowngradeContext> Downgrade<Ctx> for ast::UnaryOp {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let rhs = self.expr().unwrap().downgrade(ctx)?;
|
||||||
|
let kind = self.operator().unwrap().into();
|
||||||
|
Ok(ctx.new_expr(UnOp { rhs, kind }.to_hir()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Select {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
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<Ctx: DowngradeContext> Downgrade<Ctx> for ast::LegacyLet {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
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<Ctx: DowngradeContext> Downgrade<Ctx> for ast::LetIn {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let body = self.body().unwrap().downgrade(ctx)?;
|
||||||
|
let bindings = downgrade_static_attrs(self, ctx)?;
|
||||||
|
Ok(ctx.new_expr(Let { bindings, body }.to_hir()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::With {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let namespace = self.namespace().unwrap().downgrade(ctx)?;
|
||||||
|
let expr = self.body().unwrap().downgrade(ctx)?;
|
||||||
|
Ok(ctx.new_expr(With { namespace, expr }.to_hir()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Lambda {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
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<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Apply {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
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()))
|
||||||
|
}
|
||||||
|
}
|
||||||
202
evaluator/nixjit_hir/src/lib.rs
Normal file
202
evaluator/nixjit_hir/src/lib.rs
Normal file
@@ -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<T>(&self, id: ExprId, f: impl FnOnce(&Hir, &Self) -> T) -> T;
|
||||||
|
|
||||||
|
/// Provides temporary mutable access to an expression.
|
||||||
|
fn with_expr_mut<T>(&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<String, ExprId>, 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<Attr>,
|
||||||
|
value: ExprId,
|
||||||
|
ctx: &mut impl DowngradeContext,
|
||||||
|
) -> Result<()>;
|
||||||
|
|
||||||
|
/// Internal helper for recursively inserting an attribute.
|
||||||
|
fn _insert(
|
||||||
|
&mut self,
|
||||||
|
path: impl Iterator<Item = Attr>,
|
||||||
|
name: Attr,
|
||||||
|
value: ExprId,
|
||||||
|
ctx: &mut impl DowngradeContext,
|
||||||
|
) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Attrs for AttrSet {
|
||||||
|
fn _insert(
|
||||||
|
&mut self,
|
||||||
|
mut path: impl Iterator<Item = Attr>,
|
||||||
|
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<Attr>,
|
||||||
|
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<ExprId>)>,
|
||||||
|
/// Whether an ellipsis (`...`) is present, allowing extra arguments.
|
||||||
|
ellipsis: bool,
|
||||||
|
/// An optional alias for the entire argument set, e.g., `args @ { ... }`.
|
||||||
|
alias: Option<String>,
|
||||||
|
},
|
||||||
|
}
|
||||||
223
evaluator/nixjit_hir/src/utils.rs
Normal file
223
evaluator/nixjit_hir/src/utils.rs
Normal file
@@ -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<Param> {
|
||||||
|
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<Param> {
|
||||||
|
// 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::<Result<Vec<_>>>()?;
|
||||||
|
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<AttrSet> {
|
||||||
|
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<HashMap<String, ExprId>> {
|
||||||
|
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<String, ExprId>,
|
||||||
|
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<Attr> {
|
||||||
|
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::<Result<Vec<_>>>()?;
|
||||||
|
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<Attr>`.
|
||||||
|
pub fn downgrade_attrpath(
|
||||||
|
attrpath: ast::Attrpath,
|
||||||
|
ctx: &mut impl DowngradeContext,
|
||||||
|
) -> Result<Vec<Attr>> {
|
||||||
|
attrpath
|
||||||
|
.attrs()
|
||||||
|
.map(|attr| downgrade_attr(attr, ctx))
|
||||||
|
.collect::<Result<Vec<_>>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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)
|
||||||
|
}
|
||||||
13
evaluator/nixjit_ir/Cargo.toml
Normal file
13
evaluator/nixjit_ir/Cargo.toml
Normal file
@@ -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" }
|
||||||
255
evaluator/nixjit_ir/src/lib.rs
Normal file
255
evaluator/nixjit_ir/src/lib.rs
Normal file
@@ -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<String, ExprId>,
|
||||||
|
/// 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<ExprId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<Attr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<ast::BinOpKind> 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<ast::UnaryOpKind> 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<Attr>,
|
||||||
|
/// An optional default value to return if the selection fails.
|
||||||
|
pub default: Option<ExprId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<String>,
|
||||||
|
pub required: Option<Vec<String>>,
|
||||||
|
pub allowed: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<ExprId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<ExprId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a constant value (e.g., integer, float, boolean, null).
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Const {
|
||||||
|
pub val: PubConst,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Into<PubConst>> From<T> 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,
|
||||||
|
}
|
||||||
19
evaluator/nixjit_jit/Cargo.toml
Normal file
19
evaluator/nixjit_jit/Cargo.toml
Normal file
@@ -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" }
|
||||||
@@ -1,18 +1,30 @@
|
|||||||
use cranelift::codegen::ir::{self, StackSlot};
|
use cranelift::codegen::ir::{self, StackSlot};
|
||||||
use cranelift::prelude::*;
|
use cranelift::prelude::*;
|
||||||
|
|
||||||
use crate::ir::*;
|
use nixjit_eval::{EvalContext, Value};
|
||||||
use crate::ty::common as c;
|
use nixjit_ir::*;
|
||||||
use crate::ty::internal::Value;
|
use nixjit_lir::Lir;
|
||||||
|
|
||||||
use super::JITContext;
|
use super::{Context, JITContext};
|
||||||
|
|
||||||
pub trait JITCompile {
|
pub trait JITCompile<Ctx: JITContext> {
|
||||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot;
|
fn compile(&self, ctx: &mut Context<Ctx>, engine: ir::Value, env: ir::Value) -> StackSlot;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Attrs {
|
impl<Ctx: JITContext> JITCompile<Ctx> for ExprId {
|
||||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
fn compile(&self, ctx: &mut Context<Ctx>, engine: ir::Value, env: ir::Value) -> StackSlot {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: JITContext> JITCompile<Ctx> for Lir {
|
||||||
|
fn compile(&self, ctx: &mut Context<Ctx>, engine: ir::Value, env: ir::Value) -> StackSlot {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: JITContext> JITCompile<Ctx> for AttrSet {
|
||||||
|
fn compile(&self, ctx: &mut Context<Ctx>, engine: ir::Value, env: ir::Value) -> StackSlot {
|
||||||
let attrs = ctx.create_attrs();
|
let attrs = ctx.create_attrs();
|
||||||
for (k, v) in self.stcs.iter() {
|
for (k, v) in self.stcs.iter() {
|
||||||
let v = v.compile(ctx, engine, env);
|
let v = v.compile(ctx, engine, env);
|
||||||
@@ -22,8 +34,8 @@ impl JITCompile for Attrs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for List {
|
impl<Ctx: JITContext> JITCompile<Ctx> for List {
|
||||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
fn compile(&self, ctx: &mut Context<Ctx>, engine: ir::Value, env: ir::Value) -> StackSlot {
|
||||||
let array = ctx.alloc_array(self.items.len());
|
let array = ctx.alloc_array(self.items.len());
|
||||||
for (i, item) in self.items.iter().enumerate() {
|
for (i, item) in self.items.iter().enumerate() {
|
||||||
let item = item.compile(ctx, engine, env);
|
let item = item.compile(ctx, engine, env);
|
||||||
@@ -48,19 +60,17 @@ impl JITCompile for List {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for HasAttr {
|
impl<Ctx: JITContext> JITCompile<Ctx> for HasAttr {
|
||||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
fn compile(&self, ctx: &mut Context<Ctx>, engine: ir::Value, env: ir::Value) -> StackSlot {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for BinOp {
|
impl<Ctx: JITContext> JITCompile<Ctx> for BinOp {
|
||||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
fn compile(&self, ctx: &mut Context<Ctx>, engine: ir::Value, env: ir::Value) -> StackSlot {
|
||||||
use BinOpKind::*;
|
use BinOpKind::*;
|
||||||
let lhs = self.lhs.compile(ctx, engine, env);
|
let lhs = self.lhs.compile(ctx, engine, env);
|
||||||
let rhs = self.rhs.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 lhs_tag = ctx.get_tag(lhs);
|
||||||
let rhs_tag = ctx.get_tag(rhs);
|
let rhs_tag = ctx.get_tag(rhs);
|
||||||
let eq = ctx.builder.ins().icmp(IntCC::Equal, lhs_tag, rhs_tag);
|
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_block = ctx.builder.create_block();
|
||||||
let float_check_block = ctx.builder.create_block();
|
let float_check_block = ctx.builder.create_block();
|
||||||
|
|
||||||
let is_int = ctx
|
let is_int =
|
||||||
.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, lhs_tag, Value::INT as i64);
|
.icmp_imm(IntCC::Equal, lhs_tag, Value::<Ctx>::INT as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_int, int_block, [], float_check_block, []);
|
.brif(is_int, int_block, [], float_check_block, []);
|
||||||
@@ -99,7 +109,7 @@ impl JITCompile for BinOp {
|
|||||||
let is_float =
|
let is_float =
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, lhs_tag, Value::FLOAT as i64);
|
.icmp_imm(IntCC::Equal, lhs_tag, Value::<Ctx>::FLOAT as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_float, float_block, [], default_block, []);
|
.brif(is_float, float_block, [], default_block, []);
|
||||||
@@ -131,10 +141,10 @@ impl JITCompile for BinOp {
|
|||||||
let float_block = ctx.builder.create_block();
|
let float_block = ctx.builder.create_block();
|
||||||
let float_check_block = ctx.builder.create_block();
|
let float_check_block = ctx.builder.create_block();
|
||||||
|
|
||||||
let is_int = ctx
|
let is_int =
|
||||||
.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, lhs_tag, Value::INT as i64);
|
.icmp_imm(IntCC::Equal, lhs_tag, Value::<Ctx>::INT as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_int, int_block, [], float_check_block, []);
|
.brif(is_int, int_block, [], float_check_block, []);
|
||||||
@@ -152,7 +162,7 @@ impl JITCompile for BinOp {
|
|||||||
let is_float =
|
let is_float =
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, lhs_tag, Value::FLOAT as i64);
|
.icmp_imm(IntCC::Equal, lhs_tag, Value::<Ctx>::FLOAT as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_float, float_block, [], default_block, []);
|
.brif(is_float, float_block, [], default_block, []);
|
||||||
@@ -184,10 +194,10 @@ impl JITCompile for BinOp {
|
|||||||
let float_block = ctx.builder.create_block();
|
let float_block = ctx.builder.create_block();
|
||||||
let float_check_block = ctx.builder.create_block();
|
let float_check_block = ctx.builder.create_block();
|
||||||
|
|
||||||
let is_int = ctx
|
let is_int =
|
||||||
.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, lhs_tag, Value::INT as i64);
|
.icmp_imm(IntCC::Equal, lhs_tag, Value::<Ctx>::INT as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_int, int_block, [], float_check_block, []);
|
.brif(is_int, int_block, [], float_check_block, []);
|
||||||
@@ -205,7 +215,7 @@ impl JITCompile for BinOp {
|
|||||||
let is_float =
|
let is_float =
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, lhs_tag, Value::FLOAT as i64);
|
.icmp_imm(IntCC::Equal, lhs_tag, Value::<Ctx>::FLOAT as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_float, float_block, [], default_block, []);
|
.brif(is_float, float_block, [], default_block, []);
|
||||||
@@ -235,10 +245,10 @@ impl JITCompile for BinOp {
|
|||||||
let bool_block = ctx.builder.create_block();
|
let bool_block = ctx.builder.create_block();
|
||||||
let non_bool_block = ctx.builder.create_block();
|
let non_bool_block = ctx.builder.create_block();
|
||||||
|
|
||||||
let is_bool = ctx
|
let is_bool =
|
||||||
.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, lhs_tag, Value::BOOL as i64);
|
.icmp_imm(IntCC::Equal, lhs_tag, Value::<Ctx>::BOOL as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_bool, bool_block, [], non_bool_block, []);
|
.brif(is_bool, bool_block, [], non_bool_block, []);
|
||||||
@@ -265,10 +275,10 @@ impl JITCompile for BinOp {
|
|||||||
let bool_block = ctx.builder.create_block();
|
let bool_block = ctx.builder.create_block();
|
||||||
let non_bool_block = ctx.builder.create_block();
|
let non_bool_block = ctx.builder.create_block();
|
||||||
|
|
||||||
let is_bool = ctx
|
let is_bool =
|
||||||
.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, lhs_tag, Value::BOOL as i64);
|
.icmp_imm(IntCC::Equal, lhs_tag, Value::<Ctx>::BOOL as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_bool, bool_block, [], non_bool_block, []);
|
.brif(is_bool, bool_block, [], non_bool_block, []);
|
||||||
@@ -311,25 +321,24 @@ impl JITCompile for BinOp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for UnOp {
|
impl<Ctx: JITContext> JITCompile<Ctx> for UnOp {
|
||||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
fn compile(&self, ctx: &mut Context<Ctx>, engine: ir::Value, env: ir::Value) -> StackSlot {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Attr {
|
impl<Ctx: JITContext> JITCompile<Ctx> for Attr {
|
||||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
fn compile(&self, ctx: &mut Context<Ctx>, engine: ir::Value, env: ir::Value) -> StackSlot {
|
||||||
use Attr::*;
|
use Attr::*;
|
||||||
match self {
|
match self {
|
||||||
Str(string) => ctx.create_string(string),
|
Str(string) => ctx.create_string(string),
|
||||||
Dynamic(ir) => ir.compile(ctx, engine, env),
|
Dynamic(ir) => ir.compile(ctx, engine, env),
|
||||||
Strs(strings) => strings.compile(ctx, engine, env),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Select {
|
impl<Ctx: JITContext> JITCompile<Ctx> for Select {
|
||||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
fn compile(&self, ctx: &mut Context<Ctx>, engine: ir::Value, env: ir::Value) -> StackSlot {
|
||||||
let val = self.expr.compile(ctx, engine, env);
|
let val = self.expr.compile(ctx, engine, env);
|
||||||
let attrpath = ctx.alloc_array(self.attrpath.len());
|
let attrpath = ctx.alloc_array(self.attrpath.len());
|
||||||
for (i, attr) in self.attrpath.iter().enumerate() {
|
for (i, attr) in self.attrpath.iter().enumerate() {
|
||||||
@@ -356,10 +365,9 @@ impl JITCompile for Select {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for If {
|
impl<Ctx: JITContext> JITCompile<Ctx> for If {
|
||||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
fn compile(&self, ctx: &mut Context<Ctx>, engine: ir::Value, env: ir::Value) -> StackSlot {
|
||||||
let cond = self.cond.compile(ctx, engine, env);
|
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_type = ctx.builder.ins().stack_load(types::I64, cond, 0);
|
||||||
let cond_value = ctx.builder.ins().stack_load(types::I64, cond, 8);
|
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 judge_block = ctx.builder.create_block();
|
||||||
let slot = ctx.alloca();
|
let slot = ctx.alloca();
|
||||||
|
|
||||||
let is_bool = ctx
|
let is_bool =
|
||||||
.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp_imm(IntCC::Equal, cond_type, Value::BOOL as i64);
|
.icmp_imm(IntCC::Equal, cond_type, Value::<Ctx>::BOOL as i64);
|
||||||
ctx.builder
|
ctx.builder
|
||||||
.ins()
|
.ins()
|
||||||
.brif(is_bool, judge_block, [], error_block, []);
|
.brif(is_bool, judge_block, [], error_block, []);
|
||||||
@@ -415,21 +423,9 @@ impl JITCompile for If {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for LoadFunc {
|
impl<Ctx: JITContext> JITCompile<Ctx> for Call {
|
||||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
fn compile(&self, ctx: &mut Context<Ctx>, 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 {
|
|
||||||
let func = self.func.compile(ctx, engine, env);
|
let func = self.func.compile(ctx, engine, env);
|
||||||
ctx.force(func, engine, env);
|
|
||||||
let args = ctx.alloc_array(self.args.len());
|
let args = ctx.alloc_array(self.args.len());
|
||||||
for (i, arg) in self.args.iter().enumerate() {
|
for (i, arg) in self.args.iter().enumerate() {
|
||||||
let arg = arg.compile(ctx, engine, env);
|
let arg = arg.compile(ctx, engine, env);
|
||||||
@@ -455,14 +451,8 @@ impl JITCompile for Call {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Let {
|
impl<Ctx: JITContext> JITCompile<Ctx> for With {
|
||||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
fn compile(&self, ctx: &mut Context<Ctx>, 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 {
|
|
||||||
let namespace = self.namespace.compile(ctx, engine, env);
|
let namespace = self.namespace.compile(ctx, engine, env);
|
||||||
ctx.enter_with(env, namespace);
|
ctx.enter_with(env, namespace);
|
||||||
let ret = self.expr.compile(ctx, engine, env);
|
let ret = self.expr.compile(ctx, engine, env);
|
||||||
@@ -472,43 +462,55 @@ impl JITCompile for With {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Assert {
|
impl<Ctx: JITContext> JITCompile<Ctx> for Assert {
|
||||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
fn compile(&self, ctx: &mut Context<Ctx>, engine: ir::Value, env: ir::Value) -> StackSlot {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for ConcatStrings {
|
impl<Ctx: JITContext> JITCompile<Ctx> for ConcatStrings {
|
||||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
fn compile(&self, ctx: &mut Context<Ctx>, engine: ir::Value, env: ir::Value) -> StackSlot {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Const {
|
impl<Ctx: JITContext> JITCompile<Ctx> for Const {
|
||||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
fn compile(&self, ctx: &mut Context<Ctx>, engine: ir::Value, env: ir::Value) -> StackSlot {
|
||||||
use c::Const::*;
|
use nixjit_value::Const::*;
|
||||||
let slot = ctx.alloca();
|
let slot = ctx.alloca();
|
||||||
match self.val {
|
match self.val {
|
||||||
Bool(x) => {
|
Bool(x) => {
|
||||||
let tag = ctx.builder.ins().iconst(types::I64, Value::BOOL as i64);
|
let tag = ctx
|
||||||
|
.builder
|
||||||
|
.ins()
|
||||||
|
.iconst(types::I64, Value::<Ctx>::BOOL as i64);
|
||||||
let val = ctx.builder.ins().iconst(types::I64, x 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(tag, slot, 0);
|
||||||
ctx.builder.ins().stack_store(val, slot, 8);
|
ctx.builder.ins().stack_store(val, slot, 8);
|
||||||
}
|
}
|
||||||
Int(x) => {
|
Int(x) => {
|
||||||
let tag = ctx.builder.ins().iconst(types::I64, Value::INT as i64);
|
let tag = ctx
|
||||||
|
.builder
|
||||||
|
.ins()
|
||||||
|
.iconst(types::I64, Value::<Ctx>::INT as i64);
|
||||||
let val = ctx.builder.ins().iconst(types::I64, x);
|
let val = ctx.builder.ins().iconst(types::I64, x);
|
||||||
ctx.builder.ins().stack_store(tag, slot, 0);
|
ctx.builder.ins().stack_store(tag, slot, 0);
|
||||||
ctx.builder.ins().stack_store(val, slot, 8);
|
ctx.builder.ins().stack_store(val, slot, 8);
|
||||||
}
|
}
|
||||||
Float(x) => {
|
Float(x) => {
|
||||||
let tag = ctx.builder.ins().iconst(types::I64, Value::FLOAT as i64);
|
let tag = ctx
|
||||||
|
.builder
|
||||||
|
.ins()
|
||||||
|
.iconst(types::I64, Value::<Ctx>::FLOAT as i64);
|
||||||
let val = ctx.builder.ins().f64const(x);
|
let val = ctx.builder.ins().f64const(x);
|
||||||
ctx.builder.ins().stack_store(tag, slot, 0);
|
ctx.builder.ins().stack_store(tag, slot, 0);
|
||||||
ctx.builder.ins().stack_store(val, slot, 8);
|
ctx.builder.ins().stack_store(val, slot, 8);
|
||||||
}
|
}
|
||||||
Null => {
|
Null => {
|
||||||
let tag = ctx.builder.ins().iconst(types::I64, Value::NULL as i64);
|
let tag = ctx
|
||||||
|
.builder
|
||||||
|
.ins()
|
||||||
|
.iconst(types::I64, Value::<Ctx>::NULL as i64);
|
||||||
ctx.builder.ins().stack_store(tag, slot, 0);
|
ctx.builder.ins().stack_store(tag, slot, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -516,43 +518,20 @@ impl JITCompile for Const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Str {
|
impl<Ctx: JITContext> JITCompile<Ctx> for Str {
|
||||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
fn compile(&self, ctx: &mut Context<Ctx>, engine: ir::Value, env: ir::Value) -> StackSlot {
|
||||||
ctx.create_string(&self.val)
|
ctx.create_string(&self.val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Var {
|
impl<Ctx: JITContext> JITCompile<Ctx> for Var {
|
||||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
fn compile(&self, ctx: &mut Context<Ctx>, engine: ir::Value, env: ir::Value) -> StackSlot {
|
||||||
ctx.lookup(env, &self.sym)
|
ctx.lookup(env, &self.sym)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Arg {
|
impl<Ctx: JITContext> JITCompile<Ctx> for Path {
|
||||||
fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot {
|
fn compile(&self, ctx: &mut Context<Ctx>, 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 {
|
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
165
evaluator/nixjit_jit/src/helpers.rs
Normal file
165
evaluator/nixjit_jit/src/helpers.rs
Normal file
@@ -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<Ctx: JITContext>(
|
||||||
|
func: &mut Value<Ctx>,
|
||||||
|
args_ptr: *mut Value<Ctx>,
|
||||||
|
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: JITContext>(
|
||||||
|
ctx: &Ctx,
|
||||||
|
offset: usize,
|
||||||
|
ret: &mut MaybeUninit<Value<Ctx>>,
|
||||||
|
) {
|
||||||
|
ret.write(ctx.lookup_stack(offset).clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern "C" fn helper_lookup_arg<Ctx: JITContext>(
|
||||||
|
ctx: &Ctx,
|
||||||
|
offset: usize,
|
||||||
|
ret: &mut MaybeUninit<Value<Ctx>>,
|
||||||
|
) {
|
||||||
|
ret.write(ctx.lookup_arg(offset).clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern "C" fn helper_lookup<Ctx: JITContext>(
|
||||||
|
ctx: &Ctx,
|
||||||
|
sym_ptr: *const u8,
|
||||||
|
sym_len: usize,
|
||||||
|
ret: &mut MaybeUninit<Value<Ctx>>,
|
||||||
|
) {
|
||||||
|
// 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<Ctx: JITContext>(
|
||||||
|
val: &mut Value<Ctx>,
|
||||||
|
path_ptr: *mut Value<Ctx>,
|
||||||
|
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<Ctx: JITContext>(
|
||||||
|
val: &mut Value<Ctx>,
|
||||||
|
path_ptr: *mut Value<Ctx>,
|
||||||
|
path_len: usize,
|
||||||
|
default: NonNull<Value<Ctx>>,
|
||||||
|
) {
|
||||||
|
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<Ctx: JITContext>(lhs: &mut Value<Ctx>, rhs: &Value<Ctx>) {
|
||||||
|
lhs.eq(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn helper_create_string<Ctx: JITContext>(
|
||||||
|
ptr: *const u8,
|
||||||
|
len: usize,
|
||||||
|
ret: &mut MaybeUninit<Value<Ctx>>,
|
||||||
|
) {
|
||||||
|
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<Ctx: JITContext>(
|
||||||
|
ptr: *mut Value<Ctx>,
|
||||||
|
len: usize,
|
||||||
|
ret: &mut MaybeUninit<Value<Ctx>>,
|
||||||
|
) {
|
||||||
|
unsafe {
|
||||||
|
ret.write(Value::List(
|
||||||
|
List::from(Vec::from_raw_parts(ptr, len, len)).into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn helper_create_attrs<Ctx: JITContext>(
|
||||||
|
ret: &mut MaybeUninit<HashMap<String, Value<Ctx>>>,
|
||||||
|
) {
|
||||||
|
ret.write(HashMap::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn helper_push_attr<Ctx: JITContext>(
|
||||||
|
attrs: &mut HashMap<String, Value<Ctx>>,
|
||||||
|
sym_ptr: *const u8,
|
||||||
|
sym_len: usize,
|
||||||
|
val: NonNull<Value<Ctx>>,
|
||||||
|
) {
|
||||||
|
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<Ctx: JITContext>(
|
||||||
|
attrs: NonNull<HashMap<String, Value<Ctx>>>,
|
||||||
|
ret: &mut MaybeUninit<Value<Ctx>>,
|
||||||
|
) {
|
||||||
|
ret.write(Value::AttrSet(
|
||||||
|
AttrSet::from(unsafe { attrs.read() }).into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn helper_enter_with<Ctx: JITContext>(
|
||||||
|
ctx: &mut Ctx,
|
||||||
|
namespace: NonNull<Value<Ctx>>,
|
||||||
|
) {
|
||||||
|
ctx.enter_with(unsafe { namespace.read() }.unwrap_attr_set().into_inner());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn helper_exit_with<Ctx: JITContext>(ctx: &mut Ctx) {
|
||||||
|
ctx.exit_with();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn helper_alloc_array<Ctx: JITContext>(len: usize) -> *mut u8 {
|
||||||
|
unsafe { alloc(Layout::array::<Value<Ctx>>(len).unwrap()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern "C" fn helper_dbg<Ctx: JITContext>(value: &Value<Ctx>) {
|
||||||
|
println!("{value:?}")
|
||||||
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
|
use std::marker::PhantomData;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use cranelift::codegen::ir::Function;
|
use cranelift::codegen::ir::Function;
|
||||||
use cranelift::codegen::ir::{self, ArgumentExtension, ArgumentPurpose, StackSlot};
|
use cranelift::codegen::ir::{self, ArgumentExtension, ArgumentPurpose, StackSlot};
|
||||||
use cranelift::prelude::*;
|
use cranelift::prelude::*;
|
||||||
use cranelift_jit::{JITBuilder, JITModule};
|
use cranelift_jit::{JITBuilder, JITModule};
|
||||||
use cranelift_module::{FuncId, Linkage, Module};
|
use cranelift_module::{FuncId, Linkage, Module};
|
||||||
use hashbrown::HashSet;
|
use hashbrown::{HashMap, HashSet};
|
||||||
|
|
||||||
use crate::engine::Engine;
|
use nixjit_eval::{EvalContext, Value};
|
||||||
use crate::env::Env;
|
use nixjit_lir::Lir;
|
||||||
use crate::ir::Ir;
|
|
||||||
use crate::ty::internal::Value;
|
|
||||||
|
|
||||||
mod compile;
|
mod compile;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
@@ -18,29 +18,36 @@ mod helpers;
|
|||||||
pub use compile::JITCompile;
|
pub use compile::JITCompile;
|
||||||
use helpers::*;
|
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<Self>;
|
||||||
|
fn lookup_arg(&self, offset: usize) -> &Value<Self>;
|
||||||
|
fn enter_with(&mut self, namespace: Rc<HashMap<String, Value<Self>>>);
|
||||||
|
fn exit_with(&mut self);
|
||||||
|
}
|
||||||
|
|
||||||
pub struct JITFunc {
|
type F<Ctx: JITContext> = unsafe extern "C" fn(*const Ctx, *mut Value<Ctx>);
|
||||||
func: F,
|
|
||||||
|
pub struct JITFunc<Ctx: JITContext> {
|
||||||
|
func: F<Ctx>,
|
||||||
strings: HashSet<String>,
|
strings: HashSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for JITFunc {
|
impl<Ctx: JITContext> Deref for JITFunc<Ctx> {
|
||||||
type Target = F;
|
type Target = F<Ctx>;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.func
|
&self.func
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct JITContext<'comp, 'ctx> {
|
struct Context<'comp, 'ctx, Ctx: JITContext> {
|
||||||
pub compiler: &'comp mut JITCompiler,
|
pub compiler: &'comp mut JITCompiler<Ctx>,
|
||||||
pub builder: FunctionBuilder<'ctx>,
|
pub builder: FunctionBuilder<'ctx>,
|
||||||
free_slots: Vec<StackSlot>,
|
free_slots: Vec<StackSlot>,
|
||||||
strings: HashSet<String>,
|
strings: HashSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'comp, 'ctx> JITContext<'comp, 'ctx> {
|
impl<'comp, 'ctx, Ctx: JITContext> Context<'comp, 'ctx, Ctx> {
|
||||||
fn new(compiler: &'comp mut JITCompiler, builder: FunctionBuilder<'ctx>) -> Self {
|
fn new(compiler: &'comp mut JITCompiler<Ctx>, builder: FunctionBuilder<'ctx>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
compiler,
|
compiler,
|
||||||
builder,
|
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) {
|
pub fn eq(&mut self, lhs: StackSlot, rhs: StackSlot) {
|
||||||
let lhs = self
|
let lhs = self
|
||||||
.builder
|
.builder
|
||||||
@@ -379,10 +374,11 @@ impl<'comp, 'ctx> JITContext<'comp, 'ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct JITCompiler {
|
pub struct JITCompiler<Ctx: JITContext> {
|
||||||
ctx: codegen::Context,
|
ctx: codegen::Context,
|
||||||
module: JITModule,
|
module: JITModule,
|
||||||
builder_ctx: Option<FunctionBuilderContext>,
|
builder_ctx: Option<FunctionBuilderContext>,
|
||||||
|
_marker: PhantomData<Ctx>,
|
||||||
|
|
||||||
int_type: Type,
|
int_type: Type,
|
||||||
float_type: Type,
|
float_type: Type,
|
||||||
@@ -397,7 +393,6 @@ pub struct JITCompiler {
|
|||||||
lookup: FuncId,
|
lookup: FuncId,
|
||||||
select: FuncId,
|
select: FuncId,
|
||||||
select_with_default: FuncId,
|
select_with_default: FuncId,
|
||||||
force: FuncId,
|
|
||||||
|
|
||||||
eq: FuncId,
|
eq: FuncId,
|
||||||
|
|
||||||
@@ -412,13 +407,13 @@ pub struct JITCompiler {
|
|||||||
dbg: FuncId,
|
dbg: FuncId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for JITCompiler {
|
impl<Ctx: JITContext> Default for JITCompiler<Ctx> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompiler {
|
impl<Ctx: JITContext> JITCompiler<Ctx> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut flag_builder = settings::builder();
|
let mut flag_builder = settings::builder();
|
||||||
flag_builder.set("use_colocated_libcalls", "false").unwrap();
|
flag_builder.set("use_colocated_libcalls", "false").unwrap();
|
||||||
@@ -431,27 +426,26 @@ impl JITCompiler {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let mut builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
|
let mut builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
|
||||||
|
|
||||||
builder.symbol("helper_call", helper_call as _);
|
builder.symbol("helper_call", helper_call::<Ctx> as _);
|
||||||
builder.symbol("helper_lookup_stack", helper_lookup_stack as _);
|
builder.symbol("helper_lookup_stack", helper_lookup_stack::<Ctx> as _);
|
||||||
builder.symbol("helper_lookup_arg", helper_lookup_arg as _);
|
builder.symbol("helper_lookup_arg", helper_lookup_arg::<Ctx> as _);
|
||||||
builder.symbol("helper_lookup", helper_lookup as _);
|
builder.symbol("helper_lookup", helper_lookup::<Ctx> as _);
|
||||||
builder.symbol("helper_select", helper_select as _);
|
builder.symbol("helper_select", helper_select::<Ctx> as _);
|
||||||
builder.symbol(
|
builder.symbol(
|
||||||
"helper_select_with_default",
|
"helper_select_with_default",
|
||||||
helper_select_with_default as _,
|
helper_select_with_default::<Ctx> as _,
|
||||||
);
|
);
|
||||||
builder.symbol("helper_force", helper_force as _);
|
builder.symbol("helper_eq", helper_eq::<Ctx> as _);
|
||||||
builder.symbol("helper_eq", helper_eq as _);
|
|
||||||
|
|
||||||
builder.symbol("helper_alloc_array", helper_alloc_array as _);
|
builder.symbol("helper_alloc_array", helper_alloc_array::<Ctx> as _);
|
||||||
builder.symbol("helper_create_string", helper_create_string as _);
|
builder.symbol("helper_create_string", helper_create_string::<Ctx> as _);
|
||||||
builder.symbol("helper_create_list", helper_create_list as _);
|
builder.symbol("helper_create_list", helper_create_list::<Ctx> as _);
|
||||||
builder.symbol("helper_create_attrs", helper_create_attrs as _);
|
builder.symbol("helper_create_attrs", helper_create_attrs::<Ctx> as _);
|
||||||
builder.symbol("helper_push_attr", helper_push_attr as _);
|
builder.symbol("helper_push_attr", helper_push_attr::<Ctx> as _);
|
||||||
builder.symbol("helper_finalize_attrs", helper_finalize_attrs as _);
|
builder.symbol("helper_finalize_attrs", helper_finalize_attrs::<Ctx> as _);
|
||||||
builder.symbol("helper_enter_with", helper_enter_with as _);
|
builder.symbol("helper_enter_with", helper_enter_with::<Ctx> as _);
|
||||||
builder.symbol("helper_exit_with", helper_exit_with as _);
|
builder.symbol("helper_exit_with", helper_exit_with::<Ctx> as _);
|
||||||
builder.symbol("helper_dbg", helper_dbg as _);
|
builder.symbol("helper_dbg", helper_dbg::<Ctx> as _);
|
||||||
|
|
||||||
let mut module = JITModule::new(builder);
|
let mut module = JITModule::new(builder);
|
||||||
let ctx = module.make_context();
|
let ctx = module.make_context();
|
||||||
@@ -462,7 +456,7 @@ impl JITCompiler {
|
|||||||
let ptr_type = module.target_config().pointer_type();
|
let ptr_type = module.target_config().pointer_type();
|
||||||
let value_type = types::I128;
|
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();
|
let mut func_sig = module.make_signature();
|
||||||
func_sig.params.extend(
|
func_sig.params.extend(
|
||||||
[AbiParam {
|
[AbiParam {
|
||||||
@@ -472,7 +466,7 @@ impl JITCompiler {
|
|||||||
}; 3],
|
}; 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)
|
// &mut Env)
|
||||||
let mut call_sig = module.make_signature();
|
let mut call_sig = module.make_signature();
|
||||||
call_sig.params.extend(
|
call_sig.params.extend(
|
||||||
@@ -524,7 +518,7 @@ impl JITCompiler {
|
|||||||
.declare_function("helper_lookup", Linkage::Import, &lookup_sig)
|
.declare_function("helper_lookup", Linkage::Import, &lookup_sig)
|
||||||
.unwrap();
|
.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();
|
let mut select_sig = module.make_signature();
|
||||||
select_sig.params.extend(
|
select_sig.params.extend(
|
||||||
[AbiParam {
|
[AbiParam {
|
||||||
@@ -537,7 +531,7 @@ impl JITCompiler {
|
|||||||
.declare_function("helper_select", Linkage::Import, &select_sig)
|
.declare_function("helper_select", Linkage::Import, &select_sig)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// fn(val: &mut Value, path_ptr: *mut Value, path_len: usize, default: NonNull<Value>, engine: &mut Engine, env: &mut Env)
|
// fn(val: &mut Value, path_ptr: *mut Value, path_len: usize, default: NonNull<Value>, engine: &mut Context, env: &mut Env)
|
||||||
let mut select_with_default_sig = module.make_signature();
|
let mut select_with_default_sig = module.make_signature();
|
||||||
select_with_default_sig.params.extend(
|
select_with_default_sig.params.extend(
|
||||||
[AbiParam {
|
[AbiParam {
|
||||||
@@ -554,19 +548,6 @@ impl JITCompiler {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.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();
|
let mut eq_sig = module.make_signature();
|
||||||
eq_sig.params.extend(
|
eq_sig.params.extend(
|
||||||
[AbiParam {
|
[AbiParam {
|
||||||
@@ -690,6 +671,7 @@ impl JITCompiler {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
builder_ctx: None,
|
builder_ctx: None,
|
||||||
|
_marker: PhantomData,
|
||||||
ctx,
|
ctx,
|
||||||
module,
|
module,
|
||||||
|
|
||||||
@@ -706,7 +688,6 @@ impl JITCompiler {
|
|||||||
lookup,
|
lookup,
|
||||||
select,
|
select,
|
||||||
select_with_default,
|
select_with_default,
|
||||||
force,
|
|
||||||
|
|
||||||
eq,
|
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<Ctx> {
|
||||||
let func_id = self
|
let func_id = self
|
||||||
.module
|
.module
|
||||||
.declare_function(
|
.declare_function(
|
||||||
@@ -734,7 +715,7 @@ impl JITCompiler {
|
|||||||
let mut func = Function::new();
|
let mut func = Function::new();
|
||||||
func.signature = self.func_sig.clone();
|
func.signature = self.func_sig.clone();
|
||||||
let mut builder_ctx = self.builder_ctx.take().unwrap_or_default();
|
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();
|
let entry = ctx.builder.create_block();
|
||||||
ctx.builder.append_block_params_for_function_params(entry);
|
ctx.builder.append_block_params_for_function_params(entry);
|
||||||
@@ -760,7 +741,7 @@ impl JITCompiler {
|
|||||||
|
|
||||||
let strings = ctx.strings;
|
let strings = ctx.strings;
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
println!("{ir:?}");
|
println!("{ir:#?}");
|
||||||
println!("{}", func.display());
|
println!("{}", func.display());
|
||||||
}
|
}
|
||||||
self.ctx.func = func;
|
self.ctx.func = func;
|
||||||
@@ -771,7 +752,7 @@ impl JITCompiler {
|
|||||||
let _ = self.builder_ctx.insert(builder_ctx);
|
let _ = self.builder_ctx.insert(builder_ctx);
|
||||||
unsafe {
|
unsafe {
|
||||||
JITFunc {
|
JITFunc {
|
||||||
func: std::mem::transmute::<*const u8, F>(
|
func: std::mem::transmute::<*const u8, F<Ctx>>(
|
||||||
self.module.get_finalized_function(func_id),
|
self.module.get_finalized_function(func_id),
|
||||||
),
|
),
|
||||||
strings,
|
strings,
|
||||||
16
evaluator/nixjit_lir/Cargo.toml
Normal file
16
evaluator/nixjit_lir/Cargo.toml
Normal file
@@ -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" }
|
||||||
244
evaluator/nixjit_lir/src/lib.rs
Normal file
244
evaluator/nixjit_lir/src/lib.rs
Normal file
@@ -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<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> (bool, T);
|
||||||
|
fn with_let_env<'a, T>(
|
||||||
|
&mut self,
|
||||||
|
bindings: impl IntoIterator<Item = (&'a String, &'a ExprId)>,
|
||||||
|
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<Ctx: ResolveContext> {
|
||||||
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: ResolveContext> Resolve<Ctx> for hir::Hir {
|
||||||
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
|
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<Ctx: ResolveContext> Resolve<Ctx> for AttrSet {
|
||||||
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
|
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<Ctx: ResolveContext> Resolve<Ctx> for List {
|
||||||
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
|
for &item in self.items.iter() {
|
||||||
|
ctx.resolve(item)?;
|
||||||
|
}
|
||||||
|
Ok(self.to_lir())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: ResolveContext> Resolve<Ctx> for HasAttr {
|
||||||
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
|
ctx.resolve(self.lhs)?;
|
||||||
|
for attr in self.rhs.iter() {
|
||||||
|
if let &Attr::Dynamic(expr) = attr {
|
||||||
|
ctx.resolve(expr)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(self.to_lir())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: ResolveContext> Resolve<Ctx> for BinOp {
|
||||||
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
|
ctx.resolve(self.lhs)?;
|
||||||
|
ctx.resolve(self.rhs)?;
|
||||||
|
Ok(self.to_lir())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: ResolveContext> Resolve<Ctx> for UnOp {
|
||||||
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
|
ctx.resolve(self.rhs)?;
|
||||||
|
Ok(self.to_lir())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: ResolveContext> Resolve<Ctx> for Select {
|
||||||
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
|
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<Ctx: ResolveContext> Resolve<Ctx> for If {
|
||||||
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
|
ctx.resolve(self.cond)?;
|
||||||
|
ctx.resolve(self.consq)?;
|
||||||
|
ctx.resolve(self.alter)?;
|
||||||
|
Ok(self.to_lir())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: ResolveContext> Resolve<Ctx> for Func {
|
||||||
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
|
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<Ctx: ResolveContext> Resolve<Ctx> for Call {
|
||||||
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
|
ctx.resolve(self.func)?;
|
||||||
|
for &arg in self.args.iter() {
|
||||||
|
ctx.resolve(arg)?;
|
||||||
|
}
|
||||||
|
Ok(self.to_lir())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: ResolveContext> Resolve<Ctx> for With {
|
||||||
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
|
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<Ctx: ResolveContext> Resolve<Ctx> for Assert {
|
||||||
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
|
ctx.resolve(self.assertion)?;
|
||||||
|
ctx.resolve(self.expr)?;
|
||||||
|
Ok(self.to_lir())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: ResolveContext> Resolve<Ctx> for ConcatStrings {
|
||||||
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
|
for &part in self.parts.iter() {
|
||||||
|
ctx.resolve(part)?;
|
||||||
|
}
|
||||||
|
Ok(self.to_lir())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: ResolveContext> Resolve<Ctx> for Var {
|
||||||
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
|
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<Ctx: ResolveContext> Resolve<Ctx> for Path {
|
||||||
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
|
ctx.resolve(self.expr)?;
|
||||||
|
Ok(self.to_lir())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: ResolveContext> Resolve<Ctx> for hir::Let {
|
||||||
|
fn resolve(self, ctx: &mut Ctx) -> Result<Lir> {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
13
evaluator/nixjit_macros/Cargo.toml
Normal file
13
evaluator/nixjit_macros/Cargo.toml
Normal file
@@ -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"] }
|
||||||
54
evaluator/nixjit_macros/src/ir.rs
Normal file
54
evaluator/nixjit_macros/src/ir.rs
Normal file
@@ -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<VariantInput, Token![,]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for VariantInput {
|
||||||
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
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<Self> {
|
||||||
|
let base_name = input.parse()?;
|
||||||
|
input.parse::<Token![,]>()?;
|
||||||
|
let variants = Punctuated::parse_terminated(input)?;
|
||||||
|
|
||||||
|
Ok(MacroInput {
|
||||||
|
base_name,
|
||||||
|
variants,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
121
evaluator/nixjit_macros/src/lib.rs
Normal file
121
evaluator/nixjit_macros/src/lib.rs
Normal file
@@ -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)
|
||||||
|
}
|
||||||
8
evaluator/nixjit_value/Cargo.toml
Normal file
8
evaluator/nixjit_value/Cargo.toml
Normal file
@@ -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"
|
||||||
@@ -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::collections::BTreeMap;
|
||||||
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
use derive_more::{Constructor, IsVariant, Unwrap};
|
use derive_more::{Constructor, IsVariant, Unwrap};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use super::common::*;
|
#[derive(Clone, Debug, PartialEq, Constructor, Hash)]
|
||||||
|
pub struct Catchable {
|
||||||
|
msg: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Into<String>> From<T> for Catchable {
|
||||||
|
fn from(value: T) -> Self {
|
||||||
|
Catchable { msg: value.into() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Catchable {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
|
write!(f, "<error: {}>", 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<bool> for Const {
|
||||||
|
fn from(value: bool) -> Self {
|
||||||
|
Const::Bool(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i64> for Const {
|
||||||
|
fn from(value: i64) -> Self {
|
||||||
|
Const::Int(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<f64> for Const {
|
||||||
|
fn from(value: f64) -> Self {
|
||||||
|
Const::Float(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Constructor)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Constructor)]
|
||||||
pub struct Symbol(String);
|
pub struct Symbol(String);
|
||||||
@@ -17,12 +73,20 @@ impl<T: Into<String>> From<T> 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 {
|
impl Display for Symbol {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
if self.normal() {
|
if self.normal() {
|
||||||
write!(f, r#""{}""#, self.0)
|
|
||||||
} else {
|
|
||||||
write!(f, "{}", self.0)
|
write!(f, "{}", self.0)
|
||||||
|
} else {
|
||||||
|
write!(f, r#""{}""#, self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,7 +95,7 @@ static REGEX: LazyLock<Regex> =
|
|||||||
LazyLock::new(|| Regex::new(r#"^[a-zA-Z\_][a-zA-Z0-9\_\'\-]*$"#).unwrap());
|
LazyLock::new(|| Regex::new(r#"^[a-zA-Z\_][a-zA-Z0-9\_\'\-]*$"#).unwrap());
|
||||||
impl Symbol {
|
impl Symbol {
|
||||||
fn normal(&self) -> bool {
|
fn normal(&self) -> bool {
|
||||||
!REGEX.is_match(self)
|
REGEX.is_match(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +178,7 @@ pub enum Value {
|
|||||||
Thunk,
|
Thunk,
|
||||||
Func,
|
Func,
|
||||||
PrimOp(&'static str),
|
PrimOp(&'static str),
|
||||||
PartialPrimOp(&'static str),
|
PrimOpApp(&'static str),
|
||||||
Repeated,
|
Repeated,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +194,7 @@ impl Display for Value {
|
|||||||
Thunk => write!(f, "<CODE>"),
|
Thunk => write!(f, "<CODE>"),
|
||||||
Func => write!(f, "<LAMBDA>"),
|
Func => write!(f, "<LAMBDA>"),
|
||||||
PrimOp(x) => write!(f, "<PRIMOP {x}>"),
|
PrimOp(x) => write!(f, "<PRIMOP {x}>"),
|
||||||
PartialPrimOp(x) => write!(f, "<PARTIAL PRIMOP {x}>"),
|
PrimOpApp(x) => write!(f, "<PRIMOP-APP {x}>"),
|
||||||
Repeated => write!(f, "<REPEATED>"),
|
Repeated => write!(f, "<REPEATED>"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
18
flake.lock
generated
18
flake.lock
generated
@@ -8,11 +8,11 @@
|
|||||||
"rust-analyzer-src": "rust-analyzer-src"
|
"rust-analyzer-src": "rust-analyzer-src"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1752389012,
|
"lastModified": 1754376329,
|
||||||
"narHash": "sha256-Y9PhEOyV+MrJG0Rgrd1AiX+9MfqRPu7msM2y04t57FY=",
|
"narHash": "sha256-Uz90O6qpmXQoNV57bf78yNd+nTxOoV5sjF1MibSdqWg=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "fenix",
|
"repo": "fenix",
|
||||||
"rev": "444e34333e224a39ac3acb6d8831bde2d0e2902f",
|
"rev": "ee7cae7d4cd68f7f2e78493a1f62212640db223c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -23,11 +23,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1751984180,
|
"lastModified": 1754214453,
|
||||||
"narHash": "sha256-LwWRsENAZJKUdD3SpLluwDmdXY9F45ZEgCb0X+xgOL0=",
|
"narHash": "sha256-Q/I2xJn/j1wpkGhWkQnm20nShYnG7TI99foDBpXm1SY=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "9807714d6944a957c2e036f84b0ff8caf9930bc0",
|
"rev": "5b09dc45f24cf32316283e62aec81ffee3c3e376",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -46,11 +46,11 @@
|
|||||||
"rust-analyzer-src": {
|
"rust-analyzer-src": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1752262373,
|
"lastModified": 1754320527,
|
||||||
"narHash": "sha256-eRDeo/hVnf958ESWy8qV/jZj4ZRbFXsmMdw1cnI57dE=",
|
"narHash": "sha256-5/EHPlvDFb1MPVcnUwpAcc9sHejhDhAj4uloUU4rthk=",
|
||||||
"owner": "rust-lang",
|
"owner": "rust-lang",
|
||||||
"repo": "rust-analyzer",
|
"repo": "rust-analyzer",
|
||||||
"rev": "a489123e806ceadfdc5568bf9609b0468f5a2e6a",
|
"rev": "978393bae86212f867e0c43872989e1658f7690f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
{
|
{
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell {
|
||||||
packages = with pkgs; [
|
packages = with pkgs; [
|
||||||
/* (fenix.packages.${system}.complete.withComponents [
|
(fenix.packages.${system}.complete.withComponents [
|
||||||
"cargo"
|
"cargo"
|
||||||
"clippy"
|
"clippy"
|
||||||
"rust-src"
|
"rust-src"
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"rustfmt"
|
"rustfmt"
|
||||||
"rust-analyzer"
|
"rust-analyzer"
|
||||||
"miri"
|
"miri"
|
||||||
]) */
|
])
|
||||||
gdb
|
gdb
|
||||||
valgrind
|
valgrind
|
||||||
gemini-cli
|
gemini-cli
|
||||||
|
|||||||
@@ -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(())
|
|
||||||
}
|
|
||||||
@@ -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(())
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
use hashbrown::HashMap;
|
|
||||||
|
|
||||||
use crate::ir::{Const, DowngradeContext, Ir};
|
|
||||||
|
|
||||||
pub fn ir_env(_: &mut DowngradeContext) -> HashMap<String, Ir> {
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
map.insert("true".into(), Const::from(true).ir());
|
|
||||||
map.insert("false".into(), Const::from(false).ir());
|
|
||||||
map
|
|
||||||
}
|
|
||||||
@@ -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<HashMap<usize, usize>>,
|
|
||||||
pub func_arg_deps: Vec<Vec<usize>>,
|
|
||||||
jit: JITCompiler,
|
|
||||||
compiled: Box<[OnceCell<JITFunc>]>,
|
|
||||||
tasks: PriorityQueue<CompileTask, usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn eval(downgraded: Downgraded) -> Result<Value> {
|
|
||||||
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<HashMap<usize, usize>>,
|
|
||||||
func_arg_deps: Vec<Vec<usize>>,
|
|
||||||
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<Vec<usize>>) -> Result<Value> {
|
|
||||||
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<i::Value> {
|
|
||||||
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<i::Value> = 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<i::Value> {
|
|
||||||
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<i::Value> = 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<Env>) -> Value),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Thunk {
|
|
||||||
fn new(ir: Ir) -> Thunk {
|
|
||||||
Thunk::Expr(ir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Hash, PartialEq, Eq)]
|
|
||||||
struct CompileTask {
|
|
||||||
idx: usize,
|
|
||||||
}
|
|
||||||
95
src/env.rs
95
src/env.rs
@@ -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<Value>,
|
|
||||||
args: Vec<Value>,
|
|
||||||
frame_offsets: Vec<usize>,
|
|
||||||
with: Vec<Rc<HashMap<String, Value>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Item = Value>) {
|
|
||||||
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<Value> {
|
|
||||||
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<Item = Value>) {
|
|
||||||
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<Value> {
|
|
||||||
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<Value> {
|
|
||||||
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<HashMap<String, Value>>) {
|
|
||||||
self.with.push(map)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exit_with(&mut self) {
|
|
||||||
self.with.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<Value>) {
|
|
||||||
ret.write(env.lookup_stack(offset).clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern "C" fn helper_lookup_arg(env: &Env, offset: usize, ret: &mut MaybeUninit<Value>) {
|
|
||||||
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<Value>,
|
|
||||||
) {
|
|
||||||
// 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<Value>,
|
|
||||||
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<Value>,
|
|
||||||
) {
|
|
||||||
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<Value>,
|
|
||||||
) {
|
|
||||||
unsafe {
|
|
||||||
ret.write(Value::List(Vec::from_raw_parts(ptr, len, len).into()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe extern "C" fn helper_create_attrs(ret: &mut MaybeUninit<HashMap<String, Value>>) {
|
|
||||||
ret.write(HashMap::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe extern "C" fn helper_push_attr(
|
|
||||||
attrs: &mut HashMap<String, Value>,
|
|
||||||
sym_ptr: *const u8,
|
|
||||||
sym_len: usize,
|
|
||||||
val: NonNull<Value>,
|
|
||||||
) {
|
|
||||||
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<HashMap<String, Value>>,
|
|
||||||
ret: &mut MaybeUninit<Value>,
|
|
||||||
) {
|
|
||||||
ret.write(Value::AttrSet(Rc::new(unsafe { attrs.read() }.into())));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe extern "C" fn helper_enter_with(
|
|
||||||
env: &mut Env,
|
|
||||||
namespace: NonNull<Value>,
|
|
||||||
) {
|
|
||||||
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::<Value>(len).unwrap()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern "C" fn helper_dbg(value: &Value) {
|
|
||||||
println!("{value:?}")
|
|
||||||
}
|
|
||||||
328
src/eval/mod.rs
328
src/eval/mod.rs
@@ -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<Value>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Evaluate for ir::Attrs {
|
|
||||||
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
|
|
||||||
let mut attrs = AttrSet::new(
|
|
||||||
self.stcs
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| {
|
|
||||||
let eval_result = v.eval(engine, env);
|
|
||||||
Ok((k.clone(), eval_result?))
|
|
||||||
})
|
|
||||||
.collect::<Result<_>>()?,
|
|
||||||
);
|
|
||||||
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<Value> {
|
|
||||||
let items = self
|
|
||||||
.items
|
|
||||||
.iter()
|
|
||||||
.map(|val| {
|
|
||||||
|
|
||||||
val.eval(engine, env)
|
|
||||||
})
|
|
||||||
.collect::<Result<EcoVec<_>>>()?;
|
|
||||||
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<Value> {
|
|
||||||
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<Value> {
|
|
||||||
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<Value> {
|
|
||||||
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<Value> {
|
|
||||||
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<Value> {
|
|
||||||
// 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> {
|
|
||||||
Value::Func(self.idx).ok()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Evaluate for ir::Call {
|
|
||||||
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
|
|
||||||
let mut func = self.func.eval(engine, env)?;
|
|
||||||
func.force(engine, env)?;
|
|
||||||
func.call(
|
|
||||||
self.args
|
|
||||||
.iter()
|
|
||||||
.map(|arg| arg.eval(engine, env))
|
|
||||||
.collect::<Result<_>>()?,
|
|
||||||
engine,
|
|
||||||
env,
|
|
||||||
)?;
|
|
||||||
Ok(func.ok().unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Evaluate for ir::Let {
|
|
||||||
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Evaluate for ir::With {
|
|
||||||
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
|
|
||||||
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<Value> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Evaluate for ir::ConcatStrings {
|
|
||||||
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
|
|
||||||
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::<Result<Vec<_>>>()?
|
|
||||||
.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<Value> {
|
|
||||||
let result = Value::String(self.val.clone()).ok();
|
|
||||||
Ok(result.unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Evaluate for ir::Const {
|
|
||||||
fn eval(&self, _: &mut Engine, _: &mut Env) -> Result<Value> {
|
|
||||||
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<Value> {
|
|
||||||
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<Value> {
|
|
||||||
env.lookup_arg(self.offset).clone().ok()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Evaluate for ir::StackVar {
|
|
||||||
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
|
|
||||||
env.lookup_stack(self.offset).clone().ok()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Evaluate for ir::Thunk {
|
|
||||||
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Evaluate for ir::MaybeThunk {
|
|
||||||
fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result<Value> {
|
|
||||||
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<Value> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
282
src/ir/ctx.rs
282
src/ir/ctx.rs
@@ -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<HashMap<usize, usize>>,
|
|
||||||
pub func_deps: Vec<HashMap<usize, usize>>,
|
|
||||||
pub func_arg_deps: Vec<Vec<usize>>,
|
|
||||||
pub funcs: Vec<Func>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Env<'a, 'env> {
|
|
||||||
env: EnvNode<'a>,
|
|
||||||
prev: Option<&'env Env<'a, 'env>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum EnvNode<'a> {
|
|
||||||
Builtins(&'a HashMap<String, Ir>),
|
|
||||||
Let(&'a Vec<(String, MaybeThunk)>),
|
|
||||||
SingleArg(String),
|
|
||||||
MultiArg(HashMap<String, Option<Ir>>, Option<String>),
|
|
||||||
With,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum LookupResult {
|
|
||||||
Builtin(Ir),
|
|
||||||
MaybeThunk(MaybeThunk),
|
|
||||||
SingleArg { idx: usize },
|
|
||||||
MultiArg { idx: usize, default: Option<Ir> },
|
|
||||||
With,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'env> Env<'a, 'env> {
|
|
||||||
pub fn new(builtins: &'a HashMap<String, Ir>) -> 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<String, Option<Ir>>,
|
|
||||||
alias: Option<String>,
|
|
||||||
) -> 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<LookupResult, ()> {
|
|
||||||
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<LookupResult, ()> {
|
|
||||||
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<HashMap<usize, usize>>,
|
|
||||||
pub func_arg_deps: Vec<Vec<usize>>,
|
|
||||||
pub graph: Vec<Vec<usize>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<usize, ()>,
|
|
||||||
nodes: HashMap<usize, NodeIndex>
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Vec<usize>> {
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
1162
src/ir/mod.rs
1162
src/ir/mod.rs
File diff suppressed because it is too large
Load Diff
146
src/ir/utils.rs
146
src/ir/utils.rs
@@ -1,146 +0,0 @@
|
|||||||
use rnix::ast;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub fn downgrade_param(param: ast::Param, ctx: &mut DowngradeContext) -> Result<Param> {
|
|
||||||
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<Param> {
|
|
||||||
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::<Result<Vec<_>>>()?;
|
|
||||||
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<Attrs> {
|
|
||||||
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<String, Ir>,
|
|
||||||
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<Attr> {
|
|
||||||
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::<Result<Vec<_>>>()?;
|
|
||||||
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<Vec<Attr>> {
|
|
||||||
attrpath
|
|
||||||
.attrs()
|
|
||||||
.map(|attr| downgrade_attr(attr, ctx))
|
|
||||||
.collect::<Result<Vec<_>>>()
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
12
src/lib.rs
12
src/lib.rs
@@ -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;
|
|
||||||
98
src/stack.rs
98
src/stack.rs
@@ -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<Value<'vm>>` to `Value<'vm>`
|
|
||||||
// or `&MaybeUninit<Value<'vm>>` 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<T, const CAP: usize> {
|
|
||||||
items: [MaybeUninit<T>; CAP],
|
|
||||||
top: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const CAP: usize> Default for Stack<T, CAP> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone, const CAP: usize> Clone for Stack<T, CAP> {
|
|
||||||
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<T, const CAP: usize> Stack<T, CAP> {
|
|
||||||
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<T, const CAP: usize> Deref for Stack<T, CAP> {
|
|
||||||
type Target = [T];
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
into!(&self.items[0..self.top])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const CAP: usize> DerefMut for Stack<T, CAP> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
into!(&mut self.items[0..self.top])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const CAP: usize> Drop for Stack<T, CAP> {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<T: Into<String>> From<T> for Catchable {
|
|
||||||
fn from(value: T) -> Self {
|
|
||||||
Catchable { msg: value.into() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Catchable {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
|
||||||
write!(f, "<error: {}>", self.msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, IsVariant, Unwrap)]
|
|
||||||
pub enum Const {
|
|
||||||
Bool(bool),
|
|
||||||
Int(i64),
|
|
||||||
Float(f64),
|
|
||||||
Null,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for Const {
|
|
||||||
fn hash<H: core::hash::Hasher>(&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<bool> for Const {
|
|
||||||
fn from(value: bool) -> Self {
|
|
||||||
Const::Bool(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<i64> for Const {
|
|
||||||
fn from(value: i64) -> Self {
|
|
||||||
Const::Int(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<f64> 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 {}
|
|
||||||
@@ -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<Value>,
|
|
||||||
pub frame: Vec<Value>,
|
|
||||||
}
|
|
||||||
@@ -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<Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for List {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Into<EcoVec<Value>>> From<T> 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<Value> {
|
|
||||||
self.data
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_public(&self, engine: &Engine, seen: &mut HashSet<Value>) -> p::Value {
|
|
||||||
p::Value::List(p::List::new(
|
|
||||||
self.data
|
|
||||||
.iter()
|
|
||||||
.map(|value| value.clone().to_public(engine, seen))
|
|
||||||
.collect(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
pub mod common;
|
|
||||||
pub mod internal;
|
|
||||||
pub mod public;
|
|
||||||
Reference in New Issue
Block a user