feat: init
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
target/
|
||||||
|
|
||||||
|
/.direnv/
|
||||||
656
Cargo.lock
generated
Normal file
656
Cargo.lock
generated
Normal file
@@ -0,0 +1,656 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "addr2line"
|
||||||
|
version = "0.22.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
|
||||||
|
dependencies = [
|
||||||
|
"gimli",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.82"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "archery"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8967cd1cc9e9e1954f644e14fbd6042fe9a37da96c52a67e44a2ac18261f8561"
|
||||||
|
dependencies = [
|
||||||
|
"static_assertions",
|
||||||
|
"triomphe",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace"
|
||||||
|
version = "0.3.73"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
|
||||||
|
dependencies = [
|
||||||
|
"addr2line",
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"miniz_oxide",
|
||||||
|
"object",
|
||||||
|
"rustc-demangle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.99"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "convert_case"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "countme"
|
||||||
|
version = "3.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more"
|
||||||
|
version = "0.99.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
|
||||||
|
dependencies = [
|
||||||
|
"convert_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustc_version",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ecow"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "54bfbb1708988623190a6c4dbedaeaf0f53c20c6395abd6a01feb327b3146f4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gimli"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.155"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
|
||||||
|
dependencies = [
|
||||||
|
"adler",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "0.8.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"crossbeam-channel",
|
||||||
|
"derive_more",
|
||||||
|
"ecow",
|
||||||
|
"itertools",
|
||||||
|
"num_cpus",
|
||||||
|
"once_cell",
|
||||||
|
"rayon",
|
||||||
|
"rnix",
|
||||||
|
"rpds",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_cpus"
|
||||||
|
version = "1.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "object"
|
||||||
|
version = "0.36.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.12.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"windows-targets 0.52.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.78"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rnix"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb35cedbeb70e0ccabef2a31bcff0aebd114f19566086300b8f42c725fc2cb5f"
|
||||||
|
dependencies = [
|
||||||
|
"rowan",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rowan"
|
||||||
|
version = "0.15.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49"
|
||||||
|
dependencies = [
|
||||||
|
"countme",
|
||||||
|
"hashbrown",
|
||||||
|
"memoffset",
|
||||||
|
"rustc-hash",
|
||||||
|
"text-size",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rpds"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0e15515d3ce3313324d842629ea4905c25a13f81953eadb88f85516f59290a4"
|
||||||
|
dependencies = [
|
||||||
|
"archery",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-demangle"
|
||||||
|
version = "0.1.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.5.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.58"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "text-size"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio"
|
||||||
|
version = "1.38.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace",
|
||||||
|
"bytes",
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"num_cpus",
|
||||||
|
"parking_lot",
|
||||||
|
"pin-project-lite",
|
||||||
|
"signal-hook-registry",
|
||||||
|
"socket2",
|
||||||
|
"tokio-macros",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-macros"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.58",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "triomphe"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.48.5",
|
||||||
|
"windows_aarch64_msvc 0.48.5",
|
||||||
|
"windows_i686_gnu 0.48.5",
|
||||||
|
"windows_i686_msvc 0.48.5",
|
||||||
|
"windows_x86_64_gnu 0.48.5",
|
||||||
|
"windows_x86_64_gnullvm 0.48.5",
|
||||||
|
"windows_x86_64_msvc 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.52.5",
|
||||||
|
"windows_aarch64_msvc 0.52.5",
|
||||||
|
"windows_i686_gnu 0.52.5",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc 0.52.5",
|
||||||
|
"windows_x86_64_gnu 0.52.5",
|
||||||
|
"windows_x86_64_gnullvm 0.52.5",
|
||||||
|
"windows_x86_64_msvc 0.52.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||||
19
Cargo.toml
Normal file
19
Cargo.toml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
name = "nix-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rnix = "0.11"
|
||||||
|
anyhow = "1.0"
|
||||||
|
itertools = "0.12"
|
||||||
|
rayon = "1.10"
|
||||||
|
tokio = { version = "1.38", features = [ "full" ] }
|
||||||
|
rpds = "1.1"
|
||||||
|
derive_more = "0.99"
|
||||||
|
crossbeam-channel = "0.5"
|
||||||
|
num_cpus = "1.0"
|
||||||
|
ecow = "0.2"
|
||||||
|
once_cell = "1.19"
|
||||||
66
flake.lock
generated
Normal file
66
flake.lock
generated
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"fenix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"rust-analyzer-src": "rust-analyzer-src"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1746167999,
|
||||||
|
"narHash": "sha256-18XGHsjk/5H8F0OGUCG56CeeW1u6qQ7tAfQK3azlwWg=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"rev": "bcbc23a4f3391c1c3657f1847cb693aaea3aed76",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1746141548,
|
||||||
|
"narHash": "sha256-IgBWhX7A2oJmZFIrpRuMnw5RAufVnfvOgHWgIdds+hc=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "f02fddb8acef29a8b32f10a335d44828d7825b78",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"fenix": "fenix",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rust-analyzer-src": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1746093169,
|
||||||
|
"narHash": "sha256-3gmUmzIzfzlgF/b4HXvtoBIP4bKofVeEubX7LcPBYLo=",
|
||||||
|
"owner": "rust-lang",
|
||||||
|
"repo": "rust-analyzer",
|
||||||
|
"rev": "298fa81aacda7b06de4db55c377b1aa081906bc9",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "rust-lang",
|
||||||
|
"ref": "nightly",
|
||||||
|
"repo": "rust-analyzer",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
32
flake.nix
Normal file
32
flake.nix
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
|
fenix.url = "github:nix-community/fenix";
|
||||||
|
fenix.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
outputs = { nixpkgs, fenix, ... }:
|
||||||
|
let
|
||||||
|
system = "x86_64-linux";
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
formatter.${system} = pkgs.nixpkgs-fmt;
|
||||||
|
devShells.${system}.default = pkgs.mkShell {
|
||||||
|
packages = with pkgs; [
|
||||||
|
zsh
|
||||||
|
(fenix.packages.${system}.complete.withComponents [
|
||||||
|
"cargo"
|
||||||
|
"clippy"
|
||||||
|
"rust-src"
|
||||||
|
"rustc"
|
||||||
|
"rustfmt"
|
||||||
|
"rust-analyzer"
|
||||||
|
])
|
||||||
|
];
|
||||||
|
shellHook = ''
|
||||||
|
zsh & disown
|
||||||
|
exit
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
86
src/builtins/mod.rs
Normal file
86
src/builtins/mod.rs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
use derive_more::Constructor;
|
||||||
|
|
||||||
|
use super::vm::Env;
|
||||||
|
use super::vm::Symbol;
|
||||||
|
use super::vm::Value;
|
||||||
|
use crate::bytecode::Const;
|
||||||
|
|
||||||
|
pub fn env() -> Env {
|
||||||
|
let mut env = Env::empty();
|
||||||
|
env.insert(Symbol::from("true"), Value::Const(Const::Bool(true)));
|
||||||
|
env.insert(Symbol::from("false"), Value::Const(Const::Bool(false)));
|
||||||
|
|
||||||
|
env.insert(Symbol::from("__add"), Value::PrimOp(PrimOp::new(2, |args| {
|
||||||
|
let [first, second]: [Value; 2] = args.try_into().unwrap();
|
||||||
|
first.add(second)
|
||||||
|
})));
|
||||||
|
env.insert(Symbol::from("__sub"), Value::PrimOp(PrimOp::new(2, |args| {
|
||||||
|
let [first, second]: [Value; 2] = args.try_into().unwrap();
|
||||||
|
first.add(second.neg())
|
||||||
|
})));
|
||||||
|
env.insert(Symbol::from("__mul"), Value::PrimOp(PrimOp::new(2, |args| {
|
||||||
|
let [first, second]: [Value; 2] = args.try_into().unwrap();
|
||||||
|
first.mul(second)
|
||||||
|
})));
|
||||||
|
env.insert(Symbol::from("__div"), Value::PrimOp(PrimOp::new(2, |args| {
|
||||||
|
let [first, second]: [Value; 2] = args.try_into().unwrap();
|
||||||
|
first.div(second)
|
||||||
|
})));
|
||||||
|
env.insert(Symbol::from("__lessThan"), Value::PrimOp(PrimOp::new(2, |args| {
|
||||||
|
let [first, second]: [Value; 2] = args.try_into().unwrap();
|
||||||
|
first.lt(second)
|
||||||
|
})));
|
||||||
|
|
||||||
|
env
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Constructor)]
|
||||||
|
pub struct PrimOp {
|
||||||
|
arity: u8,
|
||||||
|
func: fn(Vec<Value>) -> Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for PrimOp {
|
||||||
|
fn eq(&self, _: &Self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimOp {
|
||||||
|
pub fn call(self, args: Vec<Value>) -> Value {
|
||||||
|
if (args.len() as u8) < self.arity {
|
||||||
|
Value::PartialPrimOp(PartialPrimOp { arity: self.arity - args.len() as u8, args, func: self.func })
|
||||||
|
} else if args.len() as u8 == self.arity {
|
||||||
|
(self.func)(args)
|
||||||
|
} else {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PartialPrimOp {
|
||||||
|
arity: u8,
|
||||||
|
args: Vec<Value>,
|
||||||
|
func: fn(Vec<Value>) -> Value
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for PartialPrimOp {
|
||||||
|
fn eq(&self, _: &Self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialPrimOp {
|
||||||
|
pub fn call(mut self, args: Vec<Value>) -> Value {
|
||||||
|
let len = args.len() as u8;
|
||||||
|
self.args.extend(args);
|
||||||
|
if len < self.arity {
|
||||||
|
Value::PartialPrimOp(PartialPrimOp { arity: self.arity - len, args: self.args, func: self.func })
|
||||||
|
} else if len == self.arity {
|
||||||
|
(self.func)(self.args)
|
||||||
|
} else {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
230
src/bytecode.rs
Normal file
230
src/bytecode.rs
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
use ecow::EcoString;
|
||||||
|
use anyhow::Error;
|
||||||
|
use derive_more::{IsVariant, Unwrap};
|
||||||
|
|
||||||
|
use crate::slice::Slice;
|
||||||
|
use crate::value::Func;
|
||||||
|
|
||||||
|
pub type ThunkIdx = usize;
|
||||||
|
pub type ConstIdx = usize;
|
||||||
|
pub type SymIdx = usize;
|
||||||
|
pub type OpCodes = Slice<OpCode>;
|
||||||
|
pub type Consts = Slice<Const>;
|
||||||
|
pub type Thunks = Slice<Thunk>;
|
||||||
|
pub type Args = Slice<Arg>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Thunk {
|
||||||
|
pub opcodes: OpCodes,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash)]
|
||||||
|
pub enum Arg {}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, IsVariant, Unwrap)]
|
||||||
|
pub enum Const {
|
||||||
|
Bool(bool),
|
||||||
|
Int(i64),
|
||||||
|
Float(f64),
|
||||||
|
String(EcoString),
|
||||||
|
Func(Func),
|
||||||
|
}
|
||||||
|
|
||||||
|
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 From<EcoString> for Const {
|
||||||
|
fn from(value: EcoString) -> Self {
|
||||||
|
Const::String(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Const {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Const::String(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for Const {
|
||||||
|
fn from(value: &str) -> Self {
|
||||||
|
Const::String(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<&'a Const> for &'a bool {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
Const::Bool(b) => Ok(b),
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> TryFrom<&'a Const> for &'a i64 {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
Const::Int(int) => Ok(int),
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<&'a Const> for &'a f64 {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
Const::Float(float) => Ok(float),
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<&'a Const> for &'a str {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
Const::String(string) => Ok(string),
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
(String(a), String(b)) => a == b,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Const {}
|
||||||
|
|
||||||
|
impl Hash for Const {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
use Const::*;
|
||||||
|
match self {
|
||||||
|
Bool(b) => b.hash(state),
|
||||||
|
Int(int) => int.hash(state),
|
||||||
|
Float(float) => float.to_bits().hash(state),
|
||||||
|
String(string) => string.hash(state),
|
||||||
|
Func(func) => func.hash(state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash)]
|
||||||
|
pub enum OpCode {
|
||||||
|
/// load a constant onto stack
|
||||||
|
Const { value: Const },
|
||||||
|
/// load a dynamic var onto stack
|
||||||
|
LookUp { sym: EcoString },
|
||||||
|
/// load a thunk lazily onto stack
|
||||||
|
LoadThunk { idx: ThunkIdx },
|
||||||
|
/// load a thunk onto stack and force its value
|
||||||
|
LoadValue { idx: ThunkIdx },
|
||||||
|
/// force TOS to value
|
||||||
|
ForceValue,
|
||||||
|
/// [ ... func, args @ .. ] call func with `arity` numbers of arg
|
||||||
|
Call { arity: usize },
|
||||||
|
/// assert TOS is true then consume it
|
||||||
|
Assert,
|
||||||
|
/// jump forward
|
||||||
|
Jmp { step: usize },
|
||||||
|
/// [ ... cond ] if (cond) is true, then jump forward
|
||||||
|
JmpIfTrue { step: usize },
|
||||||
|
/// [ ... cond ] if (cond) is false, then jump forward
|
||||||
|
JmpIfFalse { step: usize },
|
||||||
|
/// push an empty attribute set onto stack
|
||||||
|
AttrSet,
|
||||||
|
/// push an empty recursive attribute set onto stack
|
||||||
|
RecAttrSet,
|
||||||
|
/// [ ... set, value ] push the static kv pair (name, (value)) into (set)
|
||||||
|
PushStaticAttr { name: EcoString },
|
||||||
|
/// [ ... set, name, value ] push the dynamic kv pair ((name), (value)) in to (set)
|
||||||
|
PushDynamicAttr,
|
||||||
|
/// push an empty list onto stack
|
||||||
|
List,
|
||||||
|
/// [ ... list, elem ] push (elem) into (list)
|
||||||
|
PushElem,
|
||||||
|
/// [ ... a, b ] perform a binary operation ((a) `op` (b))
|
||||||
|
BinOp { op: BinOp },
|
||||||
|
/// [ ... a ] perform a unary operation (`op` (a))
|
||||||
|
UnOp { op: UnOp },
|
||||||
|
/// TODO:
|
||||||
|
ConcatString,
|
||||||
|
/// TODO:
|
||||||
|
HasAttr { sym: EcoString },
|
||||||
|
/// TODO:
|
||||||
|
HasDynamicAttr,
|
||||||
|
// HasAttr { arity: usize },
|
||||||
|
/// TODO:
|
||||||
|
Select { sym: EcoString },
|
||||||
|
// Select { arity: usize },
|
||||||
|
/// TODO:
|
||||||
|
SelectDynamic,
|
||||||
|
// SelectDynamic { arity: usize },
|
||||||
|
/// TODO:
|
||||||
|
SelectOrEmpty { sym: EcoString },
|
||||||
|
/// TODO:
|
||||||
|
SelectDynamicOrEmpty,
|
||||||
|
/// TODO:
|
||||||
|
SelectWithDefault { sym: EcoString },
|
||||||
|
/// TODO:
|
||||||
|
SelectDynamicWithDefault,
|
||||||
|
/// enter the environment of the attribute set at TOS
|
||||||
|
EnterEnv,
|
||||||
|
/// exit the envrironment
|
||||||
|
LeaveEnv,
|
||||||
|
/// return a value
|
||||||
|
Ret,
|
||||||
|
/// no-op
|
||||||
|
NoOp,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Hash)]
|
||||||
|
pub enum BinOp {
|
||||||
|
Add,
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
Eq,
|
||||||
|
Con,
|
||||||
|
Upd,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Hash)]
|
||||||
|
pub enum UnOp {
|
||||||
|
Not,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Program {
|
||||||
|
pub top_level: OpCodes,
|
||||||
|
pub thunks: Thunks,
|
||||||
|
}
|
||||||
390
src/compile/compile.rs
Normal file
390
src/compile/compile.rs
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
use crate::bytecode::*;
|
||||||
|
|
||||||
|
use super::ir;
|
||||||
|
|
||||||
|
pub struct Compiler {
|
||||||
|
opcodes: Vec<OpCode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(downgraded: ir::Downgraded) -> Program {
|
||||||
|
Program {
|
||||||
|
top_level: Compiler::new().compile(downgraded.top_level),
|
||||||
|
thunks: downgraded
|
||||||
|
.thunks
|
||||||
|
.into_iter()
|
||||||
|
.map(|thunk| Thunk {
|
||||||
|
opcodes: Compiler::new().compile(thunk),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compiler {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
opcodes: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile(mut self, ir: ir::Ir) -> OpCodes {
|
||||||
|
ir.compile(&mut self);
|
||||||
|
self.opcodes()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push(&mut self, code: OpCode) {
|
||||||
|
self.opcodes.push(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn idx(&self) -> usize {
|
||||||
|
self.opcodes.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn modify(&mut self, idx: usize, code: OpCode) {
|
||||||
|
self.opcodes[idx] = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn last(&self) -> Option<OpCode> {
|
||||||
|
self.opcodes.last().cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop(&mut self) -> Option<OpCode> {
|
||||||
|
self.opcodes.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn opcodes(self) -> OpCodes {
|
||||||
|
self.opcodes.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Compile {
|
||||||
|
fn compile(self, comp: &mut Compiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CompileWithLength {
|
||||||
|
fn compile_with_length(self, comp: &mut Compiler) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Compile> CompileWithLength for T {
|
||||||
|
fn compile_with_length(self, comp: &mut Compiler) -> usize {
|
||||||
|
let start = comp.idx();
|
||||||
|
self.compile(comp);
|
||||||
|
let end = comp.idx();
|
||||||
|
end - start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::Const {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
comp.push(OpCode::Const { value: self.value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::Var {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
comp.push(OpCode::LookUp { sym: self.sym });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::Thunk {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
comp.push(OpCode::LoadThunk { idx: self.idx });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::Attrs {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
comp.push(OpCode::AttrSet);
|
||||||
|
for stc in self.stcs {
|
||||||
|
stc.1.compile(comp);
|
||||||
|
comp.push(OpCode::PushStaticAttr { name: stc.0 });
|
||||||
|
}
|
||||||
|
for dynamic in self.dyns {
|
||||||
|
dynamic.0.compile(comp);
|
||||||
|
dynamic.1.compile(comp);
|
||||||
|
comp.push(OpCode::PushDynamicAttr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::RecAttrs {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
comp.push(OpCode::AttrSet);
|
||||||
|
for dynamic in self.dyns.clone() {
|
||||||
|
dynamic.0.compile(comp);
|
||||||
|
dynamic.1.compile(comp);
|
||||||
|
comp.push(OpCode::PushDynamicAttr)
|
||||||
|
}
|
||||||
|
comp.push(OpCode::EnterEnv);
|
||||||
|
comp.push(OpCode::AttrSet);
|
||||||
|
for stc in self.stcs {
|
||||||
|
stc.1.compile(comp);
|
||||||
|
comp.push(OpCode::PushStaticAttr { name: stc.0 });
|
||||||
|
}
|
||||||
|
for dynamic in self.dyns {
|
||||||
|
dynamic.0.compile(comp);
|
||||||
|
dynamic.1.compile(comp);
|
||||||
|
comp.push(OpCode::PushDynamicAttr)
|
||||||
|
}
|
||||||
|
comp.push(OpCode::LeaveEnv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::List {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
comp.push(OpCode::List);
|
||||||
|
for item in self.items {
|
||||||
|
item.compile(comp);
|
||||||
|
comp.push(OpCode::PushElem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::UnOp {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
use ir::UnOpKind::*;
|
||||||
|
match self.kind {
|
||||||
|
Neg => {
|
||||||
|
comp.push(OpCode::LookUp { sym: "__sub".into() });
|
||||||
|
comp.push(OpCode::Const { value: Const::Int(0) });
|
||||||
|
self.rhs.compile(comp);
|
||||||
|
comp.push(OpCode::Call { arity: 2 });
|
||||||
|
}
|
||||||
|
Not => {
|
||||||
|
self.rhs.compile(comp);
|
||||||
|
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::BinOp {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
use ir::BinOpKind::*;
|
||||||
|
match self.kind {
|
||||||
|
Add => {
|
||||||
|
self.lhs.compile(comp);
|
||||||
|
self.rhs.compile(comp);
|
||||||
|
comp.push(OpCode::BinOp { op: BinOp::Add });
|
||||||
|
}
|
||||||
|
Mul => {
|
||||||
|
comp.push(OpCode::LookUp { sym: "__mul".into() });
|
||||||
|
self.lhs.compile(comp);
|
||||||
|
self.rhs.compile(comp);
|
||||||
|
comp.push(OpCode::Call { arity: 2 });
|
||||||
|
}
|
||||||
|
Div => {
|
||||||
|
comp.push(OpCode::LookUp { sym: "__div".into() });
|
||||||
|
self.lhs.compile(comp);
|
||||||
|
self.rhs.compile(comp);
|
||||||
|
comp.push(OpCode::Call { arity: 2 });
|
||||||
|
}
|
||||||
|
And => {
|
||||||
|
self.lhs.compile(comp);
|
||||||
|
self.rhs.compile(comp);
|
||||||
|
comp.push(OpCode::BinOp { op: BinOp::And });
|
||||||
|
}
|
||||||
|
Or => {
|
||||||
|
self.lhs.compile(comp);
|
||||||
|
self.rhs.compile(comp);
|
||||||
|
comp.push(OpCode::BinOp { op: BinOp::Or });
|
||||||
|
}
|
||||||
|
Eq => {
|
||||||
|
self.lhs.compile(comp);
|
||||||
|
self.rhs.compile(comp);
|
||||||
|
comp.push(OpCode::BinOp { op: BinOp::Eq });
|
||||||
|
}
|
||||||
|
Lt => {
|
||||||
|
comp.push(OpCode::LookUp { sym: "__lessThan".into() });
|
||||||
|
self.lhs.compile(comp);
|
||||||
|
self.rhs.compile(comp);
|
||||||
|
comp.push(OpCode::Call { arity: 2 });
|
||||||
|
}
|
||||||
|
Con => {
|
||||||
|
self.lhs.compile(comp);
|
||||||
|
self.rhs.compile(comp);
|
||||||
|
comp.push(OpCode::BinOp { op: BinOp::Con });
|
||||||
|
}
|
||||||
|
Upd => {
|
||||||
|
self.lhs.compile(comp);
|
||||||
|
self.rhs.compile(comp);
|
||||||
|
comp.push(OpCode::BinOp { op: BinOp::Upd });
|
||||||
|
}
|
||||||
|
|
||||||
|
Sub => {
|
||||||
|
comp.push(OpCode::LookUp { sym: "__sub".into() });
|
||||||
|
self.lhs.compile(comp);
|
||||||
|
self.rhs.compile(comp);
|
||||||
|
comp.push(OpCode::Call { arity: 2 });
|
||||||
|
}
|
||||||
|
Impl => {
|
||||||
|
self.lhs.compile(comp);
|
||||||
|
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||||
|
self.rhs.compile(comp);
|
||||||
|
comp.push(OpCode::BinOp { op: BinOp::Or });
|
||||||
|
}
|
||||||
|
Neq => {
|
||||||
|
self.lhs.compile(comp);
|
||||||
|
self.rhs.compile(comp);
|
||||||
|
comp.push(OpCode::BinOp { op: BinOp::Eq });
|
||||||
|
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||||
|
}
|
||||||
|
Gt => {
|
||||||
|
comp.push(OpCode::LookUp { sym: "__lessThan".into() });
|
||||||
|
self.rhs.compile(comp);
|
||||||
|
self.lhs.compile(comp);
|
||||||
|
comp.push(OpCode::Call { arity: 2 });
|
||||||
|
}
|
||||||
|
Leq => {
|
||||||
|
comp.push(OpCode::LookUp { sym: "__lessThan".into() });
|
||||||
|
self.rhs.compile(comp);
|
||||||
|
self.lhs.compile(comp);
|
||||||
|
comp.push(OpCode::Call { arity: 2 });
|
||||||
|
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||||
|
}
|
||||||
|
Geq => {
|
||||||
|
comp.push(OpCode::LookUp { sym: "__lessThan".into() });
|
||||||
|
self.lhs.compile(comp);
|
||||||
|
self.rhs.compile(comp);
|
||||||
|
comp.push(OpCode::Call { arity: 2 });
|
||||||
|
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::HasAttr {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
self.lhs.compile(comp);
|
||||||
|
for attr in self.rhs {
|
||||||
|
match attr {
|
||||||
|
ir::Attr::Str(sym) => {
|
||||||
|
comp.push(OpCode::SelectOrEmpty { sym });
|
||||||
|
}
|
||||||
|
ir::Attr::Dynamic(dynamic) => {
|
||||||
|
dynamic.compile(comp);
|
||||||
|
comp.push(OpCode::SelectDynamicOrEmpty);
|
||||||
|
}
|
||||||
|
ir::Attr::Strs(string) => {
|
||||||
|
string.compile(comp);
|
||||||
|
comp.push(OpCode::SelectDynamicOrEmpty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match comp.pop().unwrap() {
|
||||||
|
OpCode::SelectOrEmpty { sym } => comp.push(OpCode::HasAttr { sym }),
|
||||||
|
OpCode::SelectDynamicOrEmpty => comp.push(OpCode::HasDynamicAttr),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::Select {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
self.expr.compile(comp);
|
||||||
|
for attr in self.attrpath {
|
||||||
|
match attr {
|
||||||
|
ir::Attr::Str(sym) => comp.push(OpCode::SelectOrEmpty { sym }),
|
||||||
|
ir::Attr::Dynamic(dynamic) => {
|
||||||
|
dynamic.compile(comp);
|
||||||
|
comp.push(OpCode::SelectDynamicOrEmpty);
|
||||||
|
}
|
||||||
|
ir::Attr::Strs(string) => {
|
||||||
|
string.compile(comp);
|
||||||
|
comp.push(OpCode::SelectDynamicOrEmpty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match self.default {
|
||||||
|
Some(default) => {
|
||||||
|
let last = comp.pop().unwrap();
|
||||||
|
default.compile(comp);
|
||||||
|
match last {
|
||||||
|
OpCode::SelectOrEmpty { sym } => comp.push(OpCode::SelectWithDefault { sym }),
|
||||||
|
OpCode::SelectDynamicOrEmpty => comp.push(OpCode::SelectDynamicWithDefault),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => match comp.pop().unwrap() {
|
||||||
|
OpCode::SelectOrEmpty { sym } => comp.push(OpCode::Select { sym }),
|
||||||
|
OpCode::SelectDynamicOrEmpty => comp.push(OpCode::SelectDynamic),
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::ConcatStrings {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
let mut iter = self.parts.into_iter();
|
||||||
|
iter.next().unwrap().compile(comp);
|
||||||
|
for item in iter {
|
||||||
|
item.compile(comp);
|
||||||
|
comp.push(OpCode::ConcatString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::If {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
self.cond.compile(comp);
|
||||||
|
|
||||||
|
let idx_jmp_if_false = comp.idx();
|
||||||
|
// place holder
|
||||||
|
comp.push(OpCode::NoOp);
|
||||||
|
|
||||||
|
let consq_length = self.consq.compile_with_length(comp);
|
||||||
|
|
||||||
|
let idx_jmp = comp.idx();
|
||||||
|
// place holder
|
||||||
|
comp.push(OpCode::NoOp);
|
||||||
|
|
||||||
|
let alter_length = self.alter.compile_with_length(comp);
|
||||||
|
|
||||||
|
comp.modify(idx_jmp_if_false, OpCode::JmpIfFalse { step: consq_length });
|
||||||
|
comp.modify(idx_jmp, OpCode::Jmp { step: alter_length });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::Let {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
self.attrs.compile(comp);
|
||||||
|
comp.push(OpCode::EnterEnv);
|
||||||
|
self.expr.compile(comp);
|
||||||
|
comp.push(OpCode::LeaveEnv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::With {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
self.namespace.compile(comp);
|
||||||
|
comp.push(OpCode::EnterEnv);
|
||||||
|
self.expr.compile(comp);
|
||||||
|
comp.push(OpCode::LeaveEnv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::Assert {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
self.assertion.compile(comp);
|
||||||
|
comp.push(OpCode::Assert);
|
||||||
|
self.expr.compile(comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::Func {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::Call {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for ir::Path {
|
||||||
|
fn compile(self, comp: &mut Compiler) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/compile/env.rs
Normal file
19
src/compile/env.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::bytecode::SymIdx;
|
||||||
|
|
||||||
|
use super::ir::Ir;
|
||||||
|
|
||||||
|
pub struct IrEnv {
|
||||||
|
pub stcs: HashMap<SymIdx, Ir>,
|
||||||
|
pub dyns: Vec<(Ir, Ir)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IrEnv {
|
||||||
|
pub fn new() -> IrEnv {
|
||||||
|
IrEnv {
|
||||||
|
stcs: HashMap::new(),
|
||||||
|
dyns: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
772
src/compile/ir.rs
Normal file
772
src/compile/ir.rs
Normal file
@@ -0,0 +1,772 @@
|
|||||||
|
// TODO: Error Handling
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use rnix::ast::{self, Expr};
|
||||||
|
use ecow::EcoString;
|
||||||
|
|
||||||
|
use crate::bytecode::{Const as ByteCodeConst, ConstIdx, Consts, ThunkIdx};
|
||||||
|
use crate::slice::Slice;
|
||||||
|
|
||||||
|
use super::compile::*;
|
||||||
|
use super::env::IrEnv;
|
||||||
|
use super::symtable::*;
|
||||||
|
|
||||||
|
pub fn downgrade(expr: Expr) -> Result<Downgraded> {
|
||||||
|
let mut state = DowngradeState::new();
|
||||||
|
let ir = expr.downgrade(&mut state)?;
|
||||||
|
Ok(Downgraded {
|
||||||
|
top_level: ir,
|
||||||
|
consts: state.consts.into(),
|
||||||
|
thunks: state.thunks.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! ir {
|
||||||
|
(
|
||||||
|
$(
|
||||||
|
$(#[$($x:tt)*])*
|
||||||
|
$ty:ident
|
||||||
|
=>
|
||||||
|
{$($name:ident : $elemtype:ty),*$(,)?}
|
||||||
|
)
|
||||||
|
,*$(,)?
|
||||||
|
) => {
|
||||||
|
use crate::downcast::Downcast;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Ir {
|
||||||
|
$(
|
||||||
|
$ty($ty),
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ir {
|
||||||
|
fn boxed(self) -> Box<Self> {
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
fn ok(self) -> Result<Self> {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compile for Ir {
|
||||||
|
fn compile(self, state: &mut Compiler) {
|
||||||
|
match self {
|
||||||
|
$(Ir::$ty(ir) => ir.compile(state),)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(
|
||||||
|
$(
|
||||||
|
#[$($x)*]
|
||||||
|
)*
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct $ty {
|
||||||
|
$(
|
||||||
|
pub $name : $elemtype,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $ty {
|
||||||
|
pub fn ir(self) -> Ir {
|
||||||
|
Ir::$ty(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Ir> for $ty {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
fn try_from(value: Ir) -> Result<Self> {
|
||||||
|
match value {
|
||||||
|
Ir::$ty(value) => Ok(value),
|
||||||
|
_ => Err(anyhow!("")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downcast<$ty> for Ir {
|
||||||
|
fn downcast_ref(&self) -> Option<&$ty> {
|
||||||
|
match self {
|
||||||
|
Ir::$ty(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn downcast_mut(&mut self) -> Option<&mut $ty> {
|
||||||
|
match self {
|
||||||
|
Ir::$ty(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn downcast(self) -> core::result::Result<$ty, Self> {
|
||||||
|
match self {
|
||||||
|
Ir::$ty(value) => Ok(value),
|
||||||
|
_ => Err(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ir! {
|
||||||
|
Attrs => { stcs: HashMap<EcoString, Ir>, dyns: Vec<DynamicAttrPair> },
|
||||||
|
RecAttrs => { stcs: HashMap<EcoString, Ir>, dyns: Vec<DynamicAttrPair> },
|
||||||
|
List => { items: Vec<Ir> },
|
||||||
|
HasAttr => { lhs: Box<Ir>, rhs: Vec<Attr> },
|
||||||
|
BinOp => { lhs: Box<Ir>, rhs: Box<Ir>, kind: BinOpKind },
|
||||||
|
UnOp => { rhs: Box<Ir>, kind: UnOpKind },
|
||||||
|
Select => { expr: Box<Ir>, attrpath: Vec<Attr>, default: Option<Box<Ir>> },
|
||||||
|
If => { cond: Box<Ir>, consq: Box<Ir>, alter: Box<Ir> },
|
||||||
|
Func => { args: Vec<Param>, body: Box<Ir> },
|
||||||
|
Call => { func: Box<Ir>, args: Vec<Ir> },
|
||||||
|
|
||||||
|
Let => { attrs: Attrs, expr: Box<Ir> },
|
||||||
|
With => { namespace: Box<Ir>, expr: Box<Ir> },
|
||||||
|
Assert => { assertion: Box<Ir>, expr: Box<Ir> },
|
||||||
|
ConcatStrings => { parts: Vec<Ir> },
|
||||||
|
Const => { value: ByteCodeConst },
|
||||||
|
Var => { sym: EcoString },
|
||||||
|
#[derive(Copy)]
|
||||||
|
Thunk => { idx: ThunkIdx },
|
||||||
|
Path => { expr: Box<Ir> },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct DynamicAttrPair(pub Ir, pub Ir);
|
||||||
|
|
||||||
|
enum Env {
|
||||||
|
Env(IrEnv),
|
||||||
|
With,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Env {
|
||||||
|
fn env(&self) -> &IrEnv {
|
||||||
|
match self {
|
||||||
|
Env::Env(env) => env,
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn env_mut(&mut self) -> &mut IrEnv {
|
||||||
|
match self {
|
||||||
|
Env::Env(env) => env,
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DowngradeError {
|
||||||
|
errno: u16,
|
||||||
|
text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DowngradeState {
|
||||||
|
sym_table: SymTable,
|
||||||
|
envs: Vec<Env>,
|
||||||
|
thunks: Vec<Ir>,
|
||||||
|
consts: Vec<ByteCodeConst>,
|
||||||
|
consts_table: HashMap<ByteCodeConst, ConstIdx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Downgraded {
|
||||||
|
pub top_level: Ir,
|
||||||
|
pub consts: Consts,
|
||||||
|
pub thunks: Slice<Ir>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DowngradeState {
|
||||||
|
fn new() -> DowngradeState {
|
||||||
|
DowngradeState {
|
||||||
|
sym_table: SymTable::new(),
|
||||||
|
envs: Vec::new(),
|
||||||
|
thunks: Vec::new(),
|
||||||
|
consts: Vec::new(),
|
||||||
|
consts_table: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_thunk(&mut self, thunk: Ir) -> Thunk {
|
||||||
|
let idx = self.thunks.len();
|
||||||
|
self.thunks.push(thunk);
|
||||||
|
Thunk { idx }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_thunk(&self, idx: ThunkIdx) -> &Ir {
|
||||||
|
self.thunks.get(idx).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Attrs {
|
||||||
|
fn _insert(&mut self, mut path: std::vec::IntoIter<Attr>, name: Attr, value: Ir) -> Result<()> {
|
||||||
|
if let Some(attr) = path.next() {
|
||||||
|
match attr {
|
||||||
|
Attr::Str(ident) => {
|
||||||
|
if self.stcs.get(&ident).is_some() {
|
||||||
|
self.stcs
|
||||||
|
.get_mut(&ident)
|
||||||
|
.unwrap()
|
||||||
|
.downcast_mut()
|
||||||
|
.ok_or(anyhow!(r#""{ident}" already exsists in this set"#))
|
||||||
|
.and_then(|attrs: &mut Attrs| attrs._insert(path, name, value))
|
||||||
|
} else {
|
||||||
|
let mut attrs = Attrs {
|
||||||
|
stcs: HashMap::new(),
|
||||||
|
dyns: Vec::new(),
|
||||||
|
};
|
||||||
|
attrs._insert(path, name, value)?;
|
||||||
|
assert!(self.stcs.insert(ident, attrs.ir()).is_none());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Attr::Strs(string) => {
|
||||||
|
let mut attrs = Attrs {
|
||||||
|
stcs: HashMap::new(),
|
||||||
|
dyns: Vec::new(),
|
||||||
|
};
|
||||||
|
attrs._insert(path, name, value)?;
|
||||||
|
self.dyns.push(DynamicAttrPair(string.ir(), attrs.ir()));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Attr::Dynamic(dynamic) => {
|
||||||
|
let mut attrs = Attrs {
|
||||||
|
stcs: HashMap::new(),
|
||||||
|
dyns: Vec::new(),
|
||||||
|
};
|
||||||
|
attrs._insert(path, name, value)?;
|
||||||
|
self.dyns.push(DynamicAttrPair(dynamic, attrs.ir()));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match name {
|
||||||
|
Attr::Str(ident) => {
|
||||||
|
if self.stcs.get(&ident).is_some() {
|
||||||
|
return Err(anyhow!(r#""{ident}" already exsists in this set"#));
|
||||||
|
}
|
||||||
|
self.stcs.insert(ident, value);
|
||||||
|
}
|
||||||
|
Attr::Strs(string) => {
|
||||||
|
self.dyns.push(DynamicAttrPair(string.ir(), value));
|
||||||
|
}
|
||||||
|
Attr::Dynamic(dynamic) => {
|
||||||
|
self.dyns.push(DynamicAttrPair(dynamic, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, path: Vec<Attr>, value: Ir) -> Result<()> {
|
||||||
|
let mut path = path.into_iter();
|
||||||
|
let name = path.next_back().unwrap();
|
||||||
|
self._insert(path, name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _has_attr(&self, mut path: std::slice::Iter<Attr>, name: Attr) -> Option<bool> {
|
||||||
|
match path.next() {
|
||||||
|
Some(Attr::Str(ident)) => self
|
||||||
|
.stcs
|
||||||
|
.get(ident.as_str())
|
||||||
|
.and_then(|attrs| attrs.downcast_ref())
|
||||||
|
.map_or(Some(false), |attrs: &Attrs| attrs._has_attr(path, name)),
|
||||||
|
None => match name {
|
||||||
|
Attr::Str(ident) => Some(self.stcs.get(&ident).is_some()),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_attr(&self, path: &[Attr]) -> Option<bool> {
|
||||||
|
let mut path = path.iter();
|
||||||
|
let name = path.next_back().unwrap().clone();
|
||||||
|
self._has_attr(path, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Attr {
|
||||||
|
Dynamic(Ir),
|
||||||
|
Strs(ConcatStrings),
|
||||||
|
Str(EcoString),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum BinOpKind {
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Div,
|
||||||
|
Mul,
|
||||||
|
Eq,
|
||||||
|
Neq,
|
||||||
|
Lt,
|
||||||
|
Gt,
|
||||||
|
Leq,
|
||||||
|
Geq,
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
Impl,
|
||||||
|
|
||||||
|
Con,
|
||||||
|
Upd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ast::BinOpKind> for BinOpKind {
|
||||||
|
fn from(op: ast::BinOpKind) -> Self {
|
||||||
|
use ast::BinOpKind as astkind;
|
||||||
|
use BinOpKind::*;
|
||||||
|
match op {
|
||||||
|
astkind::Concat => Con,
|
||||||
|
astkind::Update => Upd,
|
||||||
|
astkind::Add => Add,
|
||||||
|
astkind::Sub => Sub,
|
||||||
|
astkind::Mul => Mul,
|
||||||
|
astkind::Div => Div,
|
||||||
|
astkind::And => And,
|
||||||
|
astkind::Equal => Eq,
|
||||||
|
astkind::Implication => Impl,
|
||||||
|
astkind::Less => Lt,
|
||||||
|
astkind::LessOrEq => Leq,
|
||||||
|
astkind::More => Gt,
|
||||||
|
astkind::MoreOrEq => Geq,
|
||||||
|
astkind::NotEqual => Neq,
|
||||||
|
astkind::Or => Or,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum UnOpKind {
|
||||||
|
Neg,
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Param {
|
||||||
|
Ident(EcoString),
|
||||||
|
Formals {
|
||||||
|
formals: Vec<(EcoString, Option<Ir>)>,
|
||||||
|
ellipsis: bool,
|
||||||
|
alias: Option<EcoString>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Downgrade
|
||||||
|
where
|
||||||
|
Self: Sized
|
||||||
|
{
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for Expr {
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
match self {
|
||||||
|
Expr::Apply(apply) => apply.downgrade(state),
|
||||||
|
Expr::Assert(assert) => assert.downgrade(state),
|
||||||
|
Expr::Error(error) => return Err(anyhow!(error.to_string())),
|
||||||
|
Expr::IfElse(ifelse) => ifelse.downgrade(state),
|
||||||
|
Expr::Select(select) => select.downgrade(state),
|
||||||
|
Expr::Str(str) => str.downgrade(state),
|
||||||
|
Expr::Path(path) => path.downgrade(state),
|
||||||
|
Expr::Literal(lit) => lit.downgrade(state),
|
||||||
|
Expr::Lambda(lambda) => lambda.downgrade(state),
|
||||||
|
Expr::LegacyLet(let_) => let_.downgrade(state),
|
||||||
|
Expr::LetIn(letin) => letin.downgrade(state),
|
||||||
|
Expr::List(list) => list.downgrade(state),
|
||||||
|
Expr::BinOp(op) => op.downgrade(state),
|
||||||
|
Expr::Paren(paren) => paren.expr().unwrap().downgrade(state),
|
||||||
|
Expr::Root(root) => root.expr().unwrap().downgrade(state),
|
||||||
|
Expr::AttrSet(attrs) => attrs.downgrade(state),
|
||||||
|
Expr::UnaryOp(op) => op.downgrade(state),
|
||||||
|
Expr::Ident(ident) => ident.downgrade(state),
|
||||||
|
Expr::With(with) => with.downgrade(state),
|
||||||
|
Expr::HasAttr(has) => has.downgrade(state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for ast::Assert {
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
Assert {
|
||||||
|
assertion: self.condition().unwrap().downgrade(state)?.boxed(),
|
||||||
|
expr: self.body().unwrap().downgrade(state)?.boxed(),
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for ast::IfElse {
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
If {
|
||||||
|
cond: self.condition().unwrap().downgrade(state)?.boxed(),
|
||||||
|
consq: self.body().unwrap().downgrade(state)?.boxed(),
|
||||||
|
alter: self.else_body().unwrap().downgrade(state)?.boxed(),
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for ast::Path {
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
let parts = self
|
||||||
|
.parts()
|
||||||
|
.into_iter()
|
||||||
|
.map(|part| match part {
|
||||||
|
ast::InterpolPart::Literal(lit) => Const { value: lit.to_string().into() }
|
||||||
|
.ir()
|
||||||
|
.ok(),
|
||||||
|
ast::InterpolPart::Interpolation(interpol) => {
|
||||||
|
interpol.expr().unwrap().downgrade(state)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
if parts.len() == 1 {
|
||||||
|
Path {
|
||||||
|
expr: parts.into_iter().next().unwrap().boxed(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Path {
|
||||||
|
expr: ConcatStrings { parts }.ir().boxed(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for ast::Str {
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
let parts = self
|
||||||
|
.normalized_parts()
|
||||||
|
.into_iter()
|
||||||
|
.map(|part| match part {
|
||||||
|
ast::InterpolPart::Literal(lit) => {
|
||||||
|
Const { value: lit.into() }.ir().ok()
|
||||||
|
}
|
||||||
|
ast::InterpolPart::Interpolation(interpol) => {
|
||||||
|
interpol.expr().unwrap().downgrade(state)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
if parts.len() == 1 {
|
||||||
|
Ok(parts.into_iter().next().unwrap())
|
||||||
|
} else {
|
||||||
|
ConcatStrings { parts }.ir().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for ast::Literal {
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
match self.kind() {
|
||||||
|
ast::LiteralKind::Integer(int) => Const { value: int.value().unwrap().into() },
|
||||||
|
ast::LiteralKind::Float(float) => Const { value: float.value().unwrap().into() },
|
||||||
|
ast::LiteralKind::Uri(uri) => Const { value: uri.to_string().into() },
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for ast::Ident {
|
||||||
|
fn downgrade(self, _state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
Var {
|
||||||
|
sym: self.to_string().into()
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for ast::AttrSet {
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
let rec = self.rec_token().is_some();
|
||||||
|
downgrade_has_entry(self, rec, state).map(|attrs| attrs.ir())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for ast::List {
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
let mut items = Vec::with_capacity(self.items().size_hint().0);
|
||||||
|
for item in self.items() {
|
||||||
|
items.push(item.downgrade(state)?)
|
||||||
|
}
|
||||||
|
List { items }.ir().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for ast::BinOp {
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
BinOp {
|
||||||
|
lhs: self.lhs().unwrap().downgrade(state)?.boxed(),
|
||||||
|
rhs: self.rhs().unwrap().downgrade(state)?.boxed(),
|
||||||
|
kind: self.operator().unwrap().into(),
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for ast::HasAttr {
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
let attrs = self.expr().unwrap().downgrade(state)?;
|
||||||
|
let path = downgrade_attrpath(self.attrpath().unwrap(), state)?;
|
||||||
|
if let Some(attrs) = Downcast::<Attrs>::downcast_ref(&attrs) {
|
||||||
|
if let Some(res) = attrs.has_attr(&path) {
|
||||||
|
return Const { value: res.into() }.ir().ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HasAttr {
|
||||||
|
lhs: attrs.boxed(),
|
||||||
|
rhs: path,
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for ast::UnaryOp {
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
UnOp {
|
||||||
|
rhs: self.expr().unwrap().downgrade(state)?.boxed(),
|
||||||
|
kind: self.operator().unwrap().into(),
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for ast::Select {
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
Select {
|
||||||
|
expr: self.expr().unwrap().downgrade(state)?.boxed(),
|
||||||
|
attrpath: downgrade_attrpath(self.attrpath().unwrap(), state)?,
|
||||||
|
default: match self.default_expr() {
|
||||||
|
Some(default) => Some(default.downgrade(state)?.boxed()),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for ast::LegacyLet {
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
let attrs = downgrade_has_entry(self, true, state)?;
|
||||||
|
Select {
|
||||||
|
expr: attrs.ir().boxed(),
|
||||||
|
attrpath: vec![Attr::Str("body".to_string().into())],
|
||||||
|
default: None,
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for ast::LetIn {
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
let body = self.body().unwrap();
|
||||||
|
let attrs = downgrade_has_entry(self, true, state)?;
|
||||||
|
let expr = body.downgrade(state)?.boxed();
|
||||||
|
Let {
|
||||||
|
attrs,
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
.ir()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for ast::With {
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
let namespace = self.namespace().unwrap().downgrade(state)?;
|
||||||
|
if let Ir::Attrs(attrs) = namespace {
|
||||||
|
let expr = self.body().unwrap().downgrade(state)?.boxed();
|
||||||
|
Let { attrs, expr }.ir().ok()
|
||||||
|
} else {
|
||||||
|
let namespace = namespace.boxed();
|
||||||
|
let expr = self.body().unwrap().downgrade(state)?.boxed();
|
||||||
|
With { namespace, expr }.ir().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for ast::Lambda {
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
let mut body = self.body().unwrap();
|
||||||
|
let mut args = vec![downgrade_param(self.param().unwrap(), state)?];
|
||||||
|
while let ast::Expr::Lambda(func) = body {
|
||||||
|
body = func.body().unwrap();
|
||||||
|
args.push(downgrade_param(func.param().unwrap(), state)?);
|
||||||
|
}
|
||||||
|
let body = body.downgrade(state)?.boxed();
|
||||||
|
Func { args, body }.ir().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgrade for ast::Apply {
|
||||||
|
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||||
|
let mut args = vec![self.argument().unwrap().downgrade(state)?];
|
||||||
|
let mut func = self.lambda().unwrap();
|
||||||
|
while let ast::Expr::Apply(call) = func {
|
||||||
|
func = call.lambda().unwrap();
|
||||||
|
args.push(call.argument().unwrap().downgrade(state)?);
|
||||||
|
}
|
||||||
|
let func = func.downgrade(state)?.boxed();
|
||||||
|
args.reverse();
|
||||||
|
Call { func, args }.ir().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn downgrade_param(param: ast::Param, state: &mut DowngradeState) -> Result<Param> {
|
||||||
|
match param {
|
||||||
|
ast::Param::IdentParam(ident) => {
|
||||||
|
Ok(Param::Ident(ident.to_string().into()))
|
||||||
|
}
|
||||||
|
ast::Param::Pattern(pattern) => downgrade_pattern(pattern, state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn downgrade_pattern(pattern: ast::Pattern, state: &mut DowngradeState) -> Result<Param> {
|
||||||
|
let formals = pattern
|
||||||
|
.pat_entries()
|
||||||
|
.map(|entry| {
|
||||||
|
let ident = entry.ident().unwrap().to_string().into();
|
||||||
|
if entry.default().is_none() {
|
||||||
|
Ok((ident, None))
|
||||||
|
} else {
|
||||||
|
entry
|
||||||
|
.default()
|
||||||
|
.unwrap()
|
||||||
|
.downgrade(state)
|
||||||
|
.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().into());
|
||||||
|
Ok(Param::Formals {
|
||||||
|
formals,
|
||||||
|
ellipsis,
|
||||||
|
alias,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn downgrade_has_entry(
|
||||||
|
has_entry: impl ast::HasEntry,
|
||||||
|
rec: bool,
|
||||||
|
state: &mut DowngradeState,
|
||||||
|
) -> 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, state)?,
|
||||||
|
ast::Entry::AttrpathValue(value) => {
|
||||||
|
downgrade_attrpathvalue(value, &mut attrs, state)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(attrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn downgrade_inherit(
|
||||||
|
inherit: ast::Inherit,
|
||||||
|
stcs: &mut HashMap<EcoString, Ir>,
|
||||||
|
state: &mut DowngradeState,
|
||||||
|
) -> Result<()> {
|
||||||
|
let from = if let Some(from) = inherit.from() {
|
||||||
|
let from = from.expr().unwrap().downgrade(state)?;
|
||||||
|
Some(state.new_thunk(from))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
for attr in inherit.attrs() {
|
||||||
|
let ident: EcoString = match downgrade_attr(attr, state)? {
|
||||||
|
Attr::Str(ident) => ident.to_string().into(),
|
||||||
|
_ => return Err(anyhow!("dynamic attributes not allowed in inherit")),
|
||||||
|
};
|
||||||
|
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())
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
stcs.insert(ident, expr).unwrap();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn downgrade_attr(attr: ast::Attr, state: &mut DowngradeState) -> Result<Attr> {
|
||||||
|
match attr {
|
||||||
|
ast::Attr::Ident(ident) => Ok(Attr::Str(ident.to_string().into())),
|
||||||
|
ast::Attr::Str(string) => {
|
||||||
|
let parts = string.normalized_parts();
|
||||||
|
if parts.len() == 1 {
|
||||||
|
let ast::InterpolPart::Literal(ident) = parts.into_iter().next().unwrap() else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
Ok(Attr::Str(ident.into()))
|
||||||
|
} else {
|
||||||
|
let parts = parts
|
||||||
|
.into_iter()
|
||||||
|
.map(|part| match part {
|
||||||
|
ast::InterpolPart::Literal(lit) => {
|
||||||
|
Const { value: lit.into() }.ir().ok()
|
||||||
|
}
|
||||||
|
ast::InterpolPart::Interpolation(interpol) => {
|
||||||
|
interpol.expr().unwrap().downgrade(state)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
Ok(Attr::Strs(ConcatStrings { parts }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast::Attr::Dynamic(dynamic) => Ok(Attr::Dynamic(dynamic.expr().unwrap().downgrade(state)?)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn downgrade_attrpath(attrpath: ast::Attrpath, state: &mut DowngradeState) -> Result<Vec<Attr>> {
|
||||||
|
attrpath
|
||||||
|
.attrs()
|
||||||
|
.map(|attr| downgrade_attr(attr, state))
|
||||||
|
.collect::<Result<Vec<_>>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn downgrade_attrpathvalue(
|
||||||
|
value: ast::AttrpathValue,
|
||||||
|
attrs: &mut Attrs,
|
||||||
|
state: &mut DowngradeState,
|
||||||
|
) -> Result<()> {
|
||||||
|
let path = downgrade_attrpath(value.attrpath().unwrap(), state)?;
|
||||||
|
let value = value.value().unwrap().downgrade(state)?;
|
||||||
|
attrs.insert(path, value)
|
||||||
|
}
|
||||||
10
src/compile/mod.rs
Normal file
10
src/compile/mod.rs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
mod compile;
|
||||||
|
mod env;
|
||||||
|
mod ir;
|
||||||
|
mod symtable;
|
||||||
|
|
||||||
|
pub fn compile(expr: &str) -> anyhow::Result<crate::bytecode::Program> {
|
||||||
|
let expr = rnix::Root::parse(expr).tree().expr().unwrap();
|
||||||
|
let ir = ir::downgrade(expr)?;
|
||||||
|
Ok(compile::compile(ir))
|
||||||
|
}
|
||||||
35
src/compile/symtable.rs
Normal file
35
src/compile/symtable.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::bytecode::SymIdx;
|
||||||
|
use crate::slice::Slice;
|
||||||
|
|
||||||
|
// FIXME: don't store syms twice to make it more memory efficient?
|
||||||
|
|
||||||
|
pub struct SymTable {
|
||||||
|
syms: Vec<String>,
|
||||||
|
syms_table: HashMap<String, SymIdx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymTable {
|
||||||
|
pub fn new() -> SymTable {
|
||||||
|
SymTable {
|
||||||
|
syms: Vec::new(),
|
||||||
|
syms_table: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup(&mut self, name: String) -> SymIdx {
|
||||||
|
if let Some(sym) = self.syms_table.get(&name) {
|
||||||
|
*sym
|
||||||
|
} else {
|
||||||
|
let sym = self.syms.len();
|
||||||
|
self.syms.push(name.clone());
|
||||||
|
self.syms_table.insert(name, sym);
|
||||||
|
sym
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn syms(self) -> Slice<String> {
|
||||||
|
self.syms.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/downcast.rs
Normal file
8
src/downcast.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
pub trait Downcast<T: Sized>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
fn downcast_ref(&self) -> Option<&T>;
|
||||||
|
fn downcast_mut(&mut self) -> Option<&mut T>;
|
||||||
|
fn downcast(self) -> Result<T, Self>;
|
||||||
|
}
|
||||||
9
src/lib.rs
Normal file
9
src/lib.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
mod builtins;
|
||||||
|
mod bytecode;
|
||||||
|
mod compile;
|
||||||
|
mod downcast;
|
||||||
|
mod slice;
|
||||||
|
mod value;
|
||||||
|
mod vm;
|
||||||
2
src/slice.rs
Normal file
2
src/slice.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub type Slice<T> = Box<[T]>;
|
||||||
|
|
||||||
108
src/value/mod.rs
Normal file
108
src/value/mod.rs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use derive_more::{Constructor, IsVariant, Unwrap};
|
||||||
|
use ecow::EcoString;
|
||||||
|
use rpds::{HashTrieMapSync, VectorSync};
|
||||||
|
|
||||||
|
use crate::bytecode::{Args, Const, OpCodes};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash, PartialEq, Eq, Constructor)]
|
||||||
|
pub struct Symbol(EcoString);
|
||||||
|
|
||||||
|
impl<T: Into<EcoString>> From<T> for Symbol {
|
||||||
|
fn from(value: T) -> Self {
|
||||||
|
Symbol(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Symbol {
|
||||||
|
type Target = str;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash)]
|
||||||
|
pub struct Func {
|
||||||
|
pub args: Args,
|
||||||
|
pub opcodes: OpCodes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Func {
|
||||||
|
fn eq(&self, _: &Self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #[derive(IsVariant, Unwrap, Clone, Debug, PartialEq)]
|
||||||
|
pub enum Const {
|
||||||
|
Int(i64),
|
||||||
|
Float(f64),
|
||||||
|
Bool(bool),
|
||||||
|
String(EcoString),
|
||||||
|
Func(Arc<Func>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Const {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
|
write!(f, "Const::")?;
|
||||||
|
match self {
|
||||||
|
Const::Int(int) => write!(f, "Int@{}", int),
|
||||||
|
Const::Float(float) => write!(f, "Float@{}", float),
|
||||||
|
Const::Bool(bool) => write!(f, "Bool@{}", bool),
|
||||||
|
Const::String(string) => write!(f, r#"String@"{}""#, string.as_ref()),
|
||||||
|
Const::Func(func) => write!(f, "Func@{:?}", func.as_ref() as *const Func),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ByteCodeConst> for Const {
|
||||||
|
fn from(value: ByteCodeConst) -> Self {
|
||||||
|
use ByteCodeConst::*;
|
||||||
|
match value {
|
||||||
|
Int(int) => Const::Int(int),
|
||||||
|
Float(float) => Const::Float(float),
|
||||||
|
Bool(bool) => Const::Bool(bool),
|
||||||
|
String(string) => Const::String(EcoString::from(string)),
|
||||||
|
Func(func) => Const::Func(Arc::new(func)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} */
|
||||||
|
|
||||||
|
#[derive(Constructor, Clone, PartialEq)]
|
||||||
|
pub struct AttrSet {
|
||||||
|
data: HashTrieMapSync<Symbol, Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for AttrSet {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
|
write!(f, "{{ ")?;
|
||||||
|
for (k, v) in self.data.iter() {
|
||||||
|
write!(f, "{k:?} = {v:?}; ")?;
|
||||||
|
}
|
||||||
|
write!(f, "}}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Constructor, Clone, Debug, PartialEq)]
|
||||||
|
pub struct List {
|
||||||
|
data: VectorSync<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Constructor)]
|
||||||
|
pub struct Catchable {
|
||||||
|
msg: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(IsVariant, Unwrap, Clone, Debug, PartialEq)]
|
||||||
|
pub enum Value {
|
||||||
|
Const(Const),
|
||||||
|
AttrSet(AttrSet),
|
||||||
|
List(List),
|
||||||
|
Catchable(Catchable),
|
||||||
|
Thunk,
|
||||||
|
PrimOp,
|
||||||
|
PartialPrimOp,
|
||||||
|
}
|
||||||
47
src/vm/env.rs
Normal file
47
src/vm/env.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
use rpds::HashTrieMapSync;
|
||||||
|
|
||||||
|
use super::value::{Symbol, VmValue};
|
||||||
|
|
||||||
|
pub struct Env {
|
||||||
|
last: Option<*mut Env>,
|
||||||
|
map: HashTrieMapSync<Symbol, VmValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Env {
|
||||||
|
pub fn empty() -> Env {
|
||||||
|
Env {
|
||||||
|
last: None,
|
||||||
|
map: HashTrieMapSync::new_sync(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup(&self, symbol: Symbol) -> VmValue {
|
||||||
|
if let Some(value) = self.map.get(&symbol) {
|
||||||
|
value.clone()
|
||||||
|
} else {
|
||||||
|
let last = unsafe { &*self.last.unwrap() };
|
||||||
|
last.lookup(symbol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, symbol: Symbol, value: VmValue) {
|
||||||
|
self.map.insert_mut(symbol, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enter(&mut self, map: HashTrieMapSync<Symbol, VmValue>) {
|
||||||
|
let last = std::mem::replace(
|
||||||
|
self,
|
||||||
|
Env {
|
||||||
|
last: None,
|
||||||
|
map,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.last = Some(Box::leak(Box::new(last)) as *mut Env);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn leave(&mut self) {
|
||||||
|
let last = unsafe { &*self.last.unwrap() };
|
||||||
|
self.last = last.last;
|
||||||
|
self.map = last.map.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/vm/mod.rs
Normal file
12
src/vm/mod.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
mod env;
|
||||||
|
mod stack;
|
||||||
|
mod value;
|
||||||
|
mod vm;
|
||||||
|
mod vmthunk;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test;
|
||||||
|
|
||||||
|
pub use env::Env;
|
||||||
|
pub use value::Symbol;
|
||||||
|
pub use value::VmValue as Value;
|
||||||
93
src/vm/stack.rs
Normal file
93
src/vm/stack.rs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
use std::mem::{size_of, transmute, MaybeUninit};
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
|
use super::value::VmValue;
|
||||||
|
|
||||||
|
pub const STACK_SIZE: usize = 8 * 1024 / size_of::<VmValue>();
|
||||||
|
|
||||||
|
pub struct Stack<const CAP: usize> {
|
||||||
|
items: Box<[MaybeUninit<VmValue>; CAP]>,
|
||||||
|
top: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const CAP: usize> Stack<CAP> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Stack {
|
||||||
|
items: (0..CAP)
|
||||||
|
.map(|_| MaybeUninit::uninit())
|
||||||
|
.collect::<Box<_>>()
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
top: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, item: VmValue) -> Result<()> {
|
||||||
|
self.items
|
||||||
|
.get_mut(self.top)
|
||||||
|
.map_or(Err(anyhow!("stack overflow")), |ok| Ok(ok))?
|
||||||
|
.write(item);
|
||||||
|
self.top += 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) -> Result<VmValue> {
|
||||||
|
self.top -= 1;
|
||||||
|
let item = self
|
||||||
|
.items
|
||||||
|
.get_mut(self.top)
|
||||||
|
.map_or(Err(anyhow!("stack empty")), |ok| Ok(ok))?;
|
||||||
|
unsafe { Ok(std::mem::replace(item, MaybeUninit::uninit()).assume_init()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tos(&self) -> Result<&VmValue> {
|
||||||
|
if self.top == 0 {
|
||||||
|
Err(anyhow!(""))
|
||||||
|
} else {
|
||||||
|
unsafe { Ok(transmute(self.items.get(self.top - 1).unwrap())) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tos_mut(&mut self) -> Result<&mut VmValue> {
|
||||||
|
if self.top == 0 {
|
||||||
|
Err(anyhow!(""))
|
||||||
|
} else {
|
||||||
|
unsafe { Ok(transmute(self.items.get_mut(self.top - 1).unwrap())) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_tos(&self, func: impl Fn(&VmValue)) -> Result<()> {
|
||||||
|
if self.top != 0 {
|
||||||
|
Err(anyhow!(""))
|
||||||
|
} else {
|
||||||
|
unsafe { func(transmute(self.items.get(self.top - 1).unwrap())) }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn with_tos_mut(&mut self, func: impl Fn(&mut VmValue)) -> Result<()> {
|
||||||
|
if self.top != 0 {
|
||||||
|
Err(anyhow!(""))
|
||||||
|
} else {
|
||||||
|
unsafe { func(transmute(self.items.get_mut(self.top - 1).unwrap())) }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const CAP: usize> Deref for Stack<CAP> {
|
||||||
|
type Target = [VmValue];
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
unsafe { transmute(&self.items[0..self.top]) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const CAP: usize> Drop for Stack<CAP> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.items.as_mut_slice()[0..self.top]
|
||||||
|
.iter_mut()
|
||||||
|
.map(|item| unsafe { item.assume_init_drop() })
|
||||||
|
.for_each(drop)
|
||||||
|
}
|
||||||
|
}
|
||||||
169
src/vm/test.rs
Normal file
169
src/vm/test.rs
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
use ecow::EcoString;
|
||||||
|
use rpds::{ht_map_sync, vector_sync};
|
||||||
|
|
||||||
|
use crate::compile::compile;
|
||||||
|
use crate::value::*;
|
||||||
|
use crate::bytecode::Const;
|
||||||
|
|
||||||
|
use super::vm::run;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn test_expr(expr: &str, expected: Value) {
|
||||||
|
let prog = compile(expr).unwrap();
|
||||||
|
dbg!(&prog);
|
||||||
|
assert_eq!(run(prog).unwrap(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! int {
|
||||||
|
($e:expr) => {
|
||||||
|
Value::Const(Const::Int($e))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! float {
|
||||||
|
($e:expr) => {
|
||||||
|
Value::Const(Const::Float($e as f64))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! boolean {
|
||||||
|
($e:expr) => {
|
||||||
|
Value::Const(Const::Bool($e))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! string {
|
||||||
|
($e:expr) => {
|
||||||
|
Value::Const(Const::String(EcoString::from($e)))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! symbol {
|
||||||
|
($e:expr) => {
|
||||||
|
Symbol::from($e.to_string())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! list {
|
||||||
|
($($x:tt)*) => (
|
||||||
|
Value::List(List::new(vector_sync![$($x)*]))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! attrs {
|
||||||
|
($($x:tt)*) => (
|
||||||
|
Value::AttrSet(AttrSet::new(ht_map_sync!{$($x)*}))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_arith() {
|
||||||
|
test_expr("1", int!(1));
|
||||||
|
test_expr("1.", float!(1));
|
||||||
|
test_expr("-1", int!(-1));
|
||||||
|
test_expr("-1.", float!(-1));
|
||||||
|
test_expr("1 + 1", int!(2));
|
||||||
|
test_expr("1 + 1.", float!(2));
|
||||||
|
test_expr("1. + 1", float!(2));
|
||||||
|
test_expr("1. + 1.", float!(2));
|
||||||
|
test_expr("1 - 1", int!(0));
|
||||||
|
test_expr("1 - 1.", float!(0));
|
||||||
|
test_expr("1. - 1", float!(0));
|
||||||
|
test_expr("1. - 1.", float!(0));
|
||||||
|
test_expr("1 * 1", int!(1));
|
||||||
|
test_expr("1 * 1.", float!(1));
|
||||||
|
test_expr("1. * 1", float!(1));
|
||||||
|
test_expr("1. * 1.", float!(1));
|
||||||
|
test_expr("1 / 1", int!(1));
|
||||||
|
test_expr("1 / 1.", float!(1));
|
||||||
|
test_expr("1. / 1", float!(1));
|
||||||
|
test_expr("1. / 1.", float!(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cmp() {
|
||||||
|
test_expr("1 < 2", boolean!(true));
|
||||||
|
test_expr("1 < 1", boolean!(false));
|
||||||
|
test_expr("1 > 0", boolean!(true));
|
||||||
|
test_expr("1 > 1", boolean!(false));
|
||||||
|
test_expr("1 <= 1", boolean!(true));
|
||||||
|
test_expr("1 <= 0", boolean!(false));
|
||||||
|
test_expr("1 >= 1", boolean!(true));
|
||||||
|
test_expr("1 >= 2", boolean!(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_string() {
|
||||||
|
test_expr(r#""test""#, string!("test"));
|
||||||
|
test_expr(r#""hello" + " world""#, string!("hello world"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bool() {
|
||||||
|
test_expr("true", boolean!(true));
|
||||||
|
test_expr("false", boolean!(false));
|
||||||
|
test_expr("!false", boolean!(true));
|
||||||
|
test_expr("true && false", boolean!(false));
|
||||||
|
test_expr("true || false", boolean!(true));
|
||||||
|
test_expr("true -> false", boolean!(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_list() {
|
||||||
|
test_expr(
|
||||||
|
"[ 1 2 3 true ]",
|
||||||
|
list![int!(1), int!(2), int!(3), boolean!(true)],
|
||||||
|
);
|
||||||
|
test_expr(
|
||||||
|
"[ 1 2 ] ++ [ 3 4 ]",
|
||||||
|
list![int!(1), int!(2), int!(3), int!(4)],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_attrs() {
|
||||||
|
test_expr(
|
||||||
|
"{ a = 1; }",
|
||||||
|
attrs! {
|
||||||
|
symbol!("a") => int!(1)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
test_expr("{ a = 1; }.a", int!(1));
|
||||||
|
test_expr("{ a = 1; }.b or 1", int!(1));
|
||||||
|
test_expr(
|
||||||
|
"{ a = { a = 1; }; }.a",
|
||||||
|
attrs! {
|
||||||
|
symbol!("a") => int!(1)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
test_expr("{ a.b = 1; }.a.b", int!(1));
|
||||||
|
test_expr(
|
||||||
|
"{ a.b = 1; a.c = 2; }",
|
||||||
|
attrs! { symbol!("a") => attrs!{ symbol!("b") => int!(1), symbol!("c") => int!(2) } },
|
||||||
|
);
|
||||||
|
test_expr("{ a.b = 1; } ? a.b", boolean!(true));
|
||||||
|
test_expr(
|
||||||
|
"{ a.b = 1; } // { a.c = 2 }",
|
||||||
|
attrs! { symbol!("a") => attrs!{ symbol!("b") => int!(1), symbol!("c") => int!(2) } },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if() {
|
||||||
|
test_expr("if true || false then 1 else 2", int!(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_with() {
|
||||||
|
test_expr(r#"with { a = 1; }; a"#, int!(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_let() {
|
||||||
|
test_expr(r#"let a = 1; in a"#, int!(1));
|
||||||
|
test_expr(r#"let a = { a = 1; }; b = "a"; in a.${b}"#, int!(1));
|
||||||
|
test_expr(
|
||||||
|
r#"let b = "c"; in { a.b = 1; } // { a."a${b}" = 2 }"}"#,
|
||||||
|
attrs! { symbol!("a") => attrs!{ symbol!("b") => int!(1), symbol!("ac") => int!(2) } },
|
||||||
|
);
|
||||||
|
}
|
||||||
53
src/vm/value/attrset.rs
Normal file
53
src/vm/value/attrset.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
use derive_more::Constructor;
|
||||||
|
use rpds::HashTrieMapSync;
|
||||||
|
|
||||||
|
use crate::value::{self, Value};
|
||||||
|
|
||||||
|
use super::super::vm::VM;
|
||||||
|
use super::{Symbol, ToValue, VmValue};
|
||||||
|
|
||||||
|
#[derive(Debug, Constructor, Clone, PartialEq)]
|
||||||
|
pub struct AttrSet {
|
||||||
|
data: HashTrieMapSync<Symbol, VmValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttrSet {
|
||||||
|
pub fn push_attr(&mut self, sym: Symbol, val: VmValue) {
|
||||||
|
self.data.insert_mut(sym, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select(&self, sym: Symbol) -> Option<VmValue> {
|
||||||
|
self.data.get(&sym).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_attr(&self, sym: Symbol) -> bool {
|
||||||
|
self.data.get(&sym).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(mut self, other: AttrSet) -> AttrSet {
|
||||||
|
for (k, v) in other.data.iter() {
|
||||||
|
if let Some(attr) = self.data.get(k) {
|
||||||
|
let new_attr = attr.clone().update(v.clone());
|
||||||
|
self.data.insert_mut(k.clone(), new_attr);
|
||||||
|
} else {
|
||||||
|
self.push_attr(k.clone(), v.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_data(self) -> HashTrieMapSync<Symbol, VmValue> {
|
||||||
|
self.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToValue for AttrSet {
|
||||||
|
fn to_value(self, vm: &VM) -> Value {
|
||||||
|
Value::AttrSet(value::AttrSet::new(
|
||||||
|
self.data
|
||||||
|
.iter()
|
||||||
|
.map(|(sym, value)| (value::Symbol::new(sym.0.clone()), value.clone().to_value(vm)))
|
||||||
|
.collect(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/vm/value/list.rs
Normal file
36
src/vm/value/list.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
use derive_more::Constructor;
|
||||||
|
use rpds::VectorSync;
|
||||||
|
|
||||||
|
use crate::value::{self, Value};
|
||||||
|
|
||||||
|
use super::super::vm::VM;
|
||||||
|
use super::{ToValue, VmValue};
|
||||||
|
|
||||||
|
#[derive(Debug, Constructor, Clone, PartialEq)]
|
||||||
|
pub struct List {
|
||||||
|
data: VectorSync<VmValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl List {
|
||||||
|
pub fn push(&mut self, elem: VmValue) {
|
||||||
|
self.data.push_back_mut(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn concat(mut self, other: List) -> List {
|
||||||
|
for elem in other.data.iter() {
|
||||||
|
self.data.push_back_mut(elem.clone());
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToValue for List {
|
||||||
|
fn to_value(self, vm: &VM) -> Value {
|
||||||
|
Value::List(value::List::new(
|
||||||
|
self.data
|
||||||
|
.iter()
|
||||||
|
.map(|value| value.clone().to_value(vm))
|
||||||
|
.collect(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
251
src/vm/value/mod.rs
Normal file
251
src/vm/value/mod.rs
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
use derive_more::{Constructor, IsVariant, Unwrap};
|
||||||
|
use anyhow::Result;
|
||||||
|
use ecow::EcoString;
|
||||||
|
|
||||||
|
use crate::value::*;
|
||||||
|
use crate::bytecode::Const;
|
||||||
|
|
||||||
|
use super::vm::VM;
|
||||||
|
use super::env::Env;
|
||||||
|
|
||||||
|
mod attrset;
|
||||||
|
mod list;
|
||||||
|
mod string;
|
||||||
|
|
||||||
|
pub use attrset::AttrSet;
|
||||||
|
pub use list::List;
|
||||||
|
pub use string::ContextfulString;
|
||||||
|
|
||||||
|
pub trait ToValue {
|
||||||
|
fn to_value(self, vm: &VM) -> Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Constructor)]
|
||||||
|
pub struct Symbol(EcoString);
|
||||||
|
|
||||||
|
impl<T: Into<EcoString>> From<T> for Symbol {
|
||||||
|
fn from(value: T) -> Self {
|
||||||
|
Symbol(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Constructor)]
|
||||||
|
pub struct Thunk(usize);
|
||||||
|
|
||||||
|
#[derive(Debug, IsVariant, Unwrap, Clone, PartialEq)]
|
||||||
|
pub enum VmValue {
|
||||||
|
Const(Const),
|
||||||
|
Thunk(Thunk),
|
||||||
|
AttrSet(AttrSet),
|
||||||
|
List(List),
|
||||||
|
Catchable(crate::value::Catchable),
|
||||||
|
PrimOp(crate::builtins::PrimOp),
|
||||||
|
PartialPrimOp(crate::builtins::PartialPrimOp)
|
||||||
|
}
|
||||||
|
|
||||||
|
use VmValue::Const as VmConst;
|
||||||
|
impl VmValue {
|
||||||
|
pub fn call(self, args: Vec<VmValue>) -> VmValue {
|
||||||
|
match self {
|
||||||
|
VmValue::PrimOp(func) => func.call(args),
|
||||||
|
VmValue::PartialPrimOp(func) => func.call(args),
|
||||||
|
_ => todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn not(self) -> VmValue {
|
||||||
|
use Const::*;
|
||||||
|
match self {
|
||||||
|
VmConst(Bool(bool)) => VmConst(Bool(!bool)),
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn and(self, other: VmValue) -> VmValue {
|
||||||
|
use Const::*;
|
||||||
|
match (self, other) {
|
||||||
|
(VmConst(Bool(a)), VmConst(Bool(b))) => VmConst(Bool(a && b)),
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn or(self, other: VmValue) -> VmValue {
|
||||||
|
use Const::*;
|
||||||
|
match (self, other) {
|
||||||
|
(VmConst(Bool(a)), VmConst(Bool(b))) => VmConst(Bool(a || b)),
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eq(self, other: VmValue) -> VmValue {
|
||||||
|
use Const::Bool;
|
||||||
|
VmConst(Bool(self == other))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lt(self, other: VmValue) -> VmValue {
|
||||||
|
use Const::*;
|
||||||
|
VmConst(Bool(match (self, other) {
|
||||||
|
(VmConst(Int(a)), VmConst(Int(b))) => a < b,
|
||||||
|
(VmConst(Int(a)), VmConst(Float(b))) => (a as f64) < b,
|
||||||
|
(VmConst(Float(a)), VmConst(Int(b))) => a < b as f64,
|
||||||
|
(VmConst(Float(a)), VmConst(Float(b))) => a < b,
|
||||||
|
(VmConst(String(a)), VmConst(String(b))) => a < b,
|
||||||
|
_ => todo!()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn neg(self) -> VmValue {
|
||||||
|
use Const::*;
|
||||||
|
match self {
|
||||||
|
VmConst(Int(int)) => VmConst(Int(-int)),
|
||||||
|
VmConst(Float(float)) => VmConst(Float(-float)),
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(self, other: VmValue) -> VmValue {
|
||||||
|
use Const::*;
|
||||||
|
match (self, other) {
|
||||||
|
(VmConst(Int(a)), VmConst(Int(b))) => VmConst(Int(a + b)),
|
||||||
|
(VmConst(Int(a)), VmConst(Float(b))) => VmConst(Float(a as f64 + b)),
|
||||||
|
(VmConst(Float(a)), VmConst(Int(b))) => VmConst(Float(a + b as f64)),
|
||||||
|
(VmConst(Float(a)), VmConst(Float(b))) => VmConst(Float(a + b)),
|
||||||
|
(VmConst(String(a)), VmConst(String(b))) => {
|
||||||
|
let mut string = a.clone();
|
||||||
|
string.push_str(b.as_str());
|
||||||
|
VmConst(String(string))
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mul(self, other: VmValue) -> VmValue {
|
||||||
|
use Const::*;
|
||||||
|
match (self, other) {
|
||||||
|
(VmConst(Int(a)), VmConst(Int(b))) => VmConst(Int(a * b)),
|
||||||
|
(VmConst(Int(a)), VmConst(Float(b))) => VmConst(Float(a as f64 * b)),
|
||||||
|
(VmConst(Float(a)), VmConst(Int(b))) => VmConst(Float(a * b as f64)),
|
||||||
|
(VmConst(Float(a)), VmConst(Float(b))) => VmConst(Float(a * b)),
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn div(self, other: VmValue) -> VmValue {
|
||||||
|
use Const::*;
|
||||||
|
match (self, other) {
|
||||||
|
(_, VmConst(Int(0))) => todo!(),
|
||||||
|
(_, VmConst(Float(0.))) => todo!(),
|
||||||
|
(VmConst(Int(a)), VmConst(Int(b))) => VmConst(Int(a / b)),
|
||||||
|
(VmConst(Int(a)), VmConst(Float(b))) => VmConst(Float(a as f64 / b)),
|
||||||
|
(VmConst(Float(a)), VmConst(Int(b))) => VmConst(Float(a / b as f64)),
|
||||||
|
(VmConst(Float(a)), VmConst(Float(b))) => VmConst(Float(a / b)),
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn concat_string(&mut self, mut other: VmValue) -> &mut Self {
|
||||||
|
if let (VmConst(Const::String(a)), VmConst(Const::String(b))) = (self.coerce_to_string(), other.coerce_to_string()) {
|
||||||
|
a.push_str(b.as_str());
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, elem: VmValue) -> &mut Self {
|
||||||
|
if let VmValue::List(list) = self {
|
||||||
|
list.push(elem);
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn concat(self, other: VmValue) -> VmValue {
|
||||||
|
if let (VmValue::List(a), VmValue::List(b)) = (self, other) {
|
||||||
|
VmValue::List(a.concat(b))
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_attr(&mut self, sym: Symbol, val: VmValue) -> &mut Self {
|
||||||
|
if let VmValue::AttrSet(attrs) = self {
|
||||||
|
attrs.push_attr(sym, val)
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(self, other: VmValue) -> VmValue {
|
||||||
|
if let (VmValue::AttrSet(a), VmValue::AttrSet(b)) = (self, other) {
|
||||||
|
VmValue::AttrSet(a.update(b))
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select(&mut self, sym: Symbol) -> &mut Self {
|
||||||
|
if let VmValue::AttrSet(attrs) = self {
|
||||||
|
let val = attrs
|
||||||
|
.select(sym.clone())
|
||||||
|
.unwrap_or(VmValue::Catchable(Catchable::new(Some(format!("{sym:?} not found")))));
|
||||||
|
*self = val;
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_with_default(&mut self, sym: Symbol, default: VmValue) -> &mut Self {
|
||||||
|
if let VmValue::AttrSet(attrs) = self {
|
||||||
|
let val = attrs.select(sym).unwrap_or(default);
|
||||||
|
*self = val;
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_attr(&mut self, sym: Symbol) -> &mut Self {
|
||||||
|
if let VmValue::AttrSet(attrs) = self {
|
||||||
|
let val = VmConst(Const::Bool(attrs.has_attr(sym)));
|
||||||
|
*self = val;
|
||||||
|
} else {
|
||||||
|
*self = VmConst(Const::Bool(false));
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn coerce_to_string(&mut self) -> &mut Self {
|
||||||
|
if let VmConst(Const::String(_)) = self {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn force(&mut self, vm: &VM, env: &mut Env) -> Result<&mut Self> {
|
||||||
|
if let VmValue::Thunk(thunk) = self {
|
||||||
|
let value = vm.get_thunk_value(thunk.0, env)?;
|
||||||
|
*self = value
|
||||||
|
}
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToValue for VmValue {
|
||||||
|
fn to_value(self, vm: &VM) -> Value {
|
||||||
|
match self {
|
||||||
|
VmValue::AttrSet(attrs) => attrs.to_value(vm),
|
||||||
|
VmValue::List(list) => list.to_value(vm),
|
||||||
|
VmValue::Catchable(catchable) => Value::Catchable(catchable),
|
||||||
|
VmValue::Const(cnst) => Value::Const(cnst),
|
||||||
|
VmValue::Thunk(_) => Value::Thunk,
|
||||||
|
VmValue::PrimOp(_) => Value::PrimOp,
|
||||||
|
VmValue::PartialPrimOp(_) => Value::PartialPrimOp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/vm/value/string.rs
Normal file
30
src/vm/value/string.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// TODO: Contextful String
|
||||||
|
|
||||||
|
use ecow::EcoString;
|
||||||
|
use rpds::List;
|
||||||
|
|
||||||
|
pub struct StringContext {
|
||||||
|
context: List<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StringContext {
|
||||||
|
pub fn new() -> StringContext {
|
||||||
|
StringContext {
|
||||||
|
context: List::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ContextfulString {
|
||||||
|
string: EcoString,
|
||||||
|
context: StringContext,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContextfulString {
|
||||||
|
pub fn new(string: EcoString) -> ContextfulString {
|
||||||
|
ContextfulString {
|
||||||
|
string,
|
||||||
|
context: StringContext::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
188
src/vm/vm.rs
Normal file
188
src/vm/vm.rs
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use rpds::{HashTrieMap, HashTrieMapSync, Vector};
|
||||||
|
|
||||||
|
use crate::bytecode::{self, *};
|
||||||
|
use crate::slice::*;
|
||||||
|
use crate::value::{self, Value};
|
||||||
|
use crate::builtins::env;
|
||||||
|
|
||||||
|
use super::env::Env;
|
||||||
|
use super::stack::{Stack, STACK_SIZE};
|
||||||
|
use super::value::{self as vmValue, *};
|
||||||
|
use super::vmthunk::*;
|
||||||
|
|
||||||
|
pub fn run(prog: Program) -> Result<Value> {
|
||||||
|
let vm = VM::new(prog.thunks);
|
||||||
|
Ok(vm.eval(prog.top_level, &mut env())?.to_value(&vm))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VM {
|
||||||
|
thunks: Slice<VmThunk>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VM {
|
||||||
|
fn new(thunks: Thunks) -> Self {
|
||||||
|
let thunks = thunks
|
||||||
|
.into_iter()
|
||||||
|
.map(|bytecode::Thunk { opcodes }| VmThunk::new(opcodes))
|
||||||
|
.collect();
|
||||||
|
VM {
|
||||||
|
thunks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_thunk_value(&self, idx: usize, env: &mut Env) -> Result<VmValue> {
|
||||||
|
self.thunks.get(idx).unwrap().force(self, env)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval(&self, opcodes: OpCodes, env: &mut Env) -> Result<VmValue> {
|
||||||
|
let mut stack = Stack::<STACK_SIZE>::new();
|
||||||
|
let mut iter = opcodes.into_iter();
|
||||||
|
while let Some(opcode) = iter.next() {
|
||||||
|
let jmp = self.single_op(opcode, &mut stack, env)?;
|
||||||
|
for _ in 0..jmp {
|
||||||
|
iter.next().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_eq!(stack.len(), 1);
|
||||||
|
stack.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn single_op<const CAP: usize>(
|
||||||
|
&self,
|
||||||
|
opcode: OpCode,
|
||||||
|
stack: &mut Stack<CAP>,
|
||||||
|
env: &mut Env,
|
||||||
|
) -> Result<usize> {
|
||||||
|
match opcode {
|
||||||
|
OpCode::NoOp => (),
|
||||||
|
OpCode::Const { value } => stack.push(VmValue::Const(value))?,
|
||||||
|
OpCode::LoadThunk { idx } => stack.push(VmValue::Thunk(vmValue::Thunk::new(idx)))?,
|
||||||
|
OpCode::LoadValue { idx } => {
|
||||||
|
stack.push(self.get_thunk_value(idx, env)?)?;
|
||||||
|
}
|
||||||
|
OpCode::ForceValue => {
|
||||||
|
stack.tos_mut()?.force(self, env)?;
|
||||||
|
}
|
||||||
|
OpCode::Jmp { step } => return Ok(step),
|
||||||
|
OpCode::JmpIfTrue { step } => {
|
||||||
|
if let VmValue::Const(Const::Bool(true)) = stack.pop()? {
|
||||||
|
return Ok(step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OpCode::JmpIfFalse { step } => {
|
||||||
|
if let VmValue::Const(Const::Bool(false)) = stack.pop()? {
|
||||||
|
return Ok(step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OpCode::Call { arity } => {
|
||||||
|
let mut args = Vec::with_capacity(arity);
|
||||||
|
for _ in 0..arity {
|
||||||
|
args.insert(0, stack.pop()?);
|
||||||
|
}
|
||||||
|
let func = stack.pop()?;
|
||||||
|
stack.push(func.call(args))?;
|
||||||
|
}
|
||||||
|
OpCode::UnOp { op } => {
|
||||||
|
use UnOp::*;
|
||||||
|
let value = stack.pop()?;
|
||||||
|
stack.push(match op {
|
||||||
|
Not => value.not(),
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
OpCode::BinOp { op } => {
|
||||||
|
use BinOp::*;
|
||||||
|
let rhs = stack.pop()?;
|
||||||
|
let lhs = stack.pop()?;
|
||||||
|
stack.push(match op {
|
||||||
|
Add => lhs.add(rhs),
|
||||||
|
And => lhs.and(rhs),
|
||||||
|
Or => lhs.or(rhs),
|
||||||
|
Eq => lhs.eq(rhs),
|
||||||
|
Con => lhs.concat(rhs),
|
||||||
|
Upd => lhs.update(rhs),
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
OpCode::ConcatString => {
|
||||||
|
let rhs = stack.pop()?;
|
||||||
|
stack.tos_mut()?.concat_string(rhs);
|
||||||
|
}
|
||||||
|
OpCode::List => {
|
||||||
|
stack.push(VmValue::List(List::new(Vector::new_sync())))?;
|
||||||
|
}
|
||||||
|
OpCode::PushElem => {
|
||||||
|
let elem = stack.pop()?;
|
||||||
|
stack.tos_mut()?.push(elem);
|
||||||
|
}
|
||||||
|
OpCode::AttrSet => {
|
||||||
|
stack.push(VmValue::AttrSet(AttrSet::new(HashTrieMap::new_sync())))?;
|
||||||
|
}
|
||||||
|
OpCode::PushStaticAttr { name } => {
|
||||||
|
let val = stack.pop()?;
|
||||||
|
stack.tos_mut()?.push_attr(Symbol::new(name), val);
|
||||||
|
}
|
||||||
|
OpCode::PushDynamicAttr => {
|
||||||
|
let val = stack.pop()?;
|
||||||
|
let mut sym = stack.pop().unwrap();
|
||||||
|
sym.coerce_to_string();
|
||||||
|
let sym = sym.unwrap_const().unwrap_string().into();
|
||||||
|
stack.tos_mut()?.push_attr(sym, val);
|
||||||
|
}
|
||||||
|
OpCode::Select { sym } => {
|
||||||
|
stack.tos_mut()?.select(Symbol::new(sym)).force(self, env)?;
|
||||||
|
}
|
||||||
|
OpCode::SelectWithDefault { sym } => {
|
||||||
|
let default = stack.pop()?;
|
||||||
|
stack
|
||||||
|
.tos_mut()?
|
||||||
|
.select_with_default(Symbol::new(sym), default.clone());
|
||||||
|
}
|
||||||
|
OpCode::SelectOrEmpty { sym } => {
|
||||||
|
stack
|
||||||
|
.tos_mut()?
|
||||||
|
.select_with_default(Symbol::new(sym), VmValue::AttrSet(AttrSet::new(HashTrieMapSync::new_sync())));
|
||||||
|
}
|
||||||
|
OpCode::SelectDynamic => {
|
||||||
|
let mut val = stack.pop().unwrap();
|
||||||
|
val.coerce_to_string();
|
||||||
|
let sym = val.unwrap_const().unwrap_string().into();
|
||||||
|
stack.tos_mut()?.select(sym);
|
||||||
|
}
|
||||||
|
OpCode::SelectDynamicWithDefault => {
|
||||||
|
let mut val = stack.pop().unwrap();
|
||||||
|
val.coerce_to_string();
|
||||||
|
let sym = val.unwrap_const().unwrap_string().into();
|
||||||
|
let default = stack.pop()?;
|
||||||
|
stack.tos_mut()?.select_with_default(sym, default.clone());
|
||||||
|
}
|
||||||
|
OpCode::SelectDynamicOrEmpty => {
|
||||||
|
let mut val = stack.pop().unwrap();
|
||||||
|
val.coerce_to_string();
|
||||||
|
let sym = val.unwrap_const().unwrap_string().into();
|
||||||
|
stack.tos_mut()?.select_with_default(sym, VmValue::AttrSet(AttrSet::new(HashTrieMapSync::new_sync())));
|
||||||
|
}
|
||||||
|
OpCode::HasAttr { sym } => {
|
||||||
|
stack.tos_mut()?.has_attr(Symbol::new(sym));
|
||||||
|
}
|
||||||
|
OpCode::HasDynamicAttr => {
|
||||||
|
let mut val = stack.pop().unwrap();
|
||||||
|
val.coerce_to_string();
|
||||||
|
let sym = val.unwrap_const().unwrap_string().into();
|
||||||
|
stack.tos_mut()?.has_attr(sym);
|
||||||
|
}
|
||||||
|
OpCode::LookUp { sym } => {
|
||||||
|
stack.push(env.lookup(Symbol::new(sym)))?;
|
||||||
|
}
|
||||||
|
OpCode::EnterEnv => {
|
||||||
|
env.enter(stack.pop()?.unwrap_attr_set().to_data());
|
||||||
|
}
|
||||||
|
OpCode::LeaveEnv => {
|
||||||
|
env.leave();
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
65
src/vm/vmthunk.rs
Normal file
65
src/vm/vmthunk.rs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use derive_more::{IsVariant, Unwrap};
|
||||||
|
|
||||||
|
use crate::bytecode::OpCodes;
|
||||||
|
|
||||||
|
use super::vm::VM;
|
||||||
|
use super::env::Env;
|
||||||
|
use super::value::VmValue;
|
||||||
|
|
||||||
|
pub struct VmThunk {
|
||||||
|
thunk: RefCell<_VmThunk>,
|
||||||
|
lock: RwLock<()>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(IsVariant, Unwrap)]
|
||||||
|
enum _VmThunk {
|
||||||
|
Code(OpCodes),
|
||||||
|
SuspendedFrom(*const VmThunk),
|
||||||
|
Value(VmValue),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VmThunk {
|
||||||
|
pub fn new(opcodes: OpCodes) -> VmThunk {
|
||||||
|
VmThunk {
|
||||||
|
thunk: RefCell::new(_VmThunk::Code(opcodes)),
|
||||||
|
lock: RwLock::new(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn force(&self, vm: &VM, env: &mut Env) -> Result<VmValue> {
|
||||||
|
{
|
||||||
|
let _guard = self.lock.read().unwrap();
|
||||||
|
match &*self.thunk.borrow() {
|
||||||
|
_VmThunk::Value(value) => return Ok(value.clone()),
|
||||||
|
_VmThunk::SuspendedFrom(from) => {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"already suspended from {from:p} (infinite recursion encountered)"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_VmThunk::Code(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let _guard = self.lock.write().unwrap();
|
||||||
|
let opcodes = std::mem::replace(
|
||||||
|
&mut *self.thunk.borrow_mut(),
|
||||||
|
_VmThunk::SuspendedFrom(self as *const VmThunk),
|
||||||
|
).unwrap_code();
|
||||||
|
let value = vm.eval(opcodes, env).unwrap();
|
||||||
|
let _ = std::mem::replace(&mut *self.thunk.borrow_mut(), _VmThunk::Value(value.clone()));
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn value(&self) -> Option<VmValue> {
|
||||||
|
let _guard = self.lock.read();
|
||||||
|
match &*self.thunk.borrow() {
|
||||||
|
_VmThunk::Value(value) => Some(value.clone()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user