diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml deleted file mode 100644 index 3628f7e91..000000000 --- a/.github/workflows/benchmarks.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Benchmarks - -on: - workflow_dispatch: {} - schedule: - - cron: '0 0 * * *' - -jobs: - check_changes: - runs-on: ubuntu-latest - name: Check latest commit - outputs: - should_run: ${{steps.should_run.outputs.should_run}} - steps: - - uses: actions/checkout@v2 - - name: print latest_commit - run: echo ${{github.sha}} - - id: should_run - continue-on-error: true - name: check latest commit is less than a day - if: ${{ github.event_name == 'schedule' }} - run: test -z $(git rev-list --after="24 hours" ${{github.sha}}) && echo "::set-output name=should_run::false" - - benchmark: - needs: check_changes - if: ${{needs.check_changes.outputs.should_run != 'false'}} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@nightly - - run: cd crates/steel-core && cargo bench --bench my_benchmark -- --output-format bencher | tee output.txt - - uses: actions/cache@v1 - with: - path: ./cache - key: ${{ runner.os }}-benchmark - - uses: rhysd/github-action-benchmark@v1 - with: - tool: 'cargo' - output-file-path: crates/steel-core/output.txt - github-token: ${{secrets.GITHUB_TOKEN}} - auto-push: true diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index fefbd4fab..000000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Deploy -on: - push: - branches: - - master - -jobs: - deploy: - runs-on: ubuntu-latest - permissions: - contents: write # To push a branch - pull-requests: write # To create a PR from that branch - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Install latest mdbook - run: | - tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name') - url="https://github.com/rust-lang/mdbook/releases/download/${tag}/mdbook-${tag}-x86_64-unknown-linux-gnu.tar.gz" - mkdir mdbook - curl -sSL $url | tar -xz --directory=./mdbook - echo `pwd`/mdbook >> $GITHUB_PATH - - name: Deploy GitHub Pages - run: | - cd docs - mdbook build - git worktree add gh-pages - git config user.name "Deploy from CI" - git config user.email "" - cd gh-pages - # Delete the ref to avoid keeping history. - git update-ref -d refs/heads/gh-pages - # There are benchmarks stored in dev/bench/ that we would like to - # keep around. Otherwise, this just adds the book as a separate - # directory. - rm -rf * - mv ../book/ . - git add book - git commit -m "Deploy $GITHUB_SHA to gh-pages" - git push --force --set-upstream origin gh-pages diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml deleted file mode 100644 index 4b37ff563..000000000 --- a/.github/workflows/rust.yml +++ /dev/null @@ -1,88 +0,0 @@ -name: Build - -on: - push: - branches: [master] - pull_request: - branches: [master] - -jobs: - - test: - name: Test Suite - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true - - - name: Set the STEEL_HOME environment variable - run: echo STEEL_HOME=${PWD}/.steel >> $GITHUB_ENV - - - uses: Swatinem/rust-cache@v2 - with: - shared-key: "build" - - - name: Build - run: cargo build --verbose - - - name: Install cogs - env: - STEEL_HOME: ${{ env.STEEL_HOME }} - run: | - echo $STEEL_HOME - mkdir -p .steel/cogs - cd cogs/ - cargo run -- install.scm - - - name: install steel dylib installer - run: mkdir $STEEL_HOME/native && cd crates/cargo-steel-lib && cargo install --path . - - - name: install websockets dylib - run: cd crates/steel-websockets && cargo steel-lib - - - uses: actions-rs/cargo@v1 - env: - STEEL_HOME: ${{ env.STEEL_HOME }} - with: - command: test - args: --all - - - name: install cargo-tarpaulin - run: cargo install cargo-tarpaulin - - - name: run code coverage - run: cargo tarpaulin --all -o "lcov" - - - name: Coveralls GitHub Action - uses: coverallsapp/github-action@v1.1.2 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - path-to-lcov: ./lcov.info - - rustfmt: - name: Rustfmt - runs-on: ubuntu-latest - needs: test - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true - components: rustfmt - - name: Check formatting - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check - diff --git a/.gitignore b/.gitignore deleted file mode 100644 index e4ac3801b..000000000 --- a/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/target -**/*.rs.bk -/scratch -docs/book -.vscode/ -/result diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index f4828d39c..000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,38 +0,0 @@ -# Contributing to Steel - -## Getting Started - -The following will clone Steel from the primary repository and run the test -suite. You should have previously setup a [recent rust tool -chain](https://www.rust-lang.org/tools/install). - -```bash -git clone https://github.com/mattwparas/steel.git && -cd steel && -mkdir -p .steel/cogs && -export STEEL_HOME="$(pwd)/.steel" && -cargo build && -pushd cogs && -cargo run -- install.scm && -popd && -cargo test --all -``` - -## Commit message style - -Steel does not employ a strict commit message style or convention. Try to -follow best practices by keeping the first line concise and descriptive, -otherwise use your best judgement. - -## Submitting Patches - -IMPORTANT: By submitting a patch, you agree that your work will be licensed -under the license used by the project. - -Follow common advice when submitting a patch: - -- Keep the patch focused. -- Include tests. -- Work in a separate branch, not `master`. -- Before working on "big ideas", open an issue to discuss approaches or - requirements to limit wasted effort. diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index f8034c2ce..000000000 --- a/Cargo.lock +++ /dev/null @@ -1,4297 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "abi_stable" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d6512d3eb05ffe5004c59c206de7f99c34951504056ce23fc953842f12c445" -dependencies = [ - "abi_stable_derive", - "abi_stable_shared", - "const_panic", - "core_extensions", - "crossbeam-channel", - "generational-arena", - "libloading", - "lock_api", - "parking_lot", - "paste", - "repr_offset", - "rustc_version", - "serde", - "serde_derive", - "serde_json", -] - -[[package]] -name = "abi_stable_derive" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aecd3efa5a5294f5c67913d45f985ccb382b3c93327581529610eeecdf4821a" -dependencies = [ - "abi_stable_shared", - "as_derive_utils", - "core_extensions", - "proc-macro2", - "quote", - "rustc_version", - "syn 1.0.107", - "typed-arena", -] - -[[package]] -name = "abi_stable_shared" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b5df7688c123e63f4d4d649cba63f2967ba7f7861b1664fca3f77d3dad2b63" -dependencies = [ - "core_extensions", -] - -[[package]] -name = "addr2line" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "ahash" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" -dependencies = [ - "cfg-if 1.0.0", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" - -[[package]] -name = "anyhow" -version = "1.0.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "as_derive_utils" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff3c96645900a44cf11941c111bd08a6573b0e2f9f69bc9264b179d8fae753c4" -dependencies = [ - "core_extensions", - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "async-ffi" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4de21c0feef7e5a556e51af767c953f0501f7f300ba785cc99c47bdc8081a50" -dependencies = [ - "abi_stable", -] - -[[package]] -name = "async-trait" -version = "0.1.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "auto_impl" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "axum" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e246206a63c9830e118d12c894f56a82033da1a2361f5544deeee3df85c99d9" -dependencies = [ - "async-trait", - "axum-core", - "bitflags 1.3.2", - "bytes", - "futures-util", - "http", - "http-body", - "hyper", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tower", - "tower-http", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-core" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cae3e661676ffbacb30f1a824089a8c9150e71017f7e1e38f2aa32009188d34" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "mime", - "rustversion", - "tower-layer", - "tower-service", -] - -[[package]] -name = "backtrace" -version = "0.3.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" -dependencies = [ - "addr2line", - "cc", - "cfg-if 1.0.0", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" - -[[package]] -name = "bitmaps" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" -dependencies = [ - "typenum", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bumpalo" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[package]] -name = "camino" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77df041dc383319cc661b428b6961a005db4d6808d5e12536931b1ca9556055" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-platform" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-steel-lib" -version = "0.1.0" -dependencies = [ - "cargo_metadata", -] - -[[package]] -name = "cargo_metadata" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07" -dependencies = [ - "camino", - "cargo-platform", - "semver 1.0.16", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" -dependencies = [ - "iana-time-zone", - "num-integer", - "num-traits", - "winapi", -] - -[[package]] -name = "ciborium" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" - -[[package]] -name = "ciborium-ll" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "clap" -version = "4.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" -dependencies = [ - "bitflags 1.3.2", - "clap_derive", - "clap_lex", - "is-terminal", - "once_cell", - "strsim", - "termcolor", -] - -[[package]] -name = "clap_derive" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "clap_lex" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "clipboard-win" -version = "4.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" -dependencies = [ - "error-code", - "str-buf", - "winapi", -] - -[[package]] -name = "codegen" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff61280aed771c3070e7dcc9e050c66f1eb1e3b96431ba66f9f74641d02fc41d" -dependencies = [ - "indexmap", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - -[[package]] -name = "colored" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" -dependencies = [ - "atty", - "lazy_static", - "winapi", -] - -[[package]] -name = "const_panic" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b" - -[[package]] -name = "coolor" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4d7a805ca0d92f8c61a31c809d4323fdaa939b0b440e544d21db7797c5aaad" -dependencies = [ - "crossterm", -] - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "core_extensions" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92c71dc07c9721607e7a16108336048ee978c3a8b129294534272e8bac96c0ee" -dependencies = [ - "core_extensions_proc_macros", -] - -[[package]] -name = "core_extensions_proc_macros" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f3b219d28b6e3b4ac87bc1fc522e0803ab22e055da177bff0068c4150c61a6" - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "cranelift" -version = "0.84.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0310bd7ad567c4e939b83fc497d334ebf5d6237ba1e2f4599d0c8b81be4a5f0" -dependencies = [ - "cranelift-codegen", - "cranelift-frontend", -] - -[[package]] -name = "cranelift-bforest" -version = "0.84.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fa7c3188913c2d11a361e0431e135742372a2709a99b103e79758e11a0a797e" -dependencies = [ - "cranelift-entity", -] - -[[package]] -name = "cranelift-codegen" -version = "0.84.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29285f70fd396a8f64455a15a6e1d390322e4a5f5186de513141313211b0a23e" -dependencies = [ - "cranelift-bforest", - "cranelift-codegen-meta", - "cranelift-codegen-shared", - "cranelift-entity", - "log", - "regalloc2", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-codegen-meta" -version = "0.84.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057eac2f202ec95aebfd8d495e88560ac085f6a415b3c6c28529dc5eb116a141" -dependencies = [ - "cranelift-codegen-shared", -] - -[[package]] -name = "cranelift-codegen-shared" -version = "0.84.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75d93869efd18874a9341cfd8ad66bcb08164e86357a694a0e939d29e87410b9" - -[[package]] -name = "cranelift-entity" -version = "0.84.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e34bd7a1fefa902c90a921b36323f17a398b788fa56a75f07a29d83b6e28808" - -[[package]] -name = "cranelift-frontend" -version = "0.84.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457018dd2d6ee300953978f63215b5edf3ae42dbdf8c7c038972f10394599f72" -dependencies = [ - "cranelift-codegen", - "log", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-jit" -version = "0.84.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fa7749513eea849d9b32d45d4d0561e0ad5545a9b2f69aee00ffd79f165c01c" -dependencies = [ - "anyhow", - "cranelift-codegen", - "cranelift-entity", - "cranelift-module", - "cranelift-native", - "libc", - "log", - "region", - "target-lexicon", - "winapi", -] - -[[package]] -name = "cranelift-module" -version = "0.84.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "734f00b74a59ffcb62e56f3d73cefdef91e87e56e0ce30f1365f678b0034d0e0" -dependencies = [ - "anyhow", - "cranelift-codegen", -] - -[[package]] -name = "cranelift-native" -version = "0.84.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bba027cc41bf1d0eee2ddf16caba2ee1be682d0214520fff0129d2c6557fda89" -dependencies = [ - "cranelift-codegen", - "libc", - "target-lexicon", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "criterion" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" -dependencies = [ - "anes", - "cast", - "ciborium", - "clap", - "criterion-plot", - "is-terminal", - "itertools", - "num-traits", - "once_cell", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" -dependencies = [ - "cast", - "itertools", -] - -[[package]] -name = "crossbeam" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" -dependencies = [ - "autocfg", - "cfg-if 1.0.0", - "crossbeam-utils", - "memoffset 0.7.1", - "scopeguard", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "crossterm" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2102ea4f781910f8a5b98dd061f4c2023f479ce7bb1236330099ceb5a93cf17" -dependencies = [ - "bitflags 1.3.2", - "crossterm_winapi", - "libc", - "mio", - "parking_lot", - "signal-hook 0.3.14", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" -dependencies = [ - "winapi", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "csscolorparser" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2a7d3066da2de787b7f032c736763eb7ae5d355f81a68bab2675a96008b0bf" -dependencies = [ - "lab", - "phf 0.11.2", -] - -[[package]] -name = "cxx" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 1.0.107", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "dashmap" -version = "5.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" -dependencies = [ - "cfg-if 1.0.0", - "hashbrown 0.12.3", - "lock_api", - "once_cell", - "parking_lot_core", -] - -[[package]] -name = "deltae" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef311e2c0a388b9ba56c839ac68d33b92d18ce7104d0acc4375cb479e2b9a53" - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer 0.10.3", - "crypto-common", -] - -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if 1.0.0", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "downcast-rs" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" - -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "encoding_rs" -version = "0.8.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - -[[package]] -name = "env_logger" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" -dependencies = [ - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "error-code" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" -dependencies = [ - "libc", - "str-buf", -] - -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" - -[[package]] -name = "fastrand" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" -dependencies = [ - "instant", -] - -[[package]] -name = "fd-lock" -version = "3.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ef1a30ae415c3a691a4f41afddc2dbcd6d70baf338368d85ebc1e8ed92cedb9" -dependencies = [ - "cfg-if 1.0.0", - "rustix 0.36.8", - "windows-sys 0.45.0", -] - -[[package]] -name = "filedescriptor" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7199d965852c3bac31f779ef99cbb4537f80e952e2d6aa0ffeb30cce00f4f46e" -dependencies = [ - "libc", - "thiserror", - "winapi", -] - -[[package]] -name = "finl_unicode" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flate2" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "generational-arena" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601" -dependencies = [ - "cfg-if 0.1.10", -] - -[[package]] -name = "generic-array" -version = "0.14.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" - -[[package]] -name = "h2" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "half" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.6", -] - -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash 0.8.6", - "serde", -] - -[[package]] -name = "hashlink" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" -dependencies = [ - "hashbrown 0.12.3", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "http" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "http-range-header" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "hyper" -version = "0.14.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "winapi", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" -dependencies = [ - "cxx", - "cxx-build", -] - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "im-lists" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280cfba4e434565a1275d357b3561ca98265e3715de9406eabd6198f7137fd21" - -[[package]] -name = "im-rc" -version = "15.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe" -dependencies = [ - "bitmaps", - "rand_core", - "rand_xoshiro", - "sized-chunks", - "typenum", - "version_check", -] - -[[package]] -name = "indexmap" -version = "1.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" -dependencies = [ - "libc", - "windows-sys 0.45.0", -] - -[[package]] -name = "ioctl-rs" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7970510895cee30b3e9128319f2cefd4bde883a39f38baa279567ba3a7eb97d" -dependencies = [ - "libc", -] - -[[package]] -name = "ipnet" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" - -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi 0.3.0", - "rustix 0.38.13", - "windows-sys 0.48.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" - -[[package]] -name = "js-sys" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lab" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f" - -[[package]] -name = "lasso" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4644821e1c3d7a560fe13d842d13f587c07348a1a05d3a797152d41c90c56df2" -dependencies = [ - "dashmap", - "hashbrown 0.13.2", - "serde", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" - -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if 1.0.0", - "winapi", -] - -[[package]] -name = "libm" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" - -[[package]] -name = "libsqlite3-sys" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f835d03d717946d28b1d1ed632eb6f0e24a299388ee623d0c23118d3e8a7fa" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - -[[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - -[[package]] -name = "linux-raw-sys" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "lsp-types" -version = "0.94.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66bfd44a06ae10647fe3f8214762e9369fd4248df1350924b4ef9e770a85ea1" -dependencies = [ - "bitflags 1.3.2", - "serde", - "serde_json", - "serde_repr", - "url", -] - -[[package]] -name = "mach" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" -dependencies = [ - "libc", -] - -[[package]] -name = "matchit" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memmem" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "minimad" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1b13e2000bd8e238d97a97de6fc30224f89a08b0aa5aaa09ed1bd68ba2fa1" -dependencies = [ - "once_cell", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - -[[package]] -name = "nix" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" -dependencies = [ - "bitflags 1.3.2", - "cfg-if 1.0.0", - "libc", - "memoffset 0.6.5", -] - -[[package]] -name = "nix" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if 1.0.0", - "libc", - "memoffset 0.6.5", - "pin-utils", -] - -[[package]] -name = "nom" -version = "5.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" -dependencies = [ - "memchr", - "version_check", -] - -[[package]] -name = "nom8" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" -dependencies = [ - "memchr", -] - -[[package]] -name = "num" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", - "serde", -] - -[[package]] -name = "num-complex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-derive" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi 0.2.6", - "libc", -] - -[[package]] -name = "object" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "oorandom" -version = "11.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "openssl" -version = "0.10.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" -dependencies = [ - "bitflags 1.3.2", - "cfg-if 1.0.0", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "ordered-float" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fc2dbde8f8a79f2102cc474ceb0ad68e3b80b85289ea62389b60e66777e4213" -dependencies = [ - "num-traits", -] - -[[package]] -name = "os_str_bytes" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "redox_syscall", - "smallvec", - "windows-sys 0.45.0", -] - -[[package]] -name = "paste" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" - -[[package]] -name = "percent-encoding" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "pest" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73935e4d55e2abf7f130186537b19e7a4abc886a0252380b59248af473a3fc9" -dependencies = [ - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef623c9bbfa0eedf5a0efba11a5ee83209c326653ca31ff019bec3a95bfff2b" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e8cba4ec22bada7fc55ffe51e2deb6a0e0db2d0b7ab0b103acc80d2510c190" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "pest_meta" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01f71cb40bd8bb94232df14b946909e14660e33fc05db3e50ae2a82d7ea0ca0" -dependencies = [ - "once_cell", - "pest", - "sha2 0.10.6", -] - -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_shared 0.10.0", -] - -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_macros", - "phf_shared 0.11.2", -] - -[[package]] -name = "phf_codegen" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" -dependencies = [ - "phf_generator", - "phf_shared 0.11.2", -] - -[[package]] -name = "phf_generator" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared 0.11.2", - "rand", -] - -[[package]] -name = "phf_macros" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" -dependencies = [ - "phf_generator", - "phf_shared 0.11.2", - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" - -[[package]] -name = "plotters" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" - -[[package]] -name = "plotters-svg" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "portable-pty" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806ee80c2a03dbe1a9fb9534f8d19e4c0546b790cde8fd1fea9d6390644cb0be" -dependencies = [ - "anyhow", - "bitflags 1.3.2", - "downcast-rs", - "filedescriptor", - "lazy_static", - "libc", - "log", - "nix 0.25.1", - "serial", - "shared_library", - "shell-words", - "winapi", - "winreg", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "pretty" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "563c9d701c3a31dfffaaf9ce23507ba09cbe0b9125ba176d15e629b0235e9acc" -dependencies = [ - "arrayvec", - "typed-arena", - "unicode-segmentation", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.107", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "proptest" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f1b898011ce9595050a68e60f90bad083ff2987a695a42357134c8381fba70" -dependencies = [ - "bit-set", - "bitflags 1.3.2", - "byteorder", - "lazy_static", - "num-traits", - "quick-error 2.0.1", - "rand", - "rand_chacha", - "rand_xorshift", - "regex-syntax", - "rusty-fork", - "tempfile", - "unarray", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - -[[package]] -name = "quickscope" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d47bcfc3e13850589cf9338a02b6dfb5aebb3748a0f93a392e8df91d6193b6b" -dependencies = [ - "indexmap", - "smallvec", -] - -[[package]] -name = "quote" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radix_fmt" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce082a9940a7ace2ad4a8b7d0b1eac6aa378895f18be598230c5f2284ac05426" - -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rand_xoshiro" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rayon" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom", - "redox_syscall", - "thiserror", -] - -[[package]] -name = "regalloc2" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904196c12c9f55d3aea578613219f493ced8e05b3d0c6a42d11cb4142d8b4879" -dependencies = [ - "fxhash", - "log", - "slice-group-by", - "smallvec", -] - -[[package]] -name = "regex" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" - -[[package]] -name = "region" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0" -dependencies = [ - "bitflags 1.3.2", - "libc", - "mach", - "winapi", -] - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "repr_offset" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb1070755bd29dffc19d0971cab794e607839ba2ef4b69a9e6fbc8733c1b72ea" -dependencies = [ - "tstr", -] - -[[package]] -name = "reqwest" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" -dependencies = [ - "base64 0.21.0", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "ropey" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93411e420bcd1a75ddd1dc3caf18c23155eda2c090631a85af21ba19e97093b5" -dependencies = [ - "smallvec", - "str_indices", -] - -[[package]] -name = "rusqlite" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a" -dependencies = [ - "bitflags 1.3.2", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "smallvec", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver 1.0.16", -] - -[[package]] -name = "rustix" -version = "0.36.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" -dependencies = [ - "bitflags 1.3.2", - "errno 0.2.8", - "io-lifetimes", - "libc", - "linux-raw-sys 0.1.4", - "windows-sys 0.45.0", -] - -[[package]] -name = "rustix" -version = "0.38.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" -dependencies = [ - "bitflags 2.4.1", - "errno 0.3.5", - "libc", - "linux-raw-sys 0.4.10", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustls" -version = "0.20.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" -dependencies = [ - "log", - "ring", - "sct", - "webpki", -] - -[[package]] -name = "rustls-native-certs" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" -dependencies = [ - "openssl-probe", - "rustls-pemfile", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" -dependencies = [ - "base64 0.21.0", -] - -[[package]] -name = "rustversion" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" - -[[package]] -name = "rusty-fork" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" -dependencies = [ - "fnv", - "quick-error 1.2.3", - "tempfile", - "wait-timeout", -] - -[[package]] -name = "rustyline" -version = "10.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e83c32c3f3c33b08496e0d1df9ea8c64d39adb8eb36a1ebb1440c690697aef" -dependencies = [ - "bitflags 1.3.2", - "cfg-if 1.0.0", - "clipboard-win", - "dirs-next", - "fd-lock", - "libc", - "log", - "memchr", - "nix 0.25.1", - "radix_trie", - "scopeguard", - "unicode-segmentation", - "unicode-width", - "utf8parse", - "winapi", -] - -[[package]] -name = "rustyline-derive" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "107c3d5d7f370ac09efa62a78375f94d94b8a33c61d8c278b96683fb4dbf2d8d" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "ryu" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" -dependencies = [ - "windows-sys 0.42.0", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "scratch" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" - -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "security-framework" -version = "2.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" -dependencies = [ - "serde", -] - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - -[[package]] -name = "serde" -version = "1.0.193" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.193" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "serde_json" -version = "1.0.108" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b04f22b563c91331a10074bda3dd5492e3cc39d56bd557e91c0af42b6c7341" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0a21fba416426ac927b1691996e82079f8b6156e920c85345f135b2e9ba2de" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "serde_spanned" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serial" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1237a96570fc377c13baa1b88c7589ab66edced652e43ffb17088f003db3e86" -dependencies = [ - "serial-core", - "serial-unix", - "serial-windows", -] - -[[package]] -name = "serial-core" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f46209b345401737ae2125fe5b19a77acce90cd53e1658cda928e4fe9a64581" -dependencies = [ - "libc", -] - -[[package]] -name = "serial-unix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03fbca4c9d866e24a459cbca71283f545a37f8e3e002ad8c70593871453cab7" -dependencies = [ - "ioctl-rs", - "libc", - "serial-core", - "termios 0.2.2", -] - -[[package]] -name = "serial-windows" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c6d3b776267a75d31bbdfd5d36c0ca051251caafc285827052bc53bcdc8162" -dependencies = [ - "libc", - "serial-core", -] - -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.6", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.6", -] - -[[package]] -name = "shared_library" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" -dependencies = [ - "lazy_static", - "libc", -] - -[[package]] -name = "shell-words" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" - -[[package]] -name = "signal-hook" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" -dependencies = [ - "libc", - "mio", - "signal-hook 0.3.14", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" -dependencies = [ - "libc", -] - -[[package]] -name = "siphasher" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" - -[[package]] -name = "sized-chunks" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" -dependencies = [ - "bitmaps", - "typenum", -] - -[[package]] -name = "slab" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" -dependencies = [ - "autocfg", -] - -[[package]] -name = "slice-group-by" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "steel-core" -version = "0.5.0" -dependencies = [ - "abi_stable", - "anyhow", - "async-ffi", - "bincode", - "chrono", - "codespan-reporting", - "cranelift", - "cranelift-jit", - "cranelift-module", - "criterion", - "env_logger", - "futures-task", - "futures-util", - "fxhash", - "im-lists", - "im-rc", - "lasso", - "log", - "num", - "once_cell", - "pretty", - "proptest", - "quickscope", - "radix_fmt", - "rand", - "reqwest", - "rusqlite", - "serde", - "serde_derive", - "serde_json", - "smallvec", - "steel-derive", - "steel-gen", - "steel-parser", - "termimad", - "url", - "weak-table", - "which", -] - -[[package]] -name = "steel-derive" -version = "0.4.0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "steel-doc" -version = "0.5.0" -dependencies = [ - "steel-core", -] - -[[package]] -name = "steel-gen" -version = "0.2.0" -dependencies = [ - "codegen", - "serde", - "serde_derive", -] - -[[package]] -name = "steel-interpreter" -version = "0.5.0" -dependencies = [ - "clap", - "env_logger", - "log", - "once_cell", - "serde", - "steel-core", - "steel-derive", - "steel-doc", - "steel-repl", -] - -[[package]] -name = "steel-language-server" -version = "0.5.0" -dependencies = [ - "dashmap", - "env_logger", - "log", - "once_cell", - "ropey", - "serde", - "serde_json", - "steel-core", - "tokio", - "tower-lsp", -] - -[[package]] -name = "steel-parser" -version = "0.4.0" -dependencies = [ - "lasso", - "num-bigint", - "once_cell", - "pretty", - "serde", - "serde_derive", -] - -[[package]] -name = "steel-pty" -version = "0.1.0" -dependencies = [ - "abi_stable", - "futures", - "log", - "portable-pty", - "steel-core", - "strip-ansi-escapes", - "termwiz", - "tokio", -] - -[[package]] -name = "steel-repl" -version = "0.5.0" -dependencies = [ - "colored", - "rustyline", - "rustyline-derive", - "steel-core", - "steel-parser", -] - -[[package]] -name = "steel-sys-info" -version = "0.5.0" -dependencies = [ - "abi_stable", - "steel-core", - "sys-info", -] - -[[package]] -name = "steel-toml" -version = "0.5.0" -dependencies = [ - "abi_stable", - "steel-core", - "toml", -] - -[[package]] -name = "steel-values" -version = "0.1.0" - -[[package]] -name = "steel-webrequests" -version = "0.1.0" -dependencies = [ - "abi_stable", - "steel-core", - "ureq", -] - -[[package]] -name = "steel-webserver" -version = "0.1.0" -dependencies = [ - "abi_stable", - "axum", - "crossbeam", - "serde_json", - "steel-core", - "tokio", -] - -[[package]] -name = "steel-websockets" -version = "0.1.0" -dependencies = [ - "abi_stable", - "steel-core", - "tungstenite", -] - -[[package]] -name = "str-buf" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" - -[[package]] -name = "str_indices" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9557cb6521e8d009c51a8666f09356f4b817ba9ba0981a305bd86aee47bd35c" - -[[package]] -name = "strip-ansi-escapes" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8" -dependencies = [ - "vte", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "syn" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sys-info" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b3a0d0aba8bf96a0e1ddfdc352fc53b3df7f39318c71854910c3c4b024ae52c" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "target-lexicon" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" - -[[package]] -name = "tempfile" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" -dependencies = [ - "cfg-if 1.0.0", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "termimad" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86d19aafb932e65acc333b49cb0bd7d9c650a94fe3f2550ce91aea00f582a86" -dependencies = [ - "coolor", - "crossbeam", - "crossterm", - "minimad", - "thiserror", - "unicode-width", -] - -[[package]] -name = "terminfo" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da31aef70da0f6352dbcb462683eb4dd2bfad01cf3fc96cf204547b9a839a585" -dependencies = [ - "dirs", - "fnv", - "nom", - "phf 0.11.2", - "phf_codegen", -] - -[[package]] -name = "termios" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d9cf598a6d7ce700a4e6a9199da127e6819a61e64b68609683cc9a01b5683a" -dependencies = [ - "libc", -] - -[[package]] -name = "termios" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" -dependencies = [ - "libc", -] - -[[package]] -name = "termwiz" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9509a978a10fcbace4991deae486ae10885e0f4c2c465123e08c9714a90648fa" -dependencies = [ - "anyhow", - "base64 0.21.0", - "bitflags 1.3.2", - "filedescriptor", - "finl_unicode", - "fixedbitset", - "hex", - "lazy_static", - "libc", - "log", - "memmem", - "nix 0.24.3", - "num-derive", - "num-traits", - "ordered-float", - "pest", - "pest_derive", - "phf 0.10.1", - "regex", - "semver 0.11.0", - "sha2 0.9.9", - "signal-hook 0.1.17", - "siphasher", - "terminfo", - "termios 0.3.3", - "thiserror", - "ucd-trie", - "unicode-segmentation", - "vtparse", - "wezterm-bidi", - "wezterm-color-types", - "wezterm-dynamic", - "winapi", -] - -[[package]] -name = "thiserror" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" -dependencies = [ - "autocfg", - "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.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772c1426ab886e7362aedf4abc9c0d1348a979517efedfc25862944d10137af0" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f35c303ea3e062b6131be4de16debe23860b9d3f2396658f13b7af1987fb473" -dependencies = [ - "indexmap", - "nom8", - "serde", - "serde_spanned", - "toml_datetime", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" -dependencies = [ - "bitflags 1.3.2", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-range-header", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - -[[package]] -name = "tower-lsp" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4ba052b54a6627628d9b3c34c176e7eda8359b7da9acd497b9f20998d118508" -dependencies = [ - "async-trait", - "auto_impl", - "bytes", - "dashmap", - "futures", - "httparse", - "lsp-types", - "memchr", - "serde", - "serde_json", - "tokio", - "tokio-util", - "tower", - "tower-lsp-macros", - "tracing", -] - -[[package]] -name = "tower-lsp-macros" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if 1.0.0", - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "tstr" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cca3264971090dec0feef3b455a3c178f02762f7550cf4592991ac64b3be2d7e" -dependencies = [ - "tstr_proc_macros", -] - -[[package]] -name = "tstr_proc_macros" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78122066b0cb818b8afd08f7ed22f7fdbc3e90815035726f0840d0d26c0747a" - -[[package]] -name = "tungstenite" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" -dependencies = [ - "base64 0.13.1", - "byteorder", - "bytes", - "http", - "httparse", - "log", - "rand", - "rustls", - "rustls-native-certs", - "sha1", - "thiserror", - "url", - "utf-8", - "webpki", -] - -[[package]] -name = "typed-arena" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "ucd-trie" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" - -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - -[[package]] -name = "unicode-bidi" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" - -[[package]] -name = "unicode-ident" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "ureq" -version = "2.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "338b31dd1314f68f3aabf3ed57ab922df95ffcd902476ca7ba3c4ce7b908c46d" -dependencies = [ - "base64 0.13.1", - "flate2", - "log", - "once_cell", - "rustls", - "serde", - "serde_json", - "url", - "webpki", - "webpki-roots", -] - -[[package]] -name = "url" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8parse" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "vte" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" -dependencies = [ - "arrayvec", - "utf8parse", - "vte_generate_state_changes", -] - -[[package]] -name = "vte_generate_state_changes" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "vtparse" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9b2acfb050df409c972a37d3b8e08cdea3bddb0c09db9d53137e504cfabed0" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "wait-timeout" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -dependencies = [ - "libc", -] - -[[package]] -name = "walkdir" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" -dependencies = [ - "cfg-if 1.0.0", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 1.0.107", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" -dependencies = [ - "cfg-if 1.0.0", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" - -[[package]] -name = "weak-table" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "323f4da9523e9a669e1eaf9c6e763892769b1d38c623913647bfdc1532fe4549" - -[[package]] -name = "web-sys" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.22.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki", -] - -[[package]] -name = "wezterm-bidi" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1560382cf39b0fa92473eae4d5b3772f88c63202cbf5a72c35db72ba99e66c36" -dependencies = [ - "log", - "wezterm-dynamic", -] - -[[package]] -name = "wezterm-color-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6e7a483dd2785ba72705c51e8b1be18300302db2a78368dac9bc8773857777" -dependencies = [ - "csscolorparser", - "deltae", - "lazy_static", - "wezterm-dynamic", -] - -[[package]] -name = "wezterm-dynamic" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75e78c0cc60a76de5d93f9dad05651105351e151b6446ab305514945d7588aa" -dependencies = [ - "log", - "ordered-float", - "strsim", - "thiserror", - "wezterm-dynamic-derive", -] - -[[package]] -name = "wezterm-dynamic-derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f5ef318442d07b3d071f9f43ea40b80992f87faee14bb4d017b6991c307f0" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "which" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" -dependencies = [ - "either", - "libc", - "once_cell", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.1", -] - -[[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.1", -] - -[[package]] -name = "windows-targets" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", -] - -[[package]] -name = "windows-targets" -version = "0.48.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - -[[package]] -name = "zerocopy" -version = "0.7.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d6f15f7ade05d2a4935e34a457b936c23dc70a05cc1d97133dc99e7a3fe0f0e" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbbad221e3f78500350ecbd7dfa4e63ef945c05f4c61cb7f4d3f84cd0bba649b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 7c8196aeb..000000000 --- a/Cargo.toml +++ /dev/null @@ -1,46 +0,0 @@ -[package] -name = "steel-interpreter" -version.workspace = true -authors = ["mattwparas "] -edition = "2021" -license = "MIT OR Apache-2.0" -repository = "https://github.com/mattwparas/steel" -description = "Steel repl and command line interface" - -include = ["/src/**/*", "/Cargo.toml", "/README.md", "LICENSE*"] - -[workspace.package] -version = "0.5.0" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[[bin]] -name = "steel" -path = "src/main.rs" - -[workspace.dependencies] -# This has to line up with the workspace version above -steel-core = { path = "./crates/steel-core", version = "0.5.0", features = ["web", "sqlite", "dylibs", "markdown"] } - -[dependencies] -once_cell = "1.17.0" -steel-core = { workspace = true } -steel-derive = { path = "./crates/steel-derive", version = "0.4.0"} -steel-repl = { path = "./crates/steel-repl", version = "0.5.0" } -env_logger = "0.10.0" -log = "0.4.17" -clap = { version = "4.1.4", features = ["derive"] } -steel-doc = { path = "./crates/steel-doc", version = "0.5.0"} - -[dev-dependencies] -serde = { version = "1.0.152", features = ["derive"] } - -[workspace] -members = [ - ".", - "crates/*", -] - -[profile.release] -# debug = false -debug = true -lto = true diff --git a/LICENSE-APACHE b/LICENSE-APACHE deleted file mode 100644 index 261eeb9e9..000000000 --- a/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT deleted file mode 100644 index 24d539f06..000000000 --- a/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 Matthew Paras - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 070e3ae32..000000000 --- a/README.md +++ /dev/null @@ -1,449 +0,0 @@ -# Steel -
- -
- -
- -An embeddable and extensible scheme dialect built in Rust. - -![Actions Status](https://github.com/mattwparas/steel/workflows/Build/badge.svg) -[![Coverage Status](https://coveralls.io/repos/github/mattwparas/steel/badge.svg?branch=master)](https://coveralls.io/github/mattwparas/steel?branch=master) -[![Discord Chat](https://img.shields.io/discord/1152443024715034675.svg?logo=discord&label=discord)](https://discord.gg/WwFRXdN6HU) - - - - Try it on the Playground - - - -
- -## Getting Started - -This github repository contains a cli interpreter. To try it out on the online playground, go to the [Steel playground](https://mattwparas.github.io/steel-playground/dev). To get started using a repl with the crates, make sure you first have rust installed. - -Then, clone the repo and run the following command: - -```bash -cargo run -``` - -This will launch a REPL instance that looks something like this: - -

- -

- -### Packages - -If you would like to install and use packages, please set the `STEEL_HOME` environment variable. This will be the location that packages get installed to. Steel currently does not assume any default. - -## About - -`Steel` is an embeddable scheme interpreter, with a standalone cli included as well. Inspired largely by Racket, the language seeks to be ergonomic scheme variant helpful for embedding in applications, or to be used on its own with high performance functions implemented in Rust. The language implementation itself contains a fairly powerful macro system based on the `syntax-rules` style and a bytecode virtual machine. At the moment, it is not explicitly compliant with any individual scheme specification. - -> **Warning** -> The API is unstable with no guarantees, and may change at any time while pre 1.0. There are undoubtedly bugs that exist, and any major bug reports will be addressed quickly. That being said, I do use it as a daily driver for many scripting tasks myself. - -## Features - -* Limited `syntax-rules` style macros are supported -* Easy integration with Rust functions and structs -* Easily call a script from rust or via a separate file -* Efficient - common functions and data structures are optimized for performance (`map`, `filter`, etc) -* Higher order Contracts -* Built in immutable data structures include: - * lists - * vectors - * hashmaps - * hashsets - -## Contracts - -Inspired by Racket's higher order contracts, `Steel` implements\* higher order contracts to enable design by contract, made easy with a `define/contract` macro for easier ergonomics. Racket makes use of a concept known as _blame_ which seeks to identify the violating party - `Steel` does not quite have fully fleshed out blame but that is a work in progress. Here are some examples: - -```scheme -;; Simple flat contracts -(define/contract (test x y) - (->/c even? even? odd?) - (+ x y 1)) - -(test 2 2) ;; => 5 - -(define/contract (test-violation x y) - (->/c even? even? odd?) - (+ x y 1)) - -(test-violation 1 2) ;; contract violation - -``` - -Contracts are implemented as _values_, so they are bound to functions. This enables the use of contract checking on functions themselves since functions can be passed around: - -```scheme -;; Higher order contracts, check on application -(define/contract (higher-order func y) - (->/c (->/c even? odd?) even? even?) - (+ 1 (func y))) - -(higher-order (lambda (x) (+ x 1)) 2) ;; => 4 - -(define/contract (higher-order-violation func y) - (->/c (->/c even? odd?) even? even?) - (+ 1 (func y))) - -(higher-order-violation (lambda (x) (+ x 2)) 2) ;; contract violation -``` - -Contracts on functions do not get checked until they are applied, so a function returning a _contracted_ function won't cause a violation until that function is actually used: - -```scheme -;; More higher order contracts, get checked on application -(define/contract (output) - (->/c (->/c string? int?)) - (lambda (x) 10)) - -(define/contract (accept func) - (->/c (->/c string? int?) string?) - "cool cool cool") - -(accept (output)) ;; => "cool cool cool" - -;; different contracts on the argument -(define/contract (accept-violation func) - (->/c (->/c string? string?) string?) - (func "applesauce") - "cool cool cool") - -(accept-violation (output)) ;; contract violation - -;; generates a function -(define/contract (generate-closure) - (->/c (->/c string? int?)) - (lambda (x) 10)) - -;; calls generate-closure which should result in a contract violation -(define/contract (accept-violation) - (->/c (->/c string? string?)) - (generate-closure)) - -((accept-violation) "test") ;; contract violation -``` - -Perhaps a more nuanced case: - -```scheme -(define/contract (output) - (->/c (->/c string? int?)) - (lambda (x) 10.2)) - -(define/contract (accept) - (->/c (->/c string? number?)) - (output)) - - -((accept) "test") ;; contract violation 10.2 satisfies number? but _not_ int? -``` - -\* Very much a work in progress - -## Transducers - -Inspired by clojure's transducers, `Steel` has a similar object that is somewhere half way in between transducers and iterators. Consider the following: - -```scheme - -(mapping (lambda (x) (+ x 1))) ;; => <#iterator> -(filtering even?) ;; => <#iterator> -(taking 15) ;; => <#iterator> - -(compose - (mapping add1) - (filtering odd?) - (taking 15)) ;; => <#iterator> -``` - -Each of these expressions emit an `<#iterator>` object, which means they're compatible with `transduce`. `transduce` takes a transducer (i.e. `<#iterator>`) and a collection that can be iterated (`list`, `vector`, `stream`, `hashset`, `hashmap`, `string`, `struct`) and applies the transducer. - -```scheme -;; Accepts lists -(transduce (list 1 2 3 4 5) (mapping (lambda (x) (+ x 1))) (into-list)) ;; => '(2 3 4 5 6) - -;; Accepts vectors -(transduce (vector 1 2 3 4 5) (mapping (lambda (x) (+ x 1))) (into-vector)) ;; '#(2 3 4 5 6) - -;; Even accepts streams! -(define (integers n) - (stream-cons n (lambda () (integers (+ 1 n))))) - -(transduce (integers 0) (taking 5) (into-list)) ;; => '(0 1 2 3 4) -``` - -Transduce accepts a reducer function as well. Above we used `into-list` and `into-vector`, but below we can use any arbitrary reducer: - -```scheme -;; (-> transducer reducing-function initial-value iterable) -(transduce (list 0 1 2 3) (mapping (lambda (x) (+ x 1))) (into-reducer + 0)) ;; => 10 -``` - -Compose just combines the iterator functions and lets us avoid intermediate allocation. The composition works left to right - it chains each value through the functions and then accumulates into the output type. See the following: - -```scheme -(define xf - (compose - (mapping add1) - (filtering odd?) - (taking 5))) - -(transduce (range 0 100) xf (into-list)) ;; => '(1 3 5 7 9) -``` - -## Syntax Choices - -`Steel` is mildly opinionated in that there a few ways to define variables and functions. These choices are fairly arbitrary except for the shorthand function syntax, which I borrowed from Racket. `defn` and `fn` were really encouraged by me wanting to type less characters. - -```scheme - -;; All of the following are equivalent -(define (foo x) (+ x 1)) -(define foo (lambda (x) (+ x 1))) -(defn (foo x) (+ x 1)) -(defn foo (lambda (x) (+ x 1))) - -;; All of the following are equivalent -(lambda (x) (+ x 1)) -(λ (x) (+ x 1)) -(fn (x) (+ x 1)) -``` - -## Modules - -In order to support a growing codebase, Steel has module support for projects spanning multiple files. Steel files can `provide` values (with contracts attached) and `require` modules from other files: - -```scheme -;; main.scm -(require "provide.scm") - -(even->odd 10) - - -;; provide.scm -(provide - (contract/out even->odd (->/c even? odd?)) - no-contract - flat-value) - -(define (even->odd x) - (+ x 1)) - -(define (accept-number x) (+ x 10)) - -(define (no-contract) "cool cool cool") -(define flat-value 15) - -(displayln "Calling even->odd with some bad inputs but its okay") -(displayln (even->odd 1)) -``` - -Here we can see if we were to run `main` that it would include the contents of `provide`, and only provided values would be accessible from `main`. The contract is attached at the contract boundary, so inside the `provide` module, you can violate the contract, but outside the module the contract will be applied. - -A few notes on modules: -* Cyclical dependencies are not allowed -* Modules will be only compiled once and used across multiple files. If `A` requires `B` and `C`, and `B` requires `C`, `C` will be compiled once and shared between `A` and `B`. -* Modules will be recompiled when changed, and any dependent files will also be recompiled as necessary - -## Performance - -Preliminary benchmarks show the following on my machine: - -| Benchmark | Steel | Python | -| --------- | -------- | -------- | -| (fib 28) | 63.383ms | 65.10 ms | -| (ack 3 3) | 0.303 ms | 0.195 ms | - -## Examples of embedding Rust values in the virtual machine - -Rust values, types, and functions are easily embedded into Steel. Using the `register_fn` call, you can embed functions easily: - -```rust -use steel_vm::engine::Engine; -use steel_vm::register_fn::RegisterFn; - -fn external_function(arg1: usize, arg2: usize) -> usize { - arg1 + arg2 -} - -fn option_function(arg1: Option) -> Option { - arg1 -} - -fn result_function(arg1: Option) -> Result { - if let Some(inner) = arg1 { - Ok(inner) - } else { - Err("Got a none".to_string()) - } -} - -pub fn main() { - let mut vm = Engine::new(); - - // Here we can register functions - // Any function can accept parameters that implement `FromSteelVal` and - // return values that implement `IntoSteelVal` - vm.register_fn("external-function", external_function); - - // See the docs for more information about `FromSteelVal` and `IntoSteelVal` - // but we can see even functions that accept/return Option or Result - // can be registered - vm.register_fn("option-function", option_function); - - // Result values will map directly to errors in the VM and bubble back up - vm.register_fn("result-function", result_function); - - vm.run( - r#" - (define foo (external-function 10 25)) - (define bar (option-function "applesauce")) - (define baz (result-function "bananas")) - "#, - ) - .unwrap(); - - let foo = vm.extract::("foo").unwrap(); - println!("foo: {}", foo); - assert_eq!(35, foo); - - // Can also extract a value by specifying the type on the variable - let bar: String = vm.extract("bar").unwrap(); - println!("bar: {}", bar); - assert_eq!("applesauce".to_string(), bar); - - let baz: String = vm.extract("baz").unwrap(); - println!("baz: {}", baz); - assert_eq!("bananas".to_string(), baz); -} -``` - -We can also embed structs themselves: - -```rust -use steel_vm::engine::Engine; -use steel_vm::register_fn::RegisterFn; - -use steel_derive::Steel; - -// In order to register a type with Steel, -// it must implement Clone, Debug, and Steel -#[derive(Clone, Debug, Steel, PartialEq)] -pub struct ExternalStruct { - foo: usize, - bar: String, - baz: f64, -} - -impl ExternalStruct { - pub fn new(foo: usize, bar: String, baz: f64) -> Self { - ExternalStruct { foo, bar, baz } - } - - // Embedding functions that take self by value - pub fn method_by_value(self) -> usize { - self.foo - } - - pub fn method_by_reference(&self) -> usize { - self.foo - } - - // Setters should update the value and return a new instance (functional set) - pub fn set_foo(mut self, foo: usize) -> Self { - self.foo = foo; - self - } -} - -pub fn main() { - let mut vm = Engine::new(); - - // Registering a type gives access to a predicate for the type - vm.register_type::("ExternalStruct?"); - - // Structs in steel typically have a constructor that is the name of the struct - vm.register_fn("ExternalStruct", ExternalStruct::new); - - // register_fn can be chained - vm.register_fn("method-by-value", ExternalStruct::method_by_value) - .register_fn("method-by-reference", ExternalStruct::method_by_reference) - .register_fn("set-foo", ExternalStruct::set_foo); - - let external_struct = ExternalStruct::new(1, "foo".to_string(), 12.4); - - // Registering an external value is fallible if the conversion fails for some reason - // For instance, registering an Err(T) is fallible. However, most implementation outside of manual - // ones should not fail - vm.register_external_value("external-struct", external_struct) - .unwrap(); - - let output = vm - .run( - r#" - (define new-external-struct (set-foo external-struct 100)) - (define get-output (method-by-value external-struct)) - (define second-new-external-struct (ExternalStruct 50 "bananas" 72.6)) - "last-result" - "#, - ) - .unwrap(); - - let new_external_struct = vm.extract::("new-external-struct").unwrap(); - println!("new_external_struct: {:?}", new_external_struct); - assert_eq!( - ExternalStruct::new(100, "foo".to_string(), 12.4), - new_external_struct - ); - - // Can also extract a value by specifying the type on the variable - let get_output: usize = vm.extract("get-output").unwrap(); - println!("get_output: {}", get_output); - assert_eq!(1, get_output); - - let second_new_external_struct: ExternalStruct = - vm.extract("second-new-external-struct").unwrap(); - println!( - "second_new_external_struct: {:?}", - second_new_external_struct - ); - assert_eq!( - ExternalStruct::new(50, "bananas".to_string(), 72.6), - second_new_external_struct - ); - - // We also get the output of the VM as the value of every expression run - // we can inspect the results just by printing like so - println!("{:?}", output); -} -``` - -See the examples folder for more examples on embedding values and interacting with the outside world. - - -## License - -Licensed under either of - - * Apache License, Version 2.0 - ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license - ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -## Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in the work by you, as defined in the Apache-2.0 license, shall be -dual licensed as above, without any additional terms or conditions. - -See [CONTRIBUTING.md](./CONTRIBUTING.md). diff --git a/benchmarks/ack/ack.lua b/benchmarks/ack/ack.lua deleted file mode 100644 index e0f080a5c..000000000 --- a/benchmarks/ack/ack.lua +++ /dev/null @@ -1,15 +0,0 @@ --- Computes values of Ackermann function --- See: http://en.wikipedia.org/wiki/Ackermann_function - --- m : a nonnegative integer --- n : a nonnegative integer --- return : the value of Ackermann(m, n) -function ackermann(m, n) - if m == 0 then return n + 1 end - if n == 0 then return ackermann(m-1,1) end - return ackermann(m-1, ackermann(m, n-1)) - end - -for i=1, 50 do - ackermann(3, 3) -end \ No newline at end of file diff --git a/benchmarks/ack/ack.py b/benchmarks/ack/ack.py deleted file mode 100644 index 68811d328..000000000 --- a/benchmarks/ack/ack.py +++ /dev/null @@ -1,10 +0,0 @@ -def ack1(M, N): - return ( - (N + 1) - if M == 0 - else (ack1(M - 1, 1) if N == 0 else ack1(M - 1, ack1(M, N - 1))) - ) - - -for i in range(50): - ack1(3, 3) diff --git a/benchmarks/ack/ack.scm b/benchmarks/ack/ack.scm deleted file mode 100644 index 6a298bfab..000000000 --- a/benchmarks/ack/ack.scm +++ /dev/null @@ -1,14 +0,0 @@ -(define (ackermann m n) - (cond [(equal? m 0) (+ n 1)] - [(equal? n 0) (ackermann (- m 1) 1)] - [else (ackermann (- m 1) (ackermann m (- n 1)))])) - -(define (loop x) - (if (equal? x 100) - #true - (begin - (ackermann 3 3) - (loop (+ x 1))))) - - -(loop 50) diff --git a/benchmarks/bench.scm b/benchmarks/bench.scm deleted file mode 100644 index e11824416..000000000 --- a/benchmarks/bench.scm +++ /dev/null @@ -1,62 +0,0 @@ -(define print displayln) - -(define (build-release) - (~> (command "cargo" '("build" "--release")) (spawn-process) (Ok->value) (wait))) - -(define (run-bench args) - (~> (command "hyperfine" args) (spawn-process) (Ok->value) (wait))) - -(define *interpreter-map* (hash "py" "python3.10" "scm" "../target/release/steel" "lua" "lua")) - -(define (extension->interpreter ext) - (hash-get *interpreter-map* ext)) - -(define (combine-interpreter-and-path interpreter path) - (string-append (string-append interpreter " ") path)) - -(define (path->command-fragment path) - (define interpreter (~> path (path->extension) (extension->interpreter))) - (if (list? interpreter) - (map (lambda (interp) (combine-interpreter-and-path interp path)) interpreter) - (combine-interpreter-and-path interpreter path))) - -(define (directory->bench-command dir) - (flatten (map path->command-fragment (read-dir dir)))) - -(define (bench-group dir . options) - (run-bench (append (filter (lambda (x) (not (ends-with? x ".lua"))) (directory->bench-command dir)) - options))) - -(define *benches* - '(("startup" "--warmup" "10" "--min-runs" "100") ("map" "--warmup" "10") - ("ack" "--warmup" "10") - ("fib" "--warmup" "10" "--min-runs" "40") - ("bin-trees" "--warmup" "10"))) - -(define (main) - (print "Building steel for release...") - (build-release) - (print "Running benches...") - (transduce *benches* - (mapping (lambda (args) - (newline) - (apply bench-group args))) - (into-list)) - - ; (bench-group "bin-trees") - (print "Done")) - -(main) - -; (define (main) -; (displayln "Building steel for release...") -; (build-release) -; (displayln "Running benches...") -; (run-bench "../target/release/steel startup/startup.scm" "python3.10 startup/startup.py" "--warmup" "10" "--min-runs" "100") -; (run-bench "../target/release/steel fib/fib.scm" "python3.10 fib/fib.py" "--warmup" "10" "--min-runs" "40") -; ; (run-bench '("../target/release/steel fib/fib.scm" "python3 fib/fib.py" "lua fib/fib.lua" "--warmup" "10" "--min-runs" "40")) -; ; (run-bench '("../target/release/steel ack/ack.scm" "python3 ack/ack.py" "lua ack/ack.lua" "--warmup" "10" "--min-runs" "40")) -; ; (run-bench '("../target/release/steel bin-trees/bin-trees.scm" "python3 bin-trees/bin_trees.py" "--warmup" "5")) -; (displayln "Done")) - -; (main) diff --git a/benchmarks/bin-trees/bin-trees.py b/benchmarks/bin-trees/bin-trees.py deleted file mode 100644 index 61a1f2047..000000000 --- a/benchmarks/bin-trees/bin-trees.py +++ /dev/null @@ -1,40 +0,0 @@ -import sys - - -def make_tree(depth): - if not depth: - return None, None - depth -= 1 - return make_tree(depth), make_tree(depth) - - -def check_tree(node): - (left, right) = node - if not left: - return 1 - return 1 + check_tree(left) + check_tree(right) - - -min_depth = 4 -max_depth = max(min_depth + 2, 12) -stretch_depth = max_depth + 1 - -print( - "stretch tree of depth %d\t check:" % stretch_depth, - check_tree(make_tree(stretch_depth)), -) - -long_lived_tree = make_tree(max_depth) - -iterations = 2**max_depth - -for depth in range(min_depth, stretch_depth, 2): - - check = 0 - for i in range(1, iterations + 1): - check += check_tree(make_tree(depth)) - - print("%d\t trees of depth %d\t check:" % (iterations, depth), check) - iterations //= 4 - -print("long lived tree of depth %d\t check:" % max_depth, check_tree(long_lived_tree)) diff --git a/benchmarks/bin-trees/bin-trees.scm b/benchmarks/bin-trees/bin-trees.scm deleted file mode 100644 index ede6271ab..000000000 --- a/benchmarks/bin-trees/bin-trees.scm +++ /dev/null @@ -1,64 +0,0 @@ -; #lang racket/base - -;;; The Computer Language Benchmarks Game -;;; https://salsa.debian.org/benchmarksgame-team/benchmarksgame/ - -;;; Derived from the Chicken variant by Sven Hartrumpf -;;; contributed by Matthew Flatt -;;; *reset* - -; (require racket/cmdline) - -(struct node (left val right)) - -;; Instead of (define-struct leaf (val)): -(define (leaf val) - (node #f val #f)) -(define (leaf? l) - (not (node-left l))) -(define (leaf-val l) - node-val) - -(define (make item d) - (if (= d 0) - (leaf item) - (let ([item2 (* item 2)] [d2 (- d 1)]) (node (make (- item2 1) d2) item (make item2 d2))))) - -(define (check t) - (if (leaf? t) 1 (+ 1 (+ (check (node-left t)) (check (node-right t)))))) - -(define (iterate n m d sum) - (if (equal? n m) sum (iterate (+ n 1) m d (+ sum (check (make n d)))))) - -(define (max x y) - (if (> x y) x y)) - -(define (loop d end max-depth min-depth) - (if (>= d end) - void - (begin - (let ([iterations (arithmetic-shift 1 (+ (- max-depth d) min-depth))]) - (displayln iterations " trees of depth " d " check: " (iterate 0 iterations d 0))) - (loop (+ 2 d) end max-depth min-depth)))) - -(define (main n) - (let* ([min-depth 4] [max-depth (max (+ min-depth 2) n)]) - (let ([stretch-depth (+ max-depth 1)]) - (displayln "stretch tree of depth " stretch-depth " check: " (check (make 0 stretch-depth)))) - (let ([long-lived-tree (make 0 max-depth)]) - ; (begin - ; (define end ) - - (loop 4 (add1 max-depth) max-depth min-depth) - - ; ) - - (displayln "long lived tree of depth " max-depth " check: " (check long-lived-tree))))) - -(main 12) - -; (main 21) -; (main 21) - -; (command-line #:args (n) -; (main (string->number n))) diff --git a/benchmarks/fib/fib.lua b/benchmarks/fib/fib.lua deleted file mode 100644 index 9101c3c5e..000000000 --- a/benchmarks/fib/fib.lua +++ /dev/null @@ -1,9 +0,0 @@ -function fibonacci(n) - if n<=2 then - return 1 - else - return fibonacci(n-1) + fibonacci(n-2) - end -end - -fibonacci(28) \ No newline at end of file diff --git a/benchmarks/fib/fib.py b/benchmarks/fib/fib.py deleted file mode 100644 index ae96d1e64..000000000 --- a/benchmarks/fib/fib.py +++ /dev/null @@ -1,7 +0,0 @@ -def fib(n): - if n <= 2: - return 1 - return fib(n - 1) + fib(n - 2) - - -fib(30) diff --git a/benchmarks/fib/fib.scm b/benchmarks/fib/fib.scm deleted file mode 100644 index cdafaefa2..000000000 --- a/benchmarks/fib/fib.scm +++ /dev/null @@ -1,6 +0,0 @@ -(define (fib n) - (if (<= n 2) - 1 - (+ (fib (- n 1)) (fib (- n 2))))) - -(fib 30) \ No newline at end of file diff --git a/benchmarks/map/map.py b/benchmarks/map/map.py deleted file mode 100644 index 4ab083655..000000000 --- a/benchmarks/map/map.py +++ /dev/null @@ -1,3 +0,0 @@ -lst = [x + 1 for x in range(0, 10000)] - -print(len(lst)) diff --git a/benchmarks/map/map.scm b/benchmarks/map/map.scm deleted file mode 100644 index 7039533b9..000000000 --- a/benchmarks/map/map.scm +++ /dev/null @@ -1,3 +0,0 @@ -(define lst (map (lambda (x) (+ x 1)) (range 0 10000))) - -(displayln (length lst)) \ No newline at end of file diff --git a/benchmarks/recursive/map.scm b/benchmarks/recursive/map.scm deleted file mode 100644 index 894b67b41..000000000 --- a/benchmarks/recursive/map.scm +++ /dev/null @@ -1,23 +0,0 @@ -(define (map func lst) - (if (empty? lst) - '() - (cons (func (first lst)) (map func (rest lst))))) - -;; To do the tail recursion modulo cons, we'd need a special op code for tail recursive cons, where -;; the second argument to cons is recursive with respect to itself -;; -;; So in this case: -;; (cons (func (first lst)) (map func (rest lst))) -;; ^^^^ is the recursive call to itself, which occurs in the second position -;; -;; This can be lifted up into something like this: -(define (map2 func lst) - (define (map2-trmc func lst accum) - (if (empty? lst) - accum - (map2-trmc func (cdr lst) (begin (vector-push! accum (func (first lst))) - accum)) - - )) - (mutable-vector->list (map2-trmc func lst (mutable-vector)))) - diff --git a/benchmarks/startup/startup.lua b/benchmarks/startup/startup.lua deleted file mode 100644 index 442659b88..000000000 --- a/benchmarks/startup/startup.lua +++ /dev/null @@ -1 +0,0 @@ -print("Hello world!") \ No newline at end of file diff --git a/benchmarks/startup/startup.py b/benchmarks/startup/startup.py deleted file mode 100644 index f1a18139c..000000000 --- a/benchmarks/startup/startup.py +++ /dev/null @@ -1 +0,0 @@ -print("Hello world!") diff --git a/benchmarks/startup/startup.scm b/benchmarks/startup/startup.scm deleted file mode 100644 index dba7be1b8..000000000 --- a/benchmarks/startup/startup.scm +++ /dev/null @@ -1 +0,0 @@ -(displayln "Hello world!") \ No newline at end of file diff --git a/build.sh b/build.sh deleted file mode 100644 index 3a83e2956..000000000 --- a/build.sh +++ /dev/null @@ -1,11 +0,0 @@ -cd crates/ - -cd steel-sys-info && cargo-steel-lib && cd .. - -# cd steel-toml && cargo-steel-lib && cd .. -# cd steel-webserver && cargo-steel-lib && cd .. -# cd example-dylib && cargo-steel-lib && cd .. - -cargo build - -cd .. \ No newline at end of file diff --git a/cogs/clos/clos.scm b/cogs/clos/clos.scm deleted file mode 100644 index 040ddf74d..000000000 --- a/cogs/clos/clos.scm +++ /dev/null @@ -1,223 +0,0 @@ -;; ---------------------------- Class Object definitions -------------------------------------------- - -(struct Class-Object (name parents interfaces fields methods) #:mutable #:transparent) -(struct Interface (name methods)) -(struct Class-Instance (class-object fields) #:transparent) - -(define (list-subset? left right) - (hashset-subset? (list->hashset left) (list->hashset right))) - -;; Classes contain: -;; A name, which is required to be a symbol (string should also work, but for now a symbol simplifies this) -;; The parents are a list of class objects -> we don't want classes pointing to arbitrary objects -;; The fields are represented by a list of symbols, to refer to using the get-slot method -;; The methods in theory could be anything, as long as they're functions. -;; -;; Here we don't restrict the hash map of symbol -> function, but rather will restrict it on the -;; function when adding the methods. -(define/contract (Class name parents interfaces fields methods) - (->/c symbol? (listof Class-Object?) (listof Interface?) (listof symbol?) hash? Class-Object?) - (unless (list-subset? - ;; Collect the list of required methods for the given interfaces - (transduce interfaces (flat-mapping Interface-methods) (into-list)) - ;; Extract the methods that have been defined concretely on this class at construction - (hash-keys->list methods)) - (error! "Not all required methods are implemented for the given interfaces")) - - (Class-Object name - parents - interfaces - ;; Explicitly go collect the fields to flatten into this class given the - ;; class hierarchy - (combine-local-and-parent-fields fields parents) - methods)) - -(define (Make-Class name - #:fields (fields '()) - #:methods (methods '()) - #:parents (parents '()) - #:interfaces (interfaces '())) - (Class name parents interfaces fields methods)) - -(define (contains-duplicates? lst) - (not (equal? (hashset-length (apply hashset lst)) (length lst)))) - -;; Flatten the incoming list -(define (flatten lst) - (transduce lst (flattening) (into-list))) - -(define (collect-fields list-of-class-objects) - (transduce list-of-class-objects (flat-mapping Class-Object-fields) (into-list))) - -(define (combine-local-and-parent-fields local-fields list-of-class-objects) - (let ([appended (append local-fields (collect-fields list-of-class-objects))]) - (if (contains-duplicates? appended) (error! "Class field is unresolvable") appended))) - -;; ----------------------------------------------------------------------------------------------------- - -;; Add a method to a class -;; This can also occur in the root definition of the object in the hash, but the contract -;; won't be checked there at the moment TODO -(define/contract (define-method class-object name method) - (->/c Class-Object? symbol? function? any/c) - (let ([methods (Class-Object-methods class-object)]) - (set-Class-Object-methods! class-object (hash-insert methods name method)))) - -(define (resolve-parent-method class-object name) - (let ([possible-methods (map (lambda (x) (get-method x name)) (Class-Object-parents class-object))]) - (if (equal? 1 (length possible-methods)) - (car possible-methods) - (error! "Unable to resolve the method on the class instance")))) - -;; Attempt to resolve the method on the class object via the class hierarchy -;; This checks the current class object, and then attempt to resolve the method upwards, returning the first -;; one that it finds. If there are multiple candidates, we bail and error out since we can't determine -;; which method we intended to call. -(define/contract (get-method class-object name) - (->/c Class-Object? symbol? function?) - (let ([local-method (-> class-object (Class-Object-methods) (hash-try-get name))]) - ;; If _this_ class object contains the method, then we return this method - ;; This way we always select the correct method in the class hierarchy - (if local-method - local-method - ;; Otherwise, attempt to resolve the method on the parents - ;; For now, methods are just a hashmap from name -> method - (resolve-parent-method class-object name)))) - -;; Attempt to resolve the method on the parent class to this class objects -(define/contract (get-super-method class-object name) - (->/c Class-Object? symbol? function?) - (resolve-parent-method class-object name)) - -(define/contract (position? lst value) - (->/c list? any/c integer?) - (define list-length (length lst)) - (define (loop lst idx) - (cond - [(= idx list-length) - => - (error! "Value not a member of the list")] - [(equal? value (list-ref lst idx)) - => - idx] - [else - => - (loop lst (+ idx 1))])) - (loop lst 0)) - -;; Map the given field name to an index in the class' slot -(define/contract (%get-slot-idx class-object field-name) - (->/c Class-Object? symbol? integer?) - (-> class-object (Class-Object-fields) (position? field-name))) - -;; This returns whatever value is found in the slot -;; This _could_ be any value. TODO: find a way to bind a contract to a slot -(define/contract (get-slot class-instance field-name) - (->/c Class-Instance? symbol? any/c) - (mut-vector-ref (Class-Instance-fields class-instance) - (%get-slot-idx (Class-Instance-class-object class-instance) field-name))) - -;; Sets the slot in the class instance found a `field-name` to `value` -;; TODO: make the contract be dependent to have the result match the value's contract -(define/contract (set-slot! class-instance field-name value) - (->/c Class-Instance? symbol? any/c any/c) - (vector-set! (Class-Instance-fields class-instance) - ;; Get the slot index -> maps the field name to the index in the vector - (%get-slot-idx (Class-Instance-class-object class-instance) field-name) - value)) - -;; Instances should be represented in memory (for now) just as tagged vectors -;; Each of the fields will be zero'd out -(define/contract (%allocate-instance class-object) - (->/c Class-Object? Class-Instance?) - ;; We can just use a normal vector here, not a mutable vector - ;; Reference to the class object here - (Class-Instance class-object - ;; Fields as a mutable vector - ;; Name resolution should be done via %get-slot method - (apply mutable-vector (map (lambda (x) void) (Class-Object-fields class-object))))) - -;; Get the method on the class object denoted by the method name -;; and call it given the arguments here -;; TODO: figure out contracts for multi arity functions -(define (call class-instance method-name . args) - (apply (get-method (Class-Instance-class-object class-instance) method-name) - ;; The instance is always an implicit first argument, and as such gets - ;; cons onto the first of the arguments to call the function - (cons class-instance args))) - -;; Same as above, calls the method on the nearest parent object -(define (call-super class-instance method-name . args) - (apply (get-super-method (Class-Instance-class-object class-instance) method-name) - (cons class-instance args))) - -(define/contract (class-instance-name class-instance) - (->/c Class-Instance? symbol?) - (Class-Object-name (Class-Instance-class-object class-instance))) - -;; TODO -> check if object implements interface -;; (define/contract (class-implements-Interface? class-instance interface) -;; (->/c Class-Object? Interface? boolean?) -;; (member? interface -;; - -;; ------------------- Examples -------------------------- - -;; Base object for everything in the class hierarchy -(define Object - (Class 'Object - '() - '() - '() - (hash 'println - (lambda (self) - (displayln (-> "#<" - (string-append (symbol->string (class-instance-name self))) - (string-append ">"))))))) - -;; Define the class object for Animal -;; Is a child of the Object base class -(define Animal - (Class 'Animal - (list Object) - '() - '(name color weight) - (hash 'get-weight (lambda (self) (get-slot self 'weight))))) - -;; Dog inherits from Animal, adds on the 'good-boy? field -(define Dog (Class 'Dog (list Animal) '() '(good-boy?) (hash))) - -;; TODO: Once keyword arguments are a thing, classes could be defined like so: -;; (define Dog (Class #:name 'Dog -;; #:parents (list Animal) -;; #:interfaces '() -;; #:fields '(good-boy?) -;; #:methods (hash))) - -;; Allocates a new instance of a dog - here all of the fields are default to # -(define sherman (%allocate-instance Dog)) -;; Set the weight to be 25 -> this is set in the instance, and all fields are flattened on construction -(set-slot! sherman 'weight 25) -;; This will traverse upwards on the methods to find the method -(call sherman 'println) -;; This should find the method on the parent class -(call sherman 'get-weight) -;; Explicitly call the method on the super -(call-super sherman 'get-weight) -;; Setting a slot since the default allocation of the instance sets everything to void -(set-slot! sherman 'good-boy? #true) - -(define Stinky (Interface 'Stinky '(smelly icky))) - -(define Worm - (Class 'Worm - (list Dog) - (list Stinky) - '() - (hash 'smelly (lambda (self) "Smelly!") 'icky (lambda (self) "Icky!")))) - -(define New-Worm - (Make-Class 'Worm - #:parents (list Dog) - #:interfaces (list Stinky) - #:methods (hash 'smelly (lambda (self) "Smelly!") 'icky (lambda (self) "Icky!")))) diff --git a/cogs/clos/cog.scm b/cogs/clos/cog.scm deleted file mode 100644 index e4b785e5c..000000000 --- a/cogs/clos/cog.scm +++ /dev/null @@ -1,5 +0,0 @@ -(define package-name 'steel/clos) -(define version "0.1.0") - -;; Core library, requires no dependencies -(define dependencies '()) \ No newline at end of file diff --git a/cogs/collections/cog.scm b/cogs/collections/cog.scm deleted file mode 100644 index 73aa112b9..000000000 --- a/cogs/collections/cog.scm +++ /dev/null @@ -1,5 +0,0 @@ -(define package-name 'steel/collections) -(define version "0.1.0") - -;; Core library, requires no dependencies -(define dependencies '(steel/test)) \ No newline at end of file diff --git a/cogs/collections/dll.scm b/cogs/collections/dll.scm deleted file mode 100644 index 9b9e46003..000000000 --- a/cogs/collections/dll.scm +++ /dev/null @@ -1,72 +0,0 @@ -(provide dllist - dllink - insert-between - insert-before - insert-after - insert-head - insert-tail - remove-link - dllist-elements - dllist-head - dllist-tail - dllink-content - dllink-prev - dllink-next) - -(struct dllist (head tail) #:mutable #:transparent) -(struct dllink (content prev next) #:mutable #:transparent) - -(define (insert-between dlist before after data) - ; Insert a fresh link containing DATA after existing link - ; BEFORE if not nil and before existing link AFTER if not nil - (define new-link (dllink data before after)) - (if before (set-dllink-next! before new-link) (set-dllist-head! dlist new-link)) - (if after (set-dllink-prev! after new-link) (set-dllist-tail! dlist new-link)) - new-link) - -(define (insert-before dlist dlink data) - ; Insert a fresh link containing DATA before existing link DLINK - (insert-between dlist (dllink-prev dlink) dlink data)) - -(define (insert-after dlist dlink data) - ; Insert a fresh link containing DATA after existing link DLINK - (insert-between dlist dlink (dllink-next dlink) data)) - -(define (insert-head dlist data) - ; Insert a fresh link containing DATA at the head of DLIST - (insert-between dlist #f (dllist-head dlist) data)) - -(define (insert-tail dlist data) - ; Insert a fresh link containing DATA at the tail of DLIST - (insert-between dlist (dllist-tail dlist) #f data)) - -(define (remove-link dlist dlink) - ; Remove link DLINK from DLIST and return its content - (let ([before (dllink-prev dlink)] [after (dllink-next dlink)]) - (if before (set-dllink-next! before after) (set-dllist-head! dlist after)) - (if after (set-dllink-prev! after before) (set-dllist-tail! dlist before)))) - -(define (dllist-elements dlist) - ; Returns the elements of DLIST as a list - (define (extract-values dlink acc) - (if dlink (extract-values (dllink-next dlink) (cons (dllink-content dlink) acc)) acc)) - (reverse (extract-values (dllist-head dlist) '()))) - -(define (run) - (let ([dlist (dllist #f #f)]) - (insert-head dlist 1) - (displayln dlist) - (insert-tail dlist 4) - (displayln dlist) - (insert-after dlist (dllist-head dlist) 2) - (displayln dlist) - (let* ([next-to-last (insert-before dlist (dllist-tail dlist) 3)] - [bad-link (insert-before dlist next-to-last 42)]) - (remove-link dlist bad-link)) - (displayln dlist) - (displayln (dllist-elements dlist)) - (displayln dlist))) - -(define (loop) - (run) - (loop)) diff --git a/cogs/collections/iterators.scm b/cogs/collections/iterators.scm deleted file mode 100644 index 4224ec690..000000000 --- a/cogs/collections/iterators.scm +++ /dev/null @@ -1,33 +0,0 @@ -(struct StreamIterator - (iter-instance stream-empty-function stream-first-function stream-next-function)) - -(struct IntoIterator (iter-object next-function) #:prop:procedure 1) - -;; Use the builtin "iterator finished" symbol -(define ITERATOR-FINISHED (load-from-module! %-builtin-module-steel/meta '#%iterator-finished)) - -(define (iter-finished? value) - (eq? value ITERATOR-FINISHED)) - -(define (iter-next into-iterator) - (into-iterator (IntoIterator-iter-object into-iterator))) - -;; Generically get the iterator -(define (into-iter obj) - ;; Check if this is a builtin type - if so, delegate to the underlying iterator - (define maybe-builtin-iterator (value->iterator obj)) - - (if maybe-builtin-iterator - (IntoIterator maybe-builtin-iterator iter-next!) - ((#%struct-property-ref obj '#:prop:into-iter) obj))) - -;; Call value for each thing -(define (iter-for-each iter func) - - (define next-value (iter-next iter)) - - (if (iter-finished? next-value) - void - (begin - (func next-value) - (iter-for-each iter func)))) diff --git a/cogs/collections/mhash.scm b/cogs/collections/mhash.scm deleted file mode 100644 index 0b92f2aa1..000000000 --- a/cogs/collections/mhash.scm +++ /dev/null @@ -1,22 +0,0 @@ -(require-builtin #%private/steel/mhash as private.) - -(struct mutable-hash (inner) #:mutable) - -(define (mhash-set! mhash key value) - (private.mhash-set! (mutable-hash-inner mhash) key value)) - -(define (mhash-ref mhash key) - (private.mhash-ref (mutable-hash-inner mhash) key)) - -(define (mhash) - (mutable-hash (private.mhash))) - -(define (loop) - (define my-hash (mhash)) - - (mhash-set! my-hash 'foo 'bar) - (mhash-set! my-hash 'bar 'foo) - - (mhash-set! my-hash 'baz my-hash) - - (loop)) diff --git a/cogs/collections/mpair.scm b/cogs/collections/mpair.scm deleted file mode 100644 index 8b4208e8a..000000000 --- a/cogs/collections/mpair.scm +++ /dev/null @@ -1,83 +0,0 @@ -;; Compatible layer for R7RS Mutable pairs. - -(struct mcons (mcar mcdr) - #:mutable - #:printer (lambda (obj printer) - (if (mlist? obj) - - (begin - (simple-display "'") - (printer (mcons->list obj))) - - (begin - (simple-display "'(") - (printer (mcons-mcar obj)) - (simple-display " . ") - (printer (mcons-mcdr obj)) - (simple-display ")"))))) - -(define set-car! set-mcons-mcar!) -(define set-cdr! set-mcons-mcdr!) - -;; Mutable cons! -(define (mcons->list mutable-cons) - - (define (loop mutable-cons builder) - - (if (not (mcons? (dbg! (mcons-mcdr mutable-cons)))) - - (#%prim.cons (mcons-mcar mutable-cons) builder) - - (loop (mcons-mcdr mutable-cons) (#%prim.cons (mcons-mcar mutable-cons) builder)))) - - (reverse (loop mutable-cons '()))) - -(define (mlist? cell) - (define next (mcons-mcdr cell)) - - (or (mcons? next) (null? next))) - -(define (pair? x) - (or (mcons? x) (#%prim.pair? x))) - -(define (cons a b #:mutable [mutable #false]) - (cond - [mutable (mcons a b)] - [(list? b) (#%prim.cons a b)] - [(mcons? b) (mcons a b)] - [else (#%prim.cons a b)])) - -(define (car a) - (if (mcons? a) (mcons-mcar a) (#%prim.car a))) - -(define (cdr a) - (if (mcons? a) (mcons-mcdr a) (#%prim.cdr a))) - -; (cons 10 20 #:mutable #true) - -;; Can make a loop, and garbage collection solves it! -; (define (loop) - -; (define my-cons (mcons 10 (mcons 20 (mcons 30 void)))) - -; ;; Make a cycle! -; (set-car! my-cons my-cons) - -; (loop)) - -;; TODO: Fix this error - probably just need to not roll back, and when encountering a new value, -;; just fill the open slot / skip the slot in the global environment / fill it with a poisoned -;; value. -; > :load cogs/collections/mpair.scm -; => '(10 . 20) -; λ (cogs/collections/mpair.scm) > (cons 10 20 #:mutable) -; Unable to locate source and span information for this error: Error: ArityMismatch: Missing keyword argument! -; λ (cogs/collections/mpair.scm) > (cons 10 20 #:mutable #true) -; => '(10 . 20) -; λ (cogs/collections/mpair.scm) > (define my-pair (cons 10 20 #:mutable)) -; Unable to locate source and span information for this error: Error: ArityMismatch: Missing keyword argument! -; λ (cogs/collections/mpair.scm) > (define my-pair (cons 10 20 #:mutable #true)) -; thread 'main' panicked at 'assertion failed: `(left == right)` -; left: `999`, -; right: `1000`', crates/steel-core/src/env.rs:59:13 -; note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/cogs/collections/tests.scm b/cogs/collections/tests.scm deleted file mode 100644 index dee70d2e6..000000000 --- a/cogs/collections/tests.scm +++ /dev/null @@ -1,72 +0,0 @@ -(require "dll.scm") -(require "steel/tests/unit-test.scm" - (for-syntax "steel/tests/unit-test.scm")) - -(define __dll-module 'dll-module) - -(provide __dll-module) - -(define (run) - (let ([dlist (dllist #f #f)]) - (insert-head dlist 1) - (displayln dlist) - (insert-tail dlist 4) - (displayln dlist) - (insert-after dlist (dllist-head dlist) 2) - (displayln dlist) - (let* ([next-to-last (insert-before dlist (dllist-tail dlist) 3)] - [bad-link (insert-before dlist next-to-last 42)]) - (remove-link dlist bad-link)) - (displayln dlist) - (displayln (dllist-elements dlist)) - (displayln dlist))) - -(test-module "dll-tests" - (check-equal? "creating dll with cycle works" - #true - (begin - (run) - #true))) - -;; Testing deep collections -(struct ConsCell (car cdr) #:mutable #:transparent) - -(define (build-list x) - (cond - [(equal? x 100000) void] - [else (ConsCell x (build-list (+ x 1)))])) - -(define (build-nested-list x y) - (cond - [(equal? x y) void] - [else (ConsCell x (build-nested-list (+ x 1) y))])) - -(define (build-hashmap-chain x) - (if (equal? x 100000) (hash 'a x) (hash 'a (build-hashmap-chain (+ x 1))))) - -(define (build-list-chain x) - (if (equal? x 10000) (list x) (list x (build-list-chain (+ x 1))))) - -; (define (test-depth) - -; (define (build-deep-hash x) -; (if (equal? x 100000) (hash x x) (hash (build-deep-hash (+ x 1)) x))) - -; (define m (build-deep-hash 0)) -; (define o (build-deep-hash 0)) - -; (equal? m o)) - -(test-module "deep recursive data structures" - ; (check-equal? "deep hash structures" #true (test-depth)) - (check-equal? "dropping deep list doesn't panic" - #true - (let ([foo (build-nested-list 0 100000)]) #true)) - (check-equal? "dropping deep built in list doesn't panic" - #true - (let ([foo (build-list-chain 0)]) - - #true)) - (check-equal? "dropping deep hashmap chain does't panic" - #true - (let ([foo (build-hashmap-chain 0)]) #true))) diff --git a/cogs/colors/cog.scm b/cogs/colors/cog.scm deleted file mode 100644 index a4690e92f..000000000 --- a/cogs/colors/cog.scm +++ /dev/null @@ -1,5 +0,0 @@ -(define package-name 'steel/colors) -(define version "0.1.0") - -;; Core library, requires no dependencies -(define dependencies '()) diff --git a/cogs/colors/colors.scm b/cogs/colors/colors.scm deleted file mode 100644 index 34ff79ba3..000000000 --- a/cogs/colors/colors.scm +++ /dev/null @@ -1,49 +0,0 @@ -(require "srfi/srfi-28/format.scm") - -(provide display-color - displayln-color) - -(define (terminal-command command) - (format "~a~a" #\u001B command)) - -(define (terminal-reset) - (terminal-command "[0m")) - -(define (terminal-colors bg fg bold? underline?) - (terminal-command (format "[~a;~a~a~am" - (case bg - [(black) "40"] - [(red) "41"] - [(green) "42"] - [(yellow) "43"] - [(blue) "44"] - [(magenta) "45"] - [(cyan) "46"] - [(white) "47"] - [(default) "49"]) - (case fg - [(black) "30"] - [(red) "31"] - [(green) "32"] - [(yellow) "33"] - [(blue) "34"] - [(magenta) "35"] - [(cyan) "36"] - [(white) "37"] - [(default) "39"]) - (if bold? ";1" "") - (if underline? ";4" "")))) - -(define (output-color output-method datum #:fg fg #:bg bg) - (terminal-colors bg fg #f #f) - (output-method datum) - (display (terminal-reset))) - -(define (display-color datum #:fg [fg 'default] #:bg [bg 'default]) - (output-color display datum #:fg fg #:bg bg)) - -(define (displayln-color datum #:fg [fg 'default] #:bg [bg 'default]) - (display (terminal-colors bg fg #f #f)) - (display datum) - (display (terminal-reset)) - (newline)) diff --git a/cogs/command-line/args.scm b/cogs/command-line/args.scm deleted file mode 100644 index 12ea233a0..000000000 --- a/cogs/command-line/args.scm +++ /dev/null @@ -1,5 +0,0 @@ - -;; Basic command line parsing into a hashmap, until I figure out how to -;; dynamically deconstruct the arg list into a struct -(define (parse-args arg-list) - (error "todo!")) \ No newline at end of file diff --git a/cogs/command-line/cog.scm b/cogs/command-line/cog.scm deleted file mode 100644 index 4adf6089e..000000000 --- a/cogs/command-line/cog.scm +++ /dev/null @@ -1,5 +0,0 @@ -(define package-name 'steel/command-line) -(define version "0.1.0") - -;; Core library, requires no dependencies -(define dependencies '()) \ No newline at end of file diff --git a/cogs/contract-checker/check.scm b/cogs/contract-checker/check.scm deleted file mode 100644 index fa2ddd367..000000000 --- a/cogs/contract-checker/check.scm +++ /dev/null @@ -1 +0,0 @@ -; () \ No newline at end of file diff --git a/cogs/contracts/cog.scm b/cogs/contracts/cog.scm deleted file mode 100644 index f66256f77..000000000 --- a/cogs/contracts/cog.scm +++ /dev/null @@ -1,5 +0,0 @@ -(define package-name 'steel/contracts) -(define version "0.1.0") - -;; Core library, requires no dependencies -(define dependencies '(steel/test)) \ No newline at end of file diff --git a/cogs/contracts/contract-test.scm b/cogs/contracts/contract-test.scm deleted file mode 100644 index 5cc6aa219..000000000 --- a/cogs/contracts/contract-test.scm +++ /dev/null @@ -1,46 +0,0 @@ -(require "steel/tests/unit-test.scm" - (for-syntax "steel/tests/unit-test.scm")) - -(provide foo) - -(define/contract (foo x y) - (->/c even? odd? odd?) - (+ x y)) - -(define/contract (simple-higher-order x func) - (->/c odd? (->/c odd? even?) even?) - (func x)) - -(define (any? x) - (displayln "***** CHECKING ANY? *****") - #true) - -(define (int-checker? x) - (displayln "***** CHECKING INT? ******") - (int? x)) -(define (number-checker? x) - (displayln "***** CHECKING NUMBER? ******") - (number? x)) - -(define level1 - (bind/c (make-function/c (make-function/c (FlatContract number-checker? 'number-checker?))) - (lambda () - (lambda () - (displayln "@@@@@@@@@@ CALLING FUNCTION @@@@@@@@@@@") - 10)) - 'level1)) - -(define level2 - (bind/c (make-function/c (make-function/c (FlatContract int-checker? 'int-checker))) - (lambda () (level1)) - 'level2)) - -(define level3 - (bind/c (make-function/c (make-function/c (FlatContract any? 'any?))) (lambda () (level2)) 'level3)) - -(test-module - "check-basic-contract-checking" - (check-equal? "basic contract" (foo 10 21) 31) - (check-err? "Should raise a contract violation" (foo 11 10) 21) - (check-equal? "higher order contract works" (simple-higher-order 11 (lambda (x) (+ x 1))) 12) - (check-equal? "Multiple levels of contracts apply" ((level3)) 10)) diff --git a/cogs/contracts/contract.scm b/cogs/contracts/contract.scm deleted file mode 100644 index 84d1c4f0c..000000000 --- a/cogs/contracts/contract.scm +++ /dev/null @@ -1,415 +0,0 @@ -(provide make-function-contract - make-contract - bind-contract-to-function - FlatContract - FlatContract? - FlatContract-predicate - FlatContract-name - FunctionContract - FunctionContract? - FunctionContract-pre-conditions - FunctionContract-post-condition - contract->string - (for-syntax ->c) - (for-syntax define/c)) - -;; struct definitions -(struct FlatContract (predicate name) #:prop:procedure 0) -;; Contract Attachment - use this for understanding where something happened -(struct ContractAttachmentLocation (type name)) - -;; Function Contract - keep track of preconditions and post conditions, where -;; the contract was attached, and a pointer to the parent contract. Can probably -;; replace parent with just a list of the parents since it can be shared -;; directly -(struct FunctionContract (pre-conditions post-condition contract-attachment-location parents)) - -(struct DependentPair (argument-name arguments thunk thunk-name)) - -(struct DependentContract - (arg-positions pre-conditions post-condition contract-attachment-location parent)) - -;; TODO: Raise error with contract violation directly attached, if possible -;; -(struct ContractViolation (error-message)) - -(struct ContractedFunction (contract function name)) - -;; Alias the name for clarity -(define make-flat-contract FlatContract) - -;;#| -;; Testing out a multi line comment... -;; |# -(define (new-FunctionContract #:pre-conditions pre-conditions - #:post-condition post-condition - #:contract-attachment-location (contract-attachment-location void) - ;; TODO: so this parents business isn't even practical - ;; -> it can get removed safely, maybe revisited later - #:parents (parents '())) - (FunctionContract pre-conditions post-condition contract-attachment-location parents)) - -;; Formats a contract nicely as a string -(define (contract->string contract) - (cond - [(FlatContract? contract) - => - (symbol->string (FlatContract-name contract))] - [(FunctionContract? contract) - => - (to-string "(->" - (apply to-string - (transduce (FunctionContract-pre-conditions contract) - (mapping contract->string) - (into-list))) - (contract->string (FunctionContract-post-condition contract)) - ")")] - [else - => - (error! "Unexpected value found in contract:" contract)])) - -;; Given a list, splits off the last argument, returns as a pair -(define (split-last lst) - (define (loop accum lst) - (if (empty? (cdr lst)) (list (reverse accum) (car lst)) (loop (cons (car lst) accum) (cdr lst)))) - (loop '() lst)) - -;;@doc -;; Creates a `FunctionContract` from the list of conditions, splitting out the -;; preconditions and the postconditions -(define make-function-contract - (lambda conditions - (%plain-let ((split (split-last conditions))) - (FunctionContract (first split) (second split) void '())))) - -;; Applies a flat contract to the given argument -(define (apply-flat-contract flat-contract arg) - ; ((FlatContract-predicate flat-contract) arg) - (if (flat-contract arg) - #true - (ContractViolation - (to-string "Contract violation: found in the application of a flat contract for" - (FlatContract-name flat-contract) - ": the given input:" - arg - "resulted in a contract violation")))) - -;; ; (define (apply-parents parent name function arguments span) -;; ; (if (void? parent) -;; ; #true -;; ; (begin -;; ; (displayln "Applying parent contract") -;; ; (apply-function-contract (ContractedFunction-contract parent) -;; ; name -;; ; function -;; ; arguments -;; ; span) - -;; ; (apply-parents (FunctionContract-parent parent) name function arguments span)))) - -;; Call a contracted function -(define (apply-contracted-function contracted-function arguments span) - ; (displayln "Passed in span: " span) - (define span (if span span '(0 0 0))) - (apply-function-contract (ContractedFunction-contract contracted-function) - (ContractedFunction-name contracted-function) - (ContractedFunction-function contracted-function) - arguments - span)) - -;;@doc -;; Verifies the arguments against the FunctionContract, and then produces -;; a new list of arguments, with any arguments wrapped in function contracts if they happen -;; to be higher order -(define (verify-preconditions self-contract arguments name span) - (unless (equal? (length arguments) (length (FunctionContract-pre-conditions self-contract))) - (error-with-span span - "Arity mismatch, function expected " - (length (FunctionContract-pre-conditions self-contract)) - "Found: " - (length arguments))) - - (transduce - arguments - (zipping (FunctionContract-pre-conditions self-contract)) - (enumerating) - (mapping - (lambda (x) - (let ([i (first x)] [arg (first (second x))] [contract (second (second x))]) - - (cond - [(FlatContract? contract) - => - - (let ([result (apply-flat-contract contract arg)]) - (if (ContractViolation? result) - (error-with-span span - "This function call caused an error" - "- it occured in the domain position:" - i - ", with the contract: " - (contract->string contract) - (ContractViolation-error-message result) - ", blaming " - (ContractAttachmentLocation-name - (FunctionContract-contract-attachment-location self-contract)) - "(callsite)") - arg))] - [(FunctionContract? contract) - => - (if (ContractedFunction? arg) - (let ([pre-parent (ContractedFunction-contract arg)]) - (let ([parent (new-FunctionContract - #:pre-conditions (FunctionContract-pre-conditions pre-parent) - #:post-condition (FunctionContract-post-condition pre-parent) - #:contract-attachment-location - (ContractAttachmentLocation 'DOMAIN (ContractedFunction-name arg)) - #:parents (FunctionContract-parents pre-parent))]) - (let ([fc (new-FunctionContract - #:pre-conditions (FunctionContract-pre-conditions contract) - #:post-condition (FunctionContract-post-condition contract) - #:contract-attachment-location - (ContractAttachmentLocation 'DOMAIN (ContractedFunction-name arg)) - #:parents (cons parent (FunctionContract-parents parent)))]) - - (bind-contract-to-function fc arg name span)))) - (bind-contract-to-function contract arg name span))] - [else - => - (error! "Unexpected value in pre conditions: " contract)])))) - (into-list))) - -(define (apply-function-contract contract name function arguments span) - ;; Check that each of the arguments abides by the - (let ([validated-arguments (verify-preconditions contract arguments name span)]) - - (let ([output (with-handler (lambda (err) (raise-error err)) - (apply function validated-arguments))] - - [self-contract contract] - [self-contract-attachment-location (FunctionContract-contract-attachment-location contract)] - [contract (FunctionContract-post-condition contract)]) - - (cond - [(FlatContract? contract) - => - - (let ([result (apply-flat-contract contract output)]) - (if (ContractViolation? result) - (let ([blame-location (if (void? self-contract-attachment-location) - name - self-contract-attachment-location)]) - - (cond - [(void? blame-location) - => - (error-with-span - span - "this function call resulted in an error - occured in the range position of this contract: " - (contract->string self-contract) - (ContractViolation-error-message result) - "blaming: None - broke its own contract")] - - [else - => - (error-with-span - span - "this function call resulted in an error - occurred in the range position of this contract: " - (contract->string self-contract) - (ContractViolation-error-message result) - "blaming: " - blame-location)])) - - output))] - [(FunctionContract? contract) - => - - (define original-function output) - - (if (FunctionContract? (get-contract-struct output)) - - ;; TODO: Come back to this and understand what the heck its doing - ;; Figured it out -> its never actually a contracted function, because we're wrapping - ;; it directly in a normal function type. - (begin - (define output (get-contract-struct output)) - (define pre-parent contract) - (define contract-attachment-location - (ContractAttachmentLocation 'RANGE - (ContractAttachmentLocation-name - self-contract-attachment-location))) - (define parent - (new-FunctionContract #:pre-conditions (FunctionContract-pre-conditions pre-parent) - #:post-condition (FunctionContract-post-condition pre-parent) - #:contract-attachment-location contract-attachment-location - #:parents (FunctionContract-parents pre-parent))) - (define fc - (new-FunctionContract #:pre-conditions (FunctionContract-pre-conditions contract) - #:post-condition (FunctionContract-post-condition contract) - #:contract-attachment-location contract-attachment-location - #:parents (cons parent (FunctionContract-parents pre-parent)))) - - (bind-contract-to-function fc original-function name span)) - (bind-contract-to-function contract output name span))] - [else - => - (error! "Unhandled value in post condition: " contract)])))) - -(define (bind-contract-to-function contract function name . span) - (define post-condition (FunctionContract-post-condition contract)) - - (let ([updated-preconditions - (transduce - (FunctionContract-pre-conditions contract) - (mapping (lambda (c) - (cond - [(FlatContract? c) - => - c] - [(FunctionContract? c) - => - (FunctionContract (FunctionContract-pre-conditions c) - (FunctionContract-post-condition c) - (ContractAttachmentLocation 'DOMAIN name) - (FunctionContract-parents c))] - [else - => - (error "Unexpected value found in bind-contract-to-function preconditions: " - c)]))) - (into-list))] - - [updated-postcondition - (cond - [(FlatContract? post-condition) - => - post-condition] - [(FunctionContract? post-condition) - => - - (FunctionContract (FunctionContract-pre-conditions post-condition) - (FunctionContract-post-condition post-condition) - (ContractAttachmentLocation 'RANGE name) - (FunctionContract-parents post-condition))] - [else - => - - (error "Unexpected value found in bind-contract-to-function post condition: " - post-condition)])]) - - (let ([contracted-function - (ContractedFunction (FunctionContract updated-preconditions - updated-postcondition - ; void - ; (ContractAttachmentLocation 'TOPLEVEL name) - ; void - (ContractAttachmentLocation 'TOPLEVEL name) - (if (get-contract-struct function) - (cons (get-contract-struct function) - (FunctionContract-parents contract)) - (FunctionContract-parents contract))) - function - name)]) - - (let ([resulting-lambda-function - (lambda args - - (apply-contracted-function - contracted-function - args - ; span - ; (current-function-span) - (if span (car span) (current-function-span)) - ; (begin (displayln ("Current span: " (current-function-span))) - ; (current-function-span))) - ))]) - (attach-contract-struct! resulting-lambda-function - (ContractedFunction-contract contracted-function)) - resulting-lambda-function)))) - -(define (make-contract contract name) - (cond - [(FlatContract? contract) contract] - [(FunctionContract? contract) contract] - [else - => - (FlatContract contract name)])) - -(define-syntax ->c - (syntax-rules () - [(->c r) (make-function-contract (make-contract r 'r))] - [(->c a b) (make-function-contract (make-contract a 'a) (make-contract b 'b))] - [(->c a b c) - (make-function-contract (make-contract a 'a) (make-contract b 'b) (make-contract c 'c))] - [(->c a b c d) - (make-function-contract (make-contract a 'a) - (make-contract b 'b) - (make-contract c 'c) - (make-contract d 'd))] - [(->c a b c d e) - (make-function-contract (make-contract a 'a) - (make-contract b 'b) - (make-contract c 'c) - (make-contract d 'd) - (make-contract e 'e))] - [(->c a b c d e f) - (make-function-contract (make-contract a 'a) - (make-contract b 'b) - (make-contract c 'c) - (make-contract d 'd) - (make-contract e 'e) - (make-contract f 'f))] - [(->c a b c d e f g) - (make-function-contract (make-contract a 'a) - (make-contract b 'b) - (make-contract c 'c) - (make-contract d 'd) - (make-contract e 'e) - (make-contract f 'f) - (make-contract g 'g))] - [(->c a b c d e f g h) - (make-function-contract (make-contract a 'a) - (make-contract b 'b) - (make-contract c 'c) - (make-contract d 'd) - (make-contract e 'e) - (make-contract f 'f) - (make-contract g 'g) - (make-contract h 'h))] - [(->c a b c d e f g h i) - (make-function-contract (make-contract a 'a) - (make-contract b 'b) - (make-contract c 'c) - (make-contract d 'd) - (make-contract e 'e) - (make-contract f 'f) - (make-contract g 'g) - (make-contract h 'h) - (make-contract i 'i))])) - -;; Macro for basic usage of contracts -(define-syntax define/c - (syntax-rules () - [(define/c (name args ...) - contract - body ...) - (begin - (define name - (lambda (args ...) - body ...)) - (set! name (bind-contract-to-function contract name 'name)) - void) - ; (define name (bind/c contract (lambda (args ...) body ...) 'name)) - ] - [(define/c name - contract - expr) - (define name - ((bind-contract-to-function (make-function-contract (make-contract contract 'contract)) - (lambda () expr))))])) - -(provide (for-syntax contract/out/test)) - -(define-syntax contract/out/test - (syntax-rules () - [(contract/out/test name contract) - (%require-ident-spec name (bind-contract-to-function contract name 'name))])) diff --git a/cogs/contracts/types.scm b/cogs/contracts/types.scm deleted file mode 100644 index c6629a6a9..000000000 --- a/cogs/contracts/types.scm +++ /dev/null @@ -1,111 +0,0 @@ -(require "contract.scm" - (for-syntax "contract.scm")) - -(provide contract? - listof - hashof - non-empty-listof - /c - <=/c - >=/c - any/c - and/c - or/c) - -(define (loop pred lst) - (cond - [(null? lst) #t] - [(pred (car lst)) (loop pred (cdr lst))] - [else #f])) - -;; Contract combinators -(define (listof pred) - (make-contract (lambda (lst) - (cond - [(null? lst) #t] - [(list? lst) (loop pred lst)] - [else #f])) - (list 'listof (contract-or-procedure-name pred)))) - -(define (hashof key-pred value-pred) - (make-contract - (lambda (hashmap) - ;; For hashof - we want to assert that both all of the keys and all of the values abide by - ;; a specific predicate - (and ((listof key-pred) (hash-keys->list hashmap)) - ((listof value-pred) (hash-values->list hashmap)))) - (list 'hashof (contract-or-procedure-name key-pred) (contract-or-procedure-name value-pred)))) - -(define (contract-or-procedure-name x) - (cond - [(FlatContract? x) (FlatContract-name x)] - [(FunctionContract? x) (string->symbol (contract->string x))] - [else - (let ([lookup (function-name x)]) (if (string? lookup) (string->symbol lookup) '#))])) - -;; Like listof, however requires that the list is non empty as well -(define (non-empty-listof pred) - (make-contract (lambda (lst) - (cond - [(null? lst) - (displayln "getting here?") - #f] - [(list? lst) (loop pred lst)] - [else #f])) - (list 'non-empty-listof (contract-or-procedure-name pred)))) - -;; Contracts for < -(define ( -(define (>/c n) - (make-contract (fn (x) (> x n)) (list '>/c n))) - -;; Contracts for <= -(define (<=/c n) - (make-contract (fn (x) (<= x n)) (list '<=/c n))) - -;; Contracts for >= -(define (>=/c n) - (make-contract (fn (x) (>= x n)) (list '>=/c n))) - -;; Satisfies any single value -(define (any/c x) - (make-contract (fn (x) #t) 'any/c)) - -;; produces a function compatible with contract definitions -(define (and/c x y) - (make-contract (lambda (z) (and (x z) (y z))) (list 'and/c x y))) - -;; produces a function compatible with contract definitions -(define (or/c x y) - (make-contract (lambda (z) (or (x z) (y z))) (list 'or/c x y))) - -(define combinators (hashset listof non-empty-listof /c <=/c >=/c any/c and/c or/c)) - -(define (contract? predicate-or-contract) - (cond - [(function? predicate-or-contract) - (or (equal? (arity? predicate-or-contract) 1) - (hashset-contains? combinators predicate-or-contract))] - [else - => - #f])) - -;; TODO: Come back to this - I think I need to be very specific about the then-contract and else-contract -;; and how they get applied -; (define (if/c predicate then-contract else-contract) -; (make-contract -; (lambda (x) (if (predicate x) -; (then-contract predicate) -; (else-contract predicate))) -; (list 'if/c then-contract else-contract))) - -; (define (type/c type contract) -; (and/c - -; ) - -; (define (int/c )) diff --git a/cogs/coop/cog.scm b/cogs/coop/cog.scm deleted file mode 100644 index b2fc5d202..000000000 --- a/cogs/coop/cog.scm +++ /dev/null @@ -1,5 +0,0 @@ -(define package-name 'steel/coop) -(define version "0.1.0") - -;; Core library, requires no dependencies -(define dependencies '()) \ No newline at end of file diff --git a/cogs/coop/threads.scm b/cogs/coop/threads.scm deleted file mode 100644 index ca4445b5a..000000000 --- a/cogs/coop/threads.scm +++ /dev/null @@ -1,79 +0,0 @@ -; *thread-queue* : list[continuation] -(define *thread-queue* '()) - -; halt : continuation -(define halt #f) - -; current-continuation : -> continuation -(define (current-continuation) - (call/cc (lambda (cc) (cc cc)))) - -; spawn : (-> anything) -> void -(define (spawn thunk) - (let ([cc (current-continuation)]) - (if (continuation? cc) - (set! *thread-queue* (append *thread-queue* (list cc))) - (begin - (thunk) - (quit))))) - -; yield : value -> void -(define (yield) - (let ([cc (current-continuation)]) - (if (and (continuation? cc) (pair? *thread-queue*)) - (let ([next-thread (car *thread-queue*)]) - (set! *thread-queue* (append (cdr *thread-queue*) (list cc))) - (next-thread 'resume)) - void))) - -; quit : -> ... -(define (quit) - (if (pair? *thread-queue*) - (let ([next-thread (car *thread-queue*)]) - (set! *thread-queue* (cdr *thread-queue*)) - (next-thread 'resume)) - (halt))) - -; start-threads : -> ... -(define (start-threads) - (let ([cc (current-continuation)]) - ; (displayln cc) - (if cc - (begin - ; (displayln cc) - (set! halt - (lambda () - ; (inspect-bytecode cc) - ; (displayln cc) - (cc #f))) - ; (displayln cc) - (if (null? *thread-queue*) - void - (begin - (let ([next-thread (car *thread-queue*)]) - (set! *thread-queue* (cdr *thread-queue*)) - (next-thread 'resume))))) - void))) - -;; Example cooperatively threaded program -(define counter 10) - -(define (make-thread-thunk name) - (define (loop) - (when (< counter 0) - (quit)) - (display "in thread ") - (display name) - (display "; counter = ") - (display counter) - (newline) - (set! counter (- counter 1)) - (yield) - (loop)) - loop) - -(spawn (make-thread-thunk 'a)) -(spawn (make-thread-thunk 'b)) -(spawn (make-thread-thunk 'c)) - -(start-threads) diff --git a/cogs/dispatch.scm b/cogs/dispatch.scm deleted file mode 100644 index 634b8dbef..000000000 --- a/cogs/dispatch.scm +++ /dev/null @@ -1,80 +0,0 @@ -(require "contracts/contract.scm" - (for-syntax "contracts/contract.scm") - "contracts/types.scm") - -(require "logging/log.scm") - -; (provide apples) - -;;@doc -;; Apples are a tasty fruit -(define/c (apples x) - (->c even? odd?) - (+ x 1)) - -; (define/c (bananas y) -; (->c even? odd?) -; (+ y 1)) - -; (dbg! (apples 10)) -; (dbg! (bananas 10)) - -; (dbg! (equal? (get-contract-struct apples) -; (get-contract-struct bananas))) - -; (dbg! (get-contract-struct apples)) -; (dbg! (get-contract-struct bananas)) - - -; (define (function-contract-equal? l r) -; (and (equal? (FunctionContract-pre-conditions l) -; (FunctionContract-pre-conditions r)) -; (equal? (FunctionContract-post-condition l) -; (FunctionContract-post-condition r)))) - -; (dbg! (function-contract-equal? -; (get-contract-struct apples) -; (get-contract-struct bananas))) - -; (dbg! (->c even? odd?)) - - - -; (define (transform-doc expr comment) -; (if (equal? (car expr) 'define) -; `(begin -; (define ,(concat-symbols '__doc- (second expr)) comment) -; ,expr -; ) -; (displayln "didn't work!"))) - - -; (dbg! (transform-doc '(define x 100) "this is the value that is associated with 100")) - - -;;@doc -;; -;; Seeing if a comment will show up in the repl. -;; -;; perhaps introduce some markdown: -;; -;; ```scheme -;; (list 10 20 30 40) -;; ``` -(define x 100) - -;;@doc -;; Hello world! -(define y 20) - -; (displayln __doc-apples) -; (displayln __doc-x) -; (displayln __doc-y) - -(define/c (applesauce lst) - (->c (listof int?) (listof number?)) - (map (fn (x) (+ x 0.1)) lst)) - -;;@doc -;; A fun little doc -(struct FooBarBaz (a b c)) \ No newline at end of file diff --git a/cogs/download.scm b/cogs/download.scm deleted file mode 100644 index 142ae495f..000000000 --- a/cogs/download.scm +++ /dev/null @@ -1,70 +0,0 @@ -;; Download a package from git - These should be stored in a reasonable location, probably under the -;; $STEEL_HOME directory. - -(require-builtin steel/process) -(require "steel/result") - -;; Sources! -(define (append-with-separator path dir-name) - (if (ends-with? path "/") (string-append path dir-name) (string-append path "/" dir-name))) - -(define (path-from-steel-home dir) - (~> "STEEL_HOME" (env-var) (append-with-separator dir))) - -(define *COG_DIR* (path-from-steel-home "cogs")) -(define *NATIVE_SOURCES_DIR* (path-from-steel-home "sources")) - -;; Most likely should use gix here instead of shelling out to git? -;; Use the sha to pin to a specific commit, if interested -(define (git-clone package-name https-address installation-dir #:sha (*sha* void)) - (define resulting-path (string-append installation-dir "/" package-name)) - ;; Git clone command, run against specific directory. For now we're going to - ;; naively install them all into the same spot. - (~> (command "git" (list "clone" https-address resulting-path)) (spawn-process) (Ok->value) (wait)) - ;; If we have a SHA, check out that commit - (when (not (void? *sha*)) - (~> (command "git" (list "checkout" *sha*)) - (in-directory resulting-path) - (spawn-process) - (Ok->value) - (wait))) - - resulting-path) - -;; Run the process in the given directory -(define (in-directory command directory) - (set-current-dir! command directory) - command) - -;; Run the cargo-steel-lib installer in the target directory -(define (run-dylib-installation target-directory) - (~> (command "cargo-steel-lib" '()) - (in-directory target-directory) - (spawn-process) - (Ok->value) - (wait))) - -;; TODO: steps -;; - git clone to temporary directory (or site-packages style thing, something) -;; Probably install native dylibs to their own native section -;; Then, run the installation script. - -;; Install to the im-lists directory. What we probably have to do is install it to some -;; temporary location, parse the module name, and move it back out. Unless - we do something -;; like the org name, but I don't love that. -; (git-clone "im-lists" "https://github.com/mattwparas/im-lists.git" *NATIVE_SOURCES_DIR*) - -;; Download and install the library! -; (define (download-and-install-library library-name git-url) -; (~> (git-clone library-name git-url *NATIVE_SOURCES_DIR*) -; (run-dylib-installation installation-directory))) - -;; TODO: Publish steel in its currently form, extremely experimental. -;; Once steel is in a stable position, dylibs can reference the dependency by version -;; on crates.io - -;; -; (git-clone "helix-configuration" -; "https://github.com/mattwparas/helix-config.git" -; *COG_DIR* -; #:sha "ae01ad7a3e7a48dad0ddbe8b812ab162aba31732") diff --git a/cogs/dump.txt b/cogs/dump.txt deleted file mode 100644 index aecdfb94e..000000000 --- a/cogs/dump.txt +++ /dev/null @@ -1,338 +0,0 @@ -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__hash -> #%prim.hash -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__#%vtable-update-entry! -> #%prim.#%vtable-update-entry! -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__void -> #%prim.void -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__list-ref -> #%prim.list-ref -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__list-ref -> #%prim.list-ref -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__list-ref -> #%prim.list-ref -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__list-ref -> #%prim.list-ref -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__make-struct-type -> #%prim.make-struct-type -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__list -> #%prim.list -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__void -> #%prim.void -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__null? -> #%prim.null? -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__list? -> #%prim.list? -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__append -> #%prim.append -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__car -> #%prim.car -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__cdr -> #%prim.cdr -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__list -> #%prim.list -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__= -> #%prim.= -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__length -> #%prim.length -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__first -> #%prim.first -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__push-back -> #%prim.push-back -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__empty? -> #%prim.empty? -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__list -> #%prim.list -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__< -> #%prim.< -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__first -> #%prim.first -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__cons -> #%prim.cons -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__equal? -> #%prim.equal? -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__first -> #%prim.first -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__cons -> #%prim.cons -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__first -> #%prim.first -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__rest -> #%prim.rest -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__cons -> #%prim.cons -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__first -> #%prim.first -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__rest -> #%prim.rest -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__first -> #%prim.first -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__push-back -> #%prim.push-back -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__empty? -> #%prim.empty? -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__list -> #%prim.list -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__rest -> #%prim.rest -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__< -> #%prim.< -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__first -> #%prim.first -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__cons -> #%prim.cons -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__rest -> #%prim.rest -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__equal? -> #%prim.equal? -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__first -> #%prim.first -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__cons -> #%prim.cons -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__rest -> #%prim.rest -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__first -> #%prim.first -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__first -> #%prim.first -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__first -> #%prim.first -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__rest -> #%prim.rest -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__cons -> #%prim.cons -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__first -> #%prim.first -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__rest -> #%prim.rest -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__string->list -> #%prim.string->list -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__< -> #%prim.< -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__= -> #%prim.= -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__length -> #%prim.length -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__first -> #%prim.first -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__first -> #%prim.first -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__rest -> #%prim.rest -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__cons -> #%prim.cons -MUTATED IDENT TO BE: mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__list->string -> #%prim.list->string -(define ##__lifted_pure_function78793 - (λ (struct-type-descriptor - constructor-proto - predicate-proto - getter-proto) - (begin - (set! mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__struct:trie - struct-type-descriptor) - (#%prim.#%vtable-update-entry! - struct-type-descriptor - #false - mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#_____trie-options___) - (set! mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie - constructor-proto) - (set! mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie? - predicate-proto) - (set! mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-char - (λ (this) - (getter-proto this 0))) - (set! mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-children - (λ (this) - (getter-proto this 1))) - (set! mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-end-word? - (λ (this) - (getter-proto this 2))) - (set! mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-word-up-to - (λ (this) - (getter-proto this 3))) - #%prim.void))) - -(begin - (define mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#_____trie-options___ - (#%prim.hash - (quote - #:mutable) - #false - (quote - #:transparent) - #false - (quote - #:fields) - (quote - (char children end-word? word-up-to)) - (quote - #:printer) - #false - (quote - #:name) - (quote - trie))) - (define mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie - (quote - unintialized)) - (define mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__struct:trie - (quote - uninitialized)) - (define mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie? - (quote - uninitialized)) - (define mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-char - (quote - uninitialized)) - (define mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-children - (quote - uninitialized)) - (define mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-end-word? - (quote - uninitialized)) - (define mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-word-up-to - (quote - uninitialized)) - (let ((prototypes (#%prim.make-struct-type - (quote - trie) - 4))) - (##__lifted_pure_function78793 - (#%prim.list-ref prototypes 0) - (#%prim.list-ref prototypes 1) - (#%prim.list-ref prototypes 2) - (#%prim.list-ref prototypes 3))) - (define mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__empty - (quote - ())) - (define mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__empty-trie - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie - #%prim.void - mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__empty - #false - mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__empty)) - (define mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__flatten - (λ (lst) - (if (#%prim.null? lst) - mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__empty - (if (#%prim.list? lst) - (#%prim.append - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__flatten - (#%prim.car lst)) - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__flatten - (#%prim.cdr lst))) - (#%prim.list lst))))) - (define mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__create-children - (λ (char-list lst prefix-chars) - (if (#%prim.= (#%prim.length char-list) 1) - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__handle-last-letter - char-list - lst - prefix-chars) - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__handle-intern-letter - char-list - lst - prefix-chars)))) - (define mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__handle-last-letter - (λ (char-list lst prefix-chars) - (let ((char (#%prim.first char-list))) - (let ((next-prefix (#%prim.push-back - prefix-chars - char))) - (if (#%prim.empty? lst) - (#%prim.list - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie - char - mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__empty - #true - next-prefix)) - (if (#%prim.< - char - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-char - (#%prim.first lst))) - (#%prim.cons - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie - char - mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__empty - #true - next-prefix) - lst) - (if (#%prim.equal? - char - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-char - (#%prim.first lst))) - (#%prim.cons - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie - char - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-children - (#%prim.first lst)) - #true - next-prefix) - (#%prim.rest lst)) - (#%prim.cons - (#%prim.first lst) - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__create-children - char-list - (#%prim.rest lst) - prefix-chars))))))))) - (define mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__handle-intern-letter - (λ (char-list lst prefix-chars) - (let ((char (#%prim.first char-list))) - (let ((next-prefix (#%prim.push-back - prefix-chars - char))) - (if (#%prim.empty? lst) - (#%prim.list - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie - char - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__create-children - (#%prim.rest char-list) - mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__empty - next-prefix) - #false - next-prefix)) - (if (#%prim.< - char - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-char - (#%prim.first lst))) - (#%prim.cons - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie - char - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__create-children - (#%prim.rest char-list) - mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__empty - next-prefix) - #false - next-prefix) - lst) - (if (#%prim.equal? - char - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-char - (#%prim.first lst))) - (#%prim.cons - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie - char - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__create-children - (#%prim.rest char-list) - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-children - (#%prim.first lst)) - next-prefix) - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-end-word? - (#%prim.first lst)) - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-word-up-to - (#%prim.first lst))) - (#%prim.rest lst)) - (#%prim.cons - (#%prim.first lst) - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__create-children - char-list - (#%prim.rest lst) - prefix-chars))))))))) - (define mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__insert - (λ (root-trie word) - (let ((char-list (#%prim.string->list word))) - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-char - root-trie) - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__create-children - char-list - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-children - root-trie) - mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__empty) - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-end-word? - root-trie) - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-word-up-to - root-trie))))) - (define mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__triestring - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-word-up-to - trie-node)) - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__flatten - (map - mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__pre-order - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-children - trie-node)))) - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__flatten - (map - mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__pre-order - (mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-children - trie-node)))))) - (define __module-mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__ - (hash - (quote - trie-sort) - mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__trie-sort))) - -(define trie-sort - (%proto-hash-get% - __module-mangler/home/matt/Documents/steel/cogs/sorting/trie-sort.scm__%#__ - (quote - trie-sort))) diff --git a/cogs/foo.scm b/cogs/foo.scm deleted file mode 100644 index aae839562..000000000 --- a/cogs/foo.scm +++ /dev/null @@ -1,3 +0,0 @@ -(set-test-mode!) - -(require "sorting/tests.scm") diff --git a/cogs/fs/cog.scm b/cogs/fs/cog.scm deleted file mode 100644 index 6f27f2b14..000000000 --- a/cogs/fs/cog.scm +++ /dev/null @@ -1,5 +0,0 @@ -(define package-name 'steel/fs) -(define version "0.1.0") - -;; Core library, requires no dependencies -(define dependencies '()) \ No newline at end of file diff --git a/cogs/fs/fs.scm b/cogs/fs/fs.scm deleted file mode 100644 index 8ea2d7690..000000000 --- a/cogs/fs/fs.scm +++ /dev/null @@ -1,22 +0,0 @@ -(provide walk-files - file->string) - -(define (for-each func lst) - (if (null? lst) - void - (begin - (func (car lst)) - (when (null? lst) - (return! void)) - (for-each func (cdr lst))))) - -(define (file->string path) - (let ([file (open-input-file path)]) (read-port-to-string file))) - -;; Walk the file system, applying a function to each file found -(define/contract (walk-files path func) - (->/c string? (->/c string? any/c) any/c) - (cond - [(is-file? path) (func path)] - [(is-dir? path) (for-each (lambda (x) (walk-files x func)) (read-dir path))] - [else void])) diff --git a/cogs/install.scm b/cogs/install.scm deleted file mode 100644 index db54402f6..000000000 --- a/cogs/install.scm +++ /dev/null @@ -1,10 +0,0 @@ -;; TODO: -;; Parse the arguments from the command line, in this case either grab the path to the directory, or a git url, or the local directory -;; Parse the cog file, and check the dependencies. If any need to be downloaded, fetch those -;; Install to $STEEL_HOME/cogs to make it available for anything to download -;; Version resolution... for now just assume everything is compatible with everything without versions -;; Storing versions in a manifest would be nice - a project has an associated manifest that pins versions. - -(require "installer/package.scm") - -(package-installer-main) diff --git a/cogs/installer/cog.scm b/cogs/installer/cog.scm deleted file mode 100644 index 2992b06a2..000000000 --- a/cogs/installer/cog.scm +++ /dev/null @@ -1,4 +0,0 @@ -(define package-name 'installer) -(define version "0.1.0") - -(define dependencies '()) diff --git a/cogs/installer/package.scm b/cogs/installer/package.scm deleted file mode 100644 index 81f177ec2..000000000 --- a/cogs/installer/package.scm +++ /dev/null @@ -1,97 +0,0 @@ -;; TODO: -;; Parse the arguments from the command line, in this case either grab the path to the directory, or a git url, or the local directory -;; Parse the cog file, and check the dependencies. If any need to be downloaded, fetch those -;; Install to $STEEL_HOME/cogs to make it available for anything to download -;; Version resolution... for now just assume everything is compatible with everything without versions -;; Storing versions in a manifest would be nice - a project has an associated manifest that pins versions. - -;; Load in contracts for stress testing -(require "steel/result") - -(provide package-installer-main) - -(define (append-with-separator path) - (if (ends-with? path "/") (string-append path "cogs") (string-append path "/cogs"))) - -;; Should make this lazy? -(define *STEEL_HOME* (~> "STEEL_HOME" (env-var) (append-with-separator))) - -(define/contract (parse-cog module) - (->/c string? list?) - (if (is-dir? module) - (let ([cog-path (string-append module "/cog.scm")]) - (if (is-file? cog-path) - ;; Update the resulting map with the path to the module - (list (hash-insert (parse-cog-file cog-path) 'path module)) - - (hash-values->list (discover-cogs module)))) - (error! "Unable to locate the module " module))) - -;; Parses a cog file directly into a hashmap -(define/contract (parse-cog-file path) - (->/c string? hash?) - (define contents (let ([file (open-input-file path)]) (read-port-to-string file))) - (transduce (read! contents) (mapping cdr) (into-hashmap))) - -;; Discover the cogs located at the path, return as a list of hash maps -(define/contract (discover-cogs path) - (->/c string? hash?) - (when (not (path-exists? path)) - (displayln "cogs directory does not exist, creating now...") - (create-directory! path)) - (transduce (read-dir path) - (filtering is-dir?) - (mapping parse-cog) - (flattening) - (mapping (lambda (package) (list (hash-get package 'package-name) package))) - (into-hashmap))) - -;; Given a package spec, install that package directly to the file system -(define/contract (install-package package) - (->/c hash? string?) - (define destination - (string-append *STEEL_HOME* "/" (symbol->string (hash-get package 'package-name)))) - (copy-directory-recursively! (hash-get package 'path) destination) - destination) - -;; Given a package pec, uninstall that package by deleting the contents of the installation -(define/contract (uninstall-package package) - (->/c hash? string?) - (define destination - (string-append *STEEL_HOME* "/" (symbol->string (hash-get package 'package-name)))) - (displayln destination)) - -(define/contract (install-package-and-log cog-to-install) - (->/c hash? void?) - (let ([output-dir (install-package cog-to-install)]) - (display "✅ Installed package to: ") - (displayln output-dir) - (newline))) - -(define (check-install-package installed-cogs cog-to-install) - (define package-name (hash-get cog-to-install 'package-name)) - (if (hash-contains? installed-cogs package-name) - (begin - (displayln "Beginning installation for " package-name) - (displayln " Package already installed...") - (displayln " Overwriting existing package installation...") - (install-package-and-log cog-to-install)) - - (begin - (displayln "Package is not currently installed.") - (install-package-and-log cog-to-install)))) - -(define (parse-cogs-from-command-line) - (if (empty? std::env::args) (list (current-directory)) std::env::args)) - -(define (package-installer-main) - - (define cogs-to-install (parse-cogs-from-command-line)) - - ;; Grab the map of installed cogs on the file system. - ;; We will check if the cog is already installed before patching over the directory - (define installed-cogs (discover-cogs *STEEL_HOME*)) - - (transduce cogs-to-install - (flat-mapping parse-cog) - (into-for-each (lambda (x) (check-install-package installed-cogs x))))) diff --git a/cogs/lists/cog.scm b/cogs/lists/cog.scm deleted file mode 100644 index 2a38bfe54..000000000 --- a/cogs/lists/cog.scm +++ /dev/null @@ -1,5 +0,0 @@ -(define package-name 'steel/lists) -(define version "0.1.0") - -;; Core library, requires no dependencies -(define dependencies '()) \ No newline at end of file diff --git a/cogs/lists/lists.scm b/cogs/lists/lists.scm deleted file mode 100644 index 0787775fc..000000000 --- a/cogs/lists/lists.scm +++ /dev/null @@ -1,44 +0,0 @@ -(provide split-last - flatten - for-each) - -(define (split-last-loop accum lst) - (if (empty? (cdr lst)) - (list (reverse accum) (car lst)) - (split-last-loop (cons (car lst) accum) (cdr lst)))) - -;; @doc -;; Given a list, splits off the last argument, returns as a pair -(define (split-last lst) - (split-last-loop '() lst)) - -;;@doc -;; Flattens the incoming list one level -;; ```scheme -;; (flatten (list (list 10 20) (list 30 40))) ;; => '(10 20 30 40) -;; ``` -(define (flatten lst) - (transduce lst (flattening) (into-list))) - -;;@doc -;; Iterate over each item in the lst, calling the function on it. -;; ```scheme -;; (for-each displayln (list 1 2 3)) -;; ``` -;; Will print: -;; ``` -;; 1 -;; 2 -;; 3 -;; ``` -(define (for-each func lst) - (if (null? lst) - void - (begin - (func (car lst)) - (when (null? lst) - (return! void)) - (for-each func (cdr lst))))) - -;; Need default arguments here -; (define (remove v lst [proc ])) diff --git a/cogs/logging/cog.scm b/cogs/logging/cog.scm deleted file mode 100644 index 18821f087..000000000 --- a/cogs/logging/cog.scm +++ /dev/null @@ -1,5 +0,0 @@ -(define package-name 'steel/logging) -(define version "0.1.0") - -;; Core library, requires no dependencies -(define dependencies '()) \ No newline at end of file diff --git a/cogs/logging/log.scm b/cogs/logging/log.scm deleted file mode 100644 index 666fb709e..000000000 --- a/cogs/logging/log.scm +++ /dev/null @@ -1,43 +0,0 @@ -(require-builtin steel/time) - -(provide log! - log/info! - log/warn! - log/debug! - log/error!) - -(define *info* "INFO") -(define *warn* "WARN") -(define *debug* "DEBUG") -(define *error* "ERROR") -(define *trace* "TRACE") - -;;@doc -;; Log directly on the specified level the with arguments, as a list -(define (log! level arg-list) - (apply displayln (append (list (local-time/now! "%Y-%m-%dT%H:%M:%S") " [" level "] - ") arg-list))) - -;;@doc -;; Log the arguments using the *info* target, i.e. log on INFO -(define (log/info! . args) - (log! *info* args)) - -;;@doc -;; Log the arguments using the *warn* target, i.e. log on WARN -(define (log/warn! . args) - (log! *warn* args)) - -;;@doc -;; Log the arguments using the *debug* target, i.e. log on DEBUG -(define (log/debug! . args) - (log! *debug* args)) - -;;@doc -;; Log the arguments using the *error* target, i.e. log on ERROR -(define (log/error! . args) - (log! *error* args)) - -;;@doc -;; Log the arguments using the *trace* target, i.e. log on TRACE -(define (log/trace! . args) - (log! *trace* args)) diff --git a/cogs/module-tests/export.scm b/cogs/module-tests/export.scm deleted file mode 100644 index 225870eb9..000000000 --- a/cogs/module-tests/export.scm +++ /dev/null @@ -1,38 +0,0 @@ -(provide Applesauce - bananas - foo-bar-baz - new-identifier - one-more-identifier - another-identifier - Applesauce-foo - Applesauce-bar - Applesauce-baz - thing-should-not-escape - - my-fun-contracted-function) - -(define (bananas) - (error "Hello world")) - -(define (foo-bar-baz) - 10) - -(define/contract (my-fun-contracted-function x y) - (->/c int? int? int?) - (+ x y)) - -(define new-identifier 100) - -(define one-more-identifier 'foo-bar-baz) -(define another-identifier 100) - -(define-syntax thing-should-not-escape - (syntax-rules () - [(thing-should-not-escape x) (thing-should-not-escape2 x)])) - -(define-syntax thing-should-not-escape2 - (syntax-rules () - [(thing-should-not-escape x) x])) - -;; This should be provided! -(struct Applesauce (foo bar baz)) diff --git a/cogs/module-tests/import.scm b/cogs/module-tests/import.scm deleted file mode 100644 index 62943756d..000000000 --- a/cogs/module-tests/import.scm +++ /dev/null @@ -1,24 +0,0 @@ -(require (prefix-in export. - (only-in "export.scm" - thing-should-not-escape - Applesauce - bananas - my-fun-contracted-function))) - -export.Applesauce - -export.bananas - -export.my-fun-contracted-function - -(export.thing-should-not-escape 10) - -;; Dead code analysis would be nice as well -;; If we can run constant evaluation over the result without actually -;; taking the const evaluation branches, we can store the -;; resulting removed spans and just render them in the LSP -(cond - [(list? 10) (displayln "hello world!")] - [else - => - (displayln "foo bar")]) diff --git a/cogs/other.scm b/cogs/other.scm deleted file mode 100644 index 748e7d8d4..000000000 --- a/cogs/other.scm +++ /dev/null @@ -1,7 +0,0 @@ -; (require "dispatch.scm") - -; (apples 10) - -;; Lookup multiple keys at a time -(define (href table . keys) - (foldl (lambda (key t) (hash-get t key)) table keys)) diff --git a/cogs/r5rs.scm b/cogs/r5rs.scm deleted file mode 100644 index 2134641ce..000000000 --- a/cogs/r5rs.scm +++ /dev/null @@ -1,587 +0,0 @@ -;; Adapted from https://github.com/ashinn/chibi-scheme/blob/master/tests/r5rs-tests.scm -;; with the following copyright notice: -;; -;; ------------------------------------------------------------------- -;; -;; Copyright (c) 2009-2021 Alex Shinn -;; All rights reserved. -;; -;; Redistribution and use in source and binary forms, with or without -;; modification, are permitted provided that the following conditions -;; are met: -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. -;; 3. The name of the author may not be used to endorse or promote products -;; derived from this software without specific prior written permission. -;; -;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -;; -;; ------------------------------------------------------------------- -;; -;; Modified by Matt Paras for use within the Steel test suite - -(require "tests/unit-test.scm" - (for-syntax "tests/unit-test.scm")) - -(set-test-mode!) - -(check-equal? "Parsing hex" #x0f 15) -(check-equal? "Parsing octal" #o0777 511) -(check-equal? "Parsing binary" #b0110 6) - -(check-equal? "filter treats lists as true" (filter (lambda (n) (list 1 2)) (list 1 2)) '(1 2)) - -(check-equal? - "Symbols are interned correctly - lists should use the existing symbols that have been interned" - (eq? 'definitely-hasnt-been-seen-before - (let ([_ (#%black-box)]) (car '(definitely-hasnt-been-seen-before)))) - #true) - -(check-equal? "addition" 8 ((lambda (x) (+ x x)) 4)) - -(check-equal? "Variable arity function call" '(3 4 5 6) ((lambda x x) 3 4 5 6)) - -(check-equal? "Rest arguments" '(5 6) ((lambda (x y . z) z) 3 4 5 6)) - -(check-equal? "Branching with >" 'yes (if (> 3 2) 'yes 'no)) - -(check-equal? "Branch with <" 'no (if (> 2 3) 'yes 'no)) - -(check-equal? "Numeric operations with if" 1 (if (> 3 2) (- 3 2) (+ 3 2))) - -(check-equal? "Cond with >" - 'greater - (cond - [(> 3 2) 'greater] - [(< 3 2) 'less])) - -(check-equal? "Cond with equal" - 'equal - (cond - [(> 3 3) 'greater] - [(< 3 3) 'less] - [else 'equal])) - -(check-equal? "Case macro" - 'composite - (case (* 2 3) - [(2 3 5 7) 'prime] - [(1 4 6 8 9) 'composite])) - -(check-equal? "Case with chars" - 'consonant - (case (car '(c d)) - [(a e i o u) 'vowel] - [(w y) 'semivowel] - [else 'consonant])) - -(check-equal? "and true" #t (and (= 2 2) (> 2 1))) - -(check-equal? "and false" #f (and (= 2 2) (< 2 1))) - -(check-equal? "and returns last in the list" '(f g) (and 1 2 'c '(f g))) - -(check-equal? "and defaults to true" #t (and)) - -(check-equal? "or true on the first" #t (or (= 2 2) (> 2 1))) - -(check-equal? "or true on the first, second not true" #t (or (= 2 2) (< 2 1))) - -;; TODO -(skip-compile (check-equal? '(b c) (or (memq 'b '(a b c)) (/ 3 0)))) - -(check-equal? "basic let" 6 (let ([x 2] [y 3]) (* x y))) - -(check-equal? "basic let with multiple levels" - 35 - (let ([x 2] [y 3]) (let ([x 7] [z (+ x y)]) (* z x)))) - -(check-equal? "basic let*" 70 (let ([x 2] [y 3]) (let* ([x 7] [z (+ x y)]) (* z x)))) - -(check-equal? "interior define" - -2 - (let () - (define x 2) - (define f (lambda () (- x))) - (f))) - -;; TODO: This gets converted into a plain let, -;; and then somehow the variable name gets overwritten? Maybe during constant propagation? -(define let*-def 1) -(let* () - (define let*-def 2) - #f) -(check-equal? "Redefine top level with interior define, stays the same" 1 let*-def) - -;; TODO: `do` macro -(skip-compile - (check-equal '#(0 1 2 3 4) - (do ((vec (make-vector 5)) (i 0 (+ i 1))) ((= i 5) vec) (vector-set! vec i i))) - (check-equal 25 - (let ([x '(1 3 5 7 9)]) (do ((x x (cdr x)) (sum 0 (+ sum (car x)))) ((null? x) sum))))) - -(check-equal? "named let" - '((6 1 3) (-5 -2)) - (let loop ([numbers '(3 -2 1 6 -5)] [nonneg '()] [neg '()]) - (cond - [(null? numbers) (list nonneg neg)] - [(>= (car numbers) 0) (loop (cdr numbers) (cons (car numbers) nonneg) neg)] - [(< (car numbers) 0) (loop (cdr numbers) nonneg (cons (car numbers) neg))]))) - -(check-equal? "simple quasiquote and unquote" '(list 3 4) `(list ,(+ 1 2) 4)) - -(check-equal? "quasiquote and unquote with more" '(list a 'a) (let ([name 'a]) `(list ,name ',name))) - -(check-equal? "unquote splicing" '(a 3 4 5 6 b) `(a ,(+ 1 2) ,@(map abs '(4 -5 6)) b)) - -(check-equal? "unquote splicing inside quasiquote" - '(a 3 `(list ,@(map abs '(4 -5 6)) b)) - `(a ,(+ 1 2) `(list ,@(map abs '(4 -5 6)) b))) - -(check-equal? "unquote splicing with unquote" - '(10 5 4 16 9 8) - `(10 5 ,(expt 2 2) ,@(map (lambda (n) (expt n 2)) '(4 3)) 8)) - -(check-equal? "more complex unquote" - '(a `(b ,(+ 1 2) ,(foo 4 d) e) f) - `(a `(b ,(+ 1 2) ,(foo ,(+ 1 3) d) e) f)) - -(check-equal? "double unquote and quote" - '(a `(b ,x ,'y d) e) - (let ([name1 'x] [name2 'y]) `(a `(b ,,name1 ,',name2 d) e))) - -(check-equal? "named quasiquote" '(list 3 4) (quasiquote (list (unquote (+ 1 2)) 4))) - -;; TODO: Add eqv? -(skip-compile (check-equal? #t (eqv? 'a 'a)) - (check-equal #f (eqv? 'a 'b)) - (check-equal #t (eqv? '() '())) - (check-equal #f (eqv? (cons 1 2) (cons 1 2))) - (check-equal #f (eqv? (lambda () 1) (lambda () 2))) - (check-equal #t (let ([p (lambda (x) x)]) (eqv? p p)))) - -(check-equal? "Symbols are interned" #t (eq? 'a 'a)) - -;; TODO: With constant evaluation, these do end up being the same thing -(check-equal? "lists don't get interned" #f (eq? (list 'a) (list 'a))) - -;; Empty lists are interned -(check-equal? "Empty lists are interned" #t (eq? '() '())) - -(check-equal? "functions are equal" #t (eq? car car)) - -(check-equal? "Local vars that are constant point to the same object" #t (let ([x '(a)]) (eq? x x))) - -(check-equal? "Function objects are eq? via pointer equality" #t (let ([p (lambda (x) x)]) (eq? p p))) - -(check-equal? "Value equality for interned symbols" #t (equal? 'a 'a)) - -(check-equal? "Value equality for interned lists" #t (equal? '(a) '(a))) - -(check-equal? "Value equality for nested interned lists" #t (equal? '(a (b) c) '(a (b) c))) - -(check-equal? "String equality" #t (equal? "abc" "abc")) - -(check-equal? "String inequality" #f (equal? "abc" "abcd")) - -(check-equal? "String inequality, first char" #f (equal? "a" "b")) - -(check-equal? "Integer equality" #t (equal? 2 2)) - -;; TODO: Figure these comments ones out - -(check-equal? "equality with float and int" #f (equal? 2.0 2)) - -(check-equal? "numeric equality with float and int" #t (= 1 1.0)) - -(check-equal? "pointer equality with int and int" #t (eq? 1 1)) - -(check-equal? "pointer equality with floats" #t (eq? 1.0 1.0)) - -(check-equal? "numeric equality with float and float" #t (= 1.0 1.0)) - -(skip-compile (check-equal #f (eqv? 2 2.0)) - ;; TODO: Add make-vector function - (check-equal #t (equal? (make-vector 5 'a) (make-vector 5 'a)))) - -(check-equal? "max over ints" 4 (max 3 4)) - -(check-equal? "max with float and int" 4 (max 3.9 4)) - -(check-equal? "Addition binop" 7 (+ 3 4)) - -(check-equal? "Addition unary op" 3 (+ 3)) - -(check-equal? "Addition no args" 0 (+)) - -(check-equal? "Multiplication one arg, int" 4 (* 4)) - -(check-equal? "Multiplication no args, int " 1 (*)) - -(check-equal? "Subtraction binop" -1 (- 3 4)) - -(check-equal? "Subtract three args" -6 (- 3 4 5)) - -(check-equal? "Subtraction unary op" -3 (- 3)) - -(check-equal? "Subtraction, floating point and int" -1.0 (- 3.0 4)) - -(check-equal? "abs int" 7 (abs -7)) - -(check-equal? "basic string->number" 100 (string->number "100")) -(check-equal? "string->number with radix" 256 (string->number "100" 16)) -(check-equal? "string->number with different base" 127 (string->number "177" 8)) -(check-equal? "string->number base 2" 5 (string->number "101" 2)) -(check-equal? "string->number with scientific notation" 100.0 (string->number "1e2")) - -(check-equal? "basic number->string" "100" (number->string 100)) -(check-equal? "number->string with different base" "100" (number->string 256 16)) -(check-equal? "number->string base 16 doesn't work" "ff" (number->string 255 16)) -(check-equal? "number->string base 8" "177" (number->string 127 8)) -(check-equal? "number->string base 2" "101" (number->string 5 2)) - -;; TODO: Adjust the below check-equals - -(skip-compile (check-equal 1 (modulo 13 4)) - (check-equal 1 (remainder 13 4)) - (check-equal 3 (modulo -13 4)) - (check-equal -1 (remainder -13 4)) - (check-equal -3 (modulo 13 -4)) - (check-equal 1 (remainder 13 -4)) - (check-equal -1 (modulo -13 -4)) - (check-equal -1 (remainder -13 -4)) - (check-equal 4 (gcd 32 -36)) - (check-equal 288 (lcm 32 -36))) - -(check-equal? "integers are truthy" #f (not 3)) - -(check-equal? "lists are truthy" #f (not (list 3))) - -(check-equal? "empty lists are true" #f (not '())) - -(check-equal? "empty lists are true, constructor" #f (not (list))) - -(check-equal? "ints are not bools" #f (boolean? 0)) - -(check-equal? "empty list is not a boolean" #f (boolean? '())) - -; (check-equal #t (pair? '(a . b))) - -(check-equal? "lists are considered pairs" #t (pair? '(a b c))) - -(check-equal? "cons onto empty list" '(a) (cons 'a '())) - -(check-equal? "cons list onto list" '((a) b c d) (cons '(a) '(b c d))) - -(check-equal? "cons string onto list of symbols" '("a" b c) (cons "a" '(b c))) - -; (check-equal '(a . 3) (cons 'a 3)) - -; (check-equal '((a b) . c) (cons '(a b) 'c)) - -(check-equal? "take the car of a list of symbols" 'a (car '(a b c))) - -(check-equal? "take the car, where the car is a list" '(a) (car '((a) b c d))) - -; (check-equal 1 (car '(1 . 2))) - -(check-equal? "take the cdr of a list" '(b c d) (cdr '((a) b c d))) - -; (check-equal 2 (cdr '(1 . 2))) - -(check-equal? "Check list predicate" #t (list? '(a b c))) - -(check-equal? "Empty list is a list" #t (list? '())) - -; (check-equal #f (list? '(a . b))) - -; (check-equal #f -; (let ([x (list 'a)]) -; (set-cdr! x x) -; (list? x))) - -(check-equal? "List constructor" '(a 7 c) (list 'a (+ 3 4) 'c)) - -(check-equal? "empty list constructor" '() (list)) - -(check-equal? "length of a flat list" 3 (length '(a b c))) - -(check-equal? "length of a non flat list" 3 (length '(a (b) (c d e)))) - -(check-equal? "empty list has a length of 0" 0 (length '())) - -(check-equal? "append two lists" '(x y) (append '(x) '(y))) - -(check-equal? "append big list to small list" '(a b c d) (append '(a) '(b c d))) - -(check-equal? "append nested lists" '(a (b) (c)) (append '(a (b)) '((c)))) - -; (check-equal '(a b c . d) (append '(a b) '(c . d))) - -;; NOTE: Improper lists are not supported, so this should fail and we should note this in -;; in the test suite -; (check-equal? "append to empty list" 'a (append '() 'a)) - -(check-equal? "reverse list" '(c b a) (reverse '(a b c))) - -(check-equal? "reverse nested list" '((e (f)) d (b c) a) (reverse '(a (b c) d (e (f))))) - -(check-equal? "simple list-ref" 'c (list-ref '(a b c d) 2)) - -(skip-compile (check-equal '(a b c) (memq 'a '(a b c))) - (check-equal '(b c) (memq 'b '(a b c))) - (check-equal #f (memq 'a '(b c d))) - (check-equal #f (memq (list 'a) '(b (a) c)))) - -(check-equal? "simple member" '((a) c) (member (list 'a) '(b (a) c))) - -(skip-compile (check-equal '(101 102) (memv 101 '(100 101 102))) - (check-equal #f (assq (list 'a) '(((a)) ((b)) ((c))))) - (check-equal '(5 7) (assv 5 '((2 3) (5 7) (11 13))))) - -(check-equal? "assoc" '((a)) (assoc (list 'a) '(((a)) ((b)) ((c))))) - -(check-equal? "symbol predicate" #t (symbol? 'foo)) - -(check-equal? "symbol predicate from constant list" #t (symbol? (car '(a b)))) - -(check-equal? "symbol predicate fails on string" #f (symbol? "bar")) - -(check-equal? "nil symbol is symbol" #t (symbol? 'nil)) - -(check-equal? "empty list is not a symbol" #f (symbol? '())) - -(check-equal? "symbol->string basic case" "flying-fish" (symbol->string 'flying-fish)) - -(check-equal? "symbol-string works" "Martin" (symbol->string 'Martin)) - -(check-equal? "string losslessly moves into symbol and back" - "Malvina" - (symbol->string (string->symbol "Malvina"))) - -(check-equal? "string predicate correctly identifies a string" #t (string? "a")) - -(check-equal? "string predicate fails on a symbol" #f (string? 'a)) - -(check-equal? "empty string has a length of 0" 0 (string-length "")) - -(check-equal? "string length correctly reported for standard string" 3 (string-length "abc")) - -(check-equal? "string indexing into first character" #\a (string-ref "abc" 0)) - -(check-equal? "string indexing into last character" #\c (string-ref "abc" 2)) - -(check-equal? "empty substring" "" (substring "abc" 0 0)) -(check-equal? "substring just the first character" "a" (substring "abc" 0 1)) -(check-equal? "substring a larger chunk" "bc" (substring "abc" 1 3)) - -(check-equal? "Basic functionality of make-string" "aaa" (make-string 3 #\a)) -(check-equal? "make-string with no character" "\0\0\0" (make-string 3)) -(check-equal? "make-string with zero length" "" (make-string 0)) -(check-equal? "make-string with multiple additional characters" "error" (make-string 3 #\a #\b)) - -(check-equal? "string-equality with constructor, equal" #t (string=? "a" (string #\a))) -(check-equal? "string-equality with constructor, not equal" #f (string=? "a" (string #\b))) -(check-equal? "string<, true" #t (string, true" #t (string>? "aa" "a")) -(check-equal? "string>, false" #f (string>? "a" "aa")) -(check-equal? "string>, same strings" #f (string>? "a" "a")) -(check-equal? "string >=, true" #t (string>=? "aa" "a")) -(check-equal? "string >=, same string" #t (string>=? "a" "a")) - -(check-equal? "case-insensitive string-equality with constructor, equal" - #t - (string-ci=? "A" (string #\a))) -(check-equal? "case-insensitive string-equality with constructor, not equal" - #f - (string-ci=? "A" (string #\b))) -(check-equal? "case-insensitive string<, true" #t (string-ci, true" #t (string-ci>? "aa" "A")) -(check-equal? "case-insensitive string>, false" #f (string-ci>? "a" "AA")) -(check-equal? "case-insensitive string>, same strings" #f (string-ci>? "A" "a")) -(check-equal? "case-insensitive string >=, true" #t (string-ci>=? "aa" "A")) -(check-equal? "case-insensitive string >=, same string" #t (string-ci>=? "a" "A")) - -(check-equal? "make-string creates single character string 'a' correctly" #t (string=? "a" (make-string 1 #\a))) -(check-equal? "make-string with character 'b' does not create string 'a'" #f (string=? "a" (make-string 1 #\b))) - -(check-equal? "string-append with empty string" "abc" (string-append "abc" "")) - -(check-equal? "string-append with empty string on the lhs" "abc" (string-append "" "abc")) - -(check-equal? "string-append with two non empty strings" "abc" (string-append "a" "bc")) - -(skip-compile (check-equal '#(0 ("Sue" "Sue") "Anna") - (let ([vec (vector 0 '(2 2 2 2) "Anna")]) - (vector-set! vec 1 '("Sue" "Sue")) - vec)) - (check-equal '(dah dah didah) (vector->list '#(dah dah didah))) - (check-equal '#(dididit dah) (list->vector '(dididit dah)))) - -(check-equal? "function correctly identified as a procedure" #t (procedure? car)) - -(check-equal? "symbol correctly identified as NOT as a procedure" #f (procedure? 'car)) - -(check-equal? "user defined function correctly identified as a procedure" - #t - (procedure? (lambda (x) (* x x)))) - -(check-equal? "quoted expression correctly identified as NOT as procedure" - #f - (procedure? '(lambda (x) (* x x)))) - -(check-equal? "basic call/cc with native predicate" #t (call-with-current-continuation procedure?)) - -(check-equal? "basic call/cc with user defined function" - 7 - (call-with-current-continuation (lambda (k) (+ 2 5)))) - -(check-equal? "more complex call/cc with user defined function" - 3 - (call-with-current-continuation (lambda (k) (+ 2 5 (k 3))))) - -(check-equal? "apply with native function" 7 (apply + (list 3 4))) - -(check-equal? "map with user defined function" '(b e h) (map cadr '((a b) (d e) (g h)))) - -(check-equal? "map with numeric op" '(1 4 27 256 3125) (map (lambda (n) (expt n n)) '(1 2 3 4 5))) - -(check-equal? "map with multiple list arguments" '(5 7 9) (map + '(1 2 3) '(4 5 6))) - -(check-equal? "force and delay" 3 (force (delay (+ 1 2)))) - -(check-equal? "force and delay with local variable" - '(3 3) - (let ([p (delay (+ 1 2))]) (list (force p) (force p)))) - -(skip-compile (check-equal '#(0 1 4 9 16) - (let ([v (make-vector 5)]) - (for-each (lambda (i) (vector-set! v i (* i i))) '(0 1 2 3 4)) - v))) - -(check-equal? "using else as a variable" - 'ok - (let ([else 1]) - (cond - [else 'ok] - [#t 'bad]))) - -(check-equal? "Using an arrow as a variable" - 'ok - (let ([=> 1]) - (cond - [#t - => - 'ok]))) - -(check-equal? "Override unquote in a local context" '(,foo) (let ([unquote 1]) `(,foo))) -(check-equal? "Override unquote-splicing in a local context" - '(,@foo) - (let ([unquote-splicing 1]) `(,@foo))) - -;; TODO: Implement let-syntax -(skip-compile (check-equal 'ok - (let () - (let-syntax () - (define internal-def 'ok)) - internal-def)) - (check-equal 'ok - (let () - (letrec-syntax () - (define internal-def 'ok)) - internal-def))) - -(check-equal? "mutation within local function" - '(2 1) - ((lambda () - (let ([x 1]) - (let ([y x]) - (set! x 2) - (list x y)))))) - -(check-equal? "multiple levels of let with mutation" - '(2 2) - ((lambda () - (let ([x 1]) - (set! x 2) - (let ([y x]) (list x y)))))) - -(check-equal? "local mutation" - '(1 2) - ((lambda () - (let ([x 1]) - (let ([y x]) - (set! y 2) - (list x y)))))) - -(check-equal? "Multiple mutations inside local context" - '(2 3) - ((lambda () - (let ([x 1]) - (let ([y x]) - (set! x 2) - (set! y 3) - (list x y)))))) - -; (skip-compile -(check-equal? "Dynamic wind" - '(a b c) - (let* ([path '()] [add (lambda (s) (set! path (cons s path)))]) - (dynamic-wind (lambda () (add 'a)) (lambda () (add 'b)) (lambda () (add 'c))) - (reverse path))) - -(check-equal? "Dynamic wind more complex" - '(connect talk1 disconnect connect talk2 disconnect) - (let ([path '()] [c #f]) - (let ([add (lambda (s) (set! path (cons s path)))]) - (dynamic-wind (lambda () (add 'connect)) - (lambda () - (add (call-with-current-continuation (lambda (c0) - (set! c c0) - 'talk1)))) - (lambda () (add 'disconnect))) - (if (< (length path) 4) (c 'talk2) (reverse path))))) - -; (check-equal 2 -; (let-syntax ([foo (syntax-rules ::: -; [] -; [(foo ... args :::) (args ::: ...)])]) -; (foo 3 - 5))) -; (check-equal -; '(5 4 1 2 3) -; (let-syntax ([foo (syntax-rules () -; [(foo args ... penultimate ultimate) (list ultimate penultimate args ...)])]) -; (foo 1 2 3 4 5))) - -; ) - -;; -------------- Report ------------------ - -(require "lists/lists.scm") -(define r5rs-test-stats (get-test-stats)) - -(displayln "Passed: " (hash-ref r5rs-test-stats 'success-count)) -(displayln "Skipped compilation (expected failure): " (hash-ref r5rs-test-stats 'failed-to-compile)) -(displayln "Failed: " (hash-ref r5rs-test-stats 'failure-count)) - -(for-each (lambda (x) (displayln " > " x)) (hash-ref r5rs-test-stats 'failures)) diff --git a/cogs/r7rs.scm b/cogs/r7rs.scm deleted file mode 100644 index f04878a5b..000000000 --- a/cogs/r7rs.scm +++ /dev/null @@ -1,79 +0,0 @@ -;; Adapted from https://github.com/ashinn/chibi-scheme/blob/master/tests/r5rs-tests.scm -;; with the following copyright notice: -;; -;; ------------------------------------------------------------------- -;; -;; Copyright (c) 2009-2021 Alex Shinn -;; All rights reserved. -;; -;; Redistribution and use in source and binary forms, with or without -;; modification, are permitted provided that the following conditions -;; are met: -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. -;; 3. The name of the author may not be used to endorse or promote products -;; derived from this software without specific prior written permission. -;; -;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -;; -;; ------------------------------------------------------------------- -;; -;; Modified by Matt Paras for use within the Steel test suite - -(require "tests/unit-test.scm" - (for-syntax "tests/unit-test.scm")) - -(set-test-mode!) - -(require "lists/lists.scm") - -;;;; Parameters - -(define location (make-parameter "here")) - -(check-equal? "Simple parameterize" - "there" - (parameterize ([location "there"]) - (location))) ;; "there" - -(check-equal? "parameter keeps original value" "here" (location)) ;; "here" - -(check-equal? "Parameter changes multiple times" - (list "in a house" "with a mouse" "in a house") - (parameterize ([location "in a house"]) - (list (location) - (parameterize ([location "with a mouse"]) - (location)) - (location)))) ;; '("in a house" "with a mouse" "in a house") - -(check-equal? "parameter keeps original value after" "here" (location)) ;; "here" - -(define (would-you-could-you?) - (and (not (equal? (location) "here")) (not (equal? (location) "there")))) - -(check-equal? "Parameters refer to the same location" #false (would-you-could-you?)) - -(check-equal? "Parameters refer to the same location, changed to be the same" - #true - (parameterize ([location "on a bus"]) - (would-you-could-you?))) - -(define r7rs-test-stats (get-test-stats)) - -(displayln "Passed: " (hash-ref r7rs-test-stats 'success-count)) -(displayln "Skipped compilation (expected failure): " (hash-ref r7rs-test-stats 'failed-to-compile)) -(displayln "Failed: " (hash-ref r7rs-test-stats 'failure-count)) - -(for-each (lambda (x) (displayln " > " x)) (hash-ref r7rs-test-stats 'failures)) diff --git a/cogs/reflection/cogs.scm b/cogs/reflection/cogs.scm deleted file mode 100644 index 1bdbc2309..000000000 --- a/cogs/reflection/cogs.scm +++ /dev/null @@ -1,5 +0,0 @@ -(define package-name 'steel/reflection) -(define version "0.1.0") - -;; Core library, requires no dependencies -(define dependencies '()) \ No newline at end of file diff --git a/cogs/reflection/reflect.scm b/cogs/reflection/reflect.scm deleted file mode 100644 index d50aa0952..000000000 --- a/cogs/reflection/reflect.scm +++ /dev/null @@ -1,32 +0,0 @@ -(provide find-function-pointer reset-modules!) - -(define *modules* 'uninitialized) - -(define (reset-modules!) - (set! *modules* 'uninitialized)) - -(define (load-modules) - (when (symbol? *modules*) - (set! *modules* (%list-modules!)))) - -(define (get-modules!) - (load-modules) - *modules*) - -;;@doc -;; Attempts to search for the name of the function from the global module table -;; If not found, returns #false. -;; -;; This lazily loads the modules - if for whatever reason, a module is loaded _after_ this has been -;; called, the module table in memory will need to be refreshed using `reset-modules!` -(define (find-function-pointer function-ptr) - (define found - (transduce (get-modules!) - (mapping (lambda (mod) (%module/lookup-function mod function-ptr))) - (filtering (lambda (x) (not (boolean? x)))) - (into-list))) - - ; found) - - (if found (car found) #f)) - diff --git a/cogs/require.scm b/cogs/require.scm deleted file mode 100644 index 18710a30a..000000000 --- a/cogs/require.scm +++ /dev/null @@ -1,2 +0,0 @@ -(require "sorting/trie-sort.scm") -; (require "transducers/transducers.scm") diff --git a/cogs/slack/cog.scm b/cogs/slack/cog.scm deleted file mode 100644 index 7e83a8f41..000000000 --- a/cogs/slack/cog.scm +++ /dev/null @@ -1,5 +0,0 @@ -(define package-name 'slack/websocket) -(define version "0.1.0") - -;; Core library, requires no dependencies -(define dependencies '()) \ No newline at end of file diff --git a/cogs/slack/main.scm b/cogs/slack/main.scm deleted file mode 100644 index d3b735ebb..000000000 --- a/cogs/slack/main.scm +++ /dev/null @@ -1,25 +0,0 @@ -(require "slack.scm") -(require "steel/time/time.scm" - (for-syntax "steel/time/time.scm")) - -(require "steel/logging/log.scm") - -(define/contract (process-message body) - (->/c hash? any/c) - ; (displayln body) - - (log/info! body) - - (define event-json (-> body (hash-get 'payload) (hash-get 'event))) - (define text (hash-try-get event-json 'text)) - (define channel (hash-get event-json 'channel)) - - (when (and text (starts-with? text "!ping")) - (time! (send-message channel "pong!")))) - -(define (process-message-timed body) - (time! (process-message body))) - -(define *ws-url* (get-ws-url)) - -(event-loop *ws-url* (connect-to-slack-socket *ws-url*) process-message-timed) diff --git a/cogs/slack/slack.scm b/cogs/slack/slack.scm deleted file mode 100644 index 519ea9620..000000000 --- a/cogs/slack/slack.scm +++ /dev/null @@ -1,102 +0,0 @@ -(require-builtin steel/web/requests) - -(#%require-dylib "libsteel_websockets" - (only-in ws/message-ping? - ws/message-pong? - ws/message-text - ws/message-text? - ws/message-ping->pong - ws/message->text-payload - ws/connect - ws/read-message! - ws/write-message!)) - -(require "steel/result") -(require "steel/logging/log.scm") -(require-builtin steel/time) - -(provide event-loop - send-message - connect-to-slack-socket - get-ws-url) - -(define (env-var! var) - (let ([e (maybe-get-env-var var)]) (if (Err? e) "TODO" (unwrap-ok e)))) - -(define client (request/client)) - -(define *SLACK_API_TOKEN* (env-var! "SLACK_API_TOKEN")) -(define *SLACK_API_WS_TOKEN* (env-var! "SLACK_API_WS_TOKEN")) - -(define *post-message-url* "https://slack.com/api/chat.postMessage") -(define *ws-connection-url* "https://slack.com/api/apps.connections.open") - -(define (send-message channel content) - (~> client - (client/post *post-message-url*) - (request/bearer-auth *SLACK_API_TOKEN*) - (request/json (hash 'channel channel 'text content)) - (request/send))) - -(define (get-ws-url) - (log/info! "Requesting a websocket url") - (~> client - (client/post *ws-connection-url*) - (request/bearer-auth *SLACK_API_WS_TOKEN*) - (request/json (hash)) - (request/send) - (response->json) - (hash-get 'url))) - -; (define *ws-url* (get-ws-url)) - -(define (connect-to-slack-socket url) - (~> url (ws/connect) (first))) - -(define (send-acknowledgement socket body) - (ws/write-message! socket - (ws/message-text (value->jsexpr-string (hash 'envelope_id - (hash-get body 'envelope_id)))))) - -(define (loop url socket message-thunk) - (define message - (with-handler (lambda (err) - (displayln "Unable to read the message from the socket, retrying connection") - ;; Try to reconnect and see what happens - ;; Probably need to add a sleep here at some point to retry with a backoff - (loop url (connect-to-slack-socket (get-ws-url)) message-thunk)) - (ws/read-message! socket))) - - (log/info! message) - ;; If its a ping, respond with a pong - (cond - [(ws/message-ping? message) - => - (ws/write-message! socket (ws/message-ping->pong message)) - (loop url socket message-thunk)] - ;; If its a text message, check if its a hello message - otherwise, continue - ;; And process the message - [(ws/message-text? message) - => - (define body (string->jsexpr (ws/message->text-payload message))) - (cond - [(equal? "hello" (hash-try-get body 'type)) - => - (loop url socket message-thunk)] - - [(equal? "disconnect" (hash-try-get body 'type)) - => - (log/info! "Refreshing the connection, sleeping for 500 ms") - (time/sleep-ms 500) - (loop url (connect-to-slack-socket (get-ws-url)) message-thunk)] - - [else - => - (send-acknowledgement socket body) - (message-thunk body) - (loop url socket message-thunk)])] - [else - => - (loop url socket message-thunk)])) - -(define event-loop loop) diff --git a/cogs/sorting/cog.scm b/cogs/sorting/cog.scm deleted file mode 100644 index 14a22cc25..000000000 --- a/cogs/sorting/cog.scm +++ /dev/null @@ -1,5 +0,0 @@ -(define package-name 'steel/sorting) -(define version "0.1.0") - -;; Core library, requires no dependencies -(define dependencies '()) \ No newline at end of file diff --git a/cogs/sorting/merge-sort.scm b/cogs/sorting/merge-sort.scm deleted file mode 100644 index 735e56a27..000000000 --- a/cogs/sorting/merge-sort.scm +++ /dev/null @@ -1,49 +0,0 @@ -(provide merge-sort) - -;;; ----------------------------------------------------------------- -;;; Merge two lists of numbers which are already in increasing order - -(define merge-lists - (lambda (l1 l2) - (if (null? l1) - l2 - (if (null? l2) - l1 - (if (< (car l1) (car l2)) - (cons (car l1) (merge-lists (cdr l1) l2)) - (cons (car l2) (merge-lists (cdr l2) l1))))))) - -;;; ------------------------------------------------------------------- -;;; Given list l, output those tokens of l which are in even positions - -(define even-numbers - (lambda (l) - (if (null? l) - '() - (if (null? (cdr l)) - '() - (cons (car (cdr l)) (even-numbers (cdr (cdr l)))))))) - -;;; ------------------------------------------------------------------- -;;; Given list l, output those tokens of l which are in odd positions - -(define odd-numbers - (lambda (l) - (if (null? l) - '() - (if (null? (cdr l)) - (list (car l)) - (cons (car l) (odd-numbers (cdr (cdr l)))))))) - -;;; --------------------------------------------------------------------- -;;; Use the procedures above to create a simple and efficient merge-sort - -(define merge-sort - (lambda (l) - (if (null? l) - l - (if (null? (cdr l)) - l - (merge-lists - (merge-sort (odd-numbers l)) - (merge-sort (even-numbers l))))))) diff --git a/cogs/sorting/quick-sort.scm b/cogs/sorting/quick-sort.scm deleted file mode 100644 index f34b5d4ce..000000000 --- a/cogs/sorting/quick-sort.scm +++ /dev/null @@ -1,18 +0,0 @@ -(provide quicksort) - -;; Current append only accepts 2 arguments, need to make it accept 3 -(define (append-three one two three) - (-> one - (append two) - (append three))) - -(define (quicksort l gt?) - (if (null? l) - '() - (append-three (quicksort (filter (lambda (x) (gt? (car l) x)) (cdr l)) gt?) - (list (car l)) - (quicksort (filter (lambda (x) (not (gt? (car l) x))) (cdr l)) gt?)))) - -; (define output (quicksort '(1 3 5 7 9 8 6 4 2) >)) - -; (assert! (equal? output '(1 2 3 4 5 6 7 8 9))) \ No newline at end of file diff --git a/cogs/sorting/tests.scm b/cogs/sorting/tests.scm deleted file mode 100644 index 6372b7677..000000000 --- a/cogs/sorting/tests.scm +++ /dev/null @@ -1,14 +0,0 @@ -(require "steel/tests/unit-test.scm" - (for-syntax "steel/tests/unit-test.scm") - "merge-sort.scm" - "trie-sort.scm" - "quick-sort.scm") - -(provide __module__) - -(define __module__ "tests") - -(test-module "trie-sort-tests" - (check-equal? "basic sorting" - (trie-sort (list "zebras" "bananas" "apples" "foo" "bar")) - '("apples" "bananas" "bar" "foo" "zebras"))) diff --git a/cogs/sorting/trie-sort.scm b/cogs/sorting/trie-sort.scm deleted file mode 100644 index ec83360bd..000000000 --- a/cogs/sorting/trie-sort.scm +++ /dev/null @@ -1,87 +0,0 @@ -(provide trie-sort) - -(struct trie (char children end-word? word-up-to)) - -;; Rename functions for the sake of compatibility -(define empty (list)) -(define empty-trie (trie void empty #f empty)) - -;; Throw in a mediocre flatten definition -(define (flatten lst) - (cond - [(null? lst) empty] - [(list? lst) (append (flatten (car lst)) (flatten (cdr lst)))] - [else (list lst)])) - -;; contract: (listof char?) (listof tries?) integer? -> (listof trie?) -(define (create-children char-list lst prefix-chars) - (cond - [(= (length char-list) 1) (handle-last-letter char-list lst prefix-chars)] - ;; you are in the middle of the word - [else (handle-intern-letter char-list lst prefix-chars)])) - -;; contract: (listof char?) (listof trie?) integer? -> (listof trie?) -(define (handle-last-letter char-list lst prefix-chars) - (define char (first char-list)) - ; (define next-prefix (append prefix-chars (list char))) - (define next-prefix (push-back prefix-chars char)) - (cond - ;; children are empty, return list of empty children - [(empty? lst) (list (trie char empty #t next-prefix))] - ;; less than, put it to the left - [(< char (trie-char (first lst))) (cons (trie char empty #t next-prefix) lst)] - [(equal? char (trie-char (first lst))) ;; equal, step down a level - (cons (trie char (trie-children (first lst)) #t next-prefix) (rest lst))] - ;; move to the right - [else (cons (first lst) (create-children char-list (rest lst) prefix-chars))])) - -;; contract: (listof char?) (listof trie?) integer? -> (listof trie?) -(define (handle-intern-letter char-list lst prefix-chars) - (define char (first char-list)) - ; (define next-prefix (append prefix-chars (list char))) - (define next-prefix (push-back prefix-chars char)) - (cond - [(empty? lst) ;; no children, pop off front and step down - (list (trie char (create-children (rest char-list) empty next-prefix) #f next-prefix))] - [(< char (trie-char (first lst))) ;; place where it is, pop off front and go - (cons (trie char (create-children (rest char-list) empty next-prefix) #f next-prefix) lst)] - [(equal? char (trie-char (first lst))) ;; equal, step down - (cons (trie char - (create-children (rest char-list) (trie-children (first lst)) next-prefix) - (trie-end-word? (first lst)) - (trie-word-up-to (first lst))) - (rest lst))] - ; move to the right - [else (cons (first lst) (create-children char-list (rest lst) prefix-chars))])) - -;; contract: trie? string? integer? -> trie? -(define (insert root-trie word) - (define char-list (string->list word)) - (trie (trie-char root-trie) - (create-children char-list (trie-children root-trie) empty) - (trie-end-word? root-trie) - (trie-word-up-to root-trie))) - -;; contract: trie? trie? -> boolean? -(define (trie trie? -(define (build-trie-from-list-of-words trie list-of-words) - (cond - [(= (length list-of-words) 1) (insert trie (first list-of-words))] - [else (build-trie-from-list-of-words (insert trie (first list-of-words)) (rest list-of-words))])) - -;; ------------------ SORTING ---------------------- ;; - -(define (trie-sort list-of-words) - (define new-trie (build-trie-from-list-of-words empty-trie list-of-words)) - (pre-order new-trie)) - -;; THIS ONE WORKS (using con and flatten) -;; contract: trie? -> (listof string?) -(define (pre-order trie-node) - (if (trie-end-word? trie-node) - (cons (list->string (trie-word-up-to trie-node)) - (flatten (map pre-order (trie-children trie-node)))) - (flatten (map pre-order (trie-children trie-node))))) diff --git a/cogs/srfi/cog.scm b/cogs/srfi/cog.scm deleted file mode 100644 index 88d3a4ce6..000000000 --- a/cogs/srfi/cog.scm +++ /dev/null @@ -1,5 +0,0 @@ -(define package-name 'srfi) -(define version "0.1.0") - -;; Core library, requires no dependencies -(define dependencies '()) diff --git a/cogs/srfi/srfi-28/format.scm b/cogs/srfi/srfi-28/format.scm deleted file mode 100644 index 00e8c2c8d..000000000 --- a/cogs/srfi/srfi-28/format.scm +++ /dev/null @@ -1,56 +0,0 @@ -; Copyright (C) Scott G. Miller (2002). All Rights Reserved. -; -; Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files -; the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge -; publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to -; do so, subject to the following conditions: -; -; The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -; -; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -; -; Modified for use within Steel by Matthew Paras (2023). - -(provide format) - -(define format - (lambda (format-string . objects) - (let ([buffer (open-output-string)]) - (let loop ([format-list (string->list format-string)] [objects objects]) - (cond - [(null? format-list) (get-output-string buffer)] - [(char=? (car format-list) #\~) - (if (null? (cdr format-list)) - (error 'format "Incomplete escape sequence") - (case (cadr format-list) - [(#\a) - (if (null? objects) - (error 'format "No value for escape sequence") - (begin - (display (car objects) buffer) - (loop (cddr format-list) (cdr objects))))] - [(#\s) - (if (null? objects) - (error 'format "No value for escape sequence") - (begin - (write (car objects) buffer) - (loop (cddr format-list) (cdr objects))))] - [(#\%) - (newline buffer) - (loop (cddr format-list) objects)] - [(#\~) - (write-char #\~ buffer) - (loop (cddr format-list) objects)] - [else (error 'format "Unrecognized escape sequence")]))] - [else - (write-char (car format-list) buffer) - (loop (cdr format-list) objects)]))))) - -; (displayln (format "Hello, ~a" "World!")) -; ; => "Hello, World!" - -; (displayln (format "Error, list is too short: ~s~%" '(one "two" 3))) -; ; => "Error, list is too short: (one \"two\" 3))" diff --git a/cogs/structs/structs.scm b/cogs/structs/structs.scm deleted file mode 100644 index b3ff0c0cd..000000000 --- a/cogs/structs/structs.scm +++ /dev/null @@ -1,9 +0,0 @@ - - -(define (json->struct struct-descriptor struct-constructor json) - (define fields (hash-get struct-descriptor '#:fields)) - (apply struct-constructor (map (lambda (field) (hash-try-get json field)) fields))) - -(struct Applesauce (a b c) #:transparent) - -(json->struct ___Applesauce-options___ Applesauce (hash 'a 10 'b 20 'c 30)) \ No newline at end of file diff --git a/cogs/test-runner.scm b/cogs/test-runner.scm deleted file mode 100644 index dc9c5d881..000000000 --- a/cogs/test-runner.scm +++ /dev/null @@ -1,53 +0,0 @@ -(require "steel/fs/fs.scm") - -(define shared-engine (Engine::new)) - -(run! shared-engine "(set-test-mode!)") - -;; If the path contains a cog file, respect it -; (define (parse-cog-file path) -; (define contents (let ([file (open-input-file path)]) (read-port-to-string file))) -; (transduce (read! contents) (mapping cdr) (into-hashmap))) - -(define (read-file-to-string path) - (let ([file (open-input-file path)]) (read-port-to-string file))) - -(define (expression-contains-provide expr-list) - (contains? (λ (expr) - (cond - [(and (list? expr) (not (empty? expr))) - (cond - [(equal? 'provide (car expr)) #t] - [(equal? 'begin (car expr)) (expression-contains-provide (cdr expr))] - [else #f])] - [else #f])) - expr-list)) - -;; Open file, read it -(define (path-contains-provide path) - (~> (read-file-to-string path) read! expression-contains-provide)) - -(define (require-file path) - (when (and (ends-with? path ".scm") (path-contains-provide path)) - - (displayln "Loading: " path) - - ;; First parse the file and check that it provides something - (let ([result (run! shared-engine (list (list 'require path)))]) - - (when (Err? result) - - (error result))))) - -(define (get-directory-from-args) - (if (empty? std::env::args) "." (car std::env::args))) - -(walk-files (get-directory-from-args) require-file) - -(define test-stats - (~> (run! shared-engine '((require "steel/tests/unit-test.scm") (get-test-stats))) - (Ok->value) - (last))) - -(when (not (empty? (hash-get test-stats 'failures))) - (error! "There were test failures!")) diff --git a/cogs/tests/cog.scm b/cogs/tests/cog.scm deleted file mode 100644 index 8fdb5fadf..000000000 --- a/cogs/tests/cog.scm +++ /dev/null @@ -1,5 +0,0 @@ -(define package-name 'steel/tests) -(define version "0.1.0") - -;; Core library, requires no dependencies -(define dependencies '()) \ No newline at end of file diff --git a/cogs/tests/unit-test.scm b/cogs/tests/unit-test.scm deleted file mode 100644 index 99258e121..000000000 --- a/cogs/tests/unit-test.scm +++ /dev/null @@ -1,118 +0,0 @@ -(require "steel/colors/colors.scm") - -(provide test - (for-syntax check-equal?) - (for-syntax check-err?) - (for-syntax test-module) - get-test-stats - (for-syntax check) - (for-syntax skip-compile)) - -(define *SUCCESS-COUNT* 0) -(define *FAILURE-COUNT* 0) - -(define *FAILED-TO-COMPILE* 0) - -;; Failed tests -(define *failures* '()) - -(define (mark-success) - (set! *SUCCESS-COUNT* (+ *SUCCESS-COUNT* 1))) - -(define (mark-failed name) - (set! *FAILURE-COUNT* (+ *FAILURE-COUNT* 1)) - (set! *failures* (cons name *failures*))) - -(define (mark-skipped) - (set! *FAILED-TO-COMPILE* (+ *FAILED-TO-COMPILE* 1))) - -(define (print-success name) - (display "test > ") - (display name) - (display " ... ") - (displayln-color "Ok" #:fg 'green)) - -(define (print-failure name) - (display "test > ") - (display name) - (display " ... ") - (displayln-color "FAILED" #:fg 'red)) - -(define-syntax check-equal? - (syntax-rules () - [(check-equal? name input expected) - (with-handler (lambda (err) - (mark-failed name) - (print-failure name) - (displayln err)) - (test name input expected))])) - -(define-syntax check-err? - (syntax-rules () - [(check-err? name input expected) - (with-handler (lambda (err) - (mark-success) - (print-success name)) - (test name input expected))])) - -;; Check the equality, and otherwise do some nice printing of the result -(define (test name input expected) - (if (equal? input expected) - (begin - (mark-success) - (print-success name)) - (begin - (mark-failed name) - (print-failure name) - (displayln " Expected: " expected ", Found " input)))) - -(define-syntax test-module - (syntax-rules () - [(test-module name expr ...) - (when (get-test-mode) - (begin - (displayln "###### Running tests for module " name " ######") - (begin - expr ...) - (displayln "Test result: " *SUCCESS-COUNT* " passed; " *FAILURE-COUNT* " failed;") - (display "Failures: ") - (displayln *failures*)))] - [(test-module expr ...) - (begin - expr ...)])) - -(define-syntax skip-compile - (syntax-rules () - [(skip-compile) (begin)] - [(skip-compile expr) (mark-skipped)] - [(skip-compile expr exprs ...) - (begin - (mark-skipped) - (skip-compile exprs ...))])) - -(define (get-test-stats) - (hash 'success-count - *SUCCESS-COUNT* - 'failure-count - *FAILURE-COUNT* - 'failures - *failures* - 'failed-to-compile - *FAILED-TO-COMPILE*)) - -; (test-module -; (check-equal? "Checks that the expressions make sense" -; (+ 10 20 30) -; (+ 10 20 30)) - -; (check-err? "This should fail (in a good way)!" -; (error! "Throwing an error") -; (+ 10 20 30)) - -; (check-equal? "This should fail" -; (+ 10 20) -; (+ 30 40)) - -; ) - -; (check-equal? "test" (begin (error! "hello world") 10) 10) diff --git a/cogs/thread.scm b/cogs/thread.scm deleted file mode 100644 index 4c167ce82..000000000 --- a/cogs/thread.scm +++ /dev/null @@ -1,303 +0,0 @@ -(require-builtin steel/time) -(require "steel/result") - -(define (loop thread-id) - (displayln "Hello world!") - (time/sleep-ms 500) - (displayln "Hello world from: " thread-id) - (loop thread-id)) - -(define (run-background-loop) - (displayln "Hello world!") - ; (displayln spawn-thread!) - (let ([handle (spawn-thread! (λ () (loop 1)))] - [handle2 (spawn-thread! (λ () - (time/sleep-ms 250) - (loop 2)))]) - (thread-join! handle2))) - -; (spawn-thread! run-background-loop) - -; (define (interesting-function) -; (let ((a (hash 'a 100))) -; (spawn-thread! (lambda () (time/sleep-ms 1000) (displayln a))))) - -; (thread-join! (interesting-function)) - -;; Channels! Create a channel, send values over the channel, consume on the other one, done. - -; (let ((handle (spawn-thread! (λ () -; (+ 10 (list-ref (list "hello world") 0)))))) -; (time/sleep-ms 5000) -; (unwrap-ok -; (thread-join! handle))) - -;; Spawn thread! -> This requires some up front overhead, however spawning a new thread -;; takes about 400 microseconds, which isn't ideal. In theory we do not need to deep clone _everything_, -;; but there are certain optimizations that need to be implemented up front for this to work. -; (thread-join! -; (spawn-thread! run-background-loop)) - -;; Spawns a thread, returning a handle to the sender to that thread. -(define (message-passing) - (define channels (make-channels)) - (define sender (list-ref channels 0)) - (define receiver (list-ref channels 1)) - - ;; Worker thread, listen to requests - (spawn-thread! (lambda () - ;; Process incoming requests. - (while #true (displayln (channel->recv receiver))) - (loop))) - - sender) - -(define *CHILD_THREADS* '()) - -;; Keep track of all of the threads currently running -(define (record-thread-handle handle) - (set! *CHILD_THREADS* (cons handle *CHILD_THREADS*))) - -(struct CancellableThreadHandle (sender handle)) - -;;@doc -;; Spawn a function, func, that runs on a background thread, running at the interval `delay-ms` -(define (spawn-cancellable-thread-looping func delay-ms) - ;; Create the channels. We're going to cancel the thread using - ;; the sender here to interrupt the receiver - (define channels (make-channels)) - (define sender (list-ref channels 0)) - (define receiver (list-ref channels 1)) - - (CancellableThreadHandle sender - (spawn-thread! (lambda () - (while (not (~> (channel->try-recv receiver) (unwrap-ok))) - (begin - (func) - (time/sleep-ms delay-ms))) - (displayln "Shutting down thread: " - (thread::current/id)))))) - -; (define tasks -; (map -; (lambda (_) (spawn-cancellable-thread-looping (lambda () (displayln (thread::current/id))) 1000)) -; (range 0 100))) - -; (thread-join! (CancellableThreadHandle-handle (car tasks))) - -; (let ([cancellable-handler -; (spawn-cancellable-thread-looping (lambda () (displayln "Hello world!")) 500)]) -; (time/sleep-ms 3000) -; ;; Cancel the background thread with the interrupt token -; (channel->send (CancellableThreadHandle-sender cancellable-handler) #t) - -; (displayln "Doing more work after the function!") -; (time/sleep-ms 1000) -; (displayln "Finished")) - -; (let ([sender (message-passing)]) -; (channel->send sender "Hello world!") -; (time/sleep-ms 500) -; (channel->send sender "Second message!") -; (time/sleep-ms 500) -; (channel->send sender "and we're done") -; (time/sleep-ms 500)) - -; (let ([tasks (map (lambda (_) -; (spawn-thread! (lambda () -; (time/sleep-ms 2000) -; (displayln (thread::current/id))))) -; (range 0 10))]) -; (map thread-join! tasks)) - -(require-builtin steel/process) - -; (define (port-stream) -; (let ((head (read-line-from-port my-port))) -; (if (equal? 'eof head) -; empty-stream -; (stream-cons head (lambda () (port-stream)))))) - -(define (pipe-stdout channel port) - (let ([head (read-line-from-port port)]) - (displayln head) - (if (equal? 'eof head) - 'eof - (begin - (channel->send channel head) - (pipe-stdout channel port))))) - -(define (with-stdout-piped command) - (set-piped-stdout! command) - command) - -(require "steel/result") - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; BEGIN EXPERIMENT WITH LIVE PRINTING SUBPROCESS STUFF -; (define (message-passing-sub-process) -; (define channels (make-channels)) -; (define sender (list-ref channels 0)) -; (define receiver (list-ref channels 1)) - -; ;; Worker thread, listen to requests -; (record-thread-handle (spawn-thread! (lambda () -; (define child-process -; ; (command "cargo" '("run" "--" "sleep.scm")) -; (~> (command "cargo" '("run" "--" "sleep.scm")) -; (with-stdout-piped) -; (spawn-process) -; (Ok->value))) - -; (define child-stdout (child-stdout child-process)) - -; ;; Send stuff along the channel until there is no more stuff -; ;; to send -; (pipe-stdout sender child-stdout) - -; (wait child-process) - -; (channel->send sender 'eof) - -; ; (wait child-process) -; ))) - -; channels) - -; (require "steel/result") - -; (define (read-loop receiver) -; (let ([message (channel->try-recv receiver)]) -; (cond -; [(Ok? message) -; (let ([inner-message (unwrap-ok message)]) -; ;; #false would mean we don't have anything to read -; ;; on the channel, but the thread is not yet finished. -; (when (string? inner-message) -; (display inner-message)) - -; (unless (equal? 'eof inner-message) - -; (time/sleep-ms 7) - -; (read-loop receiver))) - -; ; (if (thread-finished? (car *CHILD_THREADS*)) -; ; (read-loop receiver) - -; ; (displayln "finished") - -; ; (read-loop receiver)) -; ] -; [else (displayln "finished")]))) - -; (define keep-alive #f) - -; (define (main) - -; (define sender-and-receiver (message-passing-sub-process)) - -; ;; If this gets dropped, it is game over -; (define sender (list-ref sender-and-receiver 0)) -; (define receiver (list-ref sender-and-receiver 1)) - -; (read-loop receiver) - -; (displayln "finished")) - -; (main) - -;; END EXPERIMENT WITH LIVE PRINTING STUFF -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -; (while (not (~> (channel->try-recv receiver) (unwrap-ok))) -; (begin -; (displayln) -; (time/sleep-ms delay-ms)))) - -(define (message-passing-sub-process) - (define channels (make-channels)) - (define sender (list-ref channels 0)) - (define receiver (list-ref channels 1)) - - (define command-channels (make-channels)) - (define command-sender (list-ref channels 0)) - (define command-receiver (list-ref channels 1)) - - ;; Worker thread, listen to requests - (record-thread-handle - (spawn-thread! (lambda () - (define child-process - ; (command "cargo" '("run" "--" "sleep.scm")) - (~> (command "bash" '()) (with-stdout-piped) (spawn-process) (Ok->value))) - - (define child-stdout (child-stdout child-process)) - (define child-stdin (child-stdin child-process)) - - (write-line! child-stdin "ls") - - ;; Send stuff along the channel until there is no more stuff - ;; to send - (pipe-stdout sender child-stdout) - - ; (channel->send sender 'eof) - - (write-line! child-stdin "ls") - - (pipe-stdout sender child-stdout) - - (channel->send sender 'eof) - - ; (pipe-stdout sender child-stdout) - - ; (wait child-process) - - ; (channel->send sender 'eof) - - ; (wait child-process) - ))) - - channels) - -(define (read-loop receiver) - (let ([message (channel->try-recv receiver)]) - (cond - [(Ok? message) - (let ([inner-message (unwrap-ok message)]) - ;; #false would mean we don't have anything to read - ;; on the channel, but the thread is not yet finished. - (when (string? inner-message) - (display inner-message)) - - (unless (equal? 'eof inner-message) - - (time/sleep-ms 7) - - (read-loop receiver))) - - ; (if (thread-finished? (car *CHILD_THREADS*)) - ; (read-loop receiver) - - ; (displayln "finished") - - ; (read-loop receiver)) - ] - [else (displayln "finished")]))) - -(define keep-alive #f) - -(define (main) - - (define sender-and-receiver (message-passing-sub-process)) - - ;; If this gets dropped, it is game over - (define sender (list-ref sender-and-receiver 0)) - (define receiver (list-ref sender-and-receiver 1)) - - (read-loop receiver) - - (thread-join! (car *CHILD_THREADS*)) - - (displayln "finished")) - -(main) diff --git a/cogs/threads/test-threads.scm b/cogs/threads/test-threads.scm deleted file mode 100644 index 225050789..000000000 --- a/cogs/threads/test-threads.scm +++ /dev/null @@ -1,25 +0,0 @@ -(require-builtin steel/time) -(require "steel/result") - -(require "steel/tests/unit-test.scm" - (for-syntax "steel/tests/unit-test.scm") - "threads.scm") - -(provide __module__) - -(define __module__ 'thread-test-module) - -(define (spawn-concurrent-tasks) - - (let ([tasks (map (lambda (_) - (spawn-thread! (lambda () - (time/sleep-ms 2000) - (stdout-simple-displayln (thread::current/id))))) - (range 0 10))]) - (map (lambda (x) (thread-join! x)) tasks))) - -; (error "HELLO WORLD") - -(test-module - "Basic threads works" - (check-equal? "spawn-threads" (spawn-concurrent-tasks) (map (lambda (x) void) (range 0 10)))) diff --git a/cogs/threads/threads.scm b/cogs/threads/threads.scm deleted file mode 100644 index 9b44443b7..000000000 --- a/cogs/threads/threads.scm +++ /dev/null @@ -1,52 +0,0 @@ -(require-builtin steel/time) -(require "steel/result") - -(provide spawn-cancellable-thread-looping) - -;; Spawns a thread, returning a handle to the sender to that thread. -; (define (message-passing) -; (define channels (make-channels)) -; (define sender (list-ref channels 0)) -; (define receiver (list-ref channels 1)) - -; ;; Worker thread, listen to requests -; (spawn-thread! (lambda () -; ;; Process incoming requests. -; (while #true (displayln (channel->recv receiver))) -; (loop))) - -; sender) - -(define *CHILD_THREADS* '()) - -;; Keep track of all of the threads currently running -(define (record-thread-handle handle) - (set! *CHILD_THREADS* (cons handle *CHILD_THREADS*))) - -(struct CancellableThreadHandle (sender handle)) - -;;@doc -;; Spawn a function, func, that runs on a background thread, running at the interval `delay-ms` -(define (spawn-cancellable-thread-looping func delay-ms) - ;; Create the channels. We're going to cancel the thread using - ;; the sender here to interrupt the receiver - (define channels (make-channels)) - (define sender (list-ref channels 0)) - (define receiver (list-ref channels 1)) - - (CancellableThreadHandle sender - (spawn-thread! (lambda () - (while (not (~> (channel->try-recv receiver) (unwrap-ok))) - (begin - (func) - (time/sleep-ms delay-ms))) - (stdout-simple-displayln "Shutting down thread: " - (thread::current/id)))))) - -; (let ([tasks (map (lambda (_) -; (spawn-thread! (lambda () -; (time/sleep-ms 2000) -; (displayln (thread::current/id)) -; 1))) -; (range 0 10))]) -; (displayln (map thread-join! tasks))) diff --git a/cogs/time/cog.scm b/cogs/time/cog.scm deleted file mode 100644 index d820e62d8..000000000 --- a/cogs/time/cog.scm +++ /dev/null @@ -1,5 +0,0 @@ -(define package-name 'steel/time) -(define version "0.1.0") - -;; Core library, requires no dependencies -(define dependencies '()) \ No newline at end of file diff --git a/cogs/time/time.scm b/cogs/time/time.scm deleted file mode 100644 index 94fe73d04..000000000 --- a/cogs/time/time.scm +++ /dev/null @@ -1,21 +0,0 @@ -(require-builtin steel/time) -(provide (for-syntax time!)) - - -(define (instant/elapsed->string t) - (~> t - (instant/elapsed) - (duration->string))) - -(define-syntax time! - (syntax-rules () - [(time! expr) - (let ((t (instant/now)) - (result expr)) - - (display (quote expr)) (display " took ") - (displayln (instant/elapsed->string t)) - result)])) - - - diff --git a/cogs/transducers/cog.scm b/cogs/transducers/cog.scm deleted file mode 100644 index d02fb1e87..000000000 --- a/cogs/transducers/cog.scm +++ /dev/null @@ -1,5 +0,0 @@ -(define package-name 'steel/transducers) -(define version "0.1.0") - -;; Core library, requires no dependencies -(define dependencies '(steel/tests)) \ No newline at end of file diff --git a/cogs/transducers/transducers.scm b/cogs/transducers/transducers.scm deleted file mode 100644 index b12925499..000000000 --- a/cogs/transducers/transducers.scm +++ /dev/null @@ -1,554 +0,0 @@ -(require "steel/tests/unit-test.scm" - (for-syntax "steel/tests/unit-test.scm")) - -(provide list-transduce - tmap - tfilter - tflatten - tdelete-neighbor-duplicates - tenumerate - tlog - tadd-between - ttake - ttake-while - tconcatenate - rcons - reverse-rcons) - -;; A reduced value is stops the transduction. -; (define-record-type -; (reduced val) -; reduced? -; (val unreduce)) - -(struct reduced (val)) - -(define unreduce reduced-val) - -;; helper function which ensures x is reduced. -(define (ensure-reduced x) - (if (reduced? x) x (reduced x))) - -;; helper function that wraps a reduced value twice since reducing functions (like list-reduce) -;; unwraps them. tconcatenate is a good example: it re-uses it's reducer on it's input using list-reduce. -;; If that reduction finishes early and returns a reduced value, list-reduce would "unreduce" -;; that value and try to continue the transducing process. -(define (preserving-reduced reducer) - (lambda (a b) (let ([return (reducer a b)]) (if (reduced? return) (reduced return) return)))) - -;; Rewrite list-reduce to abuse some internal optimizations -;; if v is in the tail position, then it will be the last used -(define (list-reduce2 f identity lst v) - (cond - [(null? lst) identity] - [(reduced? v) (unreduce v)] - [else (list-reduce2 f v (cdr lst) (f identity (car lst)))])) - -;; TODO: Original implementation here -;; This is where the magic tofu is cooked -(define (list-reduce f identity lst) - (if (null? lst) - identity - (%plain-let ((v (f identity (car lst)))) - (if (reduced? v) (unreduce v) (list-reduce f v (cdr lst)))))) - -(define (test-map func lst accum) - (if (empty? lst) (reverse accum) (test-map func (cdr lst) (cons (func (car lst)) accum)))) - -;; TODO: Come back to this when there is a better understanding -;; of how to implement let loop -(define (vector-reduce f identity vec) - (let ([len (vector-length vec)]) - (let loop ([i 0] [acc identity]) - (if (= i len) - acc - (let ([acc (f acc (vector-ref vec i))]) - (if (reduced? acc) (unreduce acc) (loop (+ i 1) acc))))))) - -; (define (string-reduce f identity str) -; (let ((len (string-length str))) -; (let loop ((i 0) (acc identity)) -; (if (= i len) -; acc -; (let ((acc (f acc (string-ref str i)))) -; (if (reduced? acc) -; (unreduce acc) -; (loop (+ i 1) acc))))))) - -; (define (bytevector-u8-reduce f identity vec) -; (let ((len (bytevector-length vec))) -; (let loop ((i 0) (acc identity)) -; (if (= i len) -; acc -; (let ((acc (f acc (bytevector-u8-ref vec i)))) -; (if (reduced? acc) -; (unreduce acc) -; (loop (+ i 1) acc))))))) - -; (define (port-reduce f identity reader port) -; (let loop ((val (reader port)) (acc identity)) -; (if (eof-object? val) -; acc -; (let ((acc (f acc val))) -; (if (reduced? acc) -; (unreduce acc) -; (loop (reader port) acc)))))) - -; (define (generator-reduce f identity gen) -; (let loop ((val (gen)) (acc identity)) -; (if (eof-object? val) -; acc -; (let ((acc (f acc val))) -; (if (reduced? acc) -; (unreduce acc) -; (loop (gen) acc)))))) - -;; A special value to be used as a placeholder where no value has been set and #f -;; doesn't cut it. Not exported, and not really needed. -; (define-record-type -; (make-nothing) -; nothing?) - -(struct ()) - -(define nothing ()) -(define nothing? ?) - -;; helper function which ensures x is reduced. -; (define (ensure-reduced x) -; (if (reduced? x) x (reduced x))) - -;; helper function that wraps a reduced value twice since reducing functions (like list-reduce) -;; unwraps them. tconcatenate is a good example: it re-uses it's reducer on it's input using list-reduce. -;; If that reduction finishes early and returns a reduced value, list-reduce would "unreduce" -;; that value and try to continue the transducing process. -; (define (preserving-reduced f) -; (lambda (a b) (let ([return (f a b)]) (if (reduced? return) (reduced return) return)))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Reducing functions meant to be used at the end at the transducing -;; process. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;;@doc -;; A transducer-friendly cons with the empty list as identity -;; This function accepts multiple arguments: -;; * 0 arguments, returns the empty list as the identity -;; * 1 argument, a list, returns the reverse of that list -;; * 2 arguments, a list and the element, then returns a new list with the element consed to the list -;; -;; Used to build up a list during a transduction: -;; ```scheme -;; (list-transduce (tfilter odd?) rcons (list 1 2 3 4)) ;; => '(1 3) -;; ``` -(define rcons - (case-lambda - [() '()] - [(lst) (reverse lst)] - [(lst x) (cons x lst)])) - -;;@doc -;; A transducer-friendly cons with the empty list as identity. Acts like rcons, however will reverse -;; the resulting list. -;; -;; This function accepts multiple arguments: -;; * 0 arguments, returns the empty list as the identity -;; * 1 argument, a list, returns that list -;; * 2 arguments, a list and the element, then returns a new list with the element consed to the list -;; -;; Used to build up a list during a transduction: -;; ```scheme -;; (list-transduce (tfilter odd?) rcons (list 1 2 3 4)) ;; => '(3 1) -;; ``` -(define reverse-rcons - (case-lambda - [() '()] - [(lst) lst] - [(lst x) (cons x lst)])) - -;;@doc -;; Use this as the f in transduce to count the amount of elements passed through. -;; -;; ```scheme -;; (list-transduce (tfilter odd?) tcount (list 1 2 3)) ;; => 2 -;; ``` -(define rcount - (case-lambda - [() 0] - [(result) result] - [(result input) (+ 1 result)])) - -;;@doc -;; These two take a predicate and returns reducing functions that behave -;; like any and every from srfi-1 -(define (rany pred) - (case-lambda - [() #f] - [(result) result] - [(result input) (let ([test (pred input)]) (if test (reduced test) #f))])) - -(define (revery pred) - (case-lambda - [() #t] - [(result) result] - [(result input) (let ([test (pred input)]) (if (and result test) test (reduced #f)))])) - -(define list-transduce - (case-lambda - [(xform f coll) (list-transduce xform f (f) coll)] - [(xform f init coll) (let* ([xf (xform f)] [result (list-reduce xf init coll)]) (xf result))])) - -(define vector-transduce - (case-lambda - [(xform f coll) (vector-transduce xform f (f) coll)] - [(xform f init coll) (let* ([xf (xform f)] [result (vector-reduce xf init coll)]) (xf result))])) - -; (define string-transduce -; (case-lambda -; ((xform f coll) -; (string-transduce xform f (f) coll)) -; ((xform f init coll) -; (let* ((xf (xform f)) -; (result (string-reduce xf init coll))) -; (xf result))))) - -; (define bytevector-u8-transduce -; (case-lambda -; ((xform f coll) -; (bytevector-u8-transduce xform f (f) coll)) -; ((xform f init coll) -; (let* ((xf (xform f)) -; (result (bytevector-u8-reduce xf init coll))) -; (xf result))))) - -; (define port-transduce -; (case-lambda -; ((xform f by) -; (generator-transduce xform f by)) -; ((xform f by port) -; (port-transduce xform f (f) by port)) -; ((xform f init by port) -; (let* ((xf (xform f)) -; (result (port-reduce xf init by port))) -; (xf result))))) - -; (define generator-transduce -; (case-lambda -; ((xform f gen) -; (generator-transduce xform f (f) gen)) -; ((xform f init gen) -; (let* ((xf (xform f)) -; (result (generator-reduce xf init gen))) -; (xf result))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Transducers! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(define (tmap f) - (lambda (reducer) - (case-lambda - [() (reducer)] - [(result) (reducer result)] - [(result input) (reducer result (f input))]))) - -(define (tfilter pred) - (lambda (reducer) - (case-lambda - [() (reducer)] - [(result) (reducer result)] - [(result input) (if (pred input) (reducer result input) result)]))) - -(define (tremove pred) - (lambda (reducer) - (case-lambda - [() (reducer)] - [(result) (reducer result)] - [(result input) (if (not (pred input)) (reducer result input) result)]))) - -; (define (tfilter-map f) -; (compose (tmap f) (tfilter values))) - -; (define (make-replacer map) -; (cond -; ((list? map) -; (lambda (x) -; (let ((replacer? (assoc x map))) -; (if replacer? -; (cdr replacer?) -; x)))) -; ((hash-table? map) -; (lambda (x) -; (hash-table-ref/default map x x))) -; ((procedure? map) map) -; (else -; (error "Unsupported mapping in treplace" map)))) - -; (define (treplace map) -; (tmap (make-replacer map))) - -; (define (tdrop n) -; (lambda (reducer) -; (let ((new-n (+ 1 n))) -; (case-lambda -; (() (reducer)) -; ((result) (reducer result)) -; ((result input) -; (set! new-n (- new-n 1)) -; (if (positive? new-n) -; result -; (reducer result input))))))) - -; (define (tdrop-while pred) -; (lambda (reducer) -; (let ((drop? #t)) -; (case-lambda -; (() (reducer)) -; ((result) (reducer result)) -; ((result input) -; (if (and (pred input) drop?) -; result -; (begin -; (set! drop? #f) -; (reducer result input)))))))) - -(define (ttake n) - (define (positive? x) - (> x 0)) - (lambda (reducer) - ;; we need to reset new-n for every new transduction - (let ([new-n n]) - (case-lambda - [() (reducer)] - [(result) (reducer result)] - [(result input) - (let ([result (if (positive? new-n) (reducer result input) result)]) - (set! new-n (- new-n 1)) - (if (not (positive? new-n)) (ensure-reduced result) result))])))) - -(define ttake-while - (case-lambda - [(pred) (ttake-while pred (lambda (result input) result))] - [(pred retf) - (lambda (reducer) - (let ([take? #t]) - (case-lambda - [() (reducer)] - [(result) (reducer result)] - [(result input) - (if (and take? (pred input)) - (reducer result input) - (begin - (set! take? #f) - (ensure-reduced (retf result input))))])))])) - -(define (tconcatenate reducer) - (let ([preserving-reducer (preserving-reduced reducer)]) - (case-lambda - [() (reducer)] - [(result) (reducer result)] - [(result input) (list-reduce preserving-reducer result input)]))) - -; (define (tappend-map f) -; (compose (tmap f) tconcatenate)) - -;;@doc -;; Flattens everything and passes each value through the reducer -;; -;; ```scheme -;; (list-transduce tflatten rcons (list 1 2 (list 3 4 '(5 6) 7 8))) ;; => (1 2 3 4 5 6 7 8) -;; ``` -(define tflatten - (lambda (reducer) - (case-lambda - [() '()] - [(result) (reducer result)] - [(result input) - (if (list? input) - (list-reduce (preserving-reduced (tflatten reducer)) result input) - (reducer result input))]))) - -;;@doc -;; removes duplicate consecutive elements -(define tdelete-neighbor-duplicates - (case-lambda - [() (tdelete-neighbor-duplicates equal?)] - [(equality-pred?) - (lambda (reducer) - (let ([prev nothing]) - (case-lambda - [() (reducer)] - [(result) (reducer result)] - [(result input) - (if (equality-pred? prev input) - result - (begin - (set! prev input) - (reducer result input)))])))])) - -;; Deletes all duplicates that passes through. -; (define tdelete-duplicates -; (case-lambda -; (() (tdelete-duplicates equal?)) -; ((equality-pred?) -; (lambda (reducer) -; (let ((already-seen (make-hash-table equality-pred?))) -; (case-lambda -; (() (reducer)) -; ((result) (reducer result)) -; ((result input) -; (if (hash-table-exists? already-seen input) -; result -; (begin -; (hash-table-set! already-seen input #t) -; (reducer result input)))))))))) - -;; Partitions the input into lists of N items. If the input stops it flushes whatever -;; it has collected, which may be shorter than n. -; (define (tsegment n) -; (if (not (and (integer? n) (positive? n))) -; (error "argument to tsegment must be a positive integer") -; (lambda (reducer) -; (let ((i 0) -; (collect (make-vector n))) -; (case-lambda -; (() (reducer)) -; ((result) -; ;; if there is anything collected when we are asked to quit -; ;; we flush it to the remaining transducers -; (let ((result -; (if (zero? i) -; result -; (reducer result (vector->list collect 0 i))))) -; (set! i 0) -; ;; now finally, pass it downstreams -; (if (reduced? result) -; (reducer (unreduce result)) -; (reducer result)))) -; ((result input) -; (vector-set! collect i input) -; (set! i (+ i 1)) -; ;; If we have collected enough input we can pass it on downstream -; (if (< i n) -; result -; (let ((next-input (vector->list collect 0 i))) -; (set! i 0) -; (reducer result next-input))))))))) - -; (define (tpartition f) -; (lambda (reducer) -; (let* ((prev nothing) -; (collect '())) -; (case-lambda -; (() (reducer)) -; ((result) -; (let ((result -; (if (null? collect) -; result -; (reducer result (reverse! collect))))) -; (set! collect '()) -; (if (reduced? result) -; (reducer (unreduce result)) -; (reducer result)))) -; ((result input) -; (let ((fout (f input))) -; (cond -; ((or (equal? fout prev) (nothing? prev)) ; collect -; (set! prev fout) -; (set! collect (cons input collect)) -; result) -; (else ; flush what we collected already to the reducer -; (let ((next-input (reverse! collect))) -; (set! prev fout) -; (set! collect (list input)) -; (reducer result next-input)))))))))) - -;;@doc -;; Interposes element between each value pushed through the transduction. -(define (tadd-between elem) - (lambda (reducer) - (let ([send-elem? #f]) - (case-lambda - [() (reducer)] - [(result) (reducer result)] - [(result input) - (if send-elem? - (let ([result (reducer result elem)]) - (if (reduced? result) result (reducer result input))) - (begin - (set! send-elem? #t) - (reducer result input)))])))) - -;;@doc -;; indexes every value passed through in a cons pair as in (index . value). By default starts at 0 -(define tenumerate - (case-lambda - [() (tenumerate 0)] - [(n) - (lambda (reducer) - (let ([n n]) - (case-lambda - [() (reducer)] - [(result) (reducer result)] - [(result input) - (let ([input (list n input)]) - (set! n (+ n 1)) - (reducer result input))])))])) - -(define tlog - (case-lambda - [() (tlog (lambda (result input) (displayln input)))] - [(log-function) - (lambda (reducer) - (case-lambda - [() (reducer)] - [(result) (reducer result)] - [(result input) - (log-function result input) - (reducer result input)]))])) - -;; rcons here seems to be... slow -;; investigate tmap or rcons and see why / how its used with the input -; (list-transduce (tmap (lambda (x) (+ x 1))) rcons (list 0 1 2 3)) - -; (list-transduce (tfilter even?) rcons (list 0 1 2 3 4 5)) - -; (list-transduce tflatten rcons (list 1 2 (list 3 4 '(5 6) 7 8))) - -; (list-transduce (tdelete-neighbor-duplicates) rcons (list 1 1 2 2 3 3 4 4)) - -; (list-transduce (tenumerate) rcons (list 1 1 2 2 3 3 4 4)) - -; (list-transduce (tadd-between 10) rcons (list 1 2 3 4 5)) - -; (list-transduce (ttake 4) rcons (list 1 2 3 4 5 6 7 8 9 10)) - -; (list-transduce (ttake-while even?) rcons (list 2 4 6 8 9 10)) - -; (list-transduce tconcatenate rcons '((10 20) (30 40) (50 60))) - -(test-module - "transducers tests" - (check-equal? "tmap with basic addition and rcons" - (list-transduce (tmap (lambda (x) (+ x 1))) rcons (list 0 1 2 3)) - (list 1 2 3 4)) - (check-equal? "tfilter with a basic predicate" - (list-transduce (tfilter even?) rcons (list 0 1 2 3 4 5)) - (list 0 2 4)) - (check-equal? "tflatten basic flattening" - (list-transduce tflatten rcons (list 1 2 (list 3 4 '(5 6) 7 8))) - (list 1 2 3 4 5 6 7 8)) - (check-equal? "tdelete-neighbor-duplicates" - (list-transduce (tdelete-neighbor-duplicates) rcons (list 1 1 2 2 3 3 4 4)) - (list 1 2 3 4)) - (check-equal? "tenumerate" - (list-transduce (tenumerate) rcons (list 1 1 2 2 3 3 4 4)) - '((0 1) (1 1) (2 2) (3 2) (4 3) (5 3) (6 4) (7 4))) - (check-equal? "tadd-between" - (list-transduce (tadd-between 10) rcons (list 1 2 3 4 5)) - '(1 10 2 10 3 10 4 10 5)) - (check-equal? "ttake" (list-transduce (ttake 4) rcons (list 1 2 3 4 5 6 7 8 9 10)) (list 1 2 3 4)) - (check-equal? "ttake-while" - (list-transduce (ttake-while even?) rcons (list 2 4 6 8 9 10)) - (list 2 4 6 8)) - (check-equal? "tconcatenate" - (list-transduce tconcatenate rcons '((10 20) (30 40) (50 60))) - '(10 20 30 40 50 60))) diff --git a/core/contracts.rkt b/core/contracts.rkt deleted file mode 100644 index e860315d7..000000000 --- a/core/contracts.rkt +++ /dev/null @@ -1,42 +0,0 @@ -; (provide -; listof /c <=/c >=/c any/c and/c or/c) - -; ;; Contract combinators -; (define (listof pred) -; (lambda (lst) -; (define (loop lst) -; (cond [(null? lst) #t] -; [(pred (car lst)) (loop (cdr lst))] -; [else #f])) -; (cond [(null? lst) #t] -; [(list? lst) -; (loop lst)] -; [else #f]))) - -; ;; Contracts for < -; (define ( -; (define (>/c n) -; (make/c (fn (x) (> x n)) (list '>/c n))) - -; ;; Contracts for <= -; (define (<=/c n) -; (make/c (fn (x) (<= x n)) (list '<=/c n))) - -; ;; Contracts for >= -; (define (>=/c n) -; (make/c (fn (x) (>= x n)) (list '>=/c n))) - -; ;; Satisfies any single value -; (define (any/c x) -; (make/c (fn (x) #t) 'any/c)) - -; ;; produces a function compatible with contract definitions -; (define (and/c x y) -; (lambda (z) (and (x z) (y z)))) - -; ;; produces a function compatible with contract definitions -; (define (or/c x y) -; (lambda (z) (or (x z) (y z)))) diff --git a/core/display.rkt b/core/display.rkt deleted file mode 100644 index e3ea38f19..000000000 --- a/core/display.rkt +++ /dev/null @@ -1,22 +0,0 @@ -(provide println) - -(define println simple-displayln) - -; (define/contract (struct-name s) -; (->/c custom-struct? symbol?) -; (mut-vector-ref s 1)) - -; (define/contract (struct-transparent? s) -; (->/c custom-struct? boolean?) -; (hash-try-get (mut-vector-ref s 2) ':transparent)) - -; (define (println x) -; (displayln -; (if (custom-struct? x) -; (if (struct-transparent? x) -; (cons (struct-name x) (transduce x (into-list))) -; (string-append "#<" -; (string-append -; (symbol->string (struct-name x)) -; ">"))) -; x))) diff --git a/core/prelude.rkt b/core/prelude.rkt deleted file mode 100644 index 556779413..000000000 --- a/core/prelude.rkt +++ /dev/null @@ -1,138 +0,0 @@ -(provide - caar cadr cdar cddr caaar caadr cadar caddr cdaar cdadr cddar cdddr caaaar caadar caaddr cadaar - cadadr caddar cadddr cdaaar cdaadr cdadar cdaddr cddaar cddadr cdddar cddddr id flip curry curry2 - not foldl map foldr unfold fold reduce max min empty? mem-helper member assoc filter even-rec? - odd-rec? sum add1 sub1 zero? take drop slice) - -(define caar (lambda (pair) (car (car pair)))) -(define cadr (lambda (pair) (car (cdr pair)))) -(define cdar (lambda (pair) (cdr (car pair)))) -(define cddr (lambda (pair) (cdr (cdr pair)))) -(define caaar (lambda (pair) (car (car (car pair))))) -(define caadr (lambda (pair) (car (car (cdr pair))))) -(define cadar (lambda (pair) (car (cdr (car pair))))) -(define caddr (lambda (pair) (car (cdr (cdr pair))))) -(define cdaar (lambda (pair) (cdr (car (car pair))))) -(define cdadr (lambda (pair) (cdr (car (cdr pair))))) -(define cddar (lambda (pair) (cdr (cdr (car pair))))) -(define cdddr (lambda (pair) (cdr (cdr (cdr pair))))) -(define caaaar (lambda (pair) (car (car (car (car pair)))))) -(define caaadr (lambda (pair) (car (car (car (cdr pair)))))) -(define caadar (lambda (pair) (car (car (cdr (car pair)))))) -(define caaddr (lambda (pair) (car (car (cdr (cdr pair)))))) -(define cadaar (lambda (pair) (car (cdr (car (car pair)))))) -(define cadadr (lambda (pair) (car (cdr (car (cdr pair)))))) -(define caddar (lambda (pair) (car (cdr (cdr (car pair)))))) -(define cadddr (lambda (pair) (car (cdr (cdr (cdr pair)))))) -(define cdaaar (lambda (pair) (cdr (car (car (car pair)))))) -(define cdaadr (lambda (pair) (cdr (car (car (cdr pair)))))) -(define cdadar (lambda (pair) (cdr (car (cdr (car pair)))))) -(define cdaddr (lambda (pair) (cdr (car (cdr (cdr pair)))))) -(define cddaar (lambda (pair) (cdr (cdr (car (car pair)))))) -(define cddadr (lambda (pair) (cdr (cdr (car (cdr pair)))))) -(define cdddar (lambda (pair) (cdr (cdr (cdr (car pair)))))) -(define cddddr (lambda (pair) (cdr (cdr (cdr (cdr pair)))))) -(define id (lambda (obj) obj)) -(define flip (lambda (func) (lambda (arg1 arg2) (func arg2 arg1)))) -(define curry (lambda (func arg1) (lambda (arg) (func arg1 arg)))) -(define curry2 (lambda (func arg1) (lambda (arg2 arg3) (func arg1 arg2 arg3)))) - - -(define (not a) - (if a - #f - #t)) - -(define (foldl func accum lst) - (if (null? lst) - accum - (foldl func - (func (car lst) accum) ; here's the change - (cdr lst)))) - - -(define (map func lst) - (if (null? lst) - '() - (transduce lst (mapping func) (into-list)))) - - -(define foldr (lambda (func accum lst) - (if (null? lst) - accum - (func (car lst) (foldr func accum (cdr lst)))))) - - - -(define unfold (lambda (func init pred) - (if (pred init) - (cons init '()) - (cons init (unfold func (func init) pred))))) - -(define fold (lambda (f a l) (foldl f a l))) -(define reduce (lambda (f a l) (fold f a l))) -(define max (lambda (x num-list) (fold (lambda (y z) (if (> y z) y z)) x (cons 0 num-list)))) -(define min (lambda (x num-list) (fold (lambda (y z) (if (< y z) y z)) x (cons 536870911 num-list)))) - -(define empty? null?) - -(define mem-helper (lambda (pred op) (lambda (acc next) (if (and (not acc) (pred (op next))) next acc)))) -;; (define memq (lambda (obj lst) (fold (mem-helper (curry eq? obj) id) #f lst))) -;; (define memv (lambda (obj lst) (fold (mem-helper (curry eqv? obj) id) #f lst))) -(define member (lambda (obj lst) (fold (mem-helper (curry equal? obj) id) #f lst))) - -;; TODO come back to this -; (define assq (lambda (obj alist) (fold (mem-helper (curry eq? obj) car) #f alist))) - -;; (define assv (lambda (obj alist) (fold (mem-helper (curry eqv? obj) car) #f alist))) -; (define assoc (lambda (obj alist) (fold (mem-helper (curry equal? obj) car) #f alist))) - -; (define assoc ) - -(define (assoc thing alist) - (if (null? alist) - #f - (if (equal? (car (car alist)) thing) - (car alist) - (assoc thing (cdr alist))))) - - -(define (filter pred lst) - (if (empty? lst) - '() - (transduce lst (filtering pred) (into-list)))) - -; (define (fact n) -; (define factorial-tail (lambda (n acc) -; (if (= n 0) -; acc -; (factorial-tail (- n 1) (* acc n ))))) -; (factorial-tail n 1)) - -(define even-rec? (lambda (x) (if (= x 0) #t (odd-rec? (- x 1))))) -(define odd-rec? (lambda (x) (if (= x 0) #f (even-rec? (- x 1))))) - -(define sum (lambda (x) (reduce + 0 x))) -;; (define head car) -;; (define tail cdr) -(define (add1 n) (+ 1 n)) -(define (sub1 n) (- n 1)) -(define (zero? n) (= n 0)) - -;; currently broken, doesn't work properly -(defn (take lst n) - (defn (loop x l acc) - (if (= x 0) - acc - (loop (- x 1) (cdr l) (cons (car l) acc)))) - (loop n lst (list))) - -(define (drop lst n) - (define (loop x l) - (if (zero? x) - l - (loop (sub1 x) (cdr l)))) - (loop n lst)) - -(define (slice l offset n) - (take (drop l offset) n)) \ No newline at end of file diff --git a/core/syntax.rkt b/core/syntax.rkt deleted file mode 100644 index c7cd28326..000000000 --- a/core/syntax.rkt +++ /dev/null @@ -1,224 +0,0 @@ -(define-syntax steel/base - (syntax-rules () - [(steel/base) - (begin - (require-builtin steel/hash) - (require-builtin steel/sets) - (require-builtin steel/lists) - (require-builtin steel/strings) - (require-builtin steel/symbols) - (require-builtin steel/vectors) - (require-builtin steel/streams) - (require-builtin steel/contracts) - (require-builtin steel/identity) - (require-builtin steel/numbers) - (require-builtin steel/equality) - (require-builtin steel/ord) - (require-builtin steel/transducers) - (require-builtin steel/io) - (require-builtin steel/filesystem) - (require-builtin steel/ports) - (require-builtin steel/meta) - (require-builtin steel/json) - (require-builtin steel/constants) - (require-builtin steel/syntax))])) - -(define-syntax quasiquote - (syntax-rules (unquote unquote-splicing) - ((quasiquote (unquote x)) x) - ((quasiquote ((unquote x) xs ...)) (cons x (quasiquote (xs ...)))) - ((quasiquote ((unquote-splicing x))) (append x '())) - ((quasiquote ((unquote-splicing x) xs ...)) (append x (quasiquote (xs ...)))) - ((quasiquote (x xs ...)) (cons (quasiquote x) (quasiquote (xs ...)))) - ((quasiquote x) 'x))) - -(define-syntax or - (syntax-rules () - [(or) #f] - [(or x) x] - [(or x y) (let ([z x]) - (if z z y))] - [(or x y ...) (or x (or y ...))])) - -(define-syntax and - (syntax-rules () - [(and) #t] - [(and x) x] - [(and x y) (if x y #f)] - [(and x y ...) (and x (and y ...))])) - -(define-syntax when - (syntax-rules () - [(when a b ...) - (if a (begin b ...) void)])) - -(define-syntax unless - (syntax-rules () - [(unless a b ...) - (if a void (begin b ...))])) - -(define-syntax cond - (syntax-rules (else =>) - [(cond [else => e1 ...]) - (begin e1 ...)] - [(cond [else e1 ...]) - (begin e1 ...)] - [(cond [e1 e2 ...]) - (when e1 e2 ...)] - [(cond [e1 => e2 ...] c1 ...) - (if e1 - (begin e2 ...) - (cond c1 ...))] - [(cond [e1 e2 ...] c1 ...) - (if e1 - (begin e2 ...) - (cond c1 ...))])) - -(define-syntax while - (syntax-rules (do) - [(while cond do body ...) - (begin - (define (loop) - (when cond - body ... - (loop))) - (loop))] - [(while cond body ...) - (begin (define (loop) - (when cond body ... (loop))) - (loop))])) - -;; TODO add the single argument case -(define-syntax f> - (syntax-rules () - [(f> fun args* ...) - (lambda (x) (fun x args* ...))] - [(f> fun) fun])) - -(define-syntax -> - (syntax-rules () - [(-> a) a] - [(-> a (b c ...)) ((f> b c ...) a)] - [(-> a (b)) ((f> b) a)] - [(-> a b c ...) (-> (-> a b) c ...)])) - -(define-syntax l> - (syntax-rules () - [(l> fun args* ...) - (lambda (x) (fun args* ... x))] - [(l> fun) fun])) - -(define-syntax ->> - (syntax-rules () - [(->> a) a] - [(->> a (b c ...)) ((l> b c ...) a)] - [(->> a (b)) ((l> b) a)] - [(->> a b c ...) (->> (->> a b) c ...)])) - -(define-syntax swap - (syntax-rules () - [(swap a b) - (let ([tmp b]) - (begin - (set! b a) - (set! a tmp)))])) - -(define-syntax let* - (syntax-rules () - ((let* () body ...) ; base case - ((lambda () body ...))) - ((let* ((var val) rest ...) body ...) ; binding case - ((lambda (var) (let* (rest ...) body ...)) val)))) - -(define-syntax letrec*-helper - (syntax-rules () - ((letrec*-helper () body ...) - (begin body ...)) - ((letrec*-helper ((var val) rest ...) body ...) - (begin - (define var val) - (letrec*-helper (rest ...) body ...))))) - -(define-syntax letrec* - (syntax-rules () - ((letrec* bindings body ...) - ((lambda () - (letrec*-helper bindings body ...)))))) - -(define-syntax ->/c - (syntax-rules () - [(->/c r) - (make-function/c (make/c r 'r))] - [(->/c a b) - (make-function/c (make/c a 'a) (make/c b 'b))] - [(->/c a b c) - (make-function/c (make/c a 'a) (make/c b 'b) (make/c c 'c))] - [(->/c a b c d) - (make-function/c (make/c a 'a) (make/c b 'b) - (make/c c 'c) (make/c d 'd))] - [(->/c a b c d e) - (make-function/c (make/c a 'a) (make/c b 'b) (make/c c 'c) - (make/c d 'd) (make/c e 'e))] - [(->/c a b c d e f) - (make-function/c (make/c a 'a) (make/c b 'b) (make/c c 'c) - (make/c d 'd) (make/c e 'e) (make/c f 'f))] - [(->/c a b c d e f g) - (make-function/c (make/c a 'a) (make/c b 'b) (make/c c 'c) - (make/c d 'd) (make/c e 'e) (make/c f 'f) - (make/c g 'g))] - [(->/c a b c d e f g h) - (make-function/c (make/c a 'a) (make/c b 'b) (make/c c 'c) - (make/c d 'd) (make/c e 'e) (make/c f 'f) - (make/c g 'g) (make/c h 'h))] - [(->/c a b c d e f g h i) - (make-function/c (make/c a 'a) (make/c b 'b) (make/c c 'c) - (make/c d 'd) (make/c e 'e) (make/c f 'f) - (make/c g 'g) (make/c h 'h) (make/c i 'i))])) - -;; Macro for basic usage of contracts -(define-syntax define/contract - (syntax-rules () - [(define/contract (name args ...) - contract - body ...) - (define name (bind/c contract (lambda (args ...) body ...) 'name))] - [(define/contract name contract expr) - (define name ((bind/c - (make-function/c (make/c contract 'contract)) - (lambda () expr))))])) - -(define-syntax module - (syntax-rules (provide gen-defines contract/out) - [(module name (provide ids ...) funcs ...) - (begin - (define (datum->syntax name) - ((lambda () funcs ... - (module provide ids ...)))) - (module gen-defines name ids ...))] - - ;; in the contract case, ignore the contract in the hash - [(module provide (contract/out name contract)) (hash 'name name)] - ;; Normal case - [(module provide name) (hash 'name name)] - - ;; in the contract case, ignore the contract in the hash - [(module provide (contract/out name contract) rest ...) - (hash-insert (module provide rest ...) 'name name)] - - ;; Normal case - [(module provide name rest ...) - (hash-insert (module provide rest ...) 'name name)] - - ;; Module contract provides - [(module gen-defines mod (contract/out name contract)) - (define (datum->syntax name) (bind/c contract (hash-get mod 'name)))] - [(module gen-defines mod (contract/out name contract) rest ...) - (begin (define (datum->syntax name) (bind/c contract (hash-get mod 'name))) - (module gen-defines mod rest ...))] - - ;; Normal provides - [(module gen-defines mod name) (define (datum->syntax name) (hash-get mod 'name))] - [(module gen-defines mod name rest ...) - (begin (define (datum->syntax name) (hash-get mod 'name)) - (module gen-defines mod rest ...))])) - diff --git a/crates/cargo-steel-lib/Cargo.toml b/crates/cargo-steel-lib/Cargo.toml deleted file mode 100644 index 960590fd6..000000000 --- a/crates/cargo-steel-lib/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "cargo-steel-lib" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -cargo_metadata = "0.15.3" \ No newline at end of file diff --git a/crates/cargo-steel-lib/src/main.rs b/crates/cargo-steel-lib/src/main.rs deleted file mode 100644 index 4218ef837..000000000 --- a/crates/cargo-steel-lib/src/main.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::{error::Error, path::PathBuf, process::Command}; - -use cargo_metadata::{Message, MetadataCommand, Package}; -use std::process::Stdio; - -fn package_contains_dependency_on_steel(packages: &[Package]) -> Option<&Package> { - packages.iter().find(|x| x.name == "steel-core") -} - -fn main() -> Result<(), Box> { - // let build_target = Command::new("cargo") - // .arg("build") - // .arg("--release") - // .spawn()? - // .wait()? - // .success(); - - let mut steel_home = PathBuf::from(std::env::var("STEEL_HOME")?); - - steel_home.push("native"); - - let metadata = MetadataCommand::new().exec()?; - - // println!("{:#?}", metadata?.root_package()); - - // let package_name = metadata?.root_package().expect("Missing cargo toml!"). - - let package = match metadata.root_package() { - Some(p) => p, - None => return Err("cargo steel-lib must be run from within a crate".into()), - }; - - println!("Attempting to install: {:#?}", package.name); - - if package_contains_dependency_on_steel(&metadata.packages).is_none() { - return Err( - "Cannot install package as a steel dylib - does not contain a dependency on steel!" - .into(), - ); - } - - let mut command = Command::new("cargo") - .args([ - "build", - "--release", - "--message-format=json-render-diagnostics", - ]) - .stdout(Stdio::piped()) - .spawn() - .unwrap(); - - let reader = std::io::BufReader::new(command.stdout.take().unwrap()); - let last = cargo_metadata::Message::parse_stream(reader) - .filter_map(|x| { - if let Ok(Message::CompilerArtifact(artifact)) = x { - Some(artifact) - } else { - None - } - }) - .last() - .unwrap(); - - if last.target.kind == ["cdylib"] { - println!("Found a cdylib!"); - // println!("{:#?}", last); - - for file in last.filenames { - let filename = file.file_name().unwrap(); - - steel_home.push(filename); - - println!("Copying {} to {}", file, &steel_home.to_str().unwrap()); - - std::fs::copy(file, &steel_home).unwrap(); - - steel_home.pop(); - } - } else if last.target.kind == ["dylib"] { - println!("Found a dylib!"); - // println!("{:#?}", last); - - for file in last.filenames { - let filename = file.file_name().unwrap(); - - steel_home.push(filename); - - println!("Copying {} to {}", file, &steel_home.to_str().unwrap()); - - std::fs::copy(file, &steel_home).unwrap(); - - steel_home.pop(); - } - } - - println!("Done!"); - - command.wait().expect("Couldn't get cargo's exit status"); - - Ok(()) -} diff --git a/crates/steel-core/.gitignore b/crates/steel-core/.gitignore deleted file mode 100644 index 53eaa2196..000000000 --- a/crates/steel-core/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -**/*.rs.bk diff --git a/crates/steel-core/Cargo.toml b/crates/steel-core/Cargo.toml deleted file mode 100644 index cfb07bde5..000000000 --- a/crates/steel-core/Cargo.toml +++ /dev/null @@ -1,93 +0,0 @@ -[package] -name = "steel-core" -# version = "0.4.0" -version.workspace = true -authors = ["mattwparas "] -edition = "2021" -license = "MIT OR Apache-2.0" -repository = "https://github.com/mattwparas/steel" -description = "Core compiler and VM implementation for steel" - - -[lib] -name = "steel" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -im-rc = "15.1.0" -codespan-reporting = "0.11.1" -log = "0.4.17" - -futures-util = "0.3.28" -futures-task = "0.3.28" - -serde_json = "1.0.108" -serde = { version = "1.0.193", features = ["derive", "rc"] } -serde_derive = "1.0.193" -bincode = "1.3.3" -pretty = "0.12.1" -im-lists = "0.6.0" -quickscope = "0.2.0" -lasso = { version = "0.7.2", features = ["multi-threaded", "serialize"] } -once_cell = "1.18.0" -fxhash = "0.2.1" -# lazy_static = "1.4.0" -steel-gen = { path = "../steel-gen", version = "0.2.0" } -steel-parser = { path = "../steel-parser", version = "0.4.0" } -steel-derive = { path = "../steel-derive", version = "0.4.0" } -chrono = { version = "0.4.23", default-features = false, features = ["std", "clock"] } -weak-table = "0.3.2" -# TODO: Consider whether rand needs to be here -rand = "0.8.5" -# TODO: Consider only depending on the sub crate -num = "0.4.0" -which = "4.4.0" -radix_fmt = "1.0.0" - -# For structs -smallvec = { version = "1.10.0", optional = true } - -# Pretty printing documentation -termimad = { version = "0.21.0", optional = true } - -# FFI for dylibs -abi_stable = { version = "0.11.2", optional = true } -async-ffi = { version = "0.5.0", features = ["abi_stable"], optional = true } -# Cranelift JIT -cranelift = { version = "0.84.0", optional = true } -cranelift-module = { version = "0.84.0", optional = true } -cranelift-jit = { version = "0.84.0", optional = true } - -# Embedded dependencies for various popular libraries -rusqlite = { version = "0.28.0", features = ["bundled"], optional = true } -reqwest = { version = "0.11.14", features = ["blocking", "json"], optional = true } -url = { version = "2.3.1", optional = true } -anyhow = { version = "1", optional = true } - -[dev-dependencies] -proptest = "1.1.0" -criterion = "0.5.1" -env_logger = "0.10.0" - -[build-dependencies] -steel-gen = { path = "../steel-gen", version = "0.2.0" } - -[features] -default = ["modules"] -modules = [] -jit = ["dep:cranelift", "dep:cranelift-module", "dep:cranelift-jit"] -dynamic = [] -profiling = [] -web = ["dep:reqwest", "dep:url"] -sqlite = ["dep:rusqlite"] -unsafe-internals = [] -anyhow = ["dep:anyhow"] -dylibs = ["dep:abi_stable", "dep:async-ffi"] -markdown = ["dep:termimad"] -smallvec = ["dep:smallvec"] -without-drop-protection = [] - -[[bench]] -name = "my_benchmark" -harness = false - diff --git a/crates/steel-core/benches/my_benchmark.rs b/crates/steel-core/benches/my_benchmark.rs deleted file mode 100644 index 285c76d6f..000000000 --- a/crates/steel-core/benches/my_benchmark.rs +++ /dev/null @@ -1,334 +0,0 @@ -#![allow(unused)] - -use criterion::{black_box, criterion_group, criterion_main, Criterion}; - -use steel::stdlib::PRELUDE; -use steel::steel_vm::{engine::Engine, register_fn::RegisterFn}; - -fn benchmark_template(c: &mut Criterion, name: &str, script: &str, warmup: &str) { - let mut vm = Engine::new(); - vm.compile_and_run_raw_program(PRELUDE).unwrap(); - vm.compile_and_run_raw_program(black_box(warmup)).unwrap(); - - let program = vm.emit_raw_program_no_path(script).unwrap(); - let executable = vm.raw_program_to_executable(program).unwrap(); - - c.bench_function(name, |b| b.iter(|| vm.run_executable(&executable))); -} - -fn range(c: &mut Criterion) { - let script = "(range 0 5000)"; - - let mut vm = Engine::new(); - - vm.compile_and_run_raw_program(PRELUDE).unwrap(); - - let program = vm.emit_raw_program_no_path(script).unwrap(); - let executable = vm.raw_program_to_executable(program).unwrap(); - - c.bench_function("range-big", |b| b.iter(|| vm.run_executable(&executable))); -} - -fn map(c: &mut Criterion) { - let script = "(map a lst)"; - let warmup = "(define lst (range 0 5000)) (define a (lambda (a) 0))"; - benchmark_template(c, "map-big", script, warmup); -} - -fn transducer_map(c: &mut Criterion) { - let script = "(transduce lst a (into-list))"; - let warmup = "(define lst (range 0 10000)) (define a (mapping (lambda (a) (* a 2))))"; - benchmark_template(c, "transducer-map", script, warmup); -} - -fn filter(c: &mut Criterion) { - let script = "(filter number? lst)"; - let warmup = "(define lst (range 0 5000))"; - benchmark_template(c, "filter-big", script, warmup); -} - -fn multiple_transducers(c: &mut Criterion) { - let warmup = "(define lst (range 0 50000))"; - let script = r#" - (transduce lst (compose - (mapping (fn (x) (* x 2))) - (filtering even?) - (mapping (fn (x) (+ x 25))) - (taking 25000) - (taking 25)) - (into-list)) - "#; - benchmark_template(c, "multiple-transducers", script, warmup); -} - -fn ackermann(c: &mut Criterion) { - let warmup = r#" - (define (ackermann m n) - (cond [(equal? m 0) (+ n 1)] - [(equal? n 0) (ackermann (- m 1) 1)] - [else (ackermann (- m 1) (ackermann m (- n 1)))]))"#; - let script = r#"(ackermann 3 3)"#; - benchmark_template(c, "ackermann-3-3", script, warmup); -} - -fn ten_thousand_iterations(c: &mut Criterion) { - let script = "(test 0)"; - let warmup = "(define test (lambda (x) (if (= x 10000) x (test (+ x 1)))))"; - benchmark_template(c, "ten-thousand-iterations", script, warmup); -} - -fn ten_thousand_iterations_letrec(c: &mut Criterion) { - let script = "(test)"; - let warmup = r#"(define (test) - (let ((loop void)) - (let ((loop-prime (lambda (x) - (if (= x 10000) - x - (loop (+ x 1)))))) - (set! loop loop-prime)) - (loop 0)))"#; - - benchmark_template(c, "ten-thousand-iterations-letrec", script, warmup); -} - -fn trie_sort(c: &mut Criterion) { - let mut vm = Engine::new(); - // vm.compile_and_run_raw_program(PRELUDE).unwrap(); - vm.compile_and_run_raw_program(steel::stdlib::TRIESORT) - .unwrap(); - - let warmup = "(define lst - (list - \"suppose\" - \"believe\" - \"changeable\" - \"absent\" - \"busy\" - \"float\" - \"debonair\" - \"throat\" - \"grey\" - \"use\" - \"measure\" - \"van\" - \"thirsty\" - \"notify\" - \"star\"))"; - - vm.compile_and_run_raw_program(black_box(warmup)).unwrap(); - - let script = "(trie-sort lst)"; - - let program = vm.emit_raw_program_no_path(script).unwrap(); - let executable = vm.raw_program_to_executable(program).unwrap(); - - c.bench_function("trie-sort-without-optimizations", |b| { - b.iter(|| vm.run_executable(&executable)) - }); -} - -fn fib_28(c: &mut Criterion) { - // std::env::set_var("CODE_GEN_V2", "true"); - - let mut vm = Engine::new(); - // vm.compile_and_run_raw_program(PRELUDE).unwrap(); - vm.compile_and_run_raw_program( - "(define (fib n) (if (<= n 2) 1 (+ (fib (- n 1)) (fib (- n 2)))))", - ) - .unwrap(); - - let script = "(fib 28)"; - let program = vm.emit_raw_program_no_path(script).unwrap(); - let executable = vm.raw_program_to_executable(program).unwrap(); - - let mut group = c.benchmark_group("fib-28"); - group.sample_size(200); - group.bench_function("fib-28", |b| b.iter(|| vm.run_executable(&executable))); - group.finish(); -} - -fn thread_creation(c: &mut Criterion) { - let mut vm = Engine::new(); - vm.compile_and_run_raw_program( - r#" -(define (foo x) - (vector 10 20 30 40 x)) - -(define (block) - (thread-join! (spawn-thread! (lambda () (vector-ref (foo 100) 4))))) -"#, - ) - .unwrap(); - - let script = "(block)"; - let program = vm.emit_raw_program_no_path(script).unwrap(); - let executable = vm.raw_program_to_executable(program).unwrap(); - - let mut group = c.benchmark_group("thread-creation"); - // group.sample_size(200); - group.bench_function("thread-creation", |b| { - b.iter(|| vm.run_executable(&executable)) - }); - group.finish(); -} - -fn fib_28_contract(c: &mut Criterion) { - let mut vm = Engine::new(); - vm.compile_and_run_raw_program(PRELUDE).unwrap(); - vm.compile_and_run_raw_program( - r#"(define/contract (fib n) - (->/c integer? integer?) - (if (<= n 2) 1 (+ (fib (- n 1)) (fib (- n 2)))))"#, - ) - .unwrap(); - - let script = "(fib 28)"; - let program = vm.emit_raw_program_no_path(script).unwrap(); - let executable = vm.raw_program_to_executable(program).unwrap(); - - let mut group = c.benchmark_group("contract-fib-28"); - group.sample_size(200); - group.bench_function("contract-fib-28", |b| { - b.iter(|| vm.run_executable(&executable)) - }); - group.finish(); -} - -// This will include the definition inside the bench -// just to match against the Rhai benchmarks -// fn fib_20(c: &mut Criterion) { -// let mut vm = Engine::new(); -// vm.compile_and_run_raw_program(PRELUDE).unwrap(); - -// let script = "(define (fib n) (if (<= n 2) 1 (+ (fib (- n 1)) (fib (- n 2))))) (fib 20)"; - -// let program = vm.emit_raw_program_no_path(&script).unwrap(); -// let executable = vm.raw_program_to_executable(program).unwrap(); - -// c.bench_function("fib-20", |b| { -// b.iter(|| { -// vm.execute(Rc::clone(&definition), &constant_map).unwrap(); -// vm.execute(Rc::clone(&bytecode), &constant_map) -// }) -// }); -// } - -fn engine_creation(c: &mut Criterion) { - c.bench_function("engine-creation", |b| b.iter(Engine::new)); -} - -fn register_function(c: &mut Criterion) { - let mut vm = Engine::new(); - let f: fn(usize, usize) -> usize = |a: usize, b: usize| a + b; - let name: &'static str = "addition"; - c.bench_function("register-fn", |b| { - b.iter(|| { - let _ = vm.register_fn(name, f); - }) - }); -} - -fn binary_trees(c: &mut Criterion) { - let mut vm = Engine::new(); - vm.compile_and_run_raw_program( - r#" - -; #lang racket/base - -;;; The Computer Language Benchmarks Game -;;; https://salsa.debian.org/benchmarksgame-team/benchmarksgame/ - -;;; Derived from the Chicken variant by Sven Hartrumpf -;;; contributed by Matthew Flatt -;;; *reset* - -; (require racket/cmdline) - -(struct node (left val right)) - -;; Instead of (define-struct leaf (val)): -(define (leaf val) - (node #f val #f)) -(define (leaf? l) - (not (node-left l))) -(define (leaf-val l) - node-val) - -(define (make item d) - (if (= d 0) - (leaf item) - (%plain-let ((item2 (* item 2)) (d2 (- d 1))) - (node (make (- item2 1) d2) item (make item2 d2))))) - -(define (check t) - (if (leaf? t) 1 (+ 1 (+ (check (node-left t)) (check (node-right t)))))) - -(define (iterate n m d sum) - (if (equal? n m) sum (iterate (+ n 1) m d (+ sum (check (make n d)))))) - -(define (max x y) - (if (> x y) x y)) - -(define (loop d end max-depth min-depth) - (if (>= d end) - void - (begin - (let ([iterations (arithmetic-shift 1 (+ (- max-depth d) min-depth))]) - (displayln iterations " trees of depth " d " check: " (iterate 0 iterations d 0))) - (loop (+ 2 d) end max-depth min-depth)))) - -(define (main n) - (let* ([min-depth 4] [max-depth (max (+ min-depth 2) n)]) - (let ([stretch-depth (+ max-depth 1)]) - (displayln "stretch tree of depth " stretch-depth " check: " (check (make 0 stretch-depth)))) - (let ([long-lived-tree (make 0 max-depth)]) - ; (begin - ; (define end ) - - (loop 4 (add1 max-depth) max-depth min-depth) - - ; ) - - (displayln "long lived tree of depth " max-depth " check: " (check long-lived-tree))))) - - - "#, - ) - .unwrap(); - - let script = "(main 12)"; - let program = vm.emit_raw_program_no_path(script).unwrap(); - let executable = vm.raw_program_to_executable(program).unwrap(); - - let mut group = c.benchmark_group("binary-trees"); - group.bench_function("binary-trees", |b| { - b.iter(|| vm.run_executable(&executable)) - }); - group.finish(); -} - -criterion_group!( - benches, - range, - map, - transducer_map, - filter, - ten_thousand_iterations, - ten_thousand_iterations_letrec, - trie_sort, - fib_28, - thread_creation, - engine_creation, - register_function, - multiple_transducers, - binary_trees, - // fib_28_contract, - ackermann // trie_sort, - // merge_sort, - // struct_construct, - // struct_construct_bigger, - // struct_get, - // struct_set -); - -criterion_main!(benches); diff --git a/crates/steel-core/build.rs b/crates/steel-core/build.rs deleted file mode 100644 index 939a6371f..000000000 --- a/crates/steel-core/build.rs +++ /dev/null @@ -1,17 +0,0 @@ -// build.rs - -fn main() { - #[cfg(feature = "dynamic")] - { - use std::env; - use std::fs; - use std::path::Path; - - let out_dir = env::var_os("OUT_DIR").unwrap(); - let dest_path = Path::new(&out_dir).join("generated.rs"); - - fs::write(dest_path, steel_gen::permutations::code_gen()).unwrap(); - - println!("cargo:rerun-if-changed=build.rs"); - } -} diff --git a/crates/steel-core/proptest-regressions/parser/prop/mod.txt b/crates/steel-core/proptest-regressions/parser/prop/mod.txt deleted file mode 100644 index 64ad00871..000000000 --- a/crates/steel-core/proptest-regressions/parser/prop/mod.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Seeds for failure cases proptest has generated in the past. It is -# automatically read and these particular cases re-run before any -# novel cases are generated. -# -# It is recommended to check this file in to source control so that -# everyone who runs the test benefits from these saved cases. -cc ba3b075e91322e3a6e4878469f37d211442105338985ca85c3cb156b4ab55a29 # shrinks to expr = If(If { test_expr: If(If { test_expr: Atom(Atom { syn: SyntaxObject { ty: IntegerLiteral(-872414935), span: 0..0 } }), then_expr: Atom(Atom { syn: SyntaxObject { ty: IntegerLiteral(-6578869408395709287), span: 0..0 } }), else_expr: Atom(Atom { syn: SyntaxObject { ty: CharacterLiteral('*'), span: 0..0 } }), location: SyntaxObject { ty: If, span: 0..0 } }), then_expr: Atom(Atom { syn: SyntaxObject { ty: IntegerLiteral(-5947150424354642015), span: 0..0 } }), else_expr: Define(Define { name: Atom(Atom { syn: SyntaxObject { ty: Identifier("\"เ🂨\\*ퟡ´.:{𞋌ȺࡒæTנּଏ<Ѩ\u{10eac}"), span: 0..0 } }), body: List(List { args: [] }), location: SyntaxObject { ty: Define, span: 0..0 } }), location: SyntaxObject { ty: If, span: 0..0 } }) -cc 6b6adf8a923572bb82a42c4ddf33909eae18c658502a8b8958d53d2031c0cb5d # shrinks to expr = Define(Define { name: Atom(Atom { syn: SyntaxObject { ty: Identifier(""), span: 0..0 } }), body: List(List { args: [] }), location: SyntaxObject { ty: Define, span: 0..0 } }) -cc b028dc7dfaf0d7ca9df1f64d35838bfc3407e1f224caa4606d300fb6a57ec0eb # shrinks to expr = Begin(Begin { exprs: [], location: SyntaxObject { ty: Begin, span: 0..0 } }) -cc 3d322ed8345904bf0c0af3c68dabd7befa587b545c2ff80461a0afe428cc13bc # shrinks to expr = Define(Define { name: Atom(Atom { syn: SyntaxObject { ty: Identifier("%"), span: 0..0 } }), body: List(List { args: [Atom(Atom { syn: SyntaxObject { ty: CharacterLiteral('\t'), span: 0..0 } })] }), location: SyntaxObject { ty: Define, span: 0..0 } }) -cc d15757be8c5d41a904a9d8f77f3d688f29a77017272ed014ac40aa99584fcb2d # shrinks to expr = List(List { args: [Atom(Atom { syn: SyntaxObject { ty: CharacterLiteral('\u{b}'), span: 0..0 } })] }) -cc 116ff47f68040a770afb3636206ddb98bde9136e5002e56d53a0f982d7cad00b # shrinks to expr = Define(Define { name: Atom(Atom { syn: SyntaxObject { ty: Identifier("a"), span: 0..0 } }), body: List(List { args: [Atom(Atom { syn: SyntaxObject { ty: CharacterLiteral(')'), span: 0..0 } })] }), location: SyntaxObject { ty: Define, span: 0..0 } }) -cc 2c4a70561f7d1499463c242766fc24ba7324882b8e32dab9353b0dbe37bee274 # shrinks to expr = If(If { test_expr: Atom(Atom { syn: SyntaxObject { ty: CharacterLiteral('¡'), span: 0..0 } }), then_expr: Quote(Quote { expr: List(List { args: [] }), location: SyntaxObject { ty: Quote, span: 0..0 } }), else_expr: Atom(Atom { syn: SyntaxObject { ty: CharacterLiteral(' '), span: 0..0 } }), location: SyntaxObject { ty: If, span: 0..0 } }) -cc 57a7a2bb7057027666f1e06b2372fe6c74d2b1404b57ef484c03a03526b78ce3 # shrinks to expr = Define(Define { name: Atom(Atom { syn: SyntaxObject { ty: Identifier("A"), span: 0..0 } }), body: List(List { args: [List(List { args: [Atom(Atom { syn: SyntaxObject { ty: CharacterLiteral('\u{a0}'), span: 0..0 } })] })] }), location: SyntaxObject { ty: Define, span: 0..0 } }) -cc 2d1240d5e780d1d7b07ff30fd590d4cc93e5559e2950d43c1c36d337eb687e75 # shrinks to expr = Define(Define { name: Atom(Atom { syn: SyntaxObject { ty: Identifier("🤌"), span: 0..0 } }), body: List(List { args: [If(If { test_expr: Atom(Atom { syn: SyntaxObject { ty: CharacterLiteral('^'), span: 0..0 } }), then_expr: Atom(Atom { syn: SyntaxObject { ty: CharacterLiteral('\u{0}'), span: 0..0 } }), else_expr: Atom(Atom { syn: SyntaxObject { ty: CharacterLiteral('¡'), span: 0..0 } }), location: SyntaxObject { ty: If, span: 0..0 } })] }), location: SyntaxObject { ty: Define, span: 0..0 } }) diff --git a/crates/steel-core/src/boot/bootstrap.bin b/crates/steel-core/src/boot/bootstrap.bin deleted file mode 100644 index d2ef982dc..000000000 Binary files a/crates/steel-core/src/boot/bootstrap.bin and /dev/null differ diff --git a/crates/steel-core/src/compiler/code_gen.rs b/crates/steel-core/src/compiler/code_gen.rs deleted file mode 100644 index a80d16fb3..000000000 --- a/crates/steel-core/src/compiler/code_gen.rs +++ /dev/null @@ -1,1090 +0,0 @@ -use std::sync::atomic::AtomicUsize; - -use steel_parser::tokens::MaybeBigInt; - -use crate::{ - compiler::passes::analysis::IdentifierStatus::{ - Captured, Free, Global, HeapAllocated, LetVar, Local, LocallyDefinedFunction, - }, - core::{ - instructions::Instruction, - labels::{resolve_labels, LabeledInstruction}, - opcode::OpCode, - }, - parser::{ - ast::{Atom, ExprKind, List}, - parser::SyntaxObject, - span_visitor::get_span, - tokens::TokenType, - visitors::VisitorMut, - }, - rvals::IntoSteelVal, - stop, SteelVal, -}; - -use super::{ - constants::ConstantMap, - passes::analysis::{ - Analysis, - CallKind::{Normal, SelfTailCall, TailCall}, - }, -}; - -use crate::rvals::Result; - -// TODO: Have this interner also be a part of what gets saved... -pub(crate) static FUNCTION_ID: AtomicUsize = AtomicUsize::new(0); - -fn fresh_function_id() -> usize { - // println!("{:?}", FUNCTION_ID); - FUNCTION_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed) -} - -pub struct CodeGenerator<'a> { - pub(crate) instructions: Vec, - constant_map: &'a mut ConstantMap, - analysis: &'a Analysis, - local_count: Vec, -} - -fn eval_atom(t: &SyntaxObject) -> Result { - match &t.ty { - TokenType::BooleanLiteral(b) => Ok((*b).into()), - TokenType::NumberLiteral(n) => Ok(SteelVal::NumV(*n)), - TokenType::StringLiteral(s) => Ok(SteelVal::StringV(s.into())), - TokenType::CharacterLiteral(c) => Ok(SteelVal::CharV(*c)), - TokenType::IntegerLiteral(steel_parser::tokens::MaybeBigInt::Small(n)) => { - Ok(SteelVal::IntV(*n)) - } - TokenType::IntegerLiteral(MaybeBigInt::Big(b)) => b.clone().into_steelval(), - // TODO: Keywords shouldn't be misused as an expression - only in function calls are keywords allowed - TokenType::Keyword(k) => Ok(SteelVal::SymbolV(k.clone().into())), - what => { - // println!("getting here in the eval_atom - code_gen"); - stop!(UnexpectedToken => what; t.span) - } - } -} - -fn try_eval_atom(t: &SyntaxObject) -> Option { - match &t.ty { - TokenType::BooleanLiteral(b) => Some((*b).into()), - // TokenType::Identifier(s) => env.borrow().lookup(&s), - TokenType::NumberLiteral(n) => Some(SteelVal::NumV(*n)), - TokenType::StringLiteral(s) => Some(SteelVal::StringV(s.into())), - TokenType::CharacterLiteral(c) => Some(SteelVal::CharV(*c)), - TokenType::IntegerLiteral(steel_parser::tokens::MaybeBigInt::Small(n)) => { - Some(SteelVal::IntV(*n)) - } - // TODO: Keywords shouldn't be misused as an expression - only in function calls are keywords allowed - TokenType::Keyword(k) => Some(SteelVal::SymbolV(k.clone().into())), - _what => { - // println!("getting here in the eval_atom - code_gen"); - // stop!(UnexpectedToken => what; t.span) - - return None; - } - } -} - -impl<'a> CodeGenerator<'a> { - pub fn new(constant_map: &'a mut ConstantMap, analysis: &'a Analysis) -> Self { - CodeGenerator { - instructions: Vec::new(), - constant_map, - analysis, - local_count: Vec::new(), - } - } - - pub fn top_level_compile(mut self, expr: &ExprKind) -> Result> { - self.visit(expr)?; - self.instructions - .push(LabeledInstruction::builder(OpCode::POPPURE)); - - Ok(resolve_labels(self.instructions)) - } - - fn push(&mut self, instr: LabeledInstruction) { - self.instructions.push(instr); - } - - fn len(&self) -> usize { - self.instructions.len() - } - - fn specialize_constant(&mut self, syn: &SyntaxObject) -> Result<()> { - let value = eval_atom(syn)?; - - let idx = self.constant_map.add_or_get(value); - self.push( - LabeledInstruction::builder(OpCode::PUSHCONST) - .payload(idx) - .contents(syn.clone()) - .constant(true), - ); - Ok(()) - } - - fn specialize_immediate(&self, l: &List) -> Option { - if l.args.len() == 3 { - let function = l.first()?; - - let _ = l.args[1].atom_identifier()?; - let _ = eval_atom(l.args[2].atom_syntax_object()?).ok()?; - - if let Some(info) = self.analysis.get(function.atom_syntax_object()?) { - if info.kind == Free || info.kind == Global { - return match function.atom_identifier().unwrap().resolve() { - "+" => Some(OpCode::ADDIMMEDIATE), - "-" => Some(OpCode::SUBIMMEDIATE), - "<=" => Some(OpCode::LTEIMMEDIATE), - _ => None, - }; - } - } - } - - None - } - - fn should_specialize_call(&self, l: &List) -> Option { - if l.args.len() == 3 { - let function = l.first()?; - - let _ = l.args[1].atom_identifier()?; - let _ = eval_atom(l.args[2].atom_syntax_object()?).ok()?; - - if let Some(info) = self.analysis.get(function.atom_syntax_object()?) { - if info.kind == Free || info.kind == Global { - return match function.atom_identifier().unwrap().resolve() { - "+" => Some(OpCode::ADDREGISTER), - "-" => Some(OpCode::SUBREGISTER), - "<=" => Some(OpCode::LTEREGISTER), - _ => None, - }; - } - } - } - - None - } - - fn specialize_immediate_call(&mut self, l: &List, op: OpCode) -> Option<()> { - // let value = eval_atom(l.args[2].atom_syntax_object().unwrap())?; - - if l.args.len() != 3 { - return None; - } - - let value = if let Some(TokenType::IntegerLiteral(MaybeBigInt::Small(l))) = - l.args[2].atom_syntax_object().map(|x| &x.ty) - { - *l - } else { - return None; - // stop!() - }; - - if value < 0 { - return None; - } - - // if let Some(analysis) = &l.args[1] - // .atom_syntax_object() - // .and_then(|a| self.analysis.get(a)) - // { - // if let Some(offset) = analysis.stack_offset { - // self.push(LabeledInstruction::builder(op).payload(offset)); - // } else { - // stop!(Generic => "Missing stack offset!"); - // } - // } else { - // panic!("Shouldn't be happening") - // } - - let analysis = &l.args[1] - .atom_syntax_object() - .and_then(|a| self.analysis.get(a))?; - - let offset = analysis.stack_offset?; - - self.push(LabeledInstruction::builder(op).payload(offset)); - - // let idx = self.constant_map.add_or_get(value); - - self.push(LabeledInstruction::builder(OpCode::PASS).payload(value as usize)); - - Some(()) - } - - #[allow(unused)] - fn specialize_call(&mut self, l: &List, op: OpCode) -> Option<()> { - if l.args.len() != 3 { - return None; - } - - let value = try_eval_atom(l.args[2].atom_syntax_object().unwrap())?; - - // Specialize SUB1 -> specializing here is a bit much but it should help - // if value == SteelVal::IntV(1) && op == OpCode::SUBREGISTER { - // self.push(LabeledInstruction::builder(OpCode::SUBREGISTER1)); - - // if let Some(analysis) = &l.args[1] - // .atom_syntax_object() - // .and_then(|a| self.analysis.get(&a)) - // { - // self.push( - // LabeledInstruction::builder(OpCode::PASS) - // .payload(analysis.stack_offset.unwrap()), - // ); - // } else { - // panic!("Shouldn't be happening") - // } - - // return Ok(()); - // } - - let analysis = &l.args[1] - .atom_syntax_object() - .and_then(|a| self.analysis.get(a))?; - - let offset = analysis.stack_offset?; - - self.push(LabeledInstruction::builder(op).payload(offset)); - - // if let Some(analysis) = - // { - // if let Some(offset) = analysis.stack_offset { - // self.push(LabeledInstruction::builder(op).payload(offset)); - // } else { - // return None; - // } - // } else { - // panic!("Shouldn't be happening") - // } - - // local variable, map to index: - // if let ExprKind::Atom(a) = &l.args[1] { - // if let Some(analysis) = self.analysis.get(&a.syn) { - // self.push( - // LabeledInstruction::builder(OpCode::PASS) - // .payload(analysis.stack_offset.unwrap()), - // ); - // } else { - // panic!("Shouldn't be getting here") - // } - // } else { - // panic!("Shouldn't be getting here") - // } - - let idx = self.constant_map.add_or_get(value); - - // println!("Index: {:?}", idx); - - self.push(LabeledInstruction::builder(OpCode::PASS).payload(idx)); - - Some(()) - - // Ok(()) - } -} - -impl<'a> VisitorMut for CodeGenerator<'a> { - type Output = Result<()>; - - // TODO come back later and resolve this properly using labels - // If looks like label resolution needs to be able to arbitrarily point to a label one - // instruction after - fn visit_if(&mut self, f: &crate::parser::ast::If) -> Self::Output { - // load in the test condition - self.visit(&f.test_expr)?; - // Get the if index - let if_idx = self.instructions.len(); - // push in if - self.push(LabeledInstruction::builder(OpCode::IF).payload(self.instructions.len() + 2)); - // save spot of jump instruction, fill in after - // let idx = self.len(); - // self.push(Instruction::new_jmp(0)); // dummy value - - // emit instructions for then - self.visit(&f.then_expr)?; - self.push(LabeledInstruction::builder(OpCode::JMP)); - let false_start = self.len(); - - // emit instructions for else expression - self.visit(&f.else_expr)?; - let j3 = self.len(); // first instruction after else - - // println!("false_start: {:?}", false_start); - // println!("j3: {:?}", j3); - - // // set index of jump instruction - // if let Some(elem) = self.instructions.get_mut(idx) { - // (*elem).payload_size = false_start; - // } else { - // stop!(Generic => "out of bounds jump"); - // } - - // if let Some(potential_pop_instr) = self.instructions.get(j3 - 1) { - // if potential_pop_instr.op_code == OpCode::POPPURE { - // println!("Found a pop instruction: {:?}", potential_pop_instr); - // } - // } - - if let Some(elem) = self.instructions.get_mut(false_start - 1) { - elem.payload_size = j3; - // (*elem).payload_size = false_start; - } else { - stop!(Generic => "out of bounds jump"); - } - - if let Some(elem) = self.instructions.get_mut(if_idx) { - elem.payload_size = false_start; - // (*elem).payload_size = false_start; - } else { - stop!(Generic => "out of bounds jump"); - } - - Ok(()) - - // self.visit(&f.test_expr)?; - - // let if_idx = self.instructions.len(); - - // self.push(LabeledInstruction::builder(OpCode::IF).payload(self.instructions.len() + 2)); - - // self.visit(&f.then_expr)?; - - // let false_start_label = fresh(); - // let j3_label = fresh(); - - // self.push(LabeledInstruction::builder(OpCode::JMP).goto(j3_label)); - - // let false_start = self.len(); // index after the jump - - // // self.instructions - // // .last_mut() - // // .unwrap() - // // .set_tag(false_start_label); - - // self.visit(&f.else_expr)?; - - // self.instructions.last_mut().unwrap().set_tag(j3_label); - - // // if let Some(elem) = self.instructions.get_mut(false_start - 1) { - // // // (*elem).goto = Some(j3); - - // // elem.set_goto(j3_label); - - // // // (*elem).payload_size = false_start; - // // } else { - // // stop!(Generic => "out of bounds jump"); - // // } - - // if let Some(elem) = self.instructions.get_mut(if_idx) { - // // (*elem).goto = Some(false_start_label); - - // elem.set_goto(false_start_label); - - // // (*elem).payload_size = false_start; - // } else { - // stop!(Generic => "out of bounds jump"); - // } - - // Ok(()) - } - - fn visit_define(&mut self, define: &crate::parser::ast::Define) -> Self::Output { - // let sidx = self.len(); - - if let ExprKind::Atom(name) = &define.name { - self.push(LabeledInstruction::builder(OpCode::SDEF).contents(name.syn.clone())); - - self.visit(&define.body)?; - - // let defn_body_size = self.len() - sidx; - - // TODO: Consider whether SDEF and EDEF are even necessary at all - // Just remove them otherwise - self.push(LabeledInstruction::builder(OpCode::EDEF)); - - // if let Some(elem) = self.instructions.get_mut(sidx) { - // (*elem).payload_size = defn_body_size; - // } else { - // stop!(Generic => "out of bounds closure len"); - // } - - // println!("binding global: {}", name); - self.push(LabeledInstruction::builder(OpCode::BIND).contents(name.syn.clone())); - - self.push(LabeledInstruction::builder(OpCode::VOID)); - } else { - panic!( - "Complex defines not supported in bytecode generation: {}", - define.name - ) - } - - Ok(()) - } - - fn visit_lambda_function( - &mut self, - lambda_function: &crate::parser::ast::LambdaFunction, - ) -> Self::Output { - let idx = self.len(); - - // Grab the function information from the analysis - this is going to tell us what the captured - // vars are, and subsequently how to compile the let for this use case - let function_info = self - .analysis - .function_info - .get(&lambda_function.syntax_object_id) - .unwrap(); - - // Distinguishing between a pure function and a non pure function will enable - // thinner code for the resulting function - let op_code = if function_info.captured_vars().is_empty() { - OpCode::PUREFUNC - } else { - OpCode::NEWSCLOSURE - }; - - // Attach the debug symbols here - self.push(LabeledInstruction::builder(op_code).contents(lambda_function.location.clone())); - self.push( - LabeledInstruction::builder(OpCode::PASS).payload(usize::from(lambda_function.rest)), - ); - - let arity = lambda_function.args.len(); - - // Patching over the changes, see old code generator for more information - // TODO: This is not a portable change. Syntax Object IDs need to have a patched unique ID - // This should be doable by supplementing everything with an offset when consuming external modules - // self.push( - // LabeledInstruction::builder(OpCode::PASS).payload(lambda_function.syntax_object_id), - // ); - - self.push(LabeledInstruction::builder(OpCode::PASS).payload(fresh_function_id())); - - // Save how many locals we have, for when we hit lets - self.local_count.push(arity); - - let mut body_instructions = { - let mut code_gen = CodeGenerator::new(self.constant_map, self.analysis); - code_gen.visit(&lambda_function.body)?; - code_gen.instructions - }; - - // In the event we actually have a closure, we need to add the necessarily - // boilerplate to lift out closed over variables since they could escape - if op_code == OpCode::NEWSCLOSURE { - // Mark the upvalues here - self.push( - LabeledInstruction::builder(OpCode::NDEFS) - .payload(function_info.captured_vars().len()), - ); - - let mut vars = function_info.captured_vars().iter().collect::>(); - - vars.sort_by_key(|x| x.1.id); - - // vars.sort_by_key(|x| x.stack_offset); - - // Here we're going to explicitly capture from either the enclosing scope - // or the stack. For example: - // - // (lambda (x) - // (lambda (y) - // (+ x y))) - // - // The inner lambda here will capture x from the stack, since that - // is the environment in which it was immediately available. - // Whereas: - // - // (lambda (x) - // (lambda (y) - // (lambda (z) - // (+ x y z)))) - // - // The innermost lambda is going to have to capture x and y from the closure above it, - // where they've already been captured. - // - // This way, at closure construction (in the VM) we can immediately patch in the kind - // of closure that we want to create, and where to get it - for (key, var) in vars { - // If we're patching in from the enclosing, check to see if this is a heap allocated var that - // we need to patch in to the current scope - if var.captured_from_enclosing { - if var.mutated { - self.push( - LabeledInstruction::builder(OpCode::COPYHEAPCAPTURECLOSURE) - .payload(var.parent_heap_offset.unwrap()) - .contents(SyntaxObject::default(TokenType::Identifier(*key))), - ); - } else { - // In this case we're gonna patch in the variable from the current captured scope - self.push( - LabeledInstruction::builder(OpCode::COPYCAPTURECLOSURE) - .payload(var.capture_offset.unwrap()) - .contents(SyntaxObject::default(TokenType::Identifier(*key))), - ); - } - } else if var.mutated { - self.push( - LabeledInstruction::builder(OpCode::FIRSTCOPYHEAPCAPTURECLOSURE) - .payload(var.heap_offset.unwrap()) - .contents(SyntaxObject::default(TokenType::Identifier(*key))), - ); - } else { - // In this case, it hasn't yet been captured, so we'll just capture - // directly from the stack - self.push( - LabeledInstruction::builder(OpCode::COPYCAPTURESTACK) - .payload(var.stack_offset.unwrap()) - .contents(SyntaxObject::default(TokenType::Identifier(*key))), - ); - } - } - } - - let pop_op_code = OpCode::POPPURE; - - // Elide the POPN if the POPPURE will take care of it - if let Some(x) = body_instructions.last_mut() { - if x.op_code == OpCode::POPN { - x.op_code = OpCode::PASS; - } - } - - body_instructions - .push(LabeledInstruction::builder(pop_op_code).payload(lambda_function.args.len())); - - // Load in the heap alloc instructions - on each invocation we can copy in to the current frame - { - let mut captured_mutable_arguments = function_info - .arguments() - .iter() - // .values() - .filter(|x| x.1.captured && x.1.mutated) - .collect::>(); - - captured_mutable_arguments.sort_by_key(|x| x.1.stack_offset); - - for (key, var) in captured_mutable_arguments { - self.push( - LabeledInstruction::builder(OpCode::ALLOC) - .payload(var.stack_offset.unwrap()) - .contents(SyntaxObject::default(TokenType::Identifier(*key))), - ); - } - } - - self.instructions.append(&mut body_instructions); - - // pop off the local variables from the run time stack, so we don't have them - - let closure_body_size = self.len() - idx; - self.push(LabeledInstruction::builder(OpCode::ECLOSURE).payload(arity)); - - if let Some(elem) = self.instructions.get_mut(idx) { - elem.payload_size = closure_body_size; - } else { - stop!(Generic => "out of bounds closure len"); - } - - self.local_count.pop(); - - Ok(()) - } - - fn visit_begin(&mut self, begin: &crate::parser::ast::Begin) -> Self::Output { - if begin.exprs.is_empty() { - self.push(LabeledInstruction::builder(OpCode::VOID)); - return Ok(()); - } - - let (last, elements) = begin.exprs.split_last().unwrap(); - - // Just insert a single pop value from stack - // when - // for expr in &begin.exprs { - // self.visit(expr)?; - // } - - for expr in elements { - self.visit(expr)?; - self.push(LabeledInstruction::builder(OpCode::POPSINGLE)); - } - - self.visit(last)?; - - // if begin.exprs.len() > 1 { - // self.push(LabeledInstruction::builder(OpCode::POPN).payload(begin.exprs.len() - 1)); - // } - - Ok(()) - } - - fn visit_return(&mut self, r: &crate::parser::ast::Return) -> Self::Output { - self.visit(&r.expr)?; - self.push(LabeledInstruction::builder(OpCode::POPPURE)); - Ok(()) - } - - fn visit_quote(&mut self, quote: &crate::parser::ast::Quote) -> Self::Output { - let converted = - SteelVal::try_from(crate::parser::ast::ExprKind::Quote(Box::new(quote.clone())))?; - - let idx = self.constant_map.add_or_get(converted); - self.push( - LabeledInstruction::builder(OpCode::PUSHCONST) - .payload(idx) - // TODO: This is a little suspect, we're doing a bunch of stuff twice - // that we really don't need. In fact, we probably can get away with just... - // embedding the steel val directly here. - .list_contents(crate::parser::ast::ExprKind::Quote(Box::new(quote.clone()))) - .constant(true), - ); - - Ok(()) - } - - fn visit_macro(&mut self, m: &crate::parser::ast::Macro) -> Self::Output { - stop!(BadSyntax => format!("unexpected macro definition: {}", m); m.location.span) - } - - fn visit_atom(&mut self, a: &crate::parser::ast::Atom) -> Self::Output { - if let Some(analysis) = self.analysis.get(&a.syn) { - let op_code = match (&analysis.kind, analysis.last_usage) { - (Global, _) => OpCode::PUSH, - (Local, true) | (LetVar, true) => OpCode::MOVEREADLOCAL, - // (Local, true) | (LetVar, true) => OpCode::READLOCAL, - (Local, false) | (LetVar, false) => OpCode::READLOCAL, - - (LocallyDefinedFunction, _) => { - stop!(Generic => "Unable to lower to bytecode: locally defined function should be lifted to an external scope") - } - // In the event we're captured, we're going to just read the - // offset from the captured var - (Captured, _) => OpCode::READCAPTURED, - (Free, _) => OpCode::PUSH, - (HeapAllocated, _) => OpCode::READALLOC, - // This is technically true, but in an incremental compilation mode, we assume the variable is already bound - // stop!(FreeIdentifier => format!("free identifier: {}", a); a.syn.span), - }; - - // println!("Atom: {}", a); - // println!("{:#?}", analysis); - - let payload = match op_code { - OpCode::READCAPTURED => analysis.read_capture_offset.unwrap(), - OpCode::READALLOC => analysis.read_heap_offset.unwrap(), - _ => analysis.stack_offset.unwrap_or_default(), - }; - - self.push( - LabeledInstruction::builder(op_code) - .payload(payload) - .contents(a.syn.clone()), - ); - - Ok(()) - } else { - self.specialize_constant(&a.syn) - } - } - - // TODO: Specialize the calls to binops here - // This should be pretty straightforward - just check if they're still globals - // then, specialize accordingly. - fn visit_list(&mut self, l: &crate::parser::ast::List) -> Self::Output { - if let Some(op) = self.specialize_immediate(l) { - match self.specialize_immediate_call(l, op) { - Some(r) => return Ok(r), - None => {} - } - } else if let Some(op) = self.should_specialize_call(l) { - match self.specialize_call(l, op) { - Some(r) => return Ok(r), - None => {} - } - } - - if l.args.is_empty() { - stop!(BadSyntax => "function application empty"); - } - - let pop_len = if !l.args.is_empty() { - l.args[1..].len() - } else { - 0 - }; - - for expr in &l.args[1..] { - self.visit(expr)?; - } - - // emit instructions for the func - self.visit(&l.args[0])?; - - let contents = if let ExprKind::Atom(Atom { syn: s }) = &l.args[0] { - s.clone() - } else { - // TODO check span information here by coalescing the entire list - SyntaxObject::new(TokenType::Identifier("lambda".into()), get_span(&l.args[0])) - }; - - if let Some(call_info) = self.analysis.call_info.get(&l.syntax_object_id) { - let op_code = match call_info.kind { - Normal => OpCode::FUNC, - TailCall => OpCode::TAILCALL, - SelfTailCall(_) => { - // We don't need to push the function onto the stack if we're doing a self - // tail call - self.instructions.pop(); - OpCode::TCOJMP - // OpCode::TAILCALL - } - }; - - self.push( - LabeledInstruction::builder(op_code) - .contents(contents) - .payload(pop_len), - ); - - if let SelfTailCall(depth) = call_info.kind { - self.push(LabeledInstruction::builder(OpCode::PASS).payload(depth - 1)); - } - - Ok(()) - } else { - self.push( - LabeledInstruction::builder(OpCode::FUNC) - .contents(contents) - .payload(pop_len), - ); - - Ok(()) - - // stop!(Generic => "Unable to find analysis information for call site!") - } - } - - fn visit_syntax_rules(&mut self, l: &crate::parser::ast::SyntaxRules) -> Self::Output { - stop!(BadSyntax => "unexpected syntax rules"; l.location.span) - } - - fn visit_set(&mut self, s: &crate::parser::ast::Set) -> Self::Output { - // stop!(BadSyntax => "set! is currently not implemented"; s.location.span); - - self.visit(&s.expr)?; - - let a = s.variable.atom_syntax_object().unwrap(); - - if let Some(analysis) = self.analysis.get(a) { - // TODO: We really want to fail as early as possible, but lets just try this here: - if analysis.builtin { - stop!(Generic => "set!: cannot mutate module-required identifier"; s.location.span); - } - - let op_code = match &analysis.kind { - Global => OpCode::SET, - Local | LetVar => OpCode::SETLOCAL, - - LocallyDefinedFunction => { - stop!(Generic => "Unable to lower to bytecode: locally defined function should be lifted to an external scope") - } - // In the event we're captured, we're going to just read the - // offset from the captured var - Captured => { - stop!(Generic => "Compiler error: Should not be able to set! an immutably captured variable") - } - Free => OpCode::SET, - HeapAllocated => OpCode::SETALLOC, - // This is technically true, but in an incremental compilation mode, we assume the variable is already bound - // stop!(FreeIdentifier => format!("free identifier: {}", a); a.syn.span), - }; - - let payload = match op_code { - // OpCode::SETCAPTURED => analysis.read_capture_offset.unwrap(), - OpCode::SETALLOC => analysis.read_heap_offset.unwrap(), - _ => analysis.stack_offset.unwrap_or_default(), - }; - - self.push( - LabeledInstruction::builder(op_code) - .payload(payload) - .contents(a.clone()), - ); - - Ok(()) - } else { - stop!(Generic => "Something went wrong with getting the var in set!"; s.location.span); - } - } - - fn visit_require(&mut self, r: &crate::parser::ast::Require) -> Self::Output { - stop!(BadSyntax => "unexpected require statement in code gen"; r.location.span) - } - - fn visit_let(&mut self, l: &crate::parser::ast::Let) -> Self::Output { - // What we're gonna do here is pretty straight forward: - // Since we're entering a scope, we need to include the code to remove from this from - // the stack as well - // - // Otherwise, the machinery is more or less the same as before - we're just not going to - // enter a new section of bytecode, its all going to live under the same block. - // - // (let ((a 10) (b 20)) - // (+ 1 2 3 4 5) <--- Could get dropped immediately, but for now it does not - // (+ 2 3 4 5 6) <--| - // (+ a b)) - // - // This should result in something like - // PUSHCONST 10 - // PUSHCONST 20 - // READLOCAL a - // READLOCAL b - // PUSH + - // FUNC 2 - // LETENDSCOPE 0 <- index of the stack when we entered this let expr - - self.push( - LabeledInstruction::builder(OpCode::BEGINSCOPE) - .payload(*self.local_count.last().unwrap_or(&0)), - ); - - let info = self - .analysis - .let_info - .get(&l.syntax_object_id) - .expect("Missing analysis information for let"); - - // Push the scope + the number of arguments in this scope - // self.push(LabeledInstruction::builder(OpCode::BEGINSCOPE).payload(l.bindings.len())); - // self.push(LabeledInstruction::builder(OpCode::BEGINSCOPE).payload(info.stack_offset)); - - // We just assume these will live on the stack at whatever position we're entering now - for expr in l.expression_arguments() { - self.visit(expr)?; - // For the JIT -> push the last instruction to the internal scope - // TODO: Rename from BEGINSCOPE to something like MARKLETVAR - // self.push(LabeledInstruction::builder(OpCode::LetVar)); - } - - let mut heap_allocated_arguments = info - .arguments - .values() - .filter(|x| x.captured && x.mutated) - .collect::>(); - - heap_allocated_arguments.sort_by_key(|x| x.stack_offset); - - for var in heap_allocated_arguments { - // println!("Found a var that is both mutated and captured"); - // println!("{:#?}", var); - - self.push( - LabeledInstruction::builder(OpCode::ALLOC).payload(var.stack_offset.unwrap()), - ); - } - - self.visit(&l.body_expr)?; - - // TODO: - // It is possible, that during the course of execution, local variables get captured. - // For example: - // - // (let ((a 10) (b 20)) - // (lambda (x) (+ x a b))) - // - // In this case, at the end of the let scope, we can move those variables - // into the slot that exists on the heap - and local variable references internally - // in the function will refer to the local function slots, rather than the spots on the stack - // - // Having things on the stack will make things much faster. Lets try to keep them there. - // - // The let will have to keep track of if any of these values are captured, and if they are - // just insert an instruction to close that upvalue - - self.push(LabeledInstruction::builder(OpCode::LETENDSCOPE).payload(info.stack_offset)); - - Ok(()) - } -} - -#[cfg(test)] -mod code_gen_tests { - use super::*; - - use crate::parser::parser::Parser; - - #[test] - fn check_function_calls() { - let expr = r#" - ;; (define fib (lambda (n) (if (<= n 2) 1 (+ (fib (- n 1)) (fib (- n 2)))))) - - (fib 10) - "#; - - let exprs = Parser::parse(expr).unwrap(); - let mut analysis = Analysis::from_exprs(&exprs); - analysis.populate_captures(&exprs); - - let mut constants = ConstantMap::new(); - - let mut code_gen = CodeGenerator::new(&mut constants, &analysis); - - code_gen.visit(&exprs[0]).unwrap(); - // code_gen.visit(&exprs[1]).unwrap(); - - println!("{:#?}", code_gen.instructions); - } - - #[test] - fn check_fib() { - let expr = r#" - (define fib (lambda (n) (if (<= n 2) 1 (+ (fib (- n 1)) (fib (- n 2)))))) - - (fib 10) - "#; - - let exprs = Parser::parse(expr).unwrap(); - let mut analysis = Analysis::from_exprs(&exprs); - analysis.populate_captures(&exprs); - - let mut constants = ConstantMap::new(); - - let mut code_gen = CodeGenerator::new(&mut constants, &analysis); - - code_gen.visit(&exprs[0]).unwrap(); - code_gen.visit(&exprs[1]).unwrap(); - - println!("{:#?}", code_gen.instructions); - } - - #[test] - fn check_new_closure() { - let expr = r#" - (lambda (x) - (lambda (y) (+ x y))) - "#; - - let exprs = Parser::parse(expr).unwrap(); - let mut analysis = Analysis::from_exprs(&exprs); - analysis.populate_captures(&exprs); - - let mut constants = ConstantMap::new(); - - let mut code_gen = CodeGenerator::new(&mut constants, &analysis); - - code_gen.visit(&exprs[0]).unwrap(); - - println!("{:#?}", code_gen.instructions); - } - - #[test] - fn check_lambda_output() { - let expr = r#" - (lambda (x y z) - (+ x y z)) - "#; - - let exprs = Parser::parse(expr).unwrap(); - - let mut analysis = Analysis::from_exprs(&exprs); - analysis.populate_captures(&exprs); - - let mut constants = ConstantMap::new(); - - let mut code_gen = CodeGenerator::new(&mut constants, &analysis); - - code_gen.visit(&exprs[0]).unwrap(); - - println!("{:#?}", code_gen.instructions); - - let expected = vec![ - (OpCode::PUREFUNC, 9), // This captures no variables - should be able to be lifted as well - (OpCode::PASS, 0), // multi arity - (OpCode::PASS, 0), // This shouldn't need to be here - (OpCode::MOVEREADLOCAL, 0), // last usage of x, first var - (OpCode::MOVEREADLOCAL, 1), // last usage of y - (OpCode::MOVEREADLOCAL, 2), // last usage of z - (OpCode::PUSH, 0), // + is a global, that is late bound and the index is resolved later - (OpCode::TAILCALL, 3), // tail call function, with 3 arguments - (OpCode::POPPURE, 3), // Pop 3 arguments - (OpCode::ECLOSURE, 3), // Something about 3 arguments... - ]; - - let mut found = code_gen - .instructions - .iter() - .map(|x| (x.op_code, x.payload_size)) - .collect::>(); - - // Wipe out the syntax object id from the PASS - found[2].1 = 0; - - assert_eq!(expected, found); - } - - #[test] - fn check_let_captured_var() { - let expr = r#" - (%plain-let ((a 10) (b 20)) - (lambda () (+ a b))) - "#; - - let exprs = Parser::parse(expr).unwrap(); - - let mut analysis = Analysis::from_exprs(&exprs); - analysis.populate_captures(&exprs); - - let mut constants = ConstantMap::new(); - - let mut code_gen = CodeGenerator::new(&mut constants, &analysis); - - code_gen.visit(&exprs[0]).unwrap(); - } - - // #[test] - // fn check_let_output() { - // let expr = r#" - // (%plain-let ((a 10) (b 20)) - // (+ a b)) - // "#; - - // let exprs = Parser::parse(expr).unwrap(); - - // let analysis = Analysis::from_exprs(&exprs); - // let mut constants = ConstantMap::new(); - - // let mut code_gen = CodeGenerator::new(&mut constants, &analysis); - - // code_gen.visit(&exprs[0]).unwrap(); - - // println!("{:#?}", code_gen.instructions); - - // let expected = vec![ - // (OpCode::BEGINSCOPE, 0), - // (OpCode::PUSHCONST, 0), // Should be the only constant in the map - // (OpCode::PUSHCONST, 1), // Should be the second constant in the map - // (OpCode::READLOCAL, 0), // Corresponds to index 0 - // (OpCode::READLOCAL, 1), // Corresponds to index 1 - // (OpCode::PUSH, 0), // + is a global, that is late bound and the index is resolved later - // (OpCode::FUNC, 2), // Function call with 2 arguments - // (OpCode::LETENDSCOPE, 2), // Exit the let scope and drop the vars and anything above it - // ]; - - // let found = code_gen - // .instructions - // .iter() - // .map(|x| (x.op_code, x.payload_size)) - // .collect::>(); - - // assert_eq!(expected, found); - // } -} diff --git a/crates/steel-core/src/compiler/compiler.rs b/crates/steel-core/src/compiler/compiler.rs deleted file mode 100644 index 9a9c5b380..000000000 --- a/crates/steel-core/src/compiler/compiler.rs +++ /dev/null @@ -1,997 +0,0 @@ -#![allow(unused)] - -use crate::{ - compiler::{ - constants::ConstantMap, - map::SymbolMap, - passes::{ - analysis::SemanticAnalysis, begin::flatten_begins_and_expand_defines, - reader::MultipleArityFunctions, shadow::RenameShadowedVariables, - }, - }, - core::labels::Expr, - parser::{ - ast::AstTools, - expand_visitor::{expand_kernel, expand_kernel_in_env}, - interner::InternedString, - kernel::Kernel, - parser::{lower_entire_ast, lower_macro_and_require_definitions}, - }, - steel_vm::{ - builtin::BuiltInModule, - cache::MemoizationTable, - engine::{ModuleContainer, DEFAULT_DOC_MACROS}, - }, -}; -use crate::{ - core::{instructions::Instruction, opcode::OpCode}, - parser::parser::Sources, -}; - -use std::{ - collections::{HashMap, HashSet}, - path::PathBuf, -}; -use std::{iter::Iterator, rc::Rc}; - -// TODO: Replace the usages of hashmap with this directly -use fxhash::FxHashMap; -use serde::{Deserialize, Serialize}; - -use crate::rvals::{Result, SteelVal}; - -use crate::parser::ast::ExprKind; -use crate::parser::expander::SteelMacro; -use crate::parser::parser::SyntaxObject; -use crate::parser::parser::{ParseError, Parser}; -use crate::parser::tokens::TokenType; - -// use crate::core::instructions::{densify, DenseInstruction}; - -use crate::stop; - -use log::{debug, log_enabled}; - -use crate::steel_vm::const_evaluation::ConstantEvaluatorManager; - -use super::{ - constants::SerializableConstantMap, - modules::{CompiledModule, ModuleManager}, - passes::analysis::Analysis, - program::RawProgramWithSymbols, -}; - -use im_rc::HashMap as ImmutableHashMap; - -use std::time::Instant; - -// use itertools::Itertools; - -#[derive(Default)] -pub struct DebruijnIndicesInterner { - flat_defines: HashSet, - second_pass_defines: HashSet, -} - -impl DebruijnIndicesInterner { - pub fn collect_first_pass_defines( - &mut self, - instructions: &mut [Instruction], - symbol_map: &mut SymbolMap, - ) -> Result<()> { - for i in 2..instructions.len() { - match (&instructions[i], &instructions[i - 1], &instructions[i - 2]) { - ( - Instruction { - op_code: OpCode::BIND, - contents: - Some(Expr::Atom(SyntaxObject { - ty: TokenType::Identifier(s), - span, - .. - })), - .. - }, - Instruction { - op_code: OpCode::EDEF, - .. - }, - Instruction { - op_code: OpCode::ECLOSURE, - .. - }, - ) => { - let idx = symbol_map.add(s); - self.flat_defines.insert(s.to_owned()); - // if !self.flat_defines.insert(s.to_owned()) { - // stop!(BadSyntax => format!("Cannot redefine define within the same scope: {}", s); *span); - // } - - if let Some(x) = instructions.get_mut(i) { - x.payload_size = idx; - } - } - ( - Instruction { - op_code: OpCode::BIND, - contents: - Some(Expr::Atom(SyntaxObject { - ty: TokenType::Identifier(s), - span, - .. - })), - .. - }, - .., - ) => { - let idx = symbol_map.add(s); - self.flat_defines.insert(s.to_owned()); - // if !self.flat_defines.insert(s.to_owned()) { - // stop!(BadSyntax => format!("Cannot redefine define within the same scope: {}", s); *span); - // } - - if let Some(x) = instructions.get_mut(i) { - x.payload_size = idx; - } - } - _ => {} - } - } - - Ok(()) - } - - pub fn collect_second_pass_defines( - &mut self, - instructions: &mut [Instruction], - symbol_map: &mut SymbolMap, - ) -> Result<()> { - // let mut second_pass_defines: HashSet = HashSet::new(); - - let mut depth = 0; - - // name mangle - // Replace all identifiers with indices - for i in 0..instructions.len() { - match &instructions[i] { - Instruction { - op_code: OpCode::SCLOSURE | OpCode::NEWSCLOSURE | OpCode::PUREFUNC, - .. - } => { - depth += 1; - } - Instruction { - op_code: OpCode::ECLOSURE, - .. - } => { - depth -= 1; - } - Instruction { - op_code: OpCode::BEGINSCOPE, - .. - } => { - depth += 1; - } - Instruction { - op_code: OpCode::LETENDSCOPE, - .. - } => { - depth -= 1; - } - Instruction { - op_code: OpCode::BIND, - contents: - Some(Expr::Atom(SyntaxObject { - ty: TokenType::Identifier(s), - .. - })), - .. - } => { - // Keep track of where the defines actually are in the process - self.second_pass_defines.insert(s.to_owned()); - } - Instruction { - op_code: OpCode::PUSH, - contents: - Some(Expr::Atom(SyntaxObject { - ty: TokenType::Identifier(s), - span, - .. - })), - .. - } - | Instruction { - op_code: OpCode::SET, - contents: - Some(Expr::Atom(SyntaxObject { - ty: TokenType::Identifier(s), - span, - .. - })), - .. - } => { - if self.flat_defines.get(s).is_some() - && self.second_pass_defines.get(s).is_none() - && depth == 0 - { - let message = - format!("Cannot reference an identifier before its definition: {s}"); - stop!(FreeIdentifier => message; *span); - } - - let idx = symbol_map.get(s).map_err(|e| e.set_span(*span))?; - - // TODO commenting this for now - if let Some(x) = instructions.get_mut(i) { - x.payload_size = idx; - } - } - Instruction { - op_code: OpCode::CALLGLOBAL, - contents: - Some(Expr::Atom(SyntaxObject { - ty: TokenType::Identifier(s), - span, - .. - })), - .. - } - | Instruction { - op_code: OpCode::CALLGLOBALTAIL, - contents: - Some(Expr::Atom(SyntaxObject { - ty: TokenType::Identifier(s), - span, - .. - })), - .. - } => { - if self.flat_defines.get(s).is_some() - && self.second_pass_defines.get(s).is_none() - && depth == 0 - { - let message = - format!("Cannot reference an identifier before its definition: {s}"); - stop!(FreeIdentifier => message; *span); - } - - let idx = symbol_map.get(s).map_err(|e| e.set_span(*span))?; - - // TODO commenting this for now - if let Some(x) = instructions.get_mut(i) { - x.payload_size = idx; - } - } - _ => {} - } - } - - Ok(()) - } -} - -#[derive(Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)] -pub enum OptLevel { - Zero = 0, - One, - Two, - Three, -} - -#[derive(Clone)] -pub struct Compiler { - pub(crate) symbol_map: SymbolMap, - pub(crate) constant_map: ConstantMap, - pub(crate) macro_env: HashMap, - module_manager: ModuleManager, - opt_level: OptLevel, - pub(crate) kernel: Option, - memoization_table: MemoizationTable, - mangled_identifiers: HashSet, -} - -#[derive(Serialize, Deserialize)] -pub struct SerializableCompiler { - pub(crate) symbol_map: SymbolMap, - pub(crate) constant_map: SerializableConstantMap, - pub(crate) macro_env: HashMap, - pub(crate) opt_level: OptLevel, - pub(crate) module_manager: ModuleManager, -} - -impl SerializableCompiler { - pub(crate) fn into_compiler(self) -> Compiler { - let mut compiler = Compiler::default(); - - compiler.symbol_map = self.symbol_map; - compiler.constant_map = ConstantMap::from_serialized(self.constant_map).unwrap(); - compiler.macro_env = self.macro_env; - compiler.opt_level = self.opt_level; - compiler.module_manager = self.module_manager; - - compiler - } -} - -impl Compiler { - pub(crate) fn into_serializable_compiler(self) -> Result { - Ok(SerializableCompiler { - symbol_map: self.symbol_map, - constant_map: self.constant_map.into_serializable_map(), - macro_env: self.macro_env, - opt_level: self.opt_level, - module_manager: self.module_manager, - }) - } -} - -impl Default for Compiler { - fn default() -> Self { - Compiler::new( - SymbolMap::new(), - ConstantMap::new(), - HashMap::new(), - ModuleManager::default(), - ) - } -} - -impl Compiler { - fn new( - symbol_map: SymbolMap, - constant_map: ConstantMap, - macro_env: HashMap, - module_manager: ModuleManager, - ) -> Compiler { - Compiler { - symbol_map, - constant_map, - macro_env, - module_manager, - opt_level: OptLevel::Three, - kernel: None, - memoization_table: MemoizationTable::new(), - mangled_identifiers: HashSet::new(), - } - } - - fn new_with_kernel( - symbol_map: SymbolMap, - constant_map: ConstantMap, - macro_env: HashMap, - module_manager: ModuleManager, - kernel: Kernel, - ) -> Compiler { - Compiler { - symbol_map, - constant_map, - macro_env, - module_manager, - opt_level: OptLevel::Three, - kernel: Some(kernel), - memoization_table: MemoizationTable::new(), - mangled_identifiers: HashSet::new(), - } - } - - pub(crate) fn default_from_kernel(kernel: Kernel) -> Compiler { - Compiler::new_with_kernel( - SymbolMap::new(), - ConstantMap::new(), - HashMap::new(), - ModuleManager::default(), - kernel, - ) - } - - pub fn default_with_kernel() -> Compiler { - Compiler::new_with_kernel( - SymbolMap::new(), - ConstantMap::new(), - HashMap::new(), - ModuleManager::default(), - Kernel::new(), - ) - } - - /// Registers a name in the underlying symbol map and returns the idx that it maps to - pub fn register(&mut self, name: &str) -> usize { - let spur = name.into(); - self.symbol_map.add(&spur) - } - - /// Get the index associated with a name in the underlying symbol map - /// If the name hasn't been registered, this will return `None` - pub fn get_idx(&self, name: &str) -> Option { - self.symbol_map.get(&InternedString::try_get(name)?).ok() - } - - pub fn register_builtin(&mut self, name: String, contents: String) { - self.module_manager.add_builtin_module(name, contents); - } - - pub fn compile_executable_from_expressions( - &mut self, - exprs: Vec, - builtin_modules: ModuleContainer, - constants: ImmutableHashMap, - sources: &mut Sources, - ) -> Result { - self.compile_raw_program(exprs, constants, builtin_modules, None, sources) - } - - pub fn compile_executable( - &mut self, - expr_str: &str, - path: Option, - constants: ImmutableHashMap, - builtin_modules: ModuleContainer, - sources: &mut Sources, - ) -> Result { - #[cfg(feature = "profiling")] - let now = Instant::now(); - - let id = sources.add_source(expr_str.to_string(), path.clone()); - - // Could fail here - let parsed: std::result::Result, ParseError> = if let Some(p) = &path { - Parser::new_from_source(expr_str, p.clone(), Some(id)) - .without_lowering() - .map(|x| x.and_then(lower_macro_and_require_definitions)) - .collect() - } else { - Parser::new(expr_str, Some(id)) - .without_lowering() - .map(|x| x.and_then(lower_macro_and_require_definitions)) - .collect() - }; - - // println!("Finished parsing"); - - #[cfg(feature = "profiling")] - if log_enabled!(target: "pipeline_time", log::Level::Debug) { - debug!(target: "pipeline_time", "Parsing Time: {:?}", now.elapsed()); - } - - let parsed = parsed?; - - // TODO fix this hack - self.compile_raw_program(parsed, constants, builtin_modules, path, sources) - } - - // TODO: Add a flag/function for parsing comments as well - // Move the body of this function into the other one, so that way we have proper - pub fn emit_expanded_ast( - &mut self, - expr_str: &str, - constants: ImmutableHashMap, - path: Option, - sources: &mut Sources, - builtin_modules: ModuleContainer, - ) -> Result> { - let id = sources.add_source(expr_str.to_string(), path.clone()); - - // Could fail here - let parsed: std::result::Result, ParseError> = - Parser::new(expr_str, Some(id)) - .without_lowering() - .map(|x| x.and_then(lower_macro_and_require_definitions)) - .collect(); - - let parsed = parsed?; - - self.lower_expressions_impl(parsed, constants, builtin_modules, path, sources) - } - - pub fn emit_expanded_ast_without_optimizations( - &mut self, - expr_str: &str, - constants: ImmutableHashMap, - path: Option, - sources: &mut Sources, - builtin_modules: ModuleContainer, - ) -> Result> { - let id = sources.add_source(expr_str.to_string(), path.clone()); - - // Could fail here - let parsed: std::result::Result, ParseError> = - Parser::new(expr_str, Some(id)) - .without_lowering() - .map(|x| x.and_then(lower_macro_and_require_definitions)) - .collect(); - - let parsed = parsed?; - - self.expand_ast(parsed, constants, builtin_modules, path, sources) - } - - pub fn compile_module( - &mut self, - path: PathBuf, - sources: &mut Sources, - builtin_modules: ModuleContainer, - ) -> Result<()> { - self.module_manager.add_module( - path, - &mut self.macro_env, - &mut self.kernel, - sources, - builtin_modules, - ) - } - - pub fn modules(&self) -> &HashMap { - self.module_manager.modules() - } - - pub fn expand_expressions( - &mut self, - exprs: Vec, - path: Option, - sources: &mut Sources, - builtin_modules: ModuleContainer, - ) -> Result> { - #[cfg(feature = "modules")] - return self.module_manager.compile_main( - &mut self.macro_env, - &mut self.kernel, - sources, - exprs, - path, - builtin_modules, - ); - - #[cfg(not(feature = "modules"))] - self.module_manager - .expand_expressions(&mut self.macro_env, exprs) - } - - fn generate_instructions_for_executable( - &mut self, - expanded_statements: Vec, - ) -> Result>> { - let mut results = Vec::new(); - let mut instruction_buffer = Vec::new(); - let mut index_buffer = Vec::new(); - - let analysis = { - let mut analysis = Analysis::from_exprs(&expanded_statements); - analysis.populate_captures(&expanded_statements); - analysis.populate_captures(&expanded_statements); - analysis - }; - - // expanded_statements.pretty_print(); - - for expr in expanded_statements { - let mut instructions = - super::code_gen::CodeGenerator::new(&mut self.constant_map, &analysis) - .top_level_compile(&expr)?; - - // TODO: I don't think this needs to be here anymore - // inject_heap_save_to_pop(&mut instructions); - index_buffer.push(instructions.len()); - instruction_buffer.append(&mut instructions); - } - - for idx in index_buffer { - let extracted: Vec = instruction_buffer.drain(0..idx).collect(); - results.push(extracted); - } - - Ok(results) - } - - fn expand_ast( - &mut self, - exprs: Vec, - constants: ImmutableHashMap, - builtin_modules: ModuleContainer, - path: Option, - sources: &mut Sources, - ) -> Result> { - let mut expanded_statements = - self.expand_expressions(exprs, path, sources, builtin_modules.clone())?; - - // expanded_statements = expanded_statements - // .into_iter() - // .map(lower_entire_ast) - // .collect::, ParseError>>()?; - - log::debug!(target: "expansion-phase", "Expanding macros -> phase 1"); - - if let Some(kernel) = self.kernel.as_mut() { - // Label anything at the top as well - top level - kernel.load_syntax_transformers(&mut expanded_statements, "top-level".to_string())?; - } - - expanded_statements = expanded_statements - .into_iter() - .map(|x| { - expand_kernel_in_env( - x, - self.kernel.as_mut(), - builtin_modules.clone(), - "top-level".to_string(), - ) - .and_then(|x| crate::parser::expand_visitor::expand(x, &self.macro_env)) - }) - .collect::>>()?; - - expanded_statements = expanded_statements - .into_iter() - .map(lower_entire_ast) - .collect::, ParseError>>()?; - - expanded_statements = expanded_statements - .into_iter() - .map(|x| { - expand_kernel_in_env( - x, - self.kernel.as_mut(), - builtin_modules.clone(), - "top-level".to_string(), - ) - .and_then(|x| crate::parser::expand_visitor::expand(x, &self.macro_env)) - }) - .collect::>>()?; - - log::debug!(target: "expansion-phase", "Beginning constant folding"); - - let mut expanded_statements = - self.apply_const_evaluation(constants.clone(), expanded_statements, false)?; - - RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements); - - let mut analysis = Analysis::from_exprs(&expanded_statements); - analysis.populate_captures(&expanded_statements); - - let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis); - - // This is definitely broken still - semantic - .elide_single_argument_lambda_applications() - .replace_non_shadowed_globals_with_builtins( - &mut self.macro_env, - &mut self.module_manager, - &mut self.mangled_identifiers, - ) - // TODO: To get this to work, we have to check the macros to make sure those - // are safe to eliminate. In interactive mode, we'll - // be unable to optimize those away - .remove_unused_globals_with_prefix("mangler", &self.macro_env, &self.module_manager); - // Don't do lambda lifting here - // .lift_pure_local_functions() - // .lift_all_local_functions(); - - // debug!("About to expand defines"); - - log::debug!(target: "expansion-phase", "Flattening begins, converting internal defines to let expressions"); - - let mut analysis = semantic.into_analysis(); - - let mut expanded_statements = flatten_begins_and_expand_defines(expanded_statements); - - // After define expansion, we'll want this - RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements); - - analysis.fresh_from_exprs(&expanded_statements); - analysis.populate_captures(&expanded_statements); - - let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis); - - semantic.check_if_values_are_redefined()?; - - semantic.refresh_variables(); - semantic.flatten_anonymous_functions(); - semantic.refresh_variables(); - - // Replace mutation with boxes - semantic.populate_captures(); - semantic.populate_captures(); - - semantic.replace_mutable_captured_variables_with_boxes(); - - log::debug!(target: "expansion-phase", "Expanding multiple arity functions"); - - let mut analysis = semantic.into_analysis(); - - // Rename them again - RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements); - - let mut expanded_statements = - MultipleArityFunctions::expand_multiple_arity_functions(expanded_statements); - - log::info!(target: "expansion-phase", "Aggressive constant evaluation with memoization"); - - // Begin lowering anonymous function calls to lets - - analysis.fresh_from_exprs(&expanded_statements); - analysis.populate_captures(&expanded_statements); - let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis); - - semantic.replace_anonymous_function_calls_with_plain_lets(); - - Ok(expanded_statements) - } - - fn lower_expressions_impl( - &mut self, - exprs: Vec, - constants: ImmutableHashMap, - builtin_modules: ModuleContainer, - path: Option, - sources: &mut Sources, - ) -> Result> { - let mut expanded_statements = - self.expand_expressions(exprs, path, sources, builtin_modules.clone())?; - - // expanded_statements = expanded_statements - // .into_iter() - // .map(lower_entire_ast) - // .collect::, ParseError>>()?; - - // if log_enabled!(log::Level::Debug) { - // debug!( - // "Generating instructions for the expression: {:?}", - // expanded_statements - // .iter() - // .map(|x| x.to_string()) - // .collect::>() - // ); - // } - - log::debug!(target: "expansion-phase", "Expanding macros -> phase 1"); - - if let Some(kernel) = self.kernel.as_mut() { - // Label anything at the top as well - top level - kernel.load_syntax_transformers(&mut expanded_statements, "top-level".to_string())?; - } - - expanded_statements = expanded_statements - .into_iter() - .map(|x| { - expand_kernel_in_env( - x, - self.kernel.as_mut(), - builtin_modules.clone(), - "top-level".to_string(), - ) - .and_then(|x| crate::parser::expand_visitor::expand(x, &self.macro_env)) - }) - .collect::>>()?; - - expanded_statements = expanded_statements - .into_iter() - .map(lower_entire_ast) - .collect::, ParseError>>()?; - - expanded_statements = expanded_statements - .into_iter() - .map(|x| { - expand_kernel_in_env( - x, - self.kernel.as_mut(), - builtin_modules.clone(), - "top-level".to_string(), - ) - // TODO: If we have this, then we have to lower all of the expressions again - .and_then(|x| crate::parser::expand_visitor::expand(x, &self.macro_env)) - }) - .collect::>>()?; - - // println!("{:#?}", expanded_statements); - - // Let's do the @doc macro here? - - log::debug!(target: "expansion-phase", "Beginning constant folding"); - - let mut expanded_statements = - self.apply_const_evaluation(constants.clone(), expanded_statements, false)?; - - RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements); - - let mut analysis = Analysis::from_exprs(&expanded_statements); - analysis.populate_captures(&expanded_statements); - - let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis); - - // This is definitely broken still - semantic - .elide_single_argument_lambda_applications() - .replace_non_shadowed_globals_with_builtins( - &mut self.macro_env, - &mut self.module_manager, - &mut self.mangled_identifiers, - ) - // TODO: To get this to work, we have to check the macros to make sure those - // are safe to eliminate. In interactive mode, we'll - // be unable to optimize those away - .remove_unused_globals_with_prefix("mangler", &self.macro_env, &self.module_manager) - .lift_pure_local_functions() - .lift_all_local_functions(); - - // debug!("About to expand defines"); - - log::debug!(target: "expansion-phase", "Flattening begins, converting internal defines to let expressions"); - - let mut analysis = semantic.into_analysis(); - - let mut expanded_statements = flatten_begins_and_expand_defines(expanded_statements); - - // After define expansion, we'll want this - RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements); - - analysis.fresh_from_exprs(&expanded_statements); - analysis.populate_captures(&expanded_statements); - - // expanded_statements.pretty_print(); - - let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis); - - // Check if the values are redefined! - semantic.check_if_values_are_redefined()?; - - semantic.refresh_variables(); - semantic.flatten_anonymous_functions(); - semantic.refresh_variables(); - - // Replace mutation with boxes - semantic.populate_captures(); - semantic.populate_captures(); - - semantic.replace_mutable_captured_variables_with_boxes(); - - // if log_enabled!(log::Level::Debug) { - // debug!( - // "Successfully expanded defines: {:?}", - // expanded_statements - // .iter() - // .map(|x| x.to_string()) - // .collect::>() - // ); - // } - - log::debug!(target: "expansion-phase", "Expanding multiple arity functions"); - - let mut analysis = semantic.into_analysis(); - - // Rename them again - RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements); - - // TODO - make sure I want to keep this - let mut expanded_statements = - MultipleArityFunctions::expand_multiple_arity_functions(expanded_statements); - - log::info!(target: "expansion-phase", "Aggressive constant evaluation with memoization"); - - // Begin lowering anonymous function calls to lets - - // let mut analysis = Analysis::from_exprs(&expanded_statements); - // let mut analysis = semantic.into_analysis(); - analysis.fresh_from_exprs(&expanded_statements); - analysis.populate_captures(&expanded_statements); - let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis); - // semantic.populate_captures(); - - semantic.replace_anonymous_function_calls_with_plain_lets(); - - Ok(expanded_statements) - - // Done lowering anonymous function calls to let - // TODO: Re-enable this, but not in the repl. This repl causes... issues with the implementation - // self.apply_const_evaluation(constants, expanded_statements, true) - } - - // TODO - // figure out how the symbols will work so that a raw program with symbols - // can be later pulled in and symbols can be interned correctly - fn compile_raw_program( - &mut self, - exprs: Vec, - constants: ImmutableHashMap, - builtin_modules: ModuleContainer, - path: Option, - sources: &mut Sources, - ) -> Result { - log::debug!(target: "expansion-phase", "Expanding macros -> phase 0"); - - let mut expanded_statements = - self.lower_expressions_impl(exprs, constants, builtin_modules, path, sources)?; - - // expanded_statements.pretty_print(); - - log::debug!(target: "expansion-phase", "Generating instructions"); - - let instructions = self.generate_instructions_for_executable(expanded_statements)?; - - let mut raw_program = RawProgramWithSymbols::new( - instructions, - self.constant_map.clone(), - "0.1.0".to_string(), - ); - - // Make sure to apply the peephole optimizations - raw_program.apply_optimizations(); - - // Lets see everything that gets run! - // raw_program.debug_print(); - - Ok(raw_program) - } - - fn run_const_evaluation_with_memoization( - &mut self, - constants: ImmutableHashMap, - mut expanded_statements: Vec, - ) -> Result> { - todo!("Implement kernel level const evaluation here!") - } - - fn apply_const_evaluation( - &mut self, - constants: ImmutableHashMap, - mut expanded_statements: Vec, - use_kernel: bool, - ) -> Result> { - #[cfg(feature = "profiling")] - let opt_time = Instant::now(); - - let mut maybe_kernel = None; - - if use_kernel { - if let Some(kernel) = self.kernel.as_mut() { - kernel.load_program_for_comptime(constants.clone(), &mut expanded_statements); - } - } - - match self.opt_level { - // TODO - // Cut this off at 10 iterations no matter what - OptLevel::Three => { - for _ in 0..10 { - let mut manager = ConstantEvaluatorManager::new( - &mut self.memoization_table, - constants.clone(), - self.opt_level, - if use_kernel { - &mut self.kernel - } else { - &mut maybe_kernel - }, - ); - expanded_statements = manager.run(expanded_statements)?; - - if !manager.changed { - break; - } - } - } - OptLevel::Two => { - expanded_statements = ConstantEvaluatorManager::new( - &mut self.memoization_table, - constants, - self.opt_level, - if use_kernel { - &mut self.kernel - } else { - &mut maybe_kernel - }, - ) - .run(expanded_statements)?; - } - _ => {} - } - - #[cfg(feature = "profiling")] - if log_enabled!(target: "pipeline_time", log::Level::Debug) { - debug!( - target: "pipeline_time", - "Const Evaluation Time: {:?}", - opt_time.elapsed() - ); - }; - - Ok(expanded_statements) - } -} diff --git a/crates/steel-core/src/compiler/constants.rs b/crates/steel-core/src/compiler/constants.rs deleted file mode 100644 index 4233e0aed..000000000 --- a/crates/steel-core/src/compiler/constants.rs +++ /dev/null @@ -1,251 +0,0 @@ -use crate::parser::tryfrom_visitor::TryFromExprKindForSteelVal; -use crate::rvals::{into_serializable_value, Result, SerializableSteelVal, SteelVal}; - -use crate::parser::{ - ast::ExprKind, - parser::{ParseError, Parser}, -}; - -use std::collections::HashMap; -use std::{cell::RefCell, rc::Rc}; - -// TODO add the serializing and deserializing for constants -use serde::{Deserialize, Serialize}; - -// Shared constant map - for repeated in memory execution of a program, this is going to share the same -// underlying representation. -#[derive(Debug, PartialEq)] -pub struct ConstantMap { - map: Rc>>, - values: Rc>>, -} - -#[derive(Serialize, Deserialize)] -pub struct SerializableConstantMap(Vec); - -// #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] -// struct ConstantExprMap { -// map: Vec, -// } -impl Default for ConstantMap { - fn default() -> Self { - Self::new() - } -} - -impl Clone for ConstantMap { - fn clone(&self) -> Self { - Self { - values: Rc::clone(&self.values), - map: Rc::clone(&self.map), - } - } -} - -impl ConstantMap { - pub fn new() -> ConstantMap { - ConstantMap { - values: Rc::new(RefCell::new(Vec::new())), - map: Rc::new(RefCell::new(HashMap::new())), - } - } - - pub(crate) fn into_serializable_map(self) -> SerializableConstantMap { - SerializableConstantMap(self.to_bytes().unwrap()) - } - - pub fn to_serializable_vec( - &self, - serializer: &mut std::collections::HashMap, - visited: &mut std::collections::HashSet, - ) -> Vec { - self.values - .borrow() - .iter() - .cloned() - .map(|x| into_serializable_value(x, serializer, visited)) - .collect::>() - .unwrap() - } - - // There might be a better way of doing this - but provide this as an option - // in the event we want a deep clone of the constant map - // pub fn deep_clone(&self) -> ConstantMap { - // ConstantMap(Rc::new(RefCell::new( - // self.0.borrow().iter().cloned().collect(), - // ))) - // } - - pub fn from_vec(vec: Vec) -> ConstantMap { - ConstantMap { - map: Rc::new(RefCell::new( - vec.clone() - .into_iter() - .enumerate() - .map(|x| (x.1, x.0)) - .collect(), - )), - values: Rc::new(RefCell::new(vec)), - } - } - - fn to_constant_expr_map(&self) -> Vec { - self.values - .borrow() - .iter() - .map(|x| match x { - SteelVal::StringV(s) => { - format!("{:?}", s) - } - SteelVal::CharV(c) => { - format!("#\\{c}") - } - _ => x.to_string(), - }) - .collect() - } - - pub fn to_bytes(&self) -> Result> { - // let vector_val = self.to_constant_expr_map()?; - - let str_vector = self.to_constant_expr_map(); - let result = bincode::serialize(&str_vector); - - Ok(result.unwrap()) - } - - pub fn from_serialized(map: SerializableConstantMap) -> Result { - Self::from_bytes(&map.0) - } - - pub fn from_bytes(encoded: &[u8]) -> Result { - let str_vector: Vec = bincode::deserialize(encoded).unwrap(); - - // println!("{:?}", str_vector); - - str_vector - .into_iter() - .map(|x| { - // Parse the input - let parsed: std::result::Result, ParseError> = - Parser::new_flat(&x, None).collect(); - let parsed = parsed?; - - TryFromExprKindForSteelVal::try_from_expr_kind_quoted(parsed[0].clone()) - - // Ok(SteelVal::try_from(parsed[0].clone()).unwrap()) - }) - .collect::>>() - .map(Self::from_vec) - } - - // pub fn from_bytes(encoded: &[u8]) -> ConstantMap { - // bincode::deserialize(encoded).unwrap() - // } -} - -impl ConstantMap { - pub fn add(&mut self, val: SteelVal) -> usize { - let idx = self.len(); - self.values.borrow_mut().push(val.clone()); - - // TODO: Consider just storing the hash code, not the actual value. - self.map.borrow_mut().insert(val, idx); - - idx - } - - // Fallible - #[inline(always)] - pub fn get(&self, idx: usize) -> SteelVal { - self.values.borrow()[idx].clone() - } - - pub fn try_get(&self, idx: usize) -> Option { - self.values.borrow().get(idx).cloned() - } - - // Replace with existing constants if they already exist - fn walk_constants(&mut self, val: &SteelVal) -> Option { - match val { - SteelVal::ListV(l) => Some(SteelVal::ListV( - l.iter() - .map(|value| { - let idx = self.add_or_get(value.clone()); - - self.get(idx) - }) - .collect(), - )), - _ => None, - } - } - - // This is certainly not what we want. This time complexity is - // questionable - pub fn add_or_get(&mut self, mut val: SteelVal) -> usize { - if let SteelVal::ListV(_) = &val { - if let Some(new_list) = self.walk_constants(&val) { - val = new_list; - }; - } - - let idx = self.map.borrow_mut().get(&val).copied(); - - if let Some(idx) = idx { - idx - } else { - self.add(val) - } - } - - pub fn len(&self) -> usize { - self.values.borrow().len() - } - - pub fn is_empty(&self) -> bool { - self.values.borrow().is_empty() - } - - pub fn roll_back(&mut self, idx: usize) { - self.values.borrow_mut().truncate(idx); - } - - #[cfg(test)] - pub fn clear(&mut self) { - self.values.borrow_mut().clear() - } -} - -#[cfg(test)] -pub mod constant_table_tests { - use super::*; - - #[test] - fn run_tests_constant_map() { - let mut instance = ConstantMap::new(); - test_add(&mut instance); - - let mut instance = ConstantMap::new(); - test_get(&mut instance); - } - - fn test_add(instance: &mut ConstantMap) { - assert_eq!(instance.len(), 0); - let val1 = SteelVal::BoolV(true); - let val2 = SteelVal::BoolV(false); - assert_eq!(instance.add(val1), 0); - assert_eq!(instance.add(val2), 1); - } - - fn test_get(instance: &mut ConstantMap) { - assert_eq!(instance.len(), 0); - let val1 = SteelVal::BoolV(true); - let val2 = SteelVal::BoolV(false); - assert_eq!(instance.add(val1), 0); - assert_eq!(instance.add(val2), 1); - - assert_eq!(instance.get(0), SteelVal::BoolV(true)); - assert_eq!(instance.get(1), SteelVal::BoolV(false)); - } -} diff --git a/crates/steel-core/src/compiler/map.rs b/crates/steel-core/src/compiler/map.rs deleted file mode 100644 index 1234f2ece..000000000 --- a/crates/steel-core/src/compiler/map.rs +++ /dev/null @@ -1,58 +0,0 @@ -use crate::throw; -use crate::{parser::interner::InternedString, rvals::Result}; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] -pub struct SymbolMap { - values: Vec, - map: HashMap, -} - -impl Default for SymbolMap { - fn default() -> Self { - Self::new() - } -} - -impl SymbolMap { - pub fn new() -> Self { - SymbolMap { - values: Vec::new(), - map: HashMap::new(), - } - } - - pub fn values(&self) -> &Vec { - &self.values - } - - pub fn len(&self) -> usize { - self.values.len() - } - - pub fn roll_back(&mut self, index: usize) { - for value in self.values.drain(index..) { - self.map.remove(&value); - } - } - - pub fn add(&mut self, ident: &InternedString) -> usize { - let idx = self.values.len(); - - self.map.insert(*ident, idx); - - // Add the values so we can do a backwards resolution - self.values.push(*ident); - - idx - } - - // fallible - pub fn get(&self, ident: &InternedString) -> Result { - self.map - .get(ident) - .copied() - .ok_or_else(throw!(FreeIdentifier => ident.resolve())) - } -} diff --git a/crates/steel-core/src/compiler/mod.rs b/crates/steel-core/src/compiler/mod.rs deleted file mode 100644 index 005c0125e..000000000 --- a/crates/steel-core/src/compiler/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -// pub mod code_generator; -#[allow(clippy::module_inception)] -pub mod compiler; -pub mod constants; -pub mod map; -pub mod modules; -pub mod passes; -pub mod program; - -pub mod code_gen; diff --git a/crates/steel-core/src/compiler/modules.rs b/crates/steel-core/src/compiler/modules.rs deleted file mode 100644 index cab29e422..000000000 --- a/crates/steel-core/src/compiler/modules.rs +++ /dev/null @@ -1,2386 +0,0 @@ -#![allow(unused)] -use crate::{ - compiler::{ - passes::{ - analysis::{Analysis, SemanticAnalysis}, - VisitorMutRefUnit, - }, - program::PROVIDE, - }, - parser::{ - ast::{AstTools, Atom, Begin, Define, ExprKind, List, Quote}, - expand_visitor::{ - expand_kernel, expand_kernel_in_env, expand_kernel_in_env_with_allowed, - expand_kernel_in_env_with_change, - }, - interner::InternedString, - kernel::Kernel, - parser::{ - lower_entire_ast, lower_macro_and_require_definitions, ParseError, Parser, Sources, - SyntaxObject, - }, - tokens::TokenType, - }, - steel_vm::{ - builtin::BuiltInModule, - engine::{ModuleContainer, DEFAULT_PRELUDE_MACROS}, - transducers::interleave, - }, -}; -use crate::{parser::expand_visitor::Expander, rvals::Result}; - -use steel_parser::expr_list; - -use std::{ - borrow::Cow, - collections::{HashMap, HashSet}, - io::Read, - path::PathBuf, -}; - -use crate::parser::expander::SteelMacro; -use crate::stop; - -use std::time::SystemTime; - -use crate::parser::expand_visitor::{expand, extract_macro_defs}; - -use log::{debug, info, log_enabled}; - -use crate::parser::ast::IteratorExtensions; - -use super::{ - passes::{ - begin::FlattenBegin, - mangle::{collect_globals, NameMangler, NameUnMangler}, - }, - program::{CONTRACT_OUT, FOR_SYNTAX, ONLY_IN, PREFIX_IN, REQUIRE_IDENT_SPEC}, -}; - -macro_rules! declare_builtins { - ( $( $name:expr => $path:expr ), *) => { - static BUILT_INS: &[(&str, &str)] = &[ - $( ($name, include_str!($path)), )* - ]; - }; -} - -pub(crate) const MANGLER_SEPARATOR: &str = "__%#__"; - -macro_rules! create_prelude { - ( - $( $module:literal, )* - $( for_syntax $module_for_syntax:literal ),* - ) => { - - pub static PRELUDE_WITHOUT_BASE: &str = concat!( - $( "(require \"", $module, "\")\n", )* - $( "(require (for-syntax \"", $module_for_syntax, "\"))\n", )* - ); - - pub static PRELUDE_STRING: &str = concat!( - "(require-builtin steel/base)\n", - $( "(require \"", $module, "\")\n", )* - $( "(require (for-syntax \"", $module_for_syntax, "\"))\n", )* - ); - } -} - -declare_builtins!( - "steel/option" => "../scheme/modules/option.scm", - "steel/result" => "../scheme/modules/result.scm", - "steel/iterators" => "../scheme/modules/iterators.scm", - "steel/mutable-vectors" => "../scheme/modules/mvector.scm", - "#%private/steel/contract" => "../scheme/modules/contracts.scm", - "#%private/steel/print" => "../scheme/print.scm", - "#%private/steel/control" => "../scheme/modules/parameters.scm", - "#%private/steel/reader" => "../scheme/modules/reader.scm", - "#%private/steel/stdlib" => "../scheme/stdlib.scm" -); - -create_prelude!( - "#%private/steel/control", - "#%private/steel/contract", - "#%private/steel/print", - "#%private/steel/reader", - for_syntax "#%private/steel/control", - for_syntax "#%private/steel/contract" -); - -/// Manages the modules -/// keeps some visited state on the manager for traversal -/// Also keeps track of the metadata for each file in order to determine -/// if it needs to be recompiled -#[derive(Clone, serde::Serialize, serde::Deserialize)] -pub(crate) struct ModuleManager { - compiled_modules: HashMap, - file_metadata: HashMap, - visited: HashSet, - custom_builtins: HashMap, -} - -impl ModuleManager { - pub(crate) fn new( - compiled_modules: HashMap, - file_metadata: HashMap, - ) -> Self { - ModuleManager { - compiled_modules, - file_metadata, - visited: HashSet::new(), - custom_builtins: HashMap::new(), - } - } - - pub fn add_builtin_module(&mut self, module_name: String, mut text: String) { - // Custom external builtins should be loaded with the prelude first, otherwise - // they'll need to handle a bunch of imports - text.insert_str(0, PRELUDE_STRING); - - self.custom_builtins.insert(module_name, text); - } - - pub fn modules(&self) -> &HashMap { - &self.compiled_modules - } - - pub fn modules_mut(&mut self) -> &mut HashMap { - &mut self.compiled_modules - } - - pub(crate) fn default() -> Self { - Self::new(HashMap::new(), HashMap::new()) - } - - // Add the module directly to the compiled module cache - pub(crate) fn add_module( - &mut self, - path: PathBuf, - global_macro_map: &mut HashMap, - kernel: &mut Option, - sources: &mut Sources, - builtin_modules: ModuleContainer, - ) -> Result<()> { - // todo!() - - self.visited.clear(); - - // TODO: Expand macros on the fly when visiting a module. Don't wait till the end - // Macro expansion should happen as we enter a module. - let mut module_builder = ModuleBuilder::new_from_path( - path, - &mut self.compiled_modules, - &mut self.visited, - &mut self.file_metadata, - sources, - kernel, - builtin_modules, - global_macro_map, - &self.custom_builtins, - )?; - - module_builder.compile()?; - - // println!("{:#?}", self.compiled_modules); - - Ok(()) - } - - #[allow(unused)] - pub(crate) fn compile_main( - &mut self, - global_macro_map: &mut HashMap, - kernel: &mut Option, - sources: &mut Sources, - exprs: Vec, - path: Option, - builtin_modules: ModuleContainer, - ) -> Result> { - // Wipe the visited set on entry - self.visited.clear(); - - // TODO - // This is also explicitly wrong -> we should separate the global macro map from the macros found locally in this module - // For instance, (cond) is global, but (define-syntax blagh) might be local to main - // if a module then defines a function (blagh) that is used inside its scope, this would expand the macro in that scope - // which we do not want - let non_macro_expressions = extract_macro_defs(exprs, global_macro_map)?; - - let mut module_builder = ModuleBuilder::main( - path, - non_macro_expressions, - &mut self.compiled_modules, - &mut self.visited, - &mut self.file_metadata, - sources, - kernel, - builtin_modules, - global_macro_map, - &self.custom_builtins, - )?; - - let mut module_statements = module_builder.compile()?; - - // println!("Compiled modules: {:?}", module_builder.compiled_modules); - - // Expand the ast first with the macros from global/source file - let mut ast = module_builder - .source_ast - .into_iter() - .map(|x| expand(x, global_macro_map)) - .collect::>>()?; - - { - module_builder.source_ast = ast; - module_builder.collect_provides(); - - ast = std::mem::take(&mut module_builder.source_ast); - } - - let mut require_defines = Vec::new(); - - let mut mangled_prefixes = module_builder - .require_objects - .iter() - .filter(|x| !x.for_syntax) - .map(|x| { - "mangler".to_string() + x.path.get_path().to_str().unwrap() + MANGLER_SEPARATOR - }) - .collect::>(); - - let mut explicit_requires = HashMap::new(); - - for require_object in &module_builder.require_objects { - let path = require_object.path.get_path(); - explicit_requires.clear(); - - // If there _are_ explicit identifiers to import, limit what we import to what - // is in the set - for ident in &require_object.idents_to_import { - match ident { - MaybeRenamed::Normal(i) => { - explicit_requires.insert(i.atom_identifier().unwrap().clone(), None); - } - - MaybeRenamed::Renamed(from, to) => { - explicit_requires.insert( - from.atom_identifier().unwrap().clone(), - Some(to.atom_identifier().unwrap().clone()), - ); - } - } - } - - // println!("{:?}", path); - let module = if let Some(module) = module_builder.compiled_modules.get(path.as_ref()) { - module - } else { - // log::debug!(target: "modules", "No provides found for module, skipping: {:?}", path); - - continue; - }; - - for provide_expr in &module.provides { - // For whatever reason, the value coming into module.provides is an expression like: (provide expr...) - for provide in &provide_expr.list().unwrap().args[1..] { - // println!("{}", provide); - - // println!("Top level provide handler"); - - // Would be nice if this could be handled by some macro expansion... - // See if contract/out - - let other_module_prefix = - "mangler".to_string() + module.name.to_str().unwrap() + MANGLER_SEPARATOR; - - // TODO: Expand the contract out into something we expect - // Otherwise, this is going to blow up - match provide { - ExprKind::List(l) => { - if let Some(qualifier) = l.first_ident() { - match *qualifier { - x if x == *CONTRACT_OUT => { - // Directly expand into define/contract, but with the value just being the hash get below - - // (bind/c contract name 'name) - - let name = l.args.get(1).unwrap(); - - if !explicit_requires.is_empty() - && !name - .atom_identifier() - .map(|x| explicit_requires.contains_key(x)) - .unwrap_or_default() - { - continue; - } - - // TODO: This should surface an error - cannot use contract - // out on a macro - if module - .macro_map - .contains_key(name.atom_identifier().unwrap()) - { - continue; - } - - // TODO: THe contract has to get mangled with the prefix as well? - let contract = l.args.get(2).unwrap(); - - let hash_get = expr_list![ - ExprKind::ident("%proto-hash-get%"), - ExprKind::atom( - "__module-".to_string() + &other_module_prefix - ), - ExprKind::Quote(Box::new(Quote::new( - name.clone(), - SyntaxObject::default(TokenType::Quote) - ))), - ]; - - let mut owned_name = name.clone(); - - // If we have the alias listed, we should use it - if !explicit_requires.is_empty() { - if let Some(alias) = explicit_requires - .get(name.atom_identifier().unwrap()) - .copied() - .flatten() - { - *owned_name.atom_identifier_mut().unwrap() = - alias.clone(); - } - } - - if let Some(prefix) = &require_object.prefix { - if let Some(existing) = owned_name.atom_identifier_mut() - { - let mut prefixed_identifier = prefix.clone(); - prefixed_identifier.push_str(existing.resolve()); - - // Update the existing identifier to point to a new one with the prefix applied - *existing = prefixed_identifier.into(); - } - } - - let define = ExprKind::Define(Box::new(Define::new( - owned_name, - hash_get, - SyntaxObject::default(TokenType::Define), - ))); - - require_defines.push(define); - } - x if x == *REQUIRE_IDENT_SPEC => { - // Directly expand into define/contract, but with the value just being the hash get below - - // (bind/c contract name 'name) - - let name = l.args.get(1).unwrap(); - - if !explicit_requires.is_empty() - && !name - .atom_identifier() - .map(|x| explicit_requires.contains_key(x)) - .unwrap_or_default() - { - continue; - } - - if module - .macro_map - .contains_key(name.atom_identifier().unwrap()) - { - continue; - } - - let hash_get = expr_list![ - ExprKind::ident("%proto-hash-get%"), - ExprKind::atom( - "__module-".to_string() + &other_module_prefix - ), - ExprKind::Quote(Box::new(Quote::new( - name.clone(), - SyntaxObject::default(TokenType::Quote) - ))), - ]; - - let mut owned_name = name.clone(); - - // If we have the alias listed, we should use it - if !explicit_requires.is_empty() { - if let Some(alias) = explicit_requires - .get(name.atom_identifier().unwrap()) - .copied() - .flatten() - { - *owned_name.atom_identifier_mut().unwrap() = - alias.clone(); - } - } - - if let Some(prefix) = &require_object.prefix { - if let Some(existing) = owned_name.atom_identifier_mut() - { - let mut prefixed_identifier = prefix.clone(); - prefixed_identifier.push_str(existing.resolve()); - - // Update the existing identifier to point to a new one with the prefix applied - *existing = prefixed_identifier.into(); - } - } - - let define = ExprKind::Define(Box::new(Define::new( - owned_name, - hash_get, - SyntaxObject::default(TokenType::Define), - ))); - - require_defines.push(define); - } - _ => { - stop!(TypeMismatch => format!("provide expects either an identifier, (for-syntax ), or (contract/out ...), found: {}", provide)) - } - } - } else { - stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax )") - } - } - ExprKind::Atom(_) => { - if !explicit_requires.is_empty() - && !provide - .atom_identifier() - .map(|x| explicit_requires.contains_key(x)) - .unwrap_or_default() - { - continue; - } - - if module - .macro_map - .contains_key(provide.atom_identifier().unwrap()) - { - continue; - } - - let hash_get = expr_list![ - ExprKind::ident("%proto-hash-get%"), - ExprKind::atom("__module-".to_string() + &other_module_prefix), - ExprKind::Quote(Box::new(Quote::new( - provide.clone(), - SyntaxObject::default(TokenType::Quote) - ))), - ]; - - let mut owned_provide = provide.clone(); - - // If we have the alias listed, we should use it - if !explicit_requires.is_empty() { - if let Some(alias) = explicit_requires - .get(provide.atom_identifier().unwrap()) - .copied() - .flatten() - { - *owned_provide.atom_identifier_mut().unwrap() = alias.clone(); - } - } - - // TODO: If there is a prefix applied, use it here? - if let Some(prefix) = &require_object.prefix { - if let Some(existing) = owned_provide.atom_identifier_mut() { - let mut prefixed_identifier = prefix.clone(); - prefixed_identifier.push_str(existing.resolve()); - - // Update the existing identifier to point to a new one with the prefix applied - *existing = prefixed_identifier.into(); - } - } - - let define = ExprKind::Define(Box::new(Define::new( - owned_provide, - hash_get, - SyntaxObject::default(TokenType::Define), - ))); - - require_defines.push(define); - } - _ => { - stop!(TypeMismatch => "provide expression needs to either be a `contract/out` form or an identifier") - } - } - } - } - } - - let mut mangled_asts = Vec::new(); - - // TODO: Move this to the lower level as well - // It seems we're only doing this expansion at the top level, but we _should_ do this at the lower level as well - for require_object in module_builder.require_objects.iter() - // .filter(|x| x.for_syntax) - // .map(|x| x.path.get_path()) - { - let require_for_syntax = require_object.path.get_path(); - - let (module, mut in_scope_macros, mut name_mangler) = Self::find_in_scope_macros( - &self.compiled_modules, - require_for_syntax.as_ref(), - &require_object, - &mut mangled_asts, - ); - - let kernel_macros_in_scope: HashSet<_> = - module.provides_for_syntax.iter().cloned().collect(); - - ast = ast - .into_iter() - .map(|x| { - // @matt 12/8/2023 - // The easiest thing to do here, is to go to the other module, and find - // what defmacros have been exposed on the require for syntax. Once those - // have been found, we run a pass with kernel expansion, limiting the - // expander to only use the macros that we've exposed. After that, - // we run the expansion again, using the full suite of defmacro capabilities. - // - // The question that remains - how to define the neat phases of what kinds - // of macros can expand into what? Can defmacro -> syntax-rules -> defmacro? - // This could eventually prove to be cumbersome, but it is still early - // for defmacro. Plus, I need to create a syntax-case or syntax-parse - // frontend before the defmacro style macros become too pervasive. - // - // TODO: Replicate this behavior over to builtin modules - - // First expand the in scope macros - // These are macros - let mut expander = Expander::new(&in_scope_macros); - let mut first_round_expanded = expander.expand(x)?; - let mut changed = false; - - // (first_round_expanded, changed) = expand_kernel_in_env_with_allowed( - // first_round_expanded, - // kernel.as_mut(), - // // We don't need to expand those here - // ModuleContainer::default(), - // module.name.to_str().unwrap().to_string(), - // &kernel_macros_in_scope, - // )?; - - // If the kernel expander expanded into something - go ahead - // and expand all of the macros in this - // if changed || expander.changed { - // Expand here? - // first_round_expanded = expand(first_round_expanded, &module.macro_map)?; - - // Probably don't need this - // (first_round_expanded, changed) = expand_kernel_in_env_with_change( - // first_round_expanded, - // kernel.as_mut(), - // ModuleContainer::default(), - // module.name.to_str().unwrap().to_string(), - // )?; - - // This is pretty suspect, and needs to be revisited - only the output of the - // macro expansion and not the whole thing needs to be mangled most likely. - // Otherwise, we'll run into weird stuff? - // if changed { - // name_mangler.visit(&mut first_round_expanded); - // } - // } - - if expander.changed || changed { - expand(first_round_expanded, &module.macro_map) - } else { - Ok(first_round_expanded) - } - }) - .collect::>()?; - - global_macro_map.extend(in_scope_macros); - } - - // Include the defines from the modules now imported - module_statements.append(&mut require_defines); - - // The next two lines here expand _all_ of the source code with the top level macros - // This is necessary because of the std library macros, although this should be able to be - // solved with scoped imports of the standard library explicitly - module_statements.append(&mut ast); - - // @Matt 7/4/23 - // TODO: With mangling, this could cause problems. We'll want to un-mangle quotes AFTER the macro has been expanded, - // in order to preserve the existing behavior. - let result = module_statements - .into_iter() - .map(|x| expand(x, global_macro_map)) - .collect::>(); - - result - } - - fn find_in_scope_macros<'a>( - compiled_modules: &'a HashMap, - require_for_syntax: &'a PathBuf, - require_object: &'a RequireObject, - mangled_asts: &'a mut Vec, - ) -> ( - &'a CompiledModule, - HashMap, - NameMangler, - ) { - let module = compiled_modules - .get(require_for_syntax) - .expect(&format!("Module missing!: {:?}", require_for_syntax)); - - let prefix = "mangler".to_string() + module.name.to_str().unwrap() + MANGLER_SEPARATOR; - - let globals = collect_globals(&module.ast); - - let mut name_mangler = NameMangler::new(globals, prefix); - - // If the module hasn't been emitted already, then include it here - if !module.emitted { - let mut module_ast = module.ast.clone(); - - name_mangler.mangle_vars(&mut module_ast); - - mangled_asts.append(&mut module_ast); - } - - // let provided_macros = module.provides_for - // let expander = Expander::new(&module.macro_map); - // TODO - // expand expressions one by one - // if expansion with _just_ public macros from the required module doesn't do anything, stop - // if it _does_ change, do another pass with all of the macros in scope - // do this for each of the expressions in the file in this loop - // TODO -> try not cloning this - // TODO -> do this in the module expansion as well - let mut in_scope_macros = module - .provides_for_syntax - .iter() - // Chain with just the normal provides! - // .chain(module.provides) - .filter_map(|x| module.macro_map.get(x).map(|m| (*x, m.clone()))) // TODO -> fix this unwrap - .map(|mut x| { - for expr in x.1.exprs_mut() { - name_mangler.visit(expr); - } - - x - }) - .collect::>(); - - // If the require_object specifically imports things, we should reference it - - if !require_object.idents_to_import.is_empty() { - for maybe in &require_object.idents_to_import { - match maybe { - MaybeRenamed::Normal(n) => { - if let Some(ident) = n.atom_identifier() { - if let Some(mut m) = module.macro_map.get(ident).cloned() { - for expr in m.exprs_mut() { - name_mangler.visit(expr); - } - - if let Some(prefix) = &require_object.prefix { - in_scope_macros - .insert((prefix.to_string() + ident.resolve()).into(), m); - } else { - in_scope_macros.insert(*ident, m); - } - } - } - } - MaybeRenamed::Renamed(from, to) => { - if let Some(ident) = from.atom_identifier() { - if let Some(mut m) = module.macro_map.get(ident).cloned() { - for expr in m.exprs_mut() { - name_mangler.visit(expr); - } - // TODO: Remove this unwrap - // in_scope_macros.insert(*to.atom_identifier().unwrap(), m); - - if let Some(prefix) = &require_object.prefix { - in_scope_macros.insert( - (prefix.to_string() - + to.atom_identifier().unwrap().resolve()) - .into(), - m, - ); - } else { - in_scope_macros.insert(*to.atom_identifier().unwrap(), m); - } - } - } - } - } - } - } else { - // Pull in all of the macros that the module exposes - - for provide_expr in &module.provides { - if let Some(provide_expr) = provide_expr.list() { - for ident in provide_expr.args.split_first().unwrap().1 { - // println!("Looking for {}", ident); - - if let Some(ident) = ident.atom_identifier() { - if let Some(mut m) = module.macro_map.get(ident).cloned() { - // println!("Pulling in macro: {}", ident); - - for expr in m.exprs_mut() { - name_mangler.visit(expr); - } - - if let Some(prefix) = &require_object.prefix { - in_scope_macros - .insert((prefix.to_string() + ident.resolve()).into(), m); - } else { - in_scope_macros.insert(*ident, m); - } - } - } - } - } - } - } - - // Check what macros are in scope here - // println!( - // "In scope macros: {:#?}", - // in_scope_macros.keys().collect::>() - // ); - (module, in_scope_macros, name_mangler) - } - - #[cfg(not(feature = "modules"))] - pub(crate) fn expand_expressions( - &mut self, - global_macro_map: &mut HashMap, - exprs: Vec, - ) -> Result> { - let non_macro_expressions = extract_macro_defs(exprs, global_macro_map)?; - non_macro_expressions - .into_iter() - .map(|x| expand(x, global_macro_map)) - .collect() - } -} - -// Pre-compile module to bytecode? Is it even possible? -// Dynamically linking the module would then make it relatively -// easy to just load everything up at the start. -// Compiled module _should_ be possible now. Just create a target -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct CompiledModule { - name: PathBuf, - provides: Vec, - require_objects: Vec, - provides_for_syntax: Vec, - pub(crate) macro_map: HashMap, - ast: Vec, - emitted: bool, -} - -// TODO: @Matt 6/12/23 - This _should_ be serializable. If possible, we can try to store intermediate objects down to some file. -impl CompiledModule { - pub fn new( - name: PathBuf, - provides: Vec, - require_objects: Vec, - provides_for_syntax: Vec, - macro_map: HashMap, - ast: Vec, - ) -> Self { - Self { - name, - provides, - require_objects, - provides_for_syntax, - macro_map, - ast, - emitted: false, - } - } - - pub fn get_ast(&self) -> &[ExprKind] { - &self.ast - } - - pub fn get_provides(&self) -> &[ExprKind] { - &self.provides - } - - // pub fn get_requires(&self) -> &[PathBuf] { - // &self.requires - // } - - pub fn set_emitted(&mut self, emitted: bool) { - self.emitted = emitted; - } - - fn to_top_level_module( - &self, - modules: &HashMap, - global_macro_map: &HashMap, - ) -> Result { - let mut globals = collect_globals(&self.ast); - - let mut exprs = self.ast.clone(); - - let mut provide_definitions = Vec::new(); - - let prefix = "mangler".to_string() + self.name.to_str().unwrap() + MANGLER_SEPARATOR; - - // Now we should be able to set up a series of requires with the right style - // ;; Refresh the module definition in this namespace - // (define a-module.rkt-b (hash-get 'b b-module.rkt-b)) - - let mut explicit_requires = HashMap::new(); - - // TODO: This is the same as the top level, they should be merged - for require_object in &self.require_objects { - let path = require_object.path.get_path(); - - explicit_requires.clear(); - - for ident in &require_object.idents_to_import { - match ident { - MaybeRenamed::Normal(i) => { - explicit_requires.insert(i.atom_identifier().unwrap().clone(), None); - } - MaybeRenamed::Renamed(from, to) => { - explicit_requires.insert( - from.atom_identifier().unwrap().clone(), - Some(to.atom_identifier().unwrap().clone()), - ); - } - } - } - - // println!("{:?}", path); - // println!("{:?}", modules.keys().collect::>()); - let module = modules.get(path.as_ref()).unwrap(); - - let other_module_prefix = - "mangler".to_string() + module.name.to_str().unwrap() + MANGLER_SEPARATOR; - - for provide_expr in &module.provides { - // For whatever reason, the value coming into module.provides is an expression like: (provide expr...) - for provide in &provide_expr.list().unwrap().args[1..] { - match provide { - ExprKind::List(l) => { - if let Some(qualifier) = l.first_ident() { - if module.macro_map.contains_key(qualifier) { - continue; - } - - match *qualifier { - x if x == *REQUIRE_IDENT_SPEC => { - // Directly expand into define/contract, but with the value just being the hash get below - - // (bind/c contract name 'name) - - let name = l.args.get(1).unwrap(); - - if !explicit_requires.is_empty() - && !name - .atom_identifier() - .map(|x| explicit_requires.contains_key(x)) - .unwrap_or_default() - { - continue; - } - - if module - .macro_map - .contains_key(name.atom_identifier().unwrap()) - { - continue; - } - - let hash_get = expr_list![ - ExprKind::ident("%proto-hash-get%"), - ExprKind::atom( - "__module-".to_string() + &other_module_prefix - ), - ExprKind::Quote(Box::new(Quote::new( - name.clone(), - SyntaxObject::default(TokenType::Quote) - ))), - ]; - - let mut owned_name = name.clone(); - - // If we have the alias listed, we should use it - if !explicit_requires.is_empty() { - if let Some(alias) = explicit_requires - .get(name.atom_identifier().unwrap()) - .copied() - .flatten() - { - *owned_name.atom_identifier_mut().unwrap() = - alias.clone(); - } - } - - if let Some(prefix) = &require_object.prefix { - if let Some(existing) = owned_name.atom_identifier_mut() - { - let mut prefixed_identifier = prefix.clone(); - prefixed_identifier.push_str(existing.resolve()); - - // Update the existing identifier to point to a new one with the prefix applied - *existing = prefixed_identifier.into(); - } - } - - globals.insert(*name.atom_identifier().unwrap()); - - let define = ExprKind::Define(Box::new(Define::new( - owned_name, - hash_get, - SyntaxObject::default(TokenType::Define), - ))); - - provide_definitions.push(define); - } - - x if x == *CONTRACT_OUT => { - // Directly expand into define/contract, but with the value just being the hash get below - - // (bind/c contract name 'name) - - let mut name = l.args.get(1).unwrap().clone(); - let contract = l.args.get(2).unwrap(); - - if !explicit_requires.is_empty() - && !name - .atom_identifier() - .map(|x| explicit_requires.contains_key(x)) - .unwrap_or_default() - { - continue; - } - - // If we have the alias listed, we should use it - if !explicit_requires.is_empty() { - if let Some(alias) = explicit_requires - .get(name.atom_identifier().unwrap()) - .copied() - .flatten() - { - *name.atom_identifier_mut().unwrap() = - alias.clone(); - } - } - - if let Some(prefix) = &require_object.prefix { - if let Some(existing) = name.atom_identifier_mut() { - let mut prefixed_identifier = prefix.clone(); - prefixed_identifier.push_str(existing.resolve()); - - // Update the existing identifier to point to a new one with the prefix applied - *existing = prefixed_identifier.into(); - } - } - - // Since this is now bound to be in the scope of the current working module, we also want - // this to be mangled. In the event we do something like, qualify the import, then we might - // have to mangle this differently - globals.insert(*name.atom_identifier().unwrap()); - - let hash_get = expr_list![ - ExprKind::ident("%proto-hash-get%"), - ExprKind::atom( - "__module-".to_string() + &other_module_prefix - ), - ExprKind::Quote(Box::new(Quote::new( - name.clone(), - SyntaxObject::default(TokenType::Quote) - ))), - ]; - - let define = ExprKind::Define(Box::new(Define::new( - ExprKind::atom( - prefix.clone() - + name.atom_identifier().unwrap().resolve(), - ), - hash_get, - SyntaxObject::default(TokenType::Define), - ))); - - provide_definitions.push(define); - } - _ => { - stop!(TypeMismatch => format!("provide expects either an identifier, (for-syntax ), or (contract/out ...) - found: {}", provide)) - } - } - } else { - stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax )") - } - } - ExprKind::Atom(_) => { - if !explicit_requires.is_empty() - && !provide - .atom_identifier() - .map(|x| explicit_requires.contains_key(x)) - .unwrap_or_default() - { - continue; - } - - if module - .macro_map - .contains_key(provide.atom_identifier().unwrap()) - { - continue; - } - - // Mangle with a prefix if necessary - let mut provide = provide.clone(); - - // If we have the alias listed, we should use it - if !explicit_requires.is_empty() { - if let Some(alias) = explicit_requires - .get(provide.atom_identifier().unwrap()) - .copied() - .flatten() - { - *provide.atom_identifier_mut().unwrap() = alias.clone(); - } - } - - if let Some(prefix) = &require_object.prefix { - if let Some(existing) = provide.atom_identifier_mut() { - let mut prefixed_identifier = prefix.clone(); - prefixed_identifier.push_str(existing.resolve()); - - // Update the existing identifier to point to a new one with the prefix applied - *existing = prefixed_identifier.into(); - } - } - - let provide_ident = provide.atom_identifier().unwrap(); - - // Since this is now bound to be in the scope of the current working module, we also want - // this to be mangled. In the event we do something like, qualify the import, then we might - // have to mangle this differently - globals.insert(*provide_ident); - - let define = ExprKind::Define(Box::new(Define::new( - ExprKind::atom(prefix.clone() + provide_ident.resolve()), - expr_list![ - ExprKind::ident("%proto-hash-get%"), - ExprKind::atom("__module-".to_string() + &other_module_prefix), - ExprKind::Quote(Box::new(Quote::new( - provide.clone(), - SyntaxObject::default(TokenType::Quote) - ))) - ], - SyntaxObject::default(TokenType::Define), - ))); - - provide_definitions.push(define); - } - _ => { - stop!(TypeMismatch => "provide expression needs to either be a `contract/out` form or an identifier") - } - } - } - } - } - - // Mangle all of the variables that are either: - // 1. Defined locally in this file - // 2. Required by another file - let mut name_mangler = NameMangler::new(globals, prefix.clone()); - - // Afterwards, walk through and unmangle any quoted values, since these - // were intended to be used with non mangled values. - let mut name_unmangler = NameUnMangler::new(&prefix); - - name_mangler.mangle_vars(&mut exprs); - - // The provide definitions should also be mangled - name_mangler.mangle_vars(&mut provide_definitions); - - // let mut hash_builder = Vec::new(); - - // These are gonna be the pairs - // hash_builder.push(()); - - // Construct the series of provides as well, we'll want these to refer to the correct values - // - let mut provides: Vec<_> = self - .provides - .iter() - .flat_map(|x| &x.list().unwrap().args[1..]) - .cloned() - .map(|x| (x.clone(), x)) - .collect(); - - for provide in &mut provides { - match &provide.1 { - ExprKind::List(l) => { - if let Some(qualifier) = l.first_ident() { - match qualifier { - x if *x == *REQUIRE_IDENT_SPEC => { - // *provide = expand(l.get(2).unwrap().clone(), global_macro_map)?; - - // *provide = expand(l.) - - provide.0 = l.get(1).unwrap().clone(); - provide.1 = expand(l.get(2).unwrap().clone(), global_macro_map)?; - - continue; - - // name_unmangler.unmangle_expr(provide); - } - x if *x == *CONTRACT_OUT => { - // Update the item to point to just the name - // - // *provide = l.get(1).unwrap().clone(); - // { - // println!("---------"); - // println!("Provide expr: {}", l.to_string()); - // } - - provide.0 = l.get(1).unwrap().clone(); - provide.1 = expand( - expr_list![ - ExprKind::ident("bind/c"), - l.get(2).unwrap().clone(), - l.get(1).unwrap().clone(), - ExprKind::Quote(Box::new(Quote::new( - l.get(1).unwrap().clone(), - SyntaxObject::default(TokenType::Quote) - ))), - ], - global_macro_map, - )?; - - // println!("-------- {}", provide.to_pretty(60)); - - name_unmangler.unmangle_expr(&mut provide.1); - // continue; - } - _ => { - stop!(TypeMismatch => "bar provide expects either an identifier, (for-syntax ), or (contract/out ...)") - } - } - } else { - stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax )") - } - } - ExprKind::Atom(_) => { - continue; - } - _ => { - stop!(TypeMismatch => "provide expression needs to either be a `contract/out` form or an identifier") - } - } - } - - // Drop all of the macro references here - provides.retain(|x| !self.macro_map.contains_key(x.0.atom_identifier().unwrap())); - - // We want one without the mangled version, for the actual provides - let un_mangled = provides.clone(); - - let left_unmangled: Vec<_> = un_mangled.into_iter().map(|x| x.0).collect(); - - let mut right: Vec<_> = provides.into_iter().map(|x| x.1).collect(); - - name_mangler.mangle_vars(&mut right); - // name_unmangler.unmangle_vars(&mut provides); - - let mut hash_body = vec![ExprKind::ident("hash")]; - - // We can put the module name in there, but that doesn't help us get the docs out... - // Probably need to just store the docs directly in the module itself as well? - // hash_body.push(ExprKind::atom("#:module-name")); - // hash_body.push(ExprKind::atom(prefix.clone())); - - // left_unmangled.pretty_print(); - - hash_body.extend(interleave( - left_unmangled.into_iter().map(|x| { - if let ExprKind::Atom(_) = x { - ExprKind::Quote(Box::new(Quote::new( - x, - SyntaxObject::default(TokenType::Quote), - ))) - } else if let ExprKind::List(l) = x { - if let Some(qualifier) = l.first_ident() { - match qualifier { - x if *x == *REQUIRE_IDENT_SPEC => { - todo!() - } - _ => { - return ExprKind::Quote(Box::new(Quote::new( - l.get(2).unwrap().clone(), - SyntaxObject::default(TokenType::Quote), - ))) - } - } - } - - // Then this is a contract out, and we should handle it here - - ExprKind::Quote(Box::new(Quote::new( - l.get(2).unwrap().clone(), - SyntaxObject::default(TokenType::Quote), - ))) - // ExprKind::Quote(Box::new(Quote::new( - // x, - // SyntaxObject::default(TokenType::Quote), - // ))) - } else { - panic!("TODO this shouldn't be possible") - } - }), - right, - )); - - let module_define = ExprKind::Define(Box::new(Define::new( - ExprKind::atom("__module-".to_string() + &prefix), - ExprKind::List(List::new(hash_body)), - SyntaxObject::default(TokenType::Quote), - ))); - - // println!("------ {}", module_define.to_pretty(60)); - - exprs.push(module_define); - - // Construct the overall definition - // TODO: Perhaps mangle these as well, especially if they have contracts associated with them - provide_definitions.append(&mut exprs); - - // Try this out? - // let mut analysis = Analysis::from_exprs(&provide_definitions); - // let mut semantic = SemanticAnalysis::from_analysis(&mut provide_definitions, analysis); - - // // This is definitely broken still - // semantic.remove_unused_globals_with_prefix("mangler"); - // .replace_non_shadowed_globals_with_builtins() - // .remove_unused_globals_with_prefix("mangler"); - - // println!("------ {}", provide_definitions.to_pretty(60)); - - Ok(ExprKind::Begin(Begin::new( - provide_definitions, - SyntaxObject::default(TokenType::Begin), - ))) - } - - // Turn the module into the AST node that represents the macro module in the stdlib - fn _to_module_ast_node(&self) -> ExprKind { - let mut body = vec![ - ExprKind::Atom(Atom::new(SyntaxObject::default(TokenType::Identifier( - "module".into(), - )))), - ExprKind::Atom(Atom::new(SyntaxObject::default(TokenType::Identifier( - ("___".to_string() + self.name.to_str().unwrap()).into(), - )))), - ]; - - // Put any provides at the top - body.append(&mut self.provides.clone()); - - // Include any dependencies here - // body.append(&mut self.requires.clone()); - - // TODO: @Matt 10/8/22 - // Reconsider how to address this expansion. - // We really don't want to pollute the module space - perhaps disallow shadowed built-ins so we don't need this? - // That would probably be annoying - // let steel_base = ExprKind::List(List::new(vec![ExprKind::atom("steel/base".to_string())])); - - // self.ast.pretty_print(); - - // body.push(steel_base); - - // Put the ast nodes inside the macro - body.append(&mut self.ast.clone()); - - // TODO clean this up - let res = ExprKind::List(List::new(body)); - - // if log_enabled!(target: "requires", log::Level::Debug) { - // debug!(target: "requires", "Module ast node: {}", res.to_string()); - // } - - res - } -} - -#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] -enum MaybeRenamed { - Normal(ExprKind), - Renamed(ExprKind, ExprKind), -} - -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct RequireObject { - path: PathOrBuiltIn, - for_syntax: bool, - idents_to_import: Vec, - prefix: Option, -} - -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -enum PathOrBuiltIn { - BuiltIn(Cow<'static, str>), - Path(PathBuf), -} - -impl PathOrBuiltIn { - pub fn get_path(&self) -> Cow<'_, PathBuf> { - match self { - Self::Path(p) => Cow::Borrowed(p), - Self::BuiltIn(p) => Cow::Owned(PathBuf::from(p.to_string())), - } - } -} - -#[derive(Default, Debug, Clone)] -struct RequireObjectBuilder { - path: Option, - for_syntax: bool, - idents_to_import: Vec, - // Built up prefix - prefix: Option, -} - -impl RequireObjectBuilder { - fn build(self) -> Result { - let path = self - .path - .ok_or_else(crate::throw!(Generic => "require must have a path!"))?; - - Ok(RequireObject { - path, - for_syntax: self.for_syntax, - idents_to_import: self.idents_to_import, - prefix: self.prefix, - }) - } -} - -struct ModuleBuilder<'a> { - name: PathBuf, - main: bool, - source_ast: Vec, - macro_map: HashMap, - // TODO: Change the requires / requires_for_syntax to just be a require enum? - require_objects: Vec, - - provides: Vec, - provides_for_syntax: Vec, - compiled_modules: &'a mut HashMap, - visited: &'a mut HashSet, - file_metadata: &'a mut HashMap, - sources: &'a mut Sources, - kernel: &'a mut Option, - builtin_modules: ModuleContainer, - global_macro_map: &'a HashMap, - custom_builtins: &'a HashMap, -} - -impl<'a> ModuleBuilder<'a> { - #[allow(clippy::too_many_arguments)] - #[allow(unused)] - fn main( - name: Option, - source_ast: Vec, - compiled_modules: &'a mut HashMap, - visited: &'a mut HashSet, - file_metadata: &'a mut HashMap, - sources: &'a mut Sources, - kernel: &'a mut Option, - builtin_modules: ModuleContainer, - global_macro_map: &'a HashMap, - custom_builtins: &'a HashMap, - ) -> Result { - // TODO don't immediately canonicalize the path unless we _know_ its coming from a path - // change the path to not always be required - // if its not required we know its not coming in - - let name = if let Some(p) = name { - std::fs::canonicalize(p)? - } else { - std::env::current_dir()? - }; - - Ok(ModuleBuilder { - name, - main: true, - source_ast, - macro_map: DEFAULT_PRELUDE_MACROS.with(|x| x.borrow().clone()), - require_objects: Vec::new(), - provides: Vec::new(), - provides_for_syntax: Vec::new(), - compiled_modules, - visited, - file_metadata, - sources, - kernel, - builtin_modules, - global_macro_map, - custom_builtins, - }) - } - - fn compile(&mut self) -> Result> { - // debug!(target: "requires", "Visiting: {:?}", self.name); - - // @Matt - 10/3/23 - // This has a relatively fatal flaw at the moment: - /* - - (define-syntax register-plugin - (syntax-rules () - [(register-plugin plugin-path identifiers ...) - (begin - (require plugin-path - (only-in identifiers ...)) - (provide identifiers ...))])) - */ - // This will fail to compile - anything that expands into a require will fail since - // require is more or less a top level primitive. What we need to do is figure - // out a way to have this be recursive - the whole compilation step should - // go again once we discover that there is another require. It shouldn't be too bad, - // but the base case has to be figured out such that we don't get infinite recursion. - // I think the condition is probably something along the following: - // - // - Did we expand anything - // - Are there require statements - // - // If we expanded anything, we then should check for require statements... maybe - - let mut new_exprs = Vec::new(); - - // self.source_ast.pretty_print(); - - self.collect_requires()?; - self.collect_provides()?; - - if log_enabled!(log::Level::Info) { - // debug!(target: "requires", "Requires: {:#?}", self.require_objects); - // debug!(target: "requires", "Provides: {:#?}", self.provides); - // debug!(target: "requires", "Provides for-syntax: {:?}", self.provides_for_syntax); - } - - if self.visited.contains(&self.name) { - stop!(Generic => format!("circular dependency found during module resolution with: {:?}", self.name)) - } - - self.visited.insert(self.name.clone()); - - if self.main { - let exprs = std::mem::take(&mut self.source_ast); - self.source_ast = exprs - .into_iter() - .filter(|x| { - if let ExprKind::List(l) = x { - if let Some(provide) = l.first_ident() { - return *provide != *PROVIDE; - } - } - true - }) - .collect(); - } - - self.extract_macro_defs()?; - - // TODO include built ins here - if self.require_objects.is_empty() && !self.main { - // We're at a leaf, put into the cache - new_exprs.push(self.compile_module()?); - } else { - // TODO come back for parsing built ins - for module in self - .require_objects - .iter() - .filter(|x| matches!(x.path, PathOrBuiltIn::BuiltIn(_))) - .map(|x| x.path.get_path()) - { - // We've established nothing has changed with this file - // Check to see if its in the cache first - // Otherwise go ahead and compile - // If we already have compiled this module, get it from the cache - if let Some(_m) = self.compiled_modules.get(module.as_ref()) { - // debug!("Getting {:?} from the module cache", module); - // println!("Already found in the cache: {:?}", module); - // new_exprs.push(m.to_module_ast_node()); - // No need to do anything - continue; - } - - // TODO this is some bad crap here don't do this - let input = BUILT_INS - .iter() - .find(|x| x.0 == module.to_str().unwrap()) - .map(|x| x.1) - .or_else(|| { - self.custom_builtins - .get(module.to_str().unwrap()) - .map(|x| x.as_str()) - }) - .ok_or_else( - crate::throw!(Generic => "Unable to find builtin module: {:?}", module), - )?; - - let mut new_module = ModuleBuilder::new_built_in( - module.into_owned(), - input, - self.compiled_modules, - self.visited, - self.file_metadata, - self.sources, - self.kernel, - self.builtin_modules.clone(), - self.global_macro_map, - self.custom_builtins, - )?; - - // Walk the tree and compile any dependencies - // This will eventually put the module in the cache - let mut module_exprs = new_module.compile()?; - - new_exprs.append(&mut module_exprs); - - // Probably want to evaluate a module even if it has no provides? - if !new_module.provides.is_empty() { - new_exprs.push(new_module.compile_module()?); - } else { - // log::debug!(target: "requires", "Found no provides, skipping compilation of module: {:?}", new_module.name); - } - } - - // At this point, requires should be fully qualified (absolute) paths - - for module in self - .require_objects - .iter() - .filter(|x| matches!(x.path, PathOrBuiltIn::Path(_))) - .map(|x| x.path.get_path()) - { - let last_modified = std::fs::metadata(module.as_ref())?.modified()?; - - // Check if we should compile based on the last time modified - // If we're unable to get information, we want to compile - let should_recompile = - if let Some(cached_modified) = self.file_metadata.get(module.as_ref()) { - &last_modified != cached_modified - } else { - true - }; - - // We've established nothing has changed with this file - // Check to see if its in the cache first - // Otherwise go ahead and compile - if !should_recompile { - // If we already have compiled this module, get it from the cache - if let Some(_m) = self.compiled_modules.get(module.as_ref()) { - // debug!("Getting {:?} from the module cache", module); - // println!("Already found in the cache: {:?}", module); - // new_exprs.push(m.to_module_ast_node()); - // No need to do anything - continue; - } - } - - let mut new_module = ModuleBuilder::new_from_path( - module.into_owned(), - self.compiled_modules, - self.visited, - self.file_metadata, - self.sources, - self.kernel, - self.builtin_modules.clone(), - self.global_macro_map, - self.custom_builtins, - )?; - - // Walk the tree and compile any dependencies - // This will eventually put the module in the cache - let mut module_exprs = new_module.compile()?; - - // debug!("Inside {:?} - append {:?}", self.name, module); - // if log_enabled!(log::Level::Debug) { - // debug!( - // target: "modules", - // "appending with {:?}", - // module_exprs.iter().map(|x| x.to_string()).join(" SEP ") - // ); - // } - - new_exprs.append(&mut module_exprs); - - // TODO evaluate this - - // let mut ast = std::mem::replace(&mut new_module.source_ast, Vec::new()); - // ast.append(&mut module_exprs); - // new_module.source_ast = ast; - - // dbg!(&new_module.name); - // dbg!(&new_module.compiled_modules.contains_key(&new_module.name)); - - // If we need to, revisit because there are new provides - if !new_module.provides.is_empty() { - new_exprs.push(new_module.compile_module()?); - // If the module hasn't yet been compiled, compile it anyway - } else if !new_module.compiled_modules.contains_key(&new_module.name) { - // else if !new_module.compiled_modules.contains_key(&new_module.name) { - new_exprs.push(new_module.compile_module()?); - } else { - // log::debug!(target: "requires", "Found no provides, skipping compilation of module: {:?}", new_module.name); - // log::debug!(target: "requires", "Module already in the cache: {}", new_module.compiled_modules.contains_key(&new_module.name)); - // log::debug!(target: "requires", "Compiled modules: {:?}", new_module.compiled_modules.keys().collect::>()); - } - - // else { - // log::debug!(target: "requires", "Found no provides, skipping compilation of module: {:?}", new_module.name); - // } - } - } - - // new_exprs.pretty_print(); - - Ok(new_exprs) - } - - // TODO: This should run again on itself, probably - fn compile_module(&mut self) -> Result { - let mut ast = std::mem::take(&mut self.source_ast); - let mut provides = std::mem::take(&mut self.provides); - // Clone the requires... I suppose - let requires = self.require_objects.clone(); - - // info!( - // target: "requires", - // "Into compiled module: provides for syntax: {:?}", - // self.provides_for_syntax - // ); - - // Attempt extracting the syntax transformers from this module - if let Some(kernel) = self.kernel.as_mut() { - kernel.load_syntax_transformers(&mut ast, self.name.to_str().unwrap().to_string())? - }; - - // Expand first with the macros from *this* module - ast = ast - .into_iter() - .map(|x| { - expand(x, &self.macro_map) - .and_then(|x| { - expand_kernel_in_env( - x, - self.kernel.as_mut(), - self.builtin_modules.clone(), - // Expanding macros in the environment? - self.name.to_str().unwrap().to_string(), - ) - }) - // Check here - I think it makes sense to expand the - // internal macros again, given now we might have defmacro - // style macros that get expanded into syntax-rules ones? - .and_then(|x| expand(x, &self.macro_map)) - }) - .collect::>>()?; - - // Expand provides for any macros that exist within there - provides = provides - .into_iter() - .map(|x| { - expand(x, &self.macro_map).and_then(|x| { - // expand_kernel(x, self.kernel.as_mut(), self.builtin_modules.clone()) - expand_kernel_in_env( - x, - self.kernel.as_mut(), - self.builtin_modules.clone(), - // Expanding macros in the environment? - self.name.to_str().unwrap().to_string(), - ) - }) - }) - .collect::>>()?; - - let mut mangled_asts = Vec::new(); - - // Look for the modules in the requires for syntax - for require_object in self.require_objects.iter() - // .filter(|x| x.for_syntax) - { - let require_for_syntax = require_object.path.get_path(); - - let (module, in_scope_macros, mut name_mangler) = ModuleManager::find_in_scope_macros( - self.compiled_modules, - require_for_syntax.as_ref(), - &require_object, - &mut mangled_asts, - ); - - let kernel_macros_in_scope: HashSet<_> = - module.provides_for_syntax.iter().cloned().collect(); - - ast = ast - .into_iter() - .map(|x| { - // First expand the in scope macros - // These are macros - let mut expander = Expander::new(&in_scope_macros); - let mut first_round_expanded = expander.expand(x)?; - let mut changed = false; - - // (first_round_expanded, changed) = expand_kernel_in_env_with_allowed( - // first_round_expanded, - // self.kernel.as_mut(), - // // We don't need to expand those here - // ModuleContainer::default(), - // module.name.to_str().unwrap().to_string(), - // &kernel_macros_in_scope, - // )?; - - // If the kernel expander expanded into something - go ahead - // and expand all of the macros in this - // if changed || expander.changed { - // Expand here? - // first_round_expanded = expand(first_round_expanded, &module.macro_map)?; - - // Probably don't need this - // (first_round_expanded, changed) = expand_kernel_in_env_with_change( - // first_round_expanded, - // self.kernel.as_mut(), - // ModuleContainer::default(), - // module.name.to_str().unwrap().to_string(), - // )?; - - // name_mangler.visit(&mut first_round_expanded); - // } - - if expander.changed || changed { - expand(first_round_expanded, &module.macro_map) - } else { - Ok(first_round_expanded) - } - }) - .collect::>()?; - - provides = provides - .into_iter() - .map(|x| { - // First expand the in scope macros - // These are macros - let mut expander = Expander::new(&in_scope_macros); - let first_round_expanded = expander.expand(x)?; - - if expander.changed { - expand(first_round_expanded, &module.macro_map) - } else { - Ok(first_round_expanded) - } - }) - .collect::>()?; - } - - // let requires_before = self.require_objects.len(); - - // self.collect_requires()?; - - // if self.require_objects.len() > requires_before { - // println!("EXPANDED INTO A REQUIRE"); - // } - - // TODO: Check HERE for whether there are more requires than were previously found. - // If so, we should go back and compile the module again - - // TODO: @Matt - fix this hack - { - self.source_ast = ast; - self.provides = provides; - - self.collect_provides(); - - // let requires_before = self.require_objects.len(); - - // self.collect_requires()?; - - // if self.require_objects.len() > requires_before { - // println!("EXPANDED INTO A REQUIRE"); - // } - - provides = std::mem::take(&mut self.provides); - ast = std::mem::take(&mut self.source_ast); - } - - // Put the mangled asts at the top - // then include the ast there - mangled_asts.append(&mut ast); - - mangled_asts = mangled_asts - .into_iter() - .map(lower_entire_ast) - // We want this to at least be flattened for querying later - .map(|x| x.map(FlattenBegin::flatten)) - .collect::>()?; - - // Take ast, expand with self modules, then expand with each of the require for-syntaxes - // Then mangle the require-for-syntax, include the mangled directly in the ast - - // TODO: Come back here - we're going to need to figure out the require objects - let mut module = CompiledModule::new( - self.name.clone(), - provides, - requires, - self.provides_for_syntax - .iter() - .map(|x| *x.atom_identifier().unwrap()) - .collect(), - std::mem::take(&mut self.macro_map), - mangled_asts, - ); - - module.set_emitted(true); - - // println!( - // "-------------- Emitting module: {:?} ----------------------", - // self.name - // ); - - let mut result = - module.to_top_level_module(self.compiled_modules, self.global_macro_map)?; - - // println!("{}", result.to_pretty(60)); - - // println!("------------------ Finish ----------------------------------"); - - // let mut analysis = Analysis::from_exprs(&[result]); - - // let mut semantic = SemanticAnalysis::from_analysis(&mut result, analysis); - - // // This is definitely broken still - // semantic - // .remove_unused_globals_with_prefix("mangler"); - - // log::debug!(target: "requires", "Adding compiled module: {:?}", self.name); - - self.compiled_modules.insert(self.name.clone(), module); - - Ok(result) - } - - fn extract_macro_defs(&mut self) -> Result<()> { - let mut non_macros = Vec::new(); - let exprs = std::mem::take(&mut self.source_ast); - - for expr in exprs { - if let ExprKind::Macro(m) = expr { - let generated_macro = SteelMacro::parse_from_ast_macro(m)?; - let name = generated_macro.name(); - self.macro_map.insert(*name, generated_macro); - } else { - non_macros.push(expr) - } - } - self.source_ast = non_macros; - Ok(()) - } - - // Takes out the (for-syntax) forms from the provides - fn filter_out_for_syntax_provides(&mut self, exprs: Vec) -> Result> { - let mut normal_provides = Vec::new(); - - for expr in exprs { - match &expr { - ExprKind::Atom(_) => { - normal_provides.push(expr); - } - ExprKind::List(l) => { - if let Some(for_syntax) = l.first_ident() { - match *for_syntax { - x if x == *FOR_SYNTAX => { - if l.args.len() != 2 { - stop!(ArityMismatch => "provide expects a single identifier in the (for-syntax )") - } - - // Collect the for syntax expressions - // TODO -> remove this clone - self.provides_for_syntax.push(l.args[1].clone()); - } - x if x == *CONTRACT_OUT || x == *REQUIRE_IDENT_SPEC => { - normal_provides.push(expr); - } - _ => { - normal_provides.push(expr); - // stop!(TypeMismatch => "provide expects either an identifier, (for-syntax ), or (contract/out ...)") - } - } - } else { - stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax )") - } - } - _ => { - stop!(TypeMismatch => "provide expects either a (for-syntax ) or an ident") - } - } - } - - Ok(normal_provides) - } - - // TODO -> collect (provide (for-syntax ...)) - // I think these will already be collected for the macro, however I think for syntax should be found earlier - // Otherwise the macro expansion will not be able to understand it - fn collect_provides(&mut self) -> Result<()> { - let mut non_provides = Vec::new(); - let exprs = std::mem::take(&mut self.source_ast); - - fn walk( - module_builder: &mut ModuleBuilder, - exprs: Vec, - non_provides: &mut Vec, - ) -> Result<()> { - for mut expr in exprs { - match &mut expr { - ExprKind::List(l) => { - if let Some(provide) = l.first_ident() { - if *provide == *PROVIDE { - if l.len() == 1 { - stop!(Generic => "provide expects at least one identifier to provide"); - } - - // Swap out the value inside the list - let args = std::mem::take(&mut l.args); - - let filtered = - module_builder.filter_out_for_syntax_provides(args)?; - - l.args = filtered; - - module_builder.provides.push(expr); - - continue; - } - } - } - ExprKind::Begin(b) => { - let exprs = std::mem::take(&mut b.exprs); - walk(module_builder, exprs, non_provides)?; - } - _ => {} - } - - non_provides.push(expr); - } - - Ok(()) - } - - walk(self, exprs, &mut non_provides)?; - - self.source_ast = non_provides; - Ok(()) - } - - fn parse_require_object( - &mut self, - home: &Option, - r: &crate::parser::ast::Require, - atom: &ExprKind, - ) -> Result { - let mut object = RequireObjectBuilder::default(); - - self.parse_require_object_inner(home, r, atom, &mut object) - .and_then(|_| object.build()) - } - - // TODO: Recursively crunch the requires to gather up the necessary information - fn parse_require_object_inner( - &mut self, - home: &Option, - r: &crate::parser::ast::Require, - atom: &ExprKind, - require_object: &mut RequireObjectBuilder, - ) -> Result<()> { - match atom { - ExprKind::Atom(Atom { - syn: - SyntaxObject { - ty: TokenType::StringLiteral(s), - span, - .. - }, - }) => { - if require_object.path.is_some() { - stop!(Generic => "require object only expects one path!") - } - - // Try this? - if let Some(lib) = BUILT_INS.into_iter().cloned().find(|x| x.0 == s.as_str()) { - // self.built_ins.push(PathBuf::from(lib.0)); - - require_object.path = Some(PathOrBuiltIn::BuiltIn(lib.0.into())); - - return Ok(()); - // continue; - } - - if self.custom_builtins.contains_key(s) { - require_object.path = Some(PathOrBuiltIn::BuiltIn(s.clone().into())); - - return Ok(()); - } - - let mut current = self.name.clone(); - if current.is_file() { - current.pop(); - } - current.push(s); - - // // If the path exists on its own, we can continue - // // But theres the case where we're searching for a module on the STEEL_HOME - if !current.exists() { - if let Some(mut home) = home.clone() { - home.push(s); - current = home; - - log::info!("Searching STEEL_HOME for {:?}", current); - } else { - stop!(Generic => format!("Module not found: {:?} with STEEL_HOME: {:?}", current, home); *span) - } - } - - // Get the absolute path and store that - // self.requires.push(current) - - require_object.path = Some(PathOrBuiltIn::Path(current)); - } - - // TODO: Requires with qualifiers, that aren't just for-syntax - // Perhaps like: - // (with-prefix ) - ExprKind::List(l) => { - match l.first_ident() { - Some(x) if *x == *ONLY_IN => { - if l.args.len() < 2 { - stop!(BadSyntax => "only-in expects a require-spec and optionally a list of ids to bind (maybe renamed)"); - } - - self.parse_require_object_inner(home, r, &l.args[1], require_object)?; - - for remaining in &l.args[2..] { - match remaining { - ExprKind::Atom(_) => { - require_object - .idents_to_import - .push(MaybeRenamed::Normal(remaining.clone())); - } - ExprKind::List(l) => { - if l.len() != 2 { - stop!(BadSyntax => "Expected a pair when renaming required identifiers"); - } - - let from = &l.args[0]; - let to = &l.args[1]; - - if from.atom_identifier().is_none() - || to.atom_identifier().is_none() - { - stop!(BadSyntax => "only-in expected identifiers to rename"); - } - - // ( ) - require_object - .idents_to_import - .push(MaybeRenamed::Renamed(from.clone(), to.clone())); - } - _ => { - stop!(BadSyntax => "unexpected syntax in only-in form during module requires") - } - } - } - } - - Some(x) if *x == *PREFIX_IN => { - if l.args.len() != 3 { - stop!(BadSyntax => "prefix-in expects a prefix to prefix a given file or module"; r.location.span; r.location.source.clone()); - } - - if let Some(prefix) = l.args[1].atom_identifier() { - match &mut require_object.prefix { - Some(existing_prefix) => { - // Append the new symbol to the existing prefix - existing_prefix.push_str(prefix.resolve()); - } - None => { - require_object.prefix = Some(prefix.resolve().to_string()); - } - } - - self.parse_require_object_inner(home, r, &l.args[2], require_object)?; - } else { - stop!(TypeMismatch => "prefix-in expects an identifier to use for the prefix"); - } - } - - Some(x) if *x == *FOR_SYNTAX => { - // We're expecting something like (for-syntax "foo") - if l.args.len() != 2 { - stop!(BadSyntax => "for-syntax expects one string literal referring to a file or module"; r.location.span; r.location.source.clone()); - } - - if let Some(path) = l.args[1].string_literal() { - if let Some(lib) = BUILT_INS.iter().find(|x| x.0 == path) { - // self.built_ins.push(PathBuf::from(lib.0)); - - require_object.path = Some(PathOrBuiltIn::BuiltIn(lib.0.into())); - require_object.for_syntax = true; - - return Ok(()); - // continue; - } else if self.custom_builtins.contains_key(path) { - require_object.path = - Some(PathOrBuiltIn::BuiltIn(Cow::Owned(path.to_string()))); - require_object.for_syntax = true; - - return Ok(()); - } else { - let mut current = self.name.clone(); - if current.is_file() { - current.pop(); - } - current.push(path); - - if !current.exists() { - if let Some(mut home) = home.clone() { - home.push(path); - current = home; - - log::info!("Searching STEEL_HOME for {:?}", current); - } else { - stop!(Generic => format!("Module not found: {:?}", current); r.location.span) - } - } - - require_object.for_syntax = true; - require_object.path = Some(PathOrBuiltIn::Path(current)); - } - } else { - stop!(BadSyntax => "for-syntax expects a string literal referring to a file or module"; r.location.span; r.location.source.clone()); - } - } - _ => { - stop!(BadSyntax => "require accepts either a string literal or a for-syntax expression"; r.location.span; r.location.source.clone()) - } - } - } - - _ => { - stop!(Generic => "require expected a string literal referring to a file/module"; r.location.span; r.location.source.clone()) - } - } - - Ok(()) - } - - fn collect_requires(&mut self) -> Result<()> { - // unimplemented!() - - let mut exprs_without_requires = Vec::new(); - let exprs = std::mem::take(&mut self.source_ast); - - let home = std::env::var("STEEL_HOME") - .map(PathBuf::from) - .map(|mut x| { - x.push("cogs"); - x - }) - .ok(); - - fn walk( - module_builder: &mut ModuleBuilder, - home: &Option, - exprs_without_requires: &mut Vec, - exprs: Vec, - ) -> Result<()> { - for expr in exprs { - match expr { - // Include require/for-syntax here - // This way we have some understanding of what dependencies a file has - ExprKind::Require(r) => { - for atom in &r.modules { - // TODO: Consider making this not a reference for r - let require_object = - module_builder.parse_require_object(&home, &r, atom)?; - - module_builder.require_objects.push(require_object); - } - } - ExprKind::Begin(b) => { - walk(module_builder, home, exprs_without_requires, b.exprs)? - } - _ => exprs_without_requires.push(expr), - } - } - - Ok(()) - } - - walk(self, &home, &mut exprs_without_requires, exprs)?; - - self.source_ast = exprs_without_requires; - Ok(()) - } - - #[allow(clippy::too_many_arguments)] - fn new_built_in( - name: PathBuf, - input: &str, - compiled_modules: &'a mut HashMap, - visited: &'a mut HashSet, - file_metadata: &'a mut HashMap, - sources: &'a mut Sources, - kernel: &'a mut Option, - builtin_modules: ModuleContainer, - global_macro_map: &'a HashMap, - custom_builtins: &'a HashMap, - ) -> Result { - ModuleBuilder::raw( - name, - compiled_modules, - visited, - file_metadata, - sources, - kernel, - builtin_modules, - global_macro_map, - custom_builtins, - ) - .parse_builtin(input) - } - - fn new_from_path( - name: PathBuf, - compiled_modules: &'a mut HashMap, - visited: &'a mut HashSet, - file_metadata: &'a mut HashMap, - sources: &'a mut Sources, - kernel: &'a mut Option, - builtin_modules: ModuleContainer, - global_macro_map: &'a HashMap, - custom_builtins: &'a HashMap, - ) -> Result { - ModuleBuilder::raw( - name, - compiled_modules, - visited, - file_metadata, - sources, - kernel, - builtin_modules, - global_macro_map, - custom_builtins, - ) - .parse_from_path() - } - - fn raw( - name: PathBuf, - compiled_modules: &'a mut HashMap, - visited: &'a mut HashSet, - file_metadata: &'a mut HashMap, - sources: &'a mut Sources, - kernel: &'a mut Option, - builtin_modules: ModuleContainer, - global_macro_map: &'a HashMap, - custom_builtins: &'a HashMap, - ) -> Self { - ModuleBuilder { - name, - main: false, - source_ast: Vec::new(), - // TODO: This used to be empty - macro_map: DEFAULT_PRELUDE_MACROS.with(|x| x.borrow().clone()), - // macro_map: global_macro_map.clone(), - require_objects: Vec::new(), - provides: Vec::new(), - provides_for_syntax: Vec::new(), - compiled_modules, - visited, - file_metadata, - sources, - kernel, - builtin_modules, - global_macro_map, - custom_builtins, - } - } - - fn parse_builtin(mut self, input: &str) -> Result { - let parsed = Parser::new_from_source(input, self.name.clone(), None) - .without_lowering() - .map(|x| x.and_then(lower_macro_and_require_definitions)) - .collect::, ParseError>>()?; - - self.source_ast = parsed; - - // self.source_ast.pretty_print(); - - Ok(self) - } - - fn parse_from_path(mut self) -> Result { - log::info!("Opening: {:?}", self.name); - - let mut file = std::fs::File::open(&self.name).map_err(|err| { - let mut err = crate::SteelErr::from(err); - err.prepend_message(&format!("Attempting to load module from: {:?}", self.name)); - err - })?; - self.file_metadata - .insert(self.name.clone(), file.metadata()?.modified()?); - - // TODO: DEFAULT MODULE LOADER PREFIX - let mut exprs = String::new(); - - // TODO: Don't do this - get the source from the cache? - // let mut exprs = PRELUDE_STRING.to_string(); - - let mut expressions = Parser::new(&PRELUDE_STRING, None) - .without_lowering() - .map(|x| x.and_then(lower_macro_and_require_definitions)) - .collect::, ParseError>>()?; - - // let expressions = Parser::new_from_source(, , ) - - // Add the modules here: - - // exprs.push_str(ALL_MODULES); - - file.read_to_string(&mut exprs)?; - - let id = self.sources.add_source(exprs, Some(self.name.clone())); - - { - // Fetch the exprs after adding them to the sources - // We did _just_ add it, so its fine to unwrap - let guard = self.sources.sources.lock().unwrap(); - - let exprs = guard.get(id).unwrap(); - - let mut parsed = Parser::new_from_source(&exprs, self.name.clone(), Some(id)) - .without_lowering() - .map(|x| x.and_then(lower_macro_and_require_definitions)) - .collect::, ParseError>>()?; - - expressions.append(&mut parsed); - - self.source_ast = expressions; - } - - Ok(self) - } -} diff --git a/crates/steel-core/src/compiler/passes/analysis.rs b/crates/steel-core/src/compiler/passes/analysis.rs deleted file mode 100644 index ff9a9a416..000000000 --- a/crates/steel-core/src/compiler/passes/analysis.rs +++ /dev/null @@ -1,5584 +0,0 @@ -use std::{ - collections::{hash_map, HashMap, HashSet}, - hash::BuildHasherDefault, -}; - -use im_rc::HashMap as ImmutableHashMap; -use quickscope::ScopeMap; -use steel_parser::{ast::PROTO_HASH_GET, parser::SourceId}; - -use crate::{ - compiler::{ - map::SymbolMap, - modules::{ModuleManager, MANGLER_SEPARATOR}, - }, - parser::{ - ast::{ - Atom, Define, ExprKind, LambdaFunction, Let, List, Quote, STANDARD_MODULE_GET, - UNREADABLE_MODULE_GET, - }, - expander::SteelMacro, - interner::InternedString, - parser::{RawSyntaxObject, SyntaxObject, SyntaxObjectId}, - span::Span, - span_visitor::get_span, - tokens::TokenType, - }, - steel_vm::primitives::MODULE_IDENTIFIERS, - stop, throw, SteelErr, SteelVal, -}; - -use super::{VisitorMutControlFlow, VisitorMutRefUnit, VisitorMutUnitRef}; - -use fxhash::{FxHashMap, FxHashSet, FxHasher}; - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum IdentifierStatus { - Global, - Local, - LocallyDefinedFunction, - LetVar, - Captured, - Free, - HeapAllocated, -} - -// TODO: Make these not just plain public variables -#[derive(Debug, Clone)] -pub struct SemanticInformation { - pub kind: IdentifierStatus, - pub set_bang: bool, - pub depth: usize, - pub shadows: Option, - pub usage_count: usize, - pub span: Span, - // Referring to a local var definition - pub refers_to: Option, - // If this is a top level define, what does this alias to? - pub aliases_to: Option, - pub builtin: bool, - pub last_usage: bool, - pub stack_offset: Option, - pub escapes: bool, - // TODO: Move a bunch of these individual things into their own structs - // something like Option - pub capture_index: Option, - pub read_capture_offset: Option, - pub captured_from_enclosing: bool, - pub heap_offset: Option, - pub read_heap_offset: Option, - pub is_shadowed: bool, - pub is_required_identifier: bool, -} - -#[test] -fn check_size_of_info() { - println!("{}", std::mem::size_of::()); -} - -impl SemanticInformation { - pub fn new(kind: IdentifierStatus, depth: usize, span: Span) -> Self { - Self { - kind, - set_bang: false, - depth, - shadows: None, - usage_count: 0, - span, - refers_to: None, - aliases_to: None, - builtin: false, - last_usage: false, - stack_offset: None, - escapes: false, - capture_index: None, - read_capture_offset: None, - captured_from_enclosing: false, - heap_offset: None, - read_heap_offset: None, - is_shadowed: false, - is_required_identifier: false, - } - } - - pub fn shadows(mut self, id: SyntaxObjectId) -> Self { - self.shadows = Some(id); - self - } - - pub fn with_usage_count(mut self, count: usize) -> Self { - self.usage_count = count; - self - } - - pub fn refers_to(mut self, id: SyntaxObjectId) -> Self { - self.refers_to = Some(id); - self - } - - pub fn aliases_to(mut self, id: SyntaxObjectId) -> Self { - self.aliases_to = Some(id); - self - } - - pub fn mark_builtin(&mut self) { - self.builtin = true; - } - - pub fn mark_required(&mut self) { - self.is_required_identifier = true; - } - - pub fn with_offset(mut self, offset: usize) -> Self { - self.stack_offset = Some(offset); - self - } - - pub fn mark_escapes(&mut self) { - self.escapes = true; - } - - pub fn with_capture_index(mut self, offset: usize) -> Self { - self.capture_index = Some(offset); - self - } - - pub fn with_read_capture_offset(mut self, offset: usize) -> Self { - self.read_capture_offset = Some(offset); - self - } - - pub fn with_heap_offset(mut self, offset: usize) -> Self { - self.heap_offset = Some(offset); - self - } - - pub fn with_read_heap_offset(mut self, offset: usize) -> Self { - self.read_heap_offset = Some(offset); - self - } - - pub fn with_captured_from_enclosing(&mut self, captured_from_enclosing: bool) { - self.captured_from_enclosing = captured_from_enclosing; - } -} - -#[derive(Debug, Clone)] -pub struct FunctionInformation { - // Just a mapping of the vars to their scope info - holds which vars are being - // captured by this function - captured_vars: FxHashMap, - arguments: FxHashMap, - // Keeps a mapping of vars to their scope info, if the variable was mutated - // if this variable was mutated and inevitably captured, we want to know - // mutated_vars: HashMap, - // If this function is defined in the tail position / and or the alias to this function escapes, - // then this should be marked as true - pub escapes: bool, - // If this function is bound to a variable, this is the id of that bound value - pub aliases_to: Option, - - // Depth the function definition occurs at - pub depth: usize, -} - -impl FunctionInformation { - pub fn new( - captured_vars: FxHashMap, - arguments: FxHashMap, - ) -> Self { - Self { - captured_vars, - arguments, - escapes: false, - aliases_to: None, - depth: 0, - } - } - - pub fn captured_vars(&self) -> &FxHashMap { - &self.captured_vars - } - - pub fn arguments(&self) -> &FxHashMap { - &self.arguments - } - - pub fn escapes(mut self, escapes: bool) -> Self { - self.escapes = escapes; - self - } - - pub fn depth(mut self, depth: usize) -> Self { - self.depth = depth; - self - } -} - -#[derive(Debug, PartialEq, Clone)] -pub enum CallKind { - Normal, - TailCall, - SelfTailCall(usize), -} - -#[derive(Debug, Clone)] -pub struct CallSiteInformation { - pub kind: CallKind, - pub span: Span, -} - -impl CallSiteInformation { - pub fn new(kind: CallKind, span: Span) -> Self { - Self { kind, span } - } -} - -#[derive(Debug, Clone)] -pub struct LetInformation { - pub stack_offset: usize, - pub function_context: Option, - pub arguments: FxHashMap, -} - -impl LetInformation { - pub fn new( - stack_offset: usize, - function_context: Option, - arguments: FxHashMap, - ) -> Self { - Self { - stack_offset, - function_context, - arguments, - } - } -} - -#[derive(Debug, Clone)] -pub enum SemanticInformationType { - Variable(SemanticInformation), - Function(FunctionInformation), - CallSite(CallSiteInformation), - Let(LetInformation), -} - -// Populate the metadata about individual -#[derive(Default, Debug, Clone)] -pub struct Analysis { - // TODO: make these be specific IDs for semantic id, function id, and call info id - pub(crate) info: FxHashMap, - pub(crate) function_info: FxHashMap, - pub(crate) call_info: FxHashMap, - pub(crate) let_info: FxHashMap, -} - -impl Analysis { - // Reuse the analysis allocation through the process! - pub fn clear(&mut self) { - self.info.clear(); - self.function_info.clear(); - self.call_info.clear(); - self.let_info.clear(); - } - - pub fn identifier_info(&self) -> &FxHashMap { - &self.info - } - - pub fn fresh_from_exprs(&mut self, exprs: &[ExprKind]) { - self.clear(); - self.run(exprs); - } - - pub fn from_exprs(exprs: &[ExprKind]) -> Self { - let mut analysis = Analysis::default(); - analysis.run(exprs); - analysis - } - - pub fn populate_captures(&mut self, exprs: &[ExprKind]) { - // Resolve all mutated and captured vars so that they're mutated after they've been captured - let mutated_and_captured_vars = self - .function_info - .values() - .flat_map(|x| x.captured_vars.values()) - .chain(self.let_info.values().flat_map(|x| x.arguments.values())) - .filter(|x| x.captured && x.mutated) - .map(|x| (x.id, x.clone())) - .collect::>(); - - self.function_info - .values_mut() - .flat_map(|x| x.captured_vars.values_mut()) - .for_each(|x| { - if mutated_and_captured_vars.get(&x.id).is_some() { - x.mutated = true; - x.captured = true; - } - }); - - self.function_info - .values_mut() - .flat_map(|x| x.arguments.values_mut()) - .for_each(|x| { - if mutated_and_captured_vars.get(&x.id).is_some() { - x.mutated = true; - x.captured = true; - } - }); - - self.run(exprs); - } - - pub fn resolve_alias(&self, mut id: SyntaxObjectId) -> Option { - while let Some(next) = self - .info - .get(&id) - .and_then(|x| x.aliases_to) - .and_then(|x| self.info.get(&x)) - .and_then(|x| x.refers_to) - { - id = next; - } - - Some(id) - } - - pub fn visit_top_level_define_function_without_body( - &mut self, - scope: &mut ScopeMap, - define: &crate::parser::ast::Define, - ) { - let name = define.name.atom_identifier().unwrap(); - - let mut semantic_info = SemanticInformation::new( - IdentifierStatus::Global, - 1, - define.name.atom_syntax_object().unwrap().span, - ); - - if is_a_builtin_definition(define) { - semantic_info.mark_builtin(); - } - - if is_a_require_definition(define) { - semantic_info.mark_required(); - } - - // If this variable name is already in scope, we should mark that this variable - // shadows the previous id - if let Some(shadowed_var) = scope.get(name) { - semantic_info = semantic_info.shadows(shadowed_var.id) - } - - // log::trace!("Defining global: {:?}", define.name); - // println!("Defining global: {}", define.name); - define_var(scope, define); - - self.insert(define.name.atom_syntax_object().unwrap(), semantic_info); - } - - // TODO: This needs to just take an iterator? - pub fn run(&mut self, exprs: &[ExprKind]) { - let mut scope: ScopeMap = ScopeMap::new(); - - // TODO: Functions should be globally resolvable but top level identifiers cannot be used before they are defined - // The way this is implemented right now doesn't respect that - for expr in exprs.iter() { - if let ExprKind::Define(define) = expr { - if define.body.lambda_function().is_some() { - self.visit_top_level_define_function_without_body(&mut scope, define); - } - } - - if let ExprKind::Begin(b) = expr { - for expr in &b.exprs { - if let ExprKind::Define(define) = expr { - if define.body.lambda_function().is_some() { - self.visit_top_level_define_function_without_body(&mut scope, define); - } - } - } - } - } - - for expr in exprs { - let mut pass = AnalysisPass::new(self, &mut scope); - - match expr { - ExprKind::Define(define) => { - if define.body.lambda_function().is_some() { - // Since we're at the top level, care should be taken to actually - // refer to the defining context correctly - pass.defining_context = define.name_id(); - pass.defining_context_depth = 0; - // Continue with the rest of the body here - pass.visit(&define.body); - pass.defining_context = None; - } else { - pass.visit_top_level_define_value_without_body(define); - pass.visit(&define.body); - } - } - ExprKind::Begin(b) => { - for expr in &b.exprs { - if let ExprKind::Define(define) = expr { - if define.body.lambda_function().is_some() { - // Since we're at the top level, care should be taken to actually - // refer to the defining context correctly - pass.defining_context = define.name_id(); - pass.defining_context_depth = 0; - // Continue with the rest of the body here - pass.visit(&define.body); - pass.defining_context = None; - } else { - pass.visit_top_level_define_value_without_body(define); - pass.visit(&define.body); - } - } else { - pass.visit(expr); - } - } - } - _ => { - pass.visit(expr); - } - } - } - } - - pub fn get_function_info(&self, function: &LambdaFunction) -> Option<&FunctionInformation> { - self.function_info.get(&function.syntax_object_id) - } - - pub fn insert(&mut self, object: &SyntaxObject, metadata: SemanticInformation) { - self.info.insert(object.syntax_object_id, metadata); - } - - pub fn update_with(&mut self, object: &SyntaxObject, metadata: SemanticInformation) { - let existing = self.info.get_mut(&object.syntax_object_id).unwrap(); - existing.kind = metadata.kind; - existing.set_bang = existing.set_bang || metadata.set_bang; - existing.shadows = metadata.shadows; - existing.depth = metadata.depth; - existing.usage_count = metadata.usage_count; - existing.aliases_to = metadata.aliases_to; - existing.refers_to = metadata.refers_to; - existing.builtin = metadata.builtin; - existing.captured_from_enclosing = metadata.captured_from_enclosing; - existing.heap_offset = metadata.heap_offset; - existing.read_heap_offset = metadata.read_heap_offset; - existing.is_required_identifier = metadata.is_required_identifier; - } - - pub fn get(&self, object: &SyntaxObject) -> Option<&SemanticInformation> { - self.info.get(&object.syntax_object_id) - } - - pub fn get_mut(&mut self, id: &SyntaxObjectId) -> Option<&mut SemanticInformation> { - self.info.get_mut(id) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ScopeInfo { - /// The ID of the variable, this is globally unique - pub id: SyntaxObjectId, - /// Whether or not this variable is captured by a scope - /// TODO: This needs to actually just mark the depth at which variable was captured, or something to signify - /// if a specific scope actually _uses_ the variable - pub captured: bool, - /// How many times has this variable been referenced - pub usage_count: usize, - /// Last touched by this ID - pub last_used: Option, - /// Represents the position on the stack that this variable - /// should live at during the execution of the program - pub stack_offset: Option, - /// Does this variable escape its scope? As in, does the value outlive the scope - /// that it was defined in - pub escapes: bool, - /// If this is a captured var, the capture index - pub capture_offset: Option, - /// Was this var captured from the stack or from an enclosing function - pub captured_from_enclosing: bool, - /// Was this var mutated? - pub mutated: bool, - /// Heap offset - pub heap_offset: Option, - pub read_capture_offset: Option, - pub read_heap_offset: Option, - pub parent_heap_offset: Option, - pub local_heap_offset: Option, -} - -impl ScopeInfo { - pub fn new(id: SyntaxObjectId) -> Self { - Self { - id, - captured: false, - usage_count: 0, - last_used: None, - stack_offset: None, - escapes: false, - capture_offset: None, - captured_from_enclosing: false, - mutated: false, - heap_offset: None, - read_capture_offset: None, - read_heap_offset: None, - parent_heap_offset: None, - local_heap_offset: None, - } - } - - pub fn new_local(id: SyntaxObjectId, offset: usize) -> Self { - Self { - id, - captured: false, - usage_count: 0, - last_used: None, - stack_offset: Some(offset), - escapes: false, - capture_offset: None, - captured_from_enclosing: false, - mutated: false, - heap_offset: None, - read_capture_offset: None, - read_heap_offset: None, - parent_heap_offset: None, - local_heap_offset: None, - } - } - - pub fn new_heap_allocated_var( - id: SyntaxObjectId, - stack_offset: usize, - heap_offset: usize, - ) -> Self { - Self { - id, - captured: true, - usage_count: 0, - last_used: None, - stack_offset: Some(stack_offset), - escapes: false, - capture_offset: None, - captured_from_enclosing: false, - mutated: true, - heap_offset: Some(heap_offset), - read_capture_offset: None, - read_heap_offset: Some(heap_offset), - parent_heap_offset: None, - local_heap_offset: None, - } - } -} - -struct AnalysisPass<'a> { - info: &'a mut Analysis, - scope: &'a mut ScopeMap, - captures: ScopeMap, - tail_call_eligible: bool, - escape_analysis: bool, - defining_context: Option, - // TODO: This should give us the depth (how many things we need to roll back) - defining_context_depth: usize, - stack_offset: usize, - function_context: Option, - contains_lambda_func: bool, - vars_used: im_rc::HashSet, - total_vars_used: im_rc::HashSet, - ids_referenced_in_tail_position: HashSet, -} - -fn define_var( - scope: &mut ScopeMap, - define: &crate::parser::ast::Define, -) { - scope.define( - *define.name.atom_identifier().unwrap(), - ScopeInfo::new(define.name.atom_syntax_object().unwrap().syntax_object_id), - ); -} - -impl<'a> AnalysisPass<'a> { - pub fn new(info: &'a mut Analysis, scope: &'a mut ScopeMap) -> Self { - AnalysisPass { - info, - scope, - captures: ScopeMap::default(), - tail_call_eligible: false, - escape_analysis: false, - defining_context: None, - defining_context_depth: 0, - stack_offset: 0, - function_context: None, - contains_lambda_func: false, - vars_used: im_rc::HashSet::new(), - total_vars_used: im_rc::HashSet::new(), - ids_referenced_in_tail_position: HashSet::new(), - } - } -} - -impl<'a> AnalysisPass<'a> { - // TODO: This needs to be fixed with interning - fn _get_possible_captures( - &self, - let_level_bindings: &[&InternedString], - ) -> HashSet { - self.scope - .iter() - .filter(|x| !x.1.captured) - .filter(|x| !let_level_bindings.contains(&x.0)) - .map(|x| x.0.clone()) - .collect() - } - - fn get_captured_vars( - &self, - let_level_bindings: &[&InternedString], - ) -> FxHashMap { - self.scope - .iter() - .filter(|x| x.1.captured) - .filter(|x| !let_level_bindings.contains(&x.0)) - .map(|x| (x.0.clone(), x.1.clone())) - .collect() - } - - fn visit_top_level_define_value_without_body(&mut self, define: &crate::parser::ast::Define) { - let name = define.name.atom_identifier().unwrap(); - - let name_syntax_object = define.name.atom_syntax_object().unwrap(); - - let mut semantic_info = - SemanticInformation::new(IdentifierStatus::Global, 1, name_syntax_object.span); - - // If this variable name is already in scope, we should mark that this variable - // shadows the previous id - if let Some(shadowed_var) = self.scope.get(name) { - semantic_info = semantic_info.shadows(shadowed_var.id); - - if let Some(existing_analysis) = self.info.info.get_mut(&shadowed_var.id) { - if existing_analysis.builtin { - existing_analysis.is_shadowed = true; - } - } - } - - if is_a_builtin_definition(define) { - semantic_info.mark_builtin(); - } - - // println!("Defining global: {}", define.name); - - if is_a_require_definition(define) { - semantic_info.mark_required(); - } - - if let Some(aliases) = define.is_an_alias_definition() { - semantic_info = semantic_info.aliases_to(aliases); - } - - // println!("Defining global: {}", define.name); - define_var(self.scope, define); - - self.info.insert(name_syntax_object, semantic_info); - } - - // TODO: I really hate this identifier status if local nonsense - fn visit_define_without_body( - &mut self, - define: &crate::parser::ast::Define, - identifier_status_if_local: IdentifierStatus, - ) { - let name = define.name.atom_identifier().unwrap(); - - let mut semantic_info = SemanticInformation::new( - if self.scope.depth() == 1 { - IdentifierStatus::Global - } else { - identifier_status_if_local - }, - self.scope.depth(), - define.name.atom_syntax_object().unwrap().span, - ); - - if is_a_builtin_definition(define) { - semantic_info.mark_builtin(); - } - - if is_a_require_definition(define) { - semantic_info.mark_required(); - } - - // If this variable name is already in scope, we should mark that this variable - // shadows the previous id - if let Some(shadowed_var) = self.scope.get(name) { - // log::debug!("Redefining previous variable: {:?}", name); - semantic_info = semantic_info.shadows(shadowed_var.id); - } - - define_var(self.scope, define); - - self.info - .insert(define.name.atom_syntax_object().unwrap(), semantic_info); - } - - // Visit the function arguments, marking these as defining in our scope - // and also defaulting them to be local identifiers. This way, in the event of a set! - // we have something to refer to - fn visit_func_args(&mut self, lambda_function: &LambdaFunction, depth: usize) { - let alloc_capture_count = self - .info - .function_info - .get(&lambda_function.syntax_object_id) - .map(|x| { - x.captured_vars() - .values() - .filter(|x| x.captured && x.mutated) - .count() - }); - - let mut mut_var_offset = 0; - - for (index, arg) in lambda_function.args.iter().enumerate() { - let name = arg.atom_identifier().unwrap(); - let id = arg.atom_syntax_object().unwrap().syntax_object_id; - - // TODO: Don't need to do these repeated hash lookups over and over - // can coalesce this into one at the top of the args - let heap_alloc = if let Some(info) = self - .info - .function_info - .get(&lambda_function.syntax_object_id) - { - if let Some(info) = info.arguments.get(name) { - // println!("Found information: {:#?}", info); - info.mutated && info.captured - } else { - false - } - } else { - false - }; - - // TODO: clean this up like a lot - if heap_alloc { - self.scope.define( - *name, - ScopeInfo::new_heap_allocated_var( - id, - index, - mut_var_offset + alloc_capture_count.unwrap(), - ), - ); - - // Throw in a dummy info so that no matter what, we have something to refer to - // in the event of a set! - // Later on in this function this gets updated accordingly - self.info.insert( - arg.atom_syntax_object().unwrap(), - SemanticInformation::new( - IdentifierStatus::HeapAllocated, - depth, - arg.atom_syntax_object().unwrap().span, - ), - ); - - mut_var_offset += 1; - } else { - self.scope.define(*name, ScopeInfo::new_local(id, index)); - - // Throw in a dummy info so that no matter what, we have something to refer to - // in the event of a set! - // Later on in this function this gets updated accordingly - self.info.insert( - arg.atom_syntax_object().unwrap(), - SemanticInformation::new( - IdentifierStatus::Local, - depth, - arg.atom_syntax_object().unwrap().span, - ), - ); - } - } - } - - fn pop_top_layer(&mut self) -> FxHashMap { - let arguments = self - .scope - .iter_top() - .map(|x| (x.0.clone(), x.1.clone())) - .collect::>(); - - self.scope.pop_layer(); - - arguments - } - - fn find_and_mark_captured_arguments( - &mut self, - lambda_function: &LambdaFunction, - captured_vars: &FxHashMap, - depth: usize, - arguments: &FxHashMap, - ) { - for var in &lambda_function.args { - let ident = var.atom_identifier().unwrap(); - - let kind = if let Some(info) = captured_vars.get(ident) { - if info.mutated { - IdentifierStatus::HeapAllocated - } else { - IdentifierStatus::Captured - } - } else if let Some(info) = self.info.get(var.atom_syntax_object().unwrap()) { - match info.kind { - IdentifierStatus::HeapAllocated => IdentifierStatus::HeapAllocated, - _ => IdentifierStatus::Local, - } - } else { - IdentifierStatus::Local - }; - - let mut semantic_info = - SemanticInformation::new(kind, depth, var.atom_syntax_object().unwrap().span); - - // Update the usage count to collect how many times the variable was referenced - // Inside of the scope in which the variable existed - // TODO: merge this into one - let count = arguments.get(ident).unwrap().usage_count; - - // if count == 0 { - // log::debug!("Found unused argument: {:?}", ident); - // } - - semantic_info = semantic_info.with_usage_count(count); - - // If this variable name is already in scope, we should mark that this variable - // shadows the previous id - if let Some(shadowed_var) = self.scope.get(ident) { - semantic_info = semantic_info.shadows(shadowed_var.id) - } - - self.info - .update_with(var.atom_syntax_object().unwrap(), semantic_info); - } - } - - fn visit_with_tail_call_eligibility(&mut self, expr: &'a ExprKind, state: bool) { - let eligibility = self.tail_call_eligible; - self.tail_call_eligible = state; - self.visit(expr); - self.tail_call_eligible = eligibility; - } -} - -impl<'a> VisitorMutUnitRef<'a> for AnalysisPass<'a> { - // TODO: define expressions are not handled by this for stack offset purposes - fn visit_define(&mut self, define: &'a crate::parser::ast::Define) { - self.visit_define_without_body(define, IdentifierStatus::Local); - - let define_ctx = self.defining_context.take(); - - // Mark defining context - self.defining_context = define.name_id(); - self.visit_with_tail_call_eligibility(&define.body, false); - self.defining_context = define_ctx; - } - - // Quoted values are just constants - lets ignore them for now? - fn visit_quote(&mut self, _quote: &'a crate::parser::ast::Quote) {} - - fn visit_if(&mut self, f: &'a crate::parser::ast::If) { - // Explicitly disallow a tail call in the test expression - // There is no way that this could be a tail call - - self.visit_with_tail_call_eligibility(&f.test_expr, false); - - self.visit(&f.then_expr); - self.visit(&f.else_expr); - } - - fn visit_list(&mut self, l: &'a List) { - let eligibility = self.tail_call_eligible; - let escape = self.escape_analysis; - - // In this case, each of the arguments (including the function itself) are not in the tail position - // However, the function call _itself_ might be in the tail position, so we save that state - self.tail_call_eligible = false; - - // Save the spot in the recursion - here we might be in something like this: - // (lambda (x y) - // (+ 10 (let ((z (function-call))) (+ x y z)))) - // In this case the 10 will be on the stack, but will be reset after - // the function call. - let stack_offset = self.stack_offset; - - for expr in &l.args[1..] { - self.escape_analysis = true; - - self.visit(expr); - - self.stack_offset += 1; - } - - if !l.is_empty() { - // self.tail_call_eligible = eligibility; - self.escape_analysis = eligibility; - - if let ExprKind::LambdaFunction(_) = &l.args[0] { - self.escape_analysis = false; - } - - self.visit(&l.args[0]); - } - - self.stack_offset = stack_offset; - - self.tail_call_eligible = eligibility; - self.escape_analysis = escape; - - // TODO: Come back here on cleanup - // This just checks that this is actually a real function call and not just an empty list - // Every actual call site should in fact have a real span, otherwise its a bit of a waste to include - // that information - it _has_ to at least be calling something - if !l.is_empty() { - // Mark the call site - see what happens - let mut call_site_kind = if eligibility && self.scope.depth() > 1 { - CallKind::TailCall - } else { - CallKind::Normal - }; - - let syntax_object = l.first().and_then(|x| x.atom_syntax_object()); - - if let Some(func) = syntax_object { - let span = func.span; - - // Assuming this information is here, otherwise we'll panic for whatever reason the symbol is missing - // But this shouldn't happen given that we're checking this _after_ we visit the function - if let Some(func_info) = self.info.get(func) { - // If we've managed to resolve this call site to the definition, then we should be able - // to identify if this refers to the correct definition - if call_site_kind == CallKind::TailCall - && self.defining_context.is_some() - && func_info.refers_to == self.defining_context - { - call_site_kind = CallKind::SelfTailCall(self.defining_context_depth); - } - - self.info.call_info.insert( - l.syntax_object_id, - CallSiteInformation::new(call_site_kind, span), - ); - } - } - } - } - - // TODO: -> understand stack offset here. Should begins drop everything inside? - fn visit_begin(&mut self, begin: &'a crate::parser::ast::Begin) { - // Collect all of the defines inside of the body first - for expr in &begin.exprs { - if let ExprKind::Define(define) = expr { - if define.body.lambda_function().is_some() { - self.visit_define_without_body( - define, - IdentifierStatus::LocallyDefinedFunction, - ); - } - } - } - - // TODO: Log the fact we have an empty begin body, or figure out what that is - if begin.exprs.is_empty() { - // self.stack_offset -= 1; - - return; - } - - let last = begin.exprs.len() - 1; - - // TODO: Clean up this bad pattern - let eligibility = self.tail_call_eligible; - self.tail_call_eligible = false; - - // After that, we can continue with everything but those - for (index, expr) in begin.exprs.iter().enumerate() { - if index == last { - self.tail_call_eligible = eligibility; - } - - if let ExprKind::Define(define) = expr { - let define_ctx = self.defining_context.take(); - let old_depth = self.defining_context_depth; - self.defining_context = define.name_id(); - self.defining_context_depth = 0; - - if define.body.lambda_function().is_some() { - // Continue with the rest of the body here - self.visit(&define.body); - } else { - self.visit(expr); - } - - self.defining_context = define_ctx; - self.defining_context_depth = old_depth; - } else { - self.visit(expr); - } - - self.tail_call_eligible = false; - } - - // Overall, 1 for the total - - if !begin.exprs.is_empty() { - // println!("BUMPTING STACK OFFSET FOR BEGIN"); - - // let is_top_level = self.scope.depth() == 1; - // println!("Bumping stack") - - // if is_top_level { - // println!("Top level begin bumping stack"); - // } - - // if !is_top_level { - // println!("BUMPING STACK OFFSET FOR BEGIN"); - // self.stack_offset += 1; - // } else { - // println!("Skiping.. top level "); - // } - - // self.stack_offset += 1; - } - - self.tail_call_eligible = eligibility; - } - - #[allow(dead_code, unused)] - fn visit_let(&mut self, l: &'a crate::parser::ast::Let) { - let eligibility = self.tail_call_eligible; - self.tail_call_eligible = false; - - let mut stack_offset = self.stack_offset; - let rollback_offset = stack_offset; - - for expr in l.expression_arguments() { - self.visit(expr); - self.stack_offset += 1; - } - - self.tail_call_eligible = eligibility; - - let is_top_level = self.scope.depth() == 1; - - if is_top_level { - self.scope.push_layer(); - } - - let alloc_capture_count = self - .function_context - .and_then(|x| self.info.function_info.get(&x)) - .map(|x| { - x.captured_vars() - .values() - .filter(|x| x.captured && x.mutated) - .count() - }); - - // We don't want to include normal variables when keeping track - // of the offset - let mut mutable_var_offset = 0; - - for arg in l.local_bindings() { - // println!("{}", arg); - // println!("{}", l); - - let name = arg.atom_identifier().unwrap(); - let id = arg.atom_syntax_object().unwrap().syntax_object_id; - - let heap_alloc = if let Some(info) = self.info.let_info.get(&l.syntax_object_id) { - if let Some(info) = info.arguments.get(name) { - info.mutated && info.captured - } else { - false - } - } else { - false - }; - - #[allow(clippy::diverging_sub_expression)] - if heap_alloc { - self.scope.define( - *name, - ScopeInfo::new_heap_allocated_var( - id, - todo!("Need to include the stack offset here!"), - mutable_var_offset + alloc_capture_count.unwrap(), - ), - ); - - // Throw in a dummy info so that no matter what, we have something to refer to - // in the event of a set! - // Later on in this function this gets updated accordingly - self.info.insert( - arg.atom_syntax_object().unwrap(), - SemanticInformation::new( - IdentifierStatus::HeapAllocated, - self.scope.depth(), - arg.atom_syntax_object().unwrap().span, - ), - ); - - mutable_var_offset += 1; - } else { - // println!("---------------"); - // println!("Defining: {} @ stack offset: {}", name, stack_offset); - // println!("Rollback offset: {}", rollback_offset); - // println!("{}", l); - // println!("Top Level: {}", is_top_level); - - self.scope - .define(*name, ScopeInfo::new_local(id, stack_offset)); - - self.info.insert( - arg.atom_syntax_object().unwrap(), - SemanticInformation::new( - IdentifierStatus::LetVar, - self.scope.depth(), - arg.atom_syntax_object().unwrap().span, - ), - ); - } - - stack_offset += 1; - } - - self.visit(&l.body_expr); - - // This is a little silly but I'm not sure why I can't call the default method on the FxHashMap directly - let mut arguments = FxHashMap::with_capacity_and_hasher( - l.bindings.len(), - BuildHasherDefault::::default(), - ); - - for arg in l.local_bindings() { - let name = arg.atom_identifier().unwrap(); - - // dbg!(name.resolve()); - // dbg!(self.scope.keys().map(|x| x.resolve()).collect::>()); - - arguments.insert(*name, self.scope.remove(name).unwrap()); - } - - if let hash_map::Entry::Vacant(e) = self.info.let_info.entry(l.syntax_object_id) { - e.insert(LetInformation::new( - // self.stack_offset, - rollback_offset, - self.function_context, - arguments, - )); - } - - if is_top_level { - self.scope.pop_layer(); - } - - self.stack_offset = rollback_offset; - } - - fn visit_lambda_function(&mut self, lambda_function: &'a crate::parser::ast::LambdaFunction) { - let stack_offset_rollback = self.stack_offset; - - // The captures correspond to what variables _this_ scope should decide to capture, and also - // arbitrarily decide the index for that capture - self.captures.push_layer(); - - // In this case, we've actually already seen this function. This works if we're on the second pass - // of running this analysis with the same information - if let Some(function_info) = self - .info - .function_info - .get_mut(&lambda_function.syntax_object_id) - { - // TODO: see if this was necessary - if function_info.escapes { - self.defining_context = None; - } - - let vars = &mut function_info.captured_vars; - - // TODO: - // If this var is both captured and mutated, lets separate it for a different kind - // of allocation - this way we can actually separately allocate where these go. Since it is possible - // that a variable is both captured, and mutated by both the closure and the stack, we want to - // make sure that things that get captured and mutated end up on the heap, and both references - // point to the same thing - - // Handle the immutable variables being patched in - { - let mut sorted_vars = vars.iter_mut().filter(|x| !x.1.mutated).collect::>(); - sorted_vars.sort_by_key(|x| x.1.id); - - // So for now, we sort by id, then map these directly to indices that will live in the - // corresponding captured closure - for (index, (key, value)) in sorted_vars.iter_mut().enumerate() { - // If we've already captured this variable, mark it as being captured from the enclosing environment - // TODO: If there is shadowing, this might not work? - if let Some(captured_var) = self.captures.get(key) { - // TODO: If the key already exists, we need to check if we're shadowing - // the variable - I think we can do this by checking what the variable shadows? - - // Check if this variable shadows another one. If so, we defer to the - // notion that this is a fresh variable - // if let Some(analysis) = self.captures.contains_key_at_top(key) { - if self.captures.depth_of(key).unwrap() > 1 { - value.capture_offset = Some(index); - value.read_capture_offset = Some(index); - let mut value = value.clone(); - value.captured_from_enclosing = false; - - self.captures.define(key.clone(), value); - - continue; - } - - value.capture_offset = captured_var.read_capture_offset; - - value.read_capture_offset = Some(index); - - let mut value = value.clone(); - value.captured_from_enclosing = true; - - self.captures.define(key.clone(), value) - } else { - value.capture_offset = Some(index); - value.read_capture_offset = Some(index); - let mut value = value.clone(); - value.captured_from_enclosing = false; - - self.captures.define(key.clone(), value); - } - } - } - - { - let mut captured_and_mutated = - vars.iter_mut().filter(|x| x.1.mutated).collect::>(); - captured_and_mutated.sort_by_key(|x| x.1.id); - - for (index, (key, value)) in captured_and_mutated.iter_mut().enumerate() { - // value.heap_offset = Some(index); - - // If we've already captured this variable, mark it as being captured from the enclosing environment - // TODO: If there is shadowing, this might not work? - if self.captures.contains_key(key) { - if self.captures.depth_of(key).unwrap() > 1 { - value.heap_offset = value.stack_offset; - value.read_heap_offset = Some(index); - - value.parent_heap_offset = value.stack_offset; - value.local_heap_offset = Some(index); - - let mut value = value.clone(); - value.captured_from_enclosing = false; - - self.captures.define(key.clone(), value); - - continue; - } - - // TODO: If theres weird bugs here, match behavior of the captures from above - // i.e. heap offset just patches in the read offset from the parent - value.heap_offset = self.captures.get(key).and_then(|x| x.read_heap_offset); - - // value.read_heap_offset = - // self.captures.get(key).and_then(|x| x.read_heap_offset); - - value.read_heap_offset = Some(index); - - value.parent_heap_offset = - self.captures.get(key).and_then(|x| x.local_heap_offset); - - value.local_heap_offset = Some(index); - - let mut value = value.clone(); - value.captured_from_enclosing = true; - - self.captures.define(key.clone(), value); - } else { - value.heap_offset = value.stack_offset; - value.read_heap_offset = Some(index); - - value.parent_heap_offset = value.stack_offset; - value.local_heap_offset = Some(index); - - let mut value = value.clone(); - value.captured_from_enclosing = false; - - self.captures.define(key.clone(), value); - } - } - } - } - - // We're entering a new scope since we've entered a lambda function - self.scope.push_layer(); - - let let_level_bindings = lambda_function.arguments().unwrap(); - let depth = self.scope.depth(); - - self.visit_func_args(lambda_function, depth); - - self.stack_offset = lambda_function.args.len(); - - let function_context = self.function_context.take(); - self.function_context = Some(lambda_function.syntax_object_id); - - self.defining_context_depth += 1; - - // Set the single used to this scope to be a new set - self.vars_used = im_rc::HashSet::new(); - - self.contains_lambda_func = false; - - // TODO: Better abstract this pattern - perhaps have the function call be passed in? - self.visit_with_tail_call_eligibility(&lambda_function.body, true); - - let lambda_bottoms_out = !self.contains_lambda_func; - - self.contains_lambda_func = true; - - for var in &self.vars_used { - self.total_vars_used.insert(var.clone()); - } - - self.defining_context_depth -= 1; - - self.function_context = function_context; - - // TODO: @Matt: 11/3/2022 -> Seems like theres kind of a bad problem here. - // This map that we're getting is much bigger than expected - // Perhaps take the diff of the vars before visiting this, and after? Then reset the state after visiting this tree? - let mut captured_vars = self.get_captured_vars(&let_level_bindings); - - for (var, value) in self.captures.iter() { - if let Some(scope_info) = captured_vars.get_mut(var) { - scope_info.captured_from_enclosing = value.captured_from_enclosing; - } - } - - if log::log_enabled!(log::Level::Trace) { - log::trace!("Captured variables: {:?}", captured_vars); - } - - // Get the arguments to get the counts - // Pop the layer here - now, we check if any of the arguments below actually already exist - // in scope. If thats the case, we've shadowed and should mark it accordingly. - let arguments = self.pop_top_layer(); - - // Pop off of the captures - self.captures.pop_layer(); - - // Mark the last usage of the variable after the values go out of scope - // TODO: This should get moved to every tail call -> if its a tail call, mark - // the last usage of the variables there. That way, all exit points of the function - // actually get marked - - for id in arguments.values().filter_map(|x| x.last_used) { - self.info.get_mut(&id).unwrap().last_usage = true; - } - - // println!("{:#?}", arguments); - - // Using the arguments, mark the vars that have been captured - self.find_and_mark_captured_arguments(lambda_function, &captured_vars, depth, &arguments); - - // If we've already seen this function, lets not do anything just quite yet - if let Some(info) = self - .info - .function_info - .get_mut(&lambda_function.syntax_object_id) - { - let mut slated_for_removal = Vec::new(); - - if lambda_bottoms_out { - captured_vars.retain(|x: &InternedString, _| self.vars_used.contains(x)); - - for var in info.captured_vars.keys() { - if !captured_vars.contains_key(var) { - slated_for_removal.push(var.clone()); - } - } - - for var in slated_for_removal { - info.captured_vars.remove(&var); - } - - // println!("Captured vars dropped: {}", before - captured_vars.len()); - } - - for (var, value) in captured_vars { - if let Some(scope_info) = info.captured_vars.get_mut(&var) { - scope_info.captured_from_enclosing = value.captured_from_enclosing; - scope_info.heap_offset = value.heap_offset; - }; - } - - self.stack_offset = stack_offset_rollback; - - return; - } else { - // Capture the information and store it in the semantic analysis for this individual function - self.info.function_info.insert( - lambda_function.syntax_object_id, - FunctionInformation::new(captured_vars, arguments) - .escapes(self.escape_analysis) - .depth(self.scope.depth()), - ); - } - - self.stack_offset = stack_offset_rollback; - } - - fn visit_set(&mut self, s: &'a crate::parser::ast::Set) { - let name = s.variable.atom_identifier(); - // let id = s.variable.atom_syntax_object().map(|x| x.syntax_object_id); - - if let Some(info) = s - .variable - .atom_syntax_object() - .and_then(|x| self.info.get(x)) - { - if info.refers_to == self.defining_context { - self.defining_context = None; - } - } - - self.visit_with_tail_call_eligibility(&s.expr, false); - - if let Some(name) = name { - // Mark that we've used this - self.vars_used.insert(*name); - - // Gather the id of the variable that is in fact mutated - if let Some(scope_info) = self.scope.get_mut(name) { - // Bump the usage count - // TODO Also mark this as mutated - scope_info.usage_count += 1; - - let id = scope_info.id; - if let Some(var) = self.info.get_mut(&id) { - var.set_bang = true; - scope_info.mutated = true; - // scope_info.read_heap_offset = var.read_heap_offset; - - // while let Some(reference) = var.refers_to.and_then(|x| self.info.get_mut(&x)) { - // reference.set_bang = true; - // var = reference; - // } - - // var.refers - } - // else { - // log::debug!("Unable to find var: {name} in info map to update to set!"); - // } - } - // else { - // log::debug!("Variable not yet in scope: {name}"); - // } - } - - self.visit(&s.variable); - } - - fn visit_atom(&mut self, a: &'a crate::parser::ast::Atom) { - let name = a.ident(); - let depth = self.scope.depth(); - // Snag the current id of this node - we're gonna want this for later - let current_id = a.syn.syntax_object_id; - - // TODO: Check if this is actually a constant - if it is, mark it accordingly and lift it out - - if let Some(ident) = name { - // Mark that we've seen this one - self.vars_used.insert(*ident); - // Mark that this was perhaps used in tail position - self.ids_referenced_in_tail_position.insert(current_id); - - // Check if its a global var - otherwise, we want to check if its a free - // identifier - if let Some(depth) = self.scope.height_of(ident) { - if depth == 0 { - // Mark the parent as used - let global_var = self.scope.get_mut(ident).unwrap(); - global_var.usage_count += 1; - - self.info.get_mut(&global_var.id).unwrap().usage_count += 1; - - let mut semantic_information = - SemanticInformation::new(IdentifierStatus::Global, depth, a.syn.span) - .with_usage_count(1) - .refers_to(global_var.id); - - // if self.info.info.get(&global_var.id).unwrap().builtin { - // println!("FOUND USAGE OF BUILTIN: {}", ident); - // } - - // TODO: We _really_ should be providing the built-ins in a better way thats not - // passing around a thread local - if crate::steel_vm::primitives::PRELUDE_MODULE - .with(|x| x.contains(ident.resolve())) - { - semantic_information.mark_builtin() - } - - self.info.insert(&a.syn, semantic_information); - - return; - } - } - - // If this contains a key at the top, then it shouldn't be marked as captured by this scope - if self.scope.contains_key_at_top(ident) { - // Set it to not be captured if its contained at the top level - // self.scope.get_mut(ident).unwrap().captured = false; - - let mut_ref = self.scope.get_mut(ident).unwrap(); - - // Not sure if this is going to be a problem... - // Because if its captured at this stage, I think we want it to be marked as captured - // TODO: - // mut_ref.captured = false; - mut_ref.usage_count += 1; - - // Mark this as last touched by this identifier - mut_ref.last_used = Some(current_id); - - // In the event there is a local define, we want to count the usage here - if let Some(local_define) = self.info.get_mut(&mut_ref.id) { - local_define.usage_count = mut_ref.usage_count; - } - - let mut semantic_info = - SemanticInformation::new(IdentifierStatus::Local, depth, a.syn.span) - .with_usage_count(1) - .refers_to(mut_ref.id); - - if let Some(stack_offset) = mut_ref.stack_offset { - semantic_info = semantic_info.with_offset(stack_offset); - } else { - // log::debug!("Stack offset missing from local define") - } - - if mut_ref.captured && mut_ref.mutated { - semantic_info.kind = IdentifierStatus::HeapAllocated; - semantic_info.heap_offset = mut_ref.heap_offset; - semantic_info.read_heap_offset = mut_ref.read_heap_offset; - } - - self.info.insert(&a.syn, semantic_info); - - return; - } - - // This is the result of some fancy fancy stuff - // We are going to do two of these passes to resolve captures, where the first pass does a holistic - // analysis of captures for each closure. Then we can do a top down analysis - // which populates the closures with their capture positions, and then when we do analysis the - // second time, we'll have closure references populated. - if let Some(captured) = self.captures.get_mut(ident) { - // TODO: Fill in this information here - when the vars are captured in the immediate enclosing, - // we want to do stuff.... - - captured.captured = true; - captured.usage_count += 1; - - // TODO: Make sure we want to mark this identifier as last used - captured.last_used = Some(current_id); - - let mut identifier_status = if captured.mutated { - IdentifierStatus::HeapAllocated - } else if let Some(info) = self.info.get(&a.syn) { - if info.kind == IdentifierStatus::HeapAllocated { - IdentifierStatus::HeapAllocated - } else { - IdentifierStatus::Captured - } - } else { - IdentifierStatus::Captured - }; - - // We also want to mark the current var thats actually in scope as last used as well - if let Some(in_scope) = self.scope.get_mut(ident) { - in_scope.last_used = Some(current_id); - in_scope.captured = true; - - // if identifier_status == IdentifierStatus::HeapAllocated { - // in_scope.read_heap_offset = captured.read_heap_offset; - // } - } - - if let Some(local_define) = self.info.get_mut(&captured.id) { - local_define.usage_count = captured.usage_count; - - // If this _is_ in fact a locally defined function, we don't want to capture it - // This is something that is going to get lifted to the top environment anyway - if local_define.kind == IdentifierStatus::LocallyDefinedFunction { - captured.captured = false; - identifier_status = IdentifierStatus::LocallyDefinedFunction; - } - } - - let mut semantic_info = - SemanticInformation::new(identifier_status, depth, a.syn.span) - .with_usage_count(1) - .refers_to(captured.id); - - // TODO: Merge the behavior of all of these separate cases into one - semantic_info.with_captured_from_enclosing(captured.captured_from_enclosing); - - // If we're getting captured and mutated, then we should be fine to do these checks - // exclusively - if let Some(capture_offset) = captured.read_capture_offset { - semantic_info = semantic_info.with_read_capture_offset(capture_offset); - semantic_info = - semantic_info.with_capture_index(captured.capture_offset.unwrap()); - } - - if let Some(heap_offset) = captured.read_heap_offset { - // semantic_info = semantic_info.with_heap_offset(heap_offset); - semantic_info = semantic_info.with_read_heap_offset(heap_offset); - } else { - // log::debug!("Stack offset missing from local define") - } - - if let Some(heap_offset) = captured.heap_offset { - // semantic_info = semantic_info.with_heap_offset(heap_offset); - semantic_info = semantic_info.with_heap_offset(heap_offset); - } else { - // log::debug!("Stack offset missing from local define") - } - - // if semantic_info.kind == IdentifierStatus::HeapAllocated - // && semantic_info.read_heap_offset.is_none() - // { - // panic!("Missing read heap offset here"); - // } - - // println!("Variable {} refers to {}", ident, is_captured.id); - - self.info.insert(&a.syn, semantic_info); - - return; - } - - // Otherwise, go ahead and mark it as captured if we can find a reference to it - // TODO: @Matt - There is an opportunity here to also check extra information. If the - // variables exists in the local scope - i.e, the var has been patched in via a closure, - // we could 1. do closure conversion on it via rewriting, or could 2. automatically - // patch those vars in - if let Some(is_captured) = self.scope.get_mut(ident) { - is_captured.captured = true; - is_captured.usage_count += 1; - - // TODO: Make sure we want to mark this identifier as last used - is_captured.last_used = Some(current_id); - - let mut identifier_status = IdentifierStatus::Captured; - - if let Some(local_define) = self.info.get_mut(&is_captured.id) { - local_define.usage_count = is_captured.usage_count; - - // If this _is_ in fact a locally defined function, we don't want to capture it - // This is something that is going to get lifted to the top environment anyway - if local_define.kind == IdentifierStatus::LocallyDefinedFunction { - // is_captured.captured = false; - identifier_status = IdentifierStatus::LocallyDefinedFunction; - } - } - - let mut semantic_info = - SemanticInformation::new(identifier_status, depth, a.syn.span) - .with_usage_count(1) - .refers_to(is_captured.id); - // .with_offset( - // is_captured - // .stack_offset - // .expect("Local variables should have an offset"), - // ); - - if let Some(stack_offset) = is_captured.stack_offset { - semantic_info = semantic_info.with_offset(stack_offset); - } else { - // log::debug!("Stack offset missing from local define") - } - - // println!("Variable {} refers to {}", ident, is_captured.id); - - self.info.insert(&a.syn, semantic_info); - - return; - } - - // TODO: Check if we've already marked it as free - also count its usage! - - if let Some(analysis) = self.info.info.get_mut(&a.syn.syntax_object_id) { - analysis.usage_count += 1; - } else { - let mut semantic_info = - SemanticInformation::new(IdentifierStatus::Free, depth, a.syn.span); - - // TODO: We _really_ should be providing the built-ins in a better way thats not - // passing around a thread local - if crate::steel_vm::primitives::PRELUDE_MODULE.with(|x| x.contains(ident.resolve())) - { - semantic_info.mark_builtin(); - semantic_info.kind = IdentifierStatus::Global - } - - // Otherwise, we've hit a free variable at this point - // TODO: WE don't need to do this? - self.info.info.insert(a.syn.syntax_object_id, semantic_info); - } - - // let mut semantic_info = - // SemanticInformation::new(IdentifierStatus::Free, depth, a.syn.span); - - // // TODO: We _really_ should be providing the built-ins in a better way thats not - // // passing around a thread local - // if crate::steel_vm::primitives::PRELUDE_MODULE.with(|x| x.contains(ident.resolve())) { - // semantic_info.mark_builtin(); - // semantic_info.kind = IdentifierStatus::Global - // } - - // // Otherwise, we've hit a free variable at this point - // // TODO: WE don't need to do this? - // self.info.insert(&a.syn, semantic_info); - - // println!("Free identifier: {}", a); - } - } -} - -impl<'a> VisitorMutUnitRef<'a> for Analysis { - fn visit_atom(&mut self, a: &'a crate::parser::ast::Atom) { - if log::log_enabled!(log::Level::Trace) { - log::trace!( - "Id: {:?}, Atom: {:?}, Semantic Information: {:?}", - a.syn.syntax_object_id, - a.syn.ty, - self.get(&a.syn) - ); - } - } - - fn visit_lambda_function(&mut self, lambda_function: &'a crate::parser::ast::LambdaFunction) { - for arg in &lambda_function.args { - if let Some(arg) = arg.atom_syntax_object() { - if log::log_enabled!(log::Level::Trace) { - log::trace!( - "Id: {:?}, Atom in function argument: {:?}, Semantic Information: {:?}", - arg.syntax_object_id, - arg.ty, - self.get(arg) - ); - } - } - } - - self.visit(&lambda_function.body); - } -} - -pub fn query_top_level_define_on_condition>( - exprs: &[ExprKind], - name: A, - mut func: impl FnMut(&str, &str) -> bool, -) -> Option<&crate::parser::ast::Define> { - let mut found_defines = Vec::new(); - for expr in exprs { - match expr { - ExprKind::Define(d) => match d.name.atom_identifier() { - Some(n) if func(name.as_ref(), n.resolve()) => found_defines.push(d.as_ref()), - _ => {} - }, - - ExprKind::Begin(b) => { - for expr in b.exprs.iter() { - if let ExprKind::Define(d) = expr { - match d.name.atom_identifier() { - Some(n) if func(name.as_ref(), n.resolve()) => { - found_defines.push(d.as_ref()) - } - _ => {} - } - } - } - } - - _ => {} - } - } - - if found_defines.len() > 1 { - log::debug!( - "Multiple defines found, unable to find one unique value to associate with a name" - ); - return None; - } - - if found_defines.len() == 1 { - return found_defines.into_iter().next(); - } - - None -} - -pub fn query_top_level_define>( - exprs: &[ExprKind], - name: A, -) -> Option<&crate::parser::ast::Define> { - let mut found_defines = Vec::new(); - for expr in exprs { - match expr { - ExprKind::Define(d) => match d.name.atom_identifier() { - Some(n) if name.as_ref() == n.resolve() => found_defines.push(d.as_ref()), - _ => {} - }, - - ExprKind::Begin(b) => { - for expr in b.exprs.iter() { - if let ExprKind::Define(d) = expr { - match d.name.atom_identifier() { - Some(n) if name.as_ref() == n.resolve() => { - found_defines.push(d.as_ref()) - } - _ => {} - } - } - } - } - - _ => {} - } - } - - if found_defines.len() > 1 { - log::debug!( - "Multiple defines found, unable to find one unique value to associate with a name" - ); - return None; - } - - if found_defines.len() == 1 { - return found_defines.into_iter().next(); - } - - None -} - -struct FindCallSiteById<'a, F> { - id: SyntaxObjectId, - analysis: &'a Analysis, - func: F, - modified: bool, -} - -impl<'a, F> FindCallSiteById<'a, F> { - pub fn _new(id: SyntaxObjectId, analysis: &'a Analysis, func: F) -> Self { - Self { - id, - analysis, - func, - modified: false, - } - } - - // TODO: clean this up a bit - pub fn is_required_call_site(&self, l: &List) -> bool { - if let Some(refers_to) = l - .args - .first() - .and_then(|x| x.atom_syntax_object()) - .and_then(|x| self.analysis.get(x)) - .and_then(|x| x.refers_to) - { - refers_to == self.id - } else { - false - } - } -} - -impl<'a, F> VisitorMutRefUnit for FindCallSiteById<'a, F> -where - F: FnMut(&Analysis, &mut crate::parser::ast::List) -> bool, -{ - fn visit_list(&mut self, l: &mut List) { - // Go downward and visit each of the arguments (including the function call) - for arg in &mut l.args { - self.visit(arg); - } - - // If we're a match, call the function - if self.is_required_call_site(l) { - self.modified |= (self.func)(self.analysis, l) - } - } -} - -struct FindUsages<'a, F> { - id: SyntaxObjectId, - analysis: &'a Analysis, - func: F, - modified: bool, -} - -impl<'a, F> FindUsages<'a, F> { - pub fn new(id: SyntaxObjectId, analysis: &'a Analysis, func: F) -> Self { - Self { - id, - analysis, - func, - modified: false, - } - } -} - -impl<'a, F> VisitorMutRefUnit for FindUsages<'a, F> -where - F: FnMut(&Analysis, &mut crate::parser::ast::Atom) -> bool, -{ - fn visit_atom(&mut self, a: &mut Atom) { - if let Some(refers_to) = self.analysis.get(&a.syn).and_then(|x| x.refers_to) { - if refers_to == self.id { - self.modified |= (self.func)(self.analysis, a) - } - } - } -} - -struct FindCallSites<'a, F> { - name: &'a str, - analysis: &'a Analysis, - func: F, -} - -impl<'a, F> FindCallSites<'a, F> { - pub fn new(name: &'a str, analysis: &'a Analysis, func: F) -> Self { - Self { - name, - analysis, - func, - } - } -} -impl<'a, F> FindCallSites<'a, F> { - fn is_required_global_function_call(&self, l: &List) -> bool { - if let Some(name) = l.first_ident() { - if let Some(semantic_info) = self.analysis.get(l.args[0].atom_syntax_object().unwrap()) - { - return name.resolve() == self.name - && semantic_info.kind == IdentifierStatus::Global; - } - } - - false - } -} - -impl<'a, F> VisitorMutUnitRef<'a> for FindCallSites<'a, F> -where - F: FnMut(&Analysis, &crate::parser::ast::List), -{ - fn visit_list(&mut self, l: &'a crate::parser::ast::List) { - if self.is_required_global_function_call(l) { - (self.func)(self.analysis, l) - } - - for arg in &l.args { - self.visit(arg); - } - } -} - -impl<'a, F> VisitorMutRefUnit for FindCallSites<'a, F> -where - F: FnMut(&Analysis, &mut crate::parser::ast::List), -{ - fn visit_list(&mut self, l: &mut crate::parser::ast::List) { - if self.is_required_global_function_call(l) { - (self.func)(self.analysis, l) - } - - for arg in &mut l.args { - self.visit(arg); - } - } -} - -struct RefreshVars; - -impl VisitorMutRefUnit for RefreshVars { - fn visit_atom(&mut self, a: &mut Atom) { - a.syn.syntax_object_id = SyntaxObjectId::fresh(); - } -} - -struct MutateCallSites<'a, F> { - name: &'a str, - analysis: &'a Analysis, - func: F, -} - -impl<'a, F> MutateCallSites<'a, F> { - pub fn new(name: &'a str, analysis: &'a Analysis, func: F) -> Self { - Self { - name, - analysis, - func, - } - } -} - -impl<'a, F> VisitorMutRefUnit for MutateCallSites<'a, F> -where - F: FnMut(&Analysis, &mut ExprKind), -{ - fn visit(&mut self, expr: &mut ExprKind) { - match expr { - ExprKind::If(f) => self.visit_if(f), - ExprKind::Define(d) => self.visit_define(d), - ExprKind::LambdaFunction(l) => self.visit_lambda_function(l), - ExprKind::Begin(b) => self.visit_begin(b), - ExprKind::Return(r) => self.visit_return(r), - ExprKind::Quote(q) => self.visit_quote(q), - ExprKind::Macro(m) => self.visit_macro(m), - ExprKind::Atom(a) => self.visit_atom(a), - list @ ExprKind::List(_) => { - if let ExprKind::List(l) = &list { - if let Some(name) = l.first_ident() { - if let Some(semantic_info) = - self.analysis.get(l.args[0].atom_syntax_object().unwrap()) - { - if name.resolve() == self.name - && semantic_info.kind == IdentifierStatus::Global - { - // At this point, call out to the user given function - if we do in fact mutate - // where the value points to, we should return a full node that needs to be visited - (self.func)(self.analysis, list); - - // TODO: Analysis should maybe be re run here - mutations might invalidate the analysis - // This might make it worth rerunning the analysis - - return self.visit(list); - } - } - } - } - - if let ExprKind::List(l) = list { - return self.visit_list(l); - } - - unreachable!() - } - ExprKind::SyntaxRules(s) => self.visit_syntax_rules(s), - ExprKind::Set(s) => self.visit_set(s), - ExprKind::Require(r) => self.visit_require(r), - ExprKind::Let(l) => self.visit_let(l), - } - } -} - -struct LetCallSites<'a, F> { - analysis: &'a Analysis, - func: F, -} - -impl<'a, F> LetCallSites<'a, F> { - pub fn new(analysis: &'a Analysis, func: F) -> Self { - Self { analysis, func } - } -} - -impl<'a, F> VisitorMutRefUnit for LetCallSites<'a, F> -where - F: FnMut(&Analysis, &mut ExprKind) -> bool, -{ - fn visit(&mut self, expr: &mut ExprKind) { - match expr { - ExprKind::If(f) => self.visit_if(f), - ExprKind::Define(d) => self.visit_define(d), - ExprKind::LambdaFunction(l) => self.visit_lambda_function(l), - ExprKind::Begin(b) => self.visit_begin(b), - ExprKind::Return(r) => self.visit_return(r), - ExprKind::Quote(q) => self.visit_quote(q), - ExprKind::Macro(m) => self.visit_macro(m), - ExprKind::Atom(a) => self.visit_atom(a), - ExprKind::List(l) => self.visit_list(l), - ExprKind::SyntaxRules(s) => self.visit_syntax_rules(s), - ExprKind::Set(s) => self.visit_set(s), - ExprKind::Require(r) => self.visit_require(r), - let_expr @ ExprKind::Let(_) => { - if let ExprKind::Let(l) = let_expr { - self.visit_let(l); - } - - if let ExprKind::Let(_) = &let_expr { - if (self.func)(self.analysis, let_expr) { - // log::debug!("Modified let expression"); - } - } - } - } - } -} - -// TODO: This will need to get changed in the event we actually modify _what_ the mutable pointer points to -// Right now, if we want to modify a call site, we can only change it to a call site - what we _should_ do is have it be able to point -// back to any arbitrary value, and subsequently change l to point to that value by doing another level of the recursion -// Something like: -// where F: FnMut(&mut ExprKind) -> () -// To do this, the main visit loop would need to be goofed with in the visitor, and we pass in the reference to the wrapped object, rather than the underlying one -struct AnonymousFunctionCallSites<'a, F> { - analysis: &'a Analysis, - func: F, -} - -impl<'a, F> AnonymousFunctionCallSites<'a, F> { - pub fn new(analysis: &'a Analysis, func: F) -> Self { - Self { analysis, func } - } -} - -impl<'a, F> VisitorMutRefUnit for AnonymousFunctionCallSites<'a, F> -where - F: FnMut(&Analysis, &mut ExprKind) -> bool, -{ - fn visit(&mut self, expr: &mut ExprKind) { - match expr { - ExprKind::If(f) => self.visit_if(f), - ExprKind::Define(d) => self.visit_define(d), - ExprKind::LambdaFunction(l) => self.visit_lambda_function(l), - ExprKind::Begin(b) => self.visit_begin(b), - ExprKind::Return(r) => self.visit_return(r), - ExprKind::Quote(q) => self.visit_quote(q), - ExprKind::Macro(m) => self.visit_macro(m), - ExprKind::Atom(a) => self.visit_atom(a), - list @ ExprKind::List(_) => { - // Bottom up approach - visit everything first, then, on the way back up, - // modify the value - if let ExprKind::List(l) = list { - self.visit_list(l); - } - - if let ExprKind::List(l) = &list { - if l.is_anonymous_function_call() { - // TODO: rerunning analysis might be worth it here - we want to be able to trigger a re run if a mutation would cause a change - // In the state of the analysis - if (self.func)(self.analysis, list) { - // return self.visit(list); - // log::debug!("Modified anonymous function call site!"); - } - } - } - } - ExprKind::SyntaxRules(s) => self.visit_syntax_rules(s), - ExprKind::Set(s) => self.visit_set(s), - ExprKind::Require(r) => self.visit_require(r), - ExprKind::Let(l) => self.visit_let(l), - } - } -} - -struct RemoveUnusedDefineImports<'a> { - analysis: &'a Analysis, - depth: usize, -} - -impl<'a> RemoveUnusedDefineImports<'a> { - pub fn new(analysis: &'a Analysis) -> Self { - Self { analysis, depth: 0 } - } -} - -// This should just be a function on the define, not a method - so that it can be moved -// into a different crate -pub(crate) fn is_a_builtin_definition(def: &Define) -> bool { - if let ExprKind::List(l) = &def.body { - match l.first_ident() { - Some(func) if *func == *UNREADABLE_MODULE_GET || *func == *STANDARD_MODULE_GET => { - // return true - - if let Some(module) = l.second_ident() { - return MODULE_IDENTIFIERS.contains(module); - } - } - _ => {} - } - } - - false -} - -pub(crate) fn is_a_require_definition(def: &Define) -> bool { - if let ExprKind::List(l) = &def.body { - match l.first_ident() { - Some(func) if *func == *PROTO_HASH_GET => { - return true; - } - _ => {} - } - } - - false -} - -impl<'a> VisitorMutRefUnit for RemoveUnusedDefineImports<'a> { - fn visit_lambda_function(&mut self, lambda_function: &mut LambdaFunction) { - self.depth += 1; - self.visit(&mut lambda_function.body); - self.depth -= 1; - } - - fn visit_begin(&mut self, begin: &mut crate::parser::ast::Begin) { - // Only remove internal definitions here - if self.depth > 0 { - let mut exprs_to_drop = Vec::new(); - - for (idx, expr) in begin.exprs.iter().enumerate() { - if let ExprKind::Define(d) = expr { - if is_a_builtin_definition(&d) { - if let Some(analysis) = - self.analysis.get(d.name.atom_syntax_object().unwrap()) - { - if analysis.usage_count == 0 { - exprs_to_drop.push(idx); - } - } - } - } - } - - for idx in exprs_to_drop.iter().rev() { - // println!("Removing: {:?}", begin.exprs.get(*idx)); - begin.exprs.remove(*idx); - } - - if begin.exprs.is_empty() { - begin.exprs.push(ExprKind::Quote(Box::new(Quote::new( - ExprKind::atom("void".to_string()), - RawSyntaxObject::default(TokenType::Quote), - )))); - } - - // println!("Resulting expr: {:?}", begin); - } - - for expr in &mut begin.exprs { - self.visit(expr); - } - } -} - -struct RemovedUnusedImports<'a> { - analysis: &'a Analysis, -} - -impl<'a> RemovedUnusedImports<'a> { - pub fn new(analysis: &'a Analysis) -> Self { - Self { analysis } - } -} - -impl<'a> VisitorMutRefUnit for RemovedUnusedImports<'a> { - fn visit_list(&mut self, l: &mut List) { - let mut unused_arguments = Vec::new(); - - if l.is_anonymous_function_call() { - let argument_count = l.args.len() - 1; - if let Some(func) = l.first_func() { - if argument_count != func.args.len() { - println!("-- Static arity mismatch -- Should actually error here"); - } else { - unused_arguments = func - .args - .iter() - .enumerate() - .filter(|x| { - self.analysis - .get(x.1.atom_syntax_object().unwrap()) - .unwrap() - .usage_count - == 0 - }) - .map(|x| x.0) - .filter(|x| match l.args.get(*x) { - Some(ExprKind::List(l)) => l.is_a_builtin_expr(), - Some(ExprKind::Quote(_)) => true, - Some(ExprKind::Atom(a)) => matches!( - a.syn.ty, - TokenType::NumberLiteral(_) - | TokenType::IntegerLiteral(_) - | TokenType::BooleanLiteral(_) - ), - _ => false, - }) - .collect(); - } - } - } - - if let Some(func) = l.first_func_mut() { - for index in unused_arguments.iter().rev() { - func.args.remove(*index); - } - } - - for index in unused_arguments.iter().rev() { - l.args.remove(index + 1); - } - - // println!("Resulting expression: {}", l); - - for arg in &mut l.args { - self.visit(arg); - } - } -} - -struct FreeIdentifierVisitor<'a> { - analysis: &'a Analysis, - // Check if identifiers is in the globals list before deciding to reject it - globals: &'a SymbolMap, - - diagnostics: Vec<(InternedString, &'a SemanticInformation)>, -} - -impl<'a> VisitorMutUnitRef<'a> for FreeIdentifierVisitor<'a> { - fn visit_atom(&mut self, a: &'a Atom) { - if let Some(info) = self.analysis.get(&a.syn) { - if info.kind == IdentifierStatus::Free { - if let Some(ident) = a.ident() { - if self.globals.get(ident).is_err() { - self.diagnostics.push((*ident, info)); - } - } - } - } - } -} - -// TODO: Don't need the analysis at all -struct IdentifierFinder<'a> { - ids: &'a mut HashMap>, -} - -impl<'a> VisitorMutUnitRef<'a> for IdentifierFinder<'a> { - fn visit_atom(&mut self, a: &'a Atom) { - if let Some(ident) = a.ident() { - self.ids - .get_mut(&a.syn.syntax_object_id) - .map(|value| *value = Some(*ident)); - } - } -} - -struct FindContextsWithOffset<'a> { - analysis: &'a Analysis, - offset: usize, - source_id: SourceId, - contexts: Vec, -} - -impl<'a> VisitorMutUnitRef<'a> for FindContextsWithOffset<'a> { - fn visit_lambda_function(&mut self, lambda_function: &'a LambdaFunction) { - if lambda_function.location.span.end >= self.offset { - return; - } - - let mut span = get_span(&lambda_function.body); - - span.start = lambda_function.location.span.start; - - log::debug!("Lambda function span: {:?} - offset: {}", span, self.offset); - - // This counts, save analysis. - // TODO: Memoize the span analysis, this is not performant - if span.range().contains(&self.offset) { - // TODO: Don't clone this - if let Some(info) = self.analysis.get_function_info(lambda_function) { - if lambda_function.location.span.source_id == Some(self.source_id) { - self.contexts - .push(SemanticInformationType::Function(info.clone())); - } - } - - self.visit(&lambda_function.body); - } - } - - fn visit_let(&mut self, l: &'a Let) { - if l.location.span.end >= self.offset { - return; - } - - let mut span = get_span(&l.body_expr); - span.start = l.location.span.start; - - if span.range().contains(&self.offset) { - if let Some(info) = self.analysis.let_info.get(&l.syntax_object_id) { - if l.location.span.source_id() == Some(self.source_id) { - self.contexts - .push(SemanticInformationType::Let(info.clone())); - } - } - - l.bindings.iter().for_each(|x| self.visit(&x.1)); - self.visit(&l.body_expr); - } - } -} - -struct GlobalDefinitionFinder<'a> { - analysis: &'a Analysis, - globals: Vec<(InternedString, Span)>, -} - -impl<'a> VisitorMutUnitRef<'a> for GlobalDefinitionFinder<'a> { - fn visit_atom(&mut self, a: &'a Atom) { - if let Some(info) = self.analysis.get(&a.syn) { - if info.kind == IdentifierStatus::Global { - self.globals.push((*a.ident().unwrap(), info.span)); - } - } - } -} - -struct UnusedArguments<'a> { - analysis: &'a Analysis, - unused_args: Vec<(InternedString, Span)>, -} - -impl<'a> UnusedArguments<'a> { - pub fn new(analysis: &'a Analysis) -> Self { - Self { - analysis, - unused_args: Vec::new(), - } - } -} - -impl<'a> VisitorMutUnitRef<'a> for UnusedArguments<'a> { - fn visit_lambda_function(&mut self, lambda_function: &'a LambdaFunction) { - for arg in &lambda_function.args { - if let Some(syntax_object) = arg.atom_syntax_object() { - if let Some(info) = self.analysis.get(syntax_object) { - if info.usage_count == 0 { - self.unused_args - .push((*arg.atom_identifier().unwrap(), syntax_object.span)); - } - } - } - } - - self.visit(&lambda_function.body); - } -} - -/// Takes functions that have one argument that is used one time, and inline the function -/// body and replace the call site. -// struct InlineFunctionsWithArgumentsUsedOnce<'a> { -// analysis: &'a Analysis, -// functions_to_inline: FxHashMap, -// } - -// struct FunctionInliner { -// variable_to_substitute: ExprKind, -// } - -// impl FunctionInliner { - -// } - -// TODO: If its _not_ a pure function, we need to both assert that the function -// does not escape, and then find the captured arguments, assert that those also -// are immutable captures, and then modify the callsites to pass in those variables -// as well. -struct LiftPureFunctionsToGlobalScope<'a> { - analysis: &'a Analysis, - lifted_functions: Vec, -} - -impl<'a> LiftPureFunctionsToGlobalScope<'a> { - pub fn new(analysis: &'a Analysis) -> Self { - Self { - analysis, - lifted_functions: Vec::new(), - } - } -} - -fn atom(name: String) -> ExprKind { - ExprKind::Atom(Atom::new(SyntaxObject::default(TokenType::Identifier( - name.into(), - )))) -} - -impl<'a> VisitorMutRefUnit for LiftPureFunctionsToGlobalScope<'a> { - fn visit(&mut self, expr: &mut ExprKind) { - match expr { - ExprKind::If(f) => self.visit_if(f), - ExprKind::Define(d) => self.visit_define(d), - lambda @ ExprKind::LambdaFunction(_) => { - // Do this bottom up - visit the children and on the way up we're going to - // apply the transformation - if let ExprKind::LambdaFunction(l) = lambda { - for var in &mut l.args { - self.visit(var); - } - self.visit(&mut l.body); - } - - if let ExprKind::LambdaFunction(l) = lambda { - if let Some(info) = self.analysis.get_function_info(l) { - // println!("depth: {}", info.depth); - - // We don't need to lift top level functions to the top level already - if info.depth == 1 { - return; - } - - for arg in info.arguments().values() { - if arg.mutated { - return; - } - } - - // If we have no captured variables, this is a pure function for our purposes - if info.captured_vars.is_empty() { - // TODO: If the function is of the shape: - // (lambda (x) (function-call x)) - // Don't lift - just replace the lambda with the body, and replace the argument - // with the right thing - - // Name the closure something mangled, but something we can refer to later - let constructed_name = "##__lifted_pure_function".to_string() - + l.syntax_object_id.to_string().as_ref(); - - // Point the reference to a dummy list - this is just an ephemeral placeholder - let mut dummy = ExprKind::List(List::new(Vec::new())); - - // Have the lambda now actually point to this dummy list - std::mem::swap(lambda, &mut dummy); - - // Construct the global definition, this is something like - // - // (define ##__lifted_pure_function42 (lambda (x) 10)) - // - // This is going to go into our vec of lifted functions - let constructed_definition = ExprKind::Define(Box::new(Define::new( - atom(constructed_name.clone()), - dummy, - SyntaxObject::default(TokenType::Define), - ))); - - self.lifted_functions.push(constructed_definition); - - // Now update the reference to just point straight to a variable reference - // This _should_ be globally unique - *lambda = atom(constructed_name); - } - } - } - } - ExprKind::Begin(b) => self.visit_begin(b), - ExprKind::Return(r) => self.visit_return(r), - ExprKind::Quote(q) => self.visit_quote(q), - ExprKind::Macro(m) => self.visit_macro(m), - ExprKind::Atom(a) => self.visit_atom(a), - ExprKind::List(l) => { - if l.is_anonymous_function_call() { - if let Some((f, rest)) = l.args.split_first_mut() { - if let ExprKind::LambdaFunction(f) = f { - self.visit(&mut f.body); - } - - for expr in rest { - self.visit(expr); - } - } - } else { - for expr in &mut l.args { - self.visit(expr); - } - } - - // self.visit_list(l) - } - ExprKind::SyntaxRules(s) => self.visit_syntax_rules(s), - ExprKind::Set(s) => self.visit_set(s), - ExprKind::Require(r) => self.visit_require(r), - ExprKind::Let(l) => self.visit_let(l), - } - } -} - -struct ElideSingleArgumentLambdaApplications<'a> { - analysis: &'a Analysis, - re_run_analysis: bool, -} - -impl<'a> VisitorMutRefUnit for ElideSingleArgumentLambdaApplications<'a> { - fn visit(&mut self, expr: &mut ExprKind) { - match expr { - ExprKind::If(f) => self.visit_if(f), - ExprKind::Define(d) => self.visit_define(d), - ExprKind::LambdaFunction(l) => self.visit_lambda_function(l), - ExprKind::Begin(b) => self.visit_begin(b), - ExprKind::Return(r) => self.visit_return(r), - ExprKind::Quote(q) => self.visit_quote(q), - ExprKind::Macro(m) => self.visit_macro(m), - ExprKind::Atom(a) => self.visit_atom(a), - application @ ExprKind::List(_) => { - if let ExprKind::List(l) = application { - for expr in &mut l.args { - self.visit(expr); - } - - if !l.is_anonymous_function_call() { - return; - } - - if l.args.len() != 2 { - return; - } - - if l.args[1].atom_identifier().is_none() { - return; - } - - let mut replacement: Option = None; - - if let ExprKind::LambdaFunction(lf) = &l.args[0] { - if lf.args.len() != 1 { - return; - } - - if let Some(info) = self.analysis.get_function_info(&lf) { - for arg in info.arguments().values() { - if arg.mutated { - return; - } - } - - if !info.captured_vars.is_empty() { - return; - } - - if let ExprKind::List(lst) = &lf.body { - if lst.len() == 2 - && lst.args[0].atom_identifier().is_some() - && lst.args[1].atom_identifier() == lf.args[0].atom_identifier() - { - self.re_run_analysis = true; - - replacement = Some(lst.args[0].clone()); - } - } - } - } else { - unreachable!(); - } - - if let Some(replacement) = replacement { - l.args[0] = replacement; - } - } - } - ExprKind::SyntaxRules(s) => self.visit_syntax_rules(s), - ExprKind::Set(s) => self.visit_set(s), - ExprKind::Require(r) => self.visit_require(r), - ExprKind::Let(l) => self.visit_let(l), - } - } -} - -fn box_argument(ident: ExprKind) -> ExprKind { - ExprKind::List(List::new(vec![ExprKind::atom("#%box"), ident])) -} - -fn unbox_argument(ident: ExprKind) -> ExprKind { - ExprKind::List(List::new(vec![ExprKind::atom("#%unbox"), ident])) -} - -fn setbox_argument(ident: ExprKind, expr: ExprKind) -> ExprKind { - ExprKind::List(List::new(vec![ExprKind::atom("#%set-box!"), ident, expr])) -} - -struct ReplaceSetOperationsWithBoxes<'a> { - analysis: &'a Analysis, -} - -impl<'a> VisitorMutRefUnit for ReplaceSetOperationsWithBoxes<'a> { - fn visit(&mut self, expr: &mut ExprKind) { - match expr { - ExprKind::If(f) => self.visit_if(f), - ExprKind::Define(d) => self.visit_define(d), - ExprKind::LambdaFunction(l) => self.visit_lambda_function(l), - ExprKind::Begin(b) => self.visit_begin(b), - ExprKind::Return(r) => self.visit_return(r), - ExprKind::Quote(_) => {} - ExprKind::Macro(m) => self.visit_macro(m), - ExprKind::Atom(a) => { - if let Some(analysis) = self.analysis.get(&a.syn) { - if analysis.kind == IdentifierStatus::HeapAllocated { - let mut dummy = ExprKind::List(List::new(Vec::new())); - std::mem::swap(&mut dummy, expr); - - *expr = unbox_argument(dummy); - } - } - } - ExprKind::List(l) => self.visit_list(l), - ExprKind::SyntaxRules(s) => self.visit_syntax_rules(s), - ExprKind::Set(s) => { - if let Some(analysis) = self.analysis.get(s.variable.atom_syntax_object().unwrap()) - { - if analysis.kind != IdentifierStatus::HeapAllocated { - self.visit(&mut s.expr); - - return; - } - } - - // Go ahead and drop down the expression - self.visit(&mut s.expr); - - let mut set_expr = ExprKind::List(List::new(Vec::new())); - std::mem::swap(&mut s.expr, &mut set_expr); - - let mut dummy_ident = ExprKind::List(List::new(Vec::new())); - std::mem::swap(&mut dummy_ident, &mut s.variable); - - let new_set_expr = setbox_argument(dummy_ident, set_expr); - - *expr = new_set_expr; - } - ExprKind::Require(r) => self.visit_require(r), - ExprKind::Let(l) => self.visit_let(l), - } - } - - fn visit_define(&mut self, define: &mut Define) { - self.visit(&mut define.body); - } - - fn visit_lambda_function(&mut self, lambda_function: &mut LambdaFunction) { - // Visit the body first, unwind the recursion on the way up - self.visit(&mut lambda_function.body); - - let function_info = self - .analysis - .function_info - .get(&lambda_function.syntax_object_id); - - if let Some(function_info) = function_info { - let mut mutable_variables = Vec::new(); - - // Which arguments do we need to wrap up - for var in &lambda_function.args { - if let Some(ident) = var.atom_identifier() { - if let Some(arg) = function_info.arguments().get(ident) { - if arg.captured && arg.mutated { - mutable_variables.push(var.clone()); - } - } - } else { - unreachable!() - } - } - - if !mutable_variables.is_empty() { - let mut body = ExprKind::List(List::new(Vec::new())); - - std::mem::swap(&mut lambda_function.body, &mut body); - - let wrapped_lambda = LambdaFunction::new( - mutable_variables.clone(), - body, - lambda_function.location.clone(), - ); - - // Box the values! - let mut mutable_variables: Vec<_> = - mutable_variables.into_iter().map(box_argument).collect(); - - mutable_variables.insert(0, ExprKind::LambdaFunction(Box::new(wrapped_lambda))); - lambda_function.body = ExprKind::List(List::new(mutable_variables)); - } - } - } -} - -struct LiftLocallyDefinedFunctions<'a> { - analysis: &'a Analysis, - lifted_functions: Vec, -} - -impl<'a> LiftLocallyDefinedFunctions<'a> { - pub fn new(analysis: &'a Analysis) -> Self { - Self { - analysis, - lifted_functions: Vec::new(), - } - } -} - -impl<'a> VisitorMutRefUnit for LiftLocallyDefinedFunctions<'a> { - fn visit_begin(&mut self, begin: &mut crate::parser::ast::Begin) { - // Traverse down the tree first - start bubbling up the lifted functions - // on the way back up - for expr in &mut begin.exprs { - self.visit(expr); - } - - let mut functions: Vec<(usize, String, SyntaxObjectId)> = Vec::new(); - - for (index, expr) in begin.exprs.iter().enumerate() { - if let ExprKind::Define(define) = expr { - let ident = define.name.atom_syntax_object().unwrap(); - if let Some(info) = define - .body - .lambda_function() - .and_then(|func| self.analysis.get_function_info(func)) - { - let ident_info = self.analysis.get(ident).unwrap(); - - if ident_info.depth > 1 { - if !info.captured_vars.is_empty() { - if log::log_enabled!(log::Level::Trace) { - log::trace!( - target: "lambda-lifting", - "Found a local function which captures variables: {} - captures vars: {:#?}", - define.name, - info.captured_vars - ); - } - - // for (var, _) in info.captured_vars() { - // log::debug!(target: "lambda-lifting", "{}", var.resolve()); - // } - - if info.captured_vars().len() == 1 { - // TODO: Check if the number of captured vars is 1, and if that 1 is equivalent to the - // define name. If it is, then we should just mark this as a pure local function, because - // then we can lift it and be happy about it! - - for (_, info) in info.captured_vars() { - if info.id == define.name_id().unwrap() { - log::trace!(target: "lambda-lifting", "Local define where it only captures itself!"); - - functions.push(( - index, - define.name.atom_identifier().unwrap().to_string(), - define.name_id().unwrap(), - )); - } - } - } - - // println!("{}", define.body); - } else { - log::trace!(target: "lambda-lifting", "Found a pure local function: {}", define.name); - functions.push(( - index, - define.name.atom_identifier().unwrap().to_string(), - define.name_id().unwrap(), - )); - } - } - } - } - } - - for (index, _name, _id) in functions.into_iter().rev() { - // let constructed_name = - // "##lambda-lifting##".to_string() + &name + id.0.to_string().as_str(); - - // let mut dummy_define = ExprKind::Define(Box::new(Define::new( - // ExprKind::atom(name), - // ExprKind::atom(constructed_name), - // SyntaxObject::default(TokenType::Define), - // ))); - - // let mut removed_function = begin.exprs.get_mut(index).unwrap(); - - // std::mem::swap(&mut dummy_define, &mut removed_function); - - let removed_function = begin.exprs.remove(index); - self.lifted_functions.push(removed_function); - } - } -} - -struct CollectReferences { - idents: fxhash::FxHashSet, -} - -impl<'a> VisitorMutUnitRef<'a> for CollectReferences { - fn visit_atom(&mut self, a: &'a Atom) { - if let TokenType::Identifier(ident) = a.syn.ty { - if ident.resolve().starts_with("mangler") { - self.idents.insert(ident); - } - } - } -} - -struct ReplaceBuiltinUsagesInsideMacros<'a> { - identifiers_to_replace: &'a mut HashSet, - analysis: &'a Analysis, -} - -impl<'a> VisitorMutRefUnit for ReplaceBuiltinUsagesInsideMacros<'a> { - fn visit_define(&mut self, define: &mut Define) { - self.visit(&mut define.body); - } - - fn visit_atom(&mut self, a: &mut Atom) { - if let Some(info) = self.analysis.get(&a.syn) { - if info.kind != IdentifierStatus::Global && info.kind != IdentifierStatus::Free { - return; - } - } - - if let Some(ident) = a.ident_mut() { - if self.identifiers_to_replace.contains(ident) { - if let Some((_, builtin_name)) = ident.resolve().split_once(MANGLER_SEPARATOR) { - // println!("RENAMING: {}", ident); - - *ident = ("#%prim.".to_string() + builtin_name).into(); - } - } - } - } -} - -struct ReplaceBuiltinUsagesWithReservedPrimitiveReferences<'a> { - analysis: &'a Analysis, - identifiers_to_replace: &'a mut HashSet, -} - -impl<'a> ReplaceBuiltinUsagesWithReservedPrimitiveReferences<'a> { - pub fn new( - analysis: &'a Analysis, - identifiers_to_replace: &'a mut HashSet, - ) -> Self { - Self { - analysis, - identifiers_to_replace, - } - } -} - -impl<'a> VisitorMutRefUnit for ReplaceBuiltinUsagesWithReservedPrimitiveReferences<'a> { - #[inline] - fn visit_define(&mut self, define: &mut Define) { - // Don't visit the name! - self.visit(&mut define.body); - } - - fn visit_atom(&mut self, a: &mut Atom) { - if let Some(info) = self.analysis.get(&a.syn) { - // println!("Visiting: {}", a); - // println!("{:#?}", info); - - // Don't even touch non globals - if info.kind != IdentifierStatus::Global && info.kind != IdentifierStatus::Free { - // println!("Skipping: {}", a); - - return; - } - - // println!("Visiting: {}", a); - // println!("{:#?}", info); - - // if a.ident() - // .map(|x| x.resolve().ends_with("get-test-mode")) - // .unwrap_or_default() - // { - // println!("Visiting: {}", a); - // println!("{:#?}", info); - // } - - if info.builtin && !info.is_shadowed && info.shadows.is_none() { - // todo!() - - // println!("FOUND UNSHADOWED USAGE OF BUILTIN: {}", a); - - if let Some(ident) = a.ident_mut() { - if let Some((_, builtin_name)) = ident.resolve().split_once(MANGLER_SEPARATOR) { - // let original = *ident; - - self.identifiers_to_replace.insert(*ident); - - *ident = ("#%prim.".to_string() + builtin_name).into(); - - // println!("top level - MUTATED IDENT TO BE: {} -> {}", original, ident); - } - } - } else { - // Check if this _refers_ to a builtin - // println!("Visiting: {}", a); - // println!("{:#?}", info); - - if let Some(refers_to) = info.refers_to { - if let Some(info) = self.analysis.info.get(&refers_to) { - if info.builtin && !info.is_shadowed && info.shadows.is_none() { - // todo!() - - // println!("FOUND UNSHADOWED USAGE OF BUILTIN: {}", a); - - if let Some(ident) = a.ident_mut() { - if let Some((_, builtin_name)) = - ident.resolve().split_once(MANGLER_SEPARATOR) - { - // let original = *ident; - - self.identifiers_to_replace.insert(*ident); - - *ident = ("#%prim.".to_string() + builtin_name).into(); - - // println!("MUTATED IDENT TO BE: {} -> {}", original, ident); - - // println!("{:#?}", info); - } - } - } - } - } - } - } - } -} - -struct ExprContainsIds<'a> { - analysis: &'a Analysis, - ids: &'a HashSet, -} - -impl<'a> ExprContainsIds<'a> { - pub fn contains( - analysis: &'a Analysis, - ids: &'a HashSet, - expr: &ExprKind, - ) -> bool { - matches!( - ExprContainsIds { analysis, ids }.visit(expr), - std::ops::ControlFlow::Break(_) - ) - } -} - -impl<'a> VisitorMutControlFlow for ExprContainsIds<'a> { - fn visit_atom(&mut self, a: &Atom) -> std::ops::ControlFlow<()> { - if let Some(refers_to) = self.analysis.get(&a.syn).and_then(|x| x.refers_to) { - if self.ids.contains(&refers_to) { - return std::ops::ControlFlow::Break(()); - } - } - - std::ops::ControlFlow::Continue(()) - } -} - -struct FlattenAnonymousFunctionCalls<'a> { - analysis: &'a Analysis, -} - -impl<'a> FlattenAnonymousFunctionCalls<'a> { - pub fn flatten(analysis: &'a Analysis, exprs: &mut Vec) { - for expr in exprs { - Self { analysis }.visit(expr); - } - } -} - -impl<'a> VisitorMutRefUnit for FlattenAnonymousFunctionCalls<'a> { - fn visit_list(&mut self, l: &mut List) { - // let mut replacement_body: Option = None; - let mut args = Vec::new(); - - let mut inner_body = None; - let mut changed = false; - - // This is an anonymous function call - if let Some(function_a) = l.first_func_mut() { - // The body is also just an anonymous function call - if let ExprKind::List(inner_l) = &mut function_a.body { - let arg_ids = function_a - .args - .iter() - .map(|x| x.atom_syntax_object().unwrap().syntax_object_id) - .collect::>(); - - let all_dont_contain_references = inner_l.args[1..] - .iter() - .all(|x| !ExprContainsIds::contains(self.analysis, &arg_ids, x)); - - if all_dont_contain_references { - if let Some(function_b) = inner_l.first_func_mut() { - // Then we should be able to flatten the function into one - - // TODO: Check that any of the vars we're using are used in the body expression - // They can only be used in the argument position - - let mut dummy = ExprKind::empty(); - - // Extract the inner body - std::mem::swap(&mut function_b.body, &mut dummy); - - inner_body = Some(dummy); - - // TODO: This doesn't work quite yet - - function_a.args.append(&mut function_b.args); - args.extend(inner_l.args.drain(1..)); - - changed = true; - } - } - } - - if let Some(new_body) = inner_body { - function_a.body = new_body; - } - } - - l.args.append(&mut args); - - if changed { - self.visit_list(l); - } else { - // Visit the children after - for expr in &mut l.args { - self.visit(expr) - } - } - } -} - -struct FunctionCallCollector<'a> { - analysis: &'a Analysis, - functions: FxHashMap>, - black_box: InternedString, - context: Option, - constants: ImmutableHashMap, - should_mangle: bool, -} - -impl<'a> FunctionCallCollector<'a> { - pub fn mangle( - analysis: &'a Analysis, - exprs: &mut Vec, - constants: ImmutableHashMap, - should_mangle: bool, - ) -> FxHashMap> { - let black_box: InternedString = "#%black-box".into(); - - let mut collector = Self { - analysis, - functions: FxHashMap::default(), - context: None, - black_box, - constants, - should_mangle, - }; - - // TODO: @Matt - This needs to get the - for expr in exprs.iter() { - match expr { - ExprKind::Define(define) => { - if let ExprKind::LambdaFunction(_) = &define.body { - let name = define.name.atom_identifier().unwrap().clone(); - - collector.functions.entry(name).or_default(); - } - } - ExprKind::Begin(b) => { - for expr in &b.exprs { - if let ExprKind::Define(define) = expr { - if let ExprKind::LambdaFunction(_) = &define.body { - let name = define.name.atom_identifier().unwrap().clone(); - - collector.functions.entry(name).or_default(); - } - } - } - } - _ => {} - } - - // if let ExprKind::Define(define) = expr { - // if let ExprKind::LambdaFunction(_) = &define.body { - // let name = define.name.atom_identifier().unwrap().clone(); - - // collector.functions.entry(name).or_default(); - // } - // } - } - - for expr in exprs { - collector.visit(expr); - } - - collector.functions - } -} - -impl<'a> VisitorMutRefUnit for FunctionCallCollector<'a> { - fn visit_define(&mut self, define: &mut Define) { - if let ExprKind::LambdaFunction(f) = &mut define.body { - let name = define.name.atom_identifier().unwrap().clone(); - - self.functions.entry(name).or_default(); - - self.context = Some(name); - - self.visit_lambda_function(f); - - // let should_remove = if let Some(callers) = self - // .context - // .as_ref() - // .and_then(|ctx| self.functions.get(ctx)) - // { - // callers.is_empty() - // } else { - // true - // }; - - // if should_remove { - // self.functions.remove(&self.context.unwrap()); - // } - - self.context = None; - } else { - self.visit(&mut define.body); - } - } - - fn visit_list(&mut self, l: &mut List) { - let mut mangle = false; - - if let Some(function) = l.first() { - if let ExprKind::Atom(a) = function { - if let Some(info) = self.analysis.get(&a.syn) { - // At this point... we don't care if its a free identifier - if info.kind == IdentifierStatus::Global || info.kind == IdentifierStatus::Free - { - if let Some(function) = a.ident() { - // If this isn't a function that has been locally defined, - // then we want to mangle the definition of it - if !self.functions.contains_key(function) - && !self.constants.contains_key(function) - { - // Remove this function from the possible list - self.context - .as_ref() - .and_then(|ctx| self.functions.remove(ctx)); - - mangle = true; - } else { - // Mark that this - self.context - .as_ref() - .and_then(|ctx| self.functions.get_mut(ctx)) - .map(|calls| calls.insert(function.clone())); - } - } - } - } - } - } - - // Adjust the function call to be a reference to the black box function - if self.should_mangle && mangle { - *l.first_ident_mut().unwrap() = self.black_box.clone() - } - - for expr in &mut l.args { - self.visit(expr); - } - } - - // Any set expr needs to get purged - fn visit_set(&mut self, s: &mut crate::parser::ast::Set) { - if let Some(function) = s.variable.atom_identifier() { - if self.functions.contains_key(function) { - self.functions.remove(function); - } - } - } -} - -// TODO: There might be opportunity to parallelize this here - perhaps shard the analysis between threads -// across some subset of expressions and then merge afterwards -pub struct SemanticAnalysis<'a> { - // We want to reserve the right to add or remove expressions from the program as needed - pub exprs: &'a mut Vec, - pub analysis: Analysis, -} - -pub enum RequiredIdentifierInformation<'a> { - Resolved(&'a SemanticInformation), - // Raw Identifier, Full path - Unresolved(InternedString, String), -} - -impl<'a> SemanticAnalysis<'a> { - pub fn into_analysis(self) -> Analysis { - self.analysis - } - - pub fn from_analysis(exprs: &'a mut Vec, analysis: Analysis) -> Self { - Self { exprs, analysis } - } - - pub fn new(exprs: &'a mut Vec) -> Self { - let analysis = Analysis::from_exprs(exprs); - Self { exprs, analysis } - } - - pub fn populate_captures(&mut self) { - self.analysis.populate_captures(self.exprs); - } - - pub fn get(&self, object: &SyntaxObject) -> Option<&SemanticInformation> { - self.analysis.get(object) - } - - pub fn get_identifier(&self, identifier: SyntaxObjectId) -> Option<&SemanticInformation> { - self.analysis.info.get(&identifier) - } - - // Syntax object must be the id associated with a given require define statement - pub fn resolve_required_identifier( - &self, - identifier: SyntaxObjectId, - ) -> Option> { - for expr in self.exprs.iter() { - match expr { - ExprKind::Define(d) => { - if is_a_require_definition(&d) && d.name_id() == Some(identifier) { - let module = d - .body - .list() - .and_then(|x| x.get(1)) - .and_then(|x| x.atom_identifier()) - .map(|x| x.resolve())?; - - let prefix = module.trim_start_matches("__module-").to_string() - + d.name.atom_identifier()?.resolve(); - - let top_level_define = self.query_top_level_define(&prefix); - - match top_level_define { - Some(top_level_define) => { - return self - .get_identifier(top_level_define.name_id()?) - .map(RequiredIdentifierInformation::Resolved); - } - None => { - return Some(RequiredIdentifierInformation::Unresolved( - *d.name.atom_identifier()?, - prefix, - )) - } - } - } - } - ExprKind::Begin(b) => { - for expr in &b.exprs { - if let ExprKind::Define(d) = expr { - if is_a_require_definition(&d) && d.name_id() == Some(identifier) { - let module = d - .body - .list() - .and_then(|x| x.get(1)) - .and_then(|x| x.atom_identifier()) - .map(|x| x.resolve())?; - - let prefix = module.trim_start_matches("__module-").to_string() - + d.name.atom_identifier()?.resolve(); - - let top_level_define = self.query_top_level_define(&prefix); - - match top_level_define { - Some(top_level_define) => { - return self - .get_identifier(top_level_define.name_id()?) - .map(RequiredIdentifierInformation::Resolved); - } - None => { - return Some(RequiredIdentifierInformation::Unresolved( - *d.name.atom_identifier()?, - prefix, - )) - } - } - } - } - } - } - _ => {} - } - } - - None - } - - // Syntax object must be the id associated with a given require define statement - pub fn resolve_required_identifiers( - &self, - identifiers: HashSet, - ) -> Vec<(SyntaxObjectId, RequiredIdentifierInformation<'_>)> { - let mut results = Vec::new(); - - let mut resolve_identifier = |d: &Define| -> Option<()> { - if is_a_require_definition(&d) && identifiers.contains(&d.name_id().unwrap()) { - let module = d - .body - .list() - .and_then(|x| x.get(1)) - .and_then(|x| x.atom_identifier()) - .map(|x| x.resolve())?; - - let prefix = module.trim_start_matches("__module-").to_string() - + d.name.atom_identifier()?.resolve(); - - let top_level_define = self.query_top_level_define(&prefix); - - match top_level_define { - Some(top_level_define) => { - results.push(( - d.name_id()?, - self.get_identifier(top_level_define.name_id()?) - .map(RequiredIdentifierInformation::Resolved)?, - )); - } - None => { - results.push(( - d.name_id()?, - RequiredIdentifierInformation::Unresolved( - *d.name.atom_identifier()?, - prefix, - ), - )); - } - } - } - - None - }; - - for expr in self.exprs.iter() { - match expr { - ExprKind::Define(d) => { - resolve_identifier(&d); - } - ExprKind::Begin(b) => { - for expr in &b.exprs { - if let ExprKind::Define(d) = expr { - resolve_identifier(&d); - } - } - } - _ => {} - } - } - - results - } - - pub fn query_top_level_define>( - &self, - name: A, - ) -> Option<&crate::parser::ast::Define> { - query_top_level_define(self.exprs, name) - } - - // Takes the function call, and inlines it at the call sites. In theory, with constant evaluation and - // dead code elimination, this should help streamline some of the more complex cases. This is also just a start. - pub fn inline_function_call>(&mut self, name: A) -> Result<(), SteelErr> { - // TODO: Cloning here is expensive. We should strive to make these trees somehow share the nodes a bit more elegantly. - // As it stands, each time we close a syntax tree, we're going to do a deep clone of the whole thing, which we really don't - // want to do. - let top_level_define_body = self.query_top_level_define(name.as_ref()).ok_or_else( - throw!(TypeMismatch => format!("Cannot inline free identifier!: {}", name.as_ref())), - )?.body.lambda_function().ok_or_else(throw!(TypeMismatch => format!("Cannot inline non function for: {}", name.as_ref())))?.clone(); - - self.find_call_sites_and_modify_with( - name.as_ref(), - |_: &Analysis, lst: &mut crate::parser::ast::List| { - lst.args[0] = ExprKind::LambdaFunction(Box::new(top_level_define_body.clone())); - }, - ); - - Ok(()) - } - - pub fn get_global_id>(&self, name: A) -> Option { - self.query_top_level_define(name)? - .name - .atom_syntax_object() - .map(|x| x.syntax_object_id) - } - - pub fn find_let_call_sites_and_mutate_with(&mut self, func: F) - where - F: FnMut(&Analysis, &mut ExprKind) -> bool, - { - let mut let_call_sites = LetCallSites::new(&self.analysis, func); - for expr in self.exprs.iter_mut() { - let_call_sites.visit(expr); - } - } - - /// In this case, `let` also translates directly to an anonymous function call - pub fn find_anonymous_function_calls_and_mutate_with(&mut self, func: F) - where - F: FnMut(&Analysis, &mut ExprKind) -> bool, - { - let mut anonymous_function_call_sites = - AnonymousFunctionCallSites::new(&self.analysis, func); - for expr in self.exprs.iter_mut() { - anonymous_function_call_sites.visit(expr); - } - } - - pub fn replace_mutable_captured_variables_with_boxes(&mut self) -> &mut Self { - let mut replacer = ReplaceSetOperationsWithBoxes { - analysis: &self.analysis, - }; - - for expr in self.exprs.iter_mut() { - replacer.visit(expr); - } - - self - } - - /// Find all local pure functions, except those defined already at the top level and those defined with 'define', - /// and replace them with a globally defined function. This means we're not going to be recreating - /// the function _on every instance_ and instead can just grab them each time. - /// TODO: @Matt - figure out a way to simplify the construction of closures as well - perhaps introduce - /// some sort of combinator for closures as well. - pub fn lift_all_local_functions(&mut self) -> &mut Self { - let mut lifter = LiftPureFunctionsToGlobalScope::new(&self.analysis); - - for expr in self.exprs.iter_mut() { - lifter.visit(expr); - } - - if !lifter.lifted_functions.is_empty() { - // This is a silly little way to put the lifted functions actually at the top of - // the expression list - lifter.lifted_functions.append(self.exprs); - - *self.exprs = lifter.lifted_functions; - - // log::debug!("Re-running the analysis after lifting local functions"); - self.analysis.fresh_from_exprs(&self.exprs); - self.analysis.populate_captures(self.exprs); - } - - self - } - - pub(crate) fn replace_non_shadowed_globals_with_builtins( - &mut self, - macros: &mut HashMap, - module_manager: &mut ModuleManager, - table: &mut HashSet, - ) -> &mut Self { - let mut replacer = - ReplaceBuiltinUsagesWithReservedPrimitiveReferences::new(&self.analysis, table); - - for expr in self.exprs.iter_mut() { - replacer.visit(expr); - } - - let mut macro_replacer = ReplaceBuiltinUsagesInsideMacros { - identifiers_to_replace: replacer.identifiers_to_replace, - analysis: &self.analysis, - }; - - for steel_macro in macros.values_mut() { - if !steel_macro.is_mangled() { - for expr in steel_macro.exprs_mut() { - macro_replacer.visit(expr); - } - - steel_macro.mark_mangled(); - } - } - - for module in module_manager.modules_mut() { - for steel_macro in module.1.macro_map.values_mut() { - if !steel_macro.is_mangled() { - for expr in steel_macro.exprs_mut() { - macro_replacer.visit(expr); - } - } - - steel_macro.mark_mangled(); - } - } - - for expr in self.exprs.iter_mut() { - macro_replacer.visit(expr); - } - - self.analysis.fresh_from_exprs(self.exprs); - - self - } - - pub(crate) fn remove_unused_globals_with_prefix( - &mut self, - prefix: &str, - macros: &HashMap, - module_manager: &ModuleManager, - ) -> &mut Self { - let module_get_interned: InternedString = "%module-get%".into(); - let proto_hash_get: InternedString = "%proto-hash-get%".into(); - - let steel_constant_module: InternedString = "%-builtin-module-steel/constants".into(); - let void: InternedString = "void".into(); - - let mut collected = CollectReferences { - idents: fxhash::FxHashSet::default(), - }; - - for steel_macro in macros.values() { - // if !steel_macro.is_mangled() { - for expr in steel_macro.exprs() { - collected.visit(expr); - } - // } - } - - for module in module_manager.modules() { - for steel_macro in module.1.macro_map.values() { - // if !steel_macro.is_mangled() { - for expr in steel_macro.exprs() { - collected.visit(expr); - } - // } - } - } - - let found = collected.idents; - - // for ident in &found { - // println!("{}", ident); - // } - - self.exprs.retain_mut(|expression| { - match expression { - ExprKind::Define(define) => { - if let Some(name) = define.name.atom_identifier() { - if name.resolve().starts_with(prefix) { - if let Some(analysis) = define - .name - .atom_syntax_object() - .and_then(|x| self.analysis.get(x)) - { - if analysis.usage_count == 0 { - if let Some(func) = - define.body.list().and_then(|x| x.first_ident()) - { - if *func == module_get_interned || *func == proto_hash_get { - // If this is found inside of a macro, do not remove it - if found.contains(&name) { - // println!("Keeping: {}", name); - return true; - } - - // println!("REMOVING: {}", name); - - return false; - } - } - } - } - } - } - } - ExprKind::Begin(b) => { - let mut offset = 0; - let total_length = b.exprs.len(); - - b.exprs.retain_mut(|expression| { - if let ExprKind::Define(define) = expression { - if let Some(name) = define.name.atom_identifier() { - if name.resolve().starts_with(prefix) { - if let Some(analysis) = define - .name - .atom_syntax_object() - .and_then(|x| self.analysis.get(x)) - { - if analysis.usage_count == 0 { - if let Some(func) = - define.body.list().and_then(|x| x.first_ident()) - { - // (%module-get% - // %-builtin-module-steel/constants - // (quote - // void)) - - if *func == module_get_interned - || *func == proto_hash_get - { - // If this is found inside of a macro, do not remove it - if found.contains(&name) { - // println!("Keeping: {}", name); - offset += 1; - return true; - } - - // println!("REMOVING: {}", name); - - offset += 1; - return false; - } - } - } - } - } - } - } else { - if let ExprKind::List(l) = expression { - if let Some(func) = l.first_ident() { - if *func == module_get_interned { - if l[1].atom_identifier() == Some(&steel_constant_module) { - if let ExprKind::Quote(q) = &l[2] { - if q.expr.atom_identifier() == Some(&void) - && offset < total_length - { - offset += 1; - return false; - } - } - } - } - } - } - } - - offset += 1; - return true; - }); - } - _ => {} - } - - return true; - }); - - // self.exprs.push(ExprKind::ident("void")); - - // log::debug!("Re-running the semantic analysis after removing unused globals"); - - self.analysis.fresh_from_exprs(self.exprs); - - self - } - - /// Find lets without arguments and replace these with just the body of the function. - /// For instance: - /// ```scheme - /// (let () (+ 1 2 3 4 5)) ;; => (+ 1 2 3 4 5) - /// ``` - /// - /// The other function - `replace_pure_empty_lets_with_body` explicitly refers to anonymous functions - /// that lets are naively translated to, so something like this: - /// - /// ```scheme - /// ((lambda () (+ 1 2 3 4 5))) - /// ``` - /// - pub fn replace_no_argument_lets_with_body(&mut self) -> &mut Self { - let mut re_run_analysis = false; - - let func = |_: &Analysis, anon: &mut ExprKind| { - if let ExprKind::Let(l) = anon { - if l.bindings.is_empty() { - let mut dummy = ExprKind::List(List::new(Vec::new())); - std::mem::swap(&mut l.body_expr, &mut dummy); - *anon = dummy; - - re_run_analysis = true; - - return true; - } - } else { - unreachable!() - } - - false - }; - - self.find_let_call_sites_and_mutate_with(func); - - if re_run_analysis { - // log::debug!("Re-running the semantic analysis after modifying let call sites"); - - self.analysis.fresh_from_exprs(self.exprs); - } - - self - } - - pub fn run_black_box( - &mut self, - constants: ImmutableHashMap, - should_mangle: bool, - ) -> FxHashMap> { - let map = FunctionCallCollector::mangle( - &self.analysis, - &mut self.exprs, - constants, - should_mangle, - ); - - // These are all of the global functions referenced - let global_keys_that_reference_non_constant_function = map - .iter() - .filter(|(_, v)| v.is_empty()) - .map(|x| x.0.clone()) - .collect::>(); - - // Only constant evaluatable functions should be ones that references _other_ const functions - map.into_iter() - // This can be better - but this more or less excludes anything recursive from being evaluated - // by the constant evaluator. - .filter(|(k, v)| !v.contains(k)) - .filter(|(_, v)| { - !v.is_empty() && v.is_disjoint(&global_keys_that_reference_non_constant_function) - }) - .collect() - - // map.drain().filter(|x| ) - - // map - } - - /// Find anonymous function calls with no arguments that don't capture anything, - /// and replace this with just the body of the function. For instance: - /// - /// ```scheme - /// (let () (+ 1 2 3 4 5)) ;; => (+ 1 2 3 4 5) - /// - /// ``` - pub fn replace_pure_empty_lets_with_body(&mut self) -> &mut Self { - let mut re_run_analysis = false; - - let func = |analysis: &Analysis, anon: &mut ExprKind| { - if let ExprKind::List(l) = anon { - let arg_count = l.args.len() - 1; - let function = l.args.get_mut(0).unwrap(); - - if let ExprKind::LambdaFunction(f) = function { - let analysis = analysis.get_function_info(f).unwrap(); - - if analysis.captured_vars.is_empty() { - // log::debug!("Found a function that does not capture variables"); - - if f.args.is_empty() && arg_count == 0 { - // Take out the body of the function - we're going to want to use that now - let mut dummy = ExprKind::List(List::new(Vec::new())); - std::mem::swap(&mut f.body, &mut dummy); - *anon = dummy; - - re_run_analysis = true; - - // We changed the function call - we should adjust accordingly - return true; - } - } - } else { - unreachable!() - } - } else { - unreachable!() - } - - false - }; - - self.find_anonymous_function_calls_and_mutate_with(func); - - if re_run_analysis { - // log::debug!("Re-running the semantic analysis after modifications"); - - // self.analysis = Analysis::from_exprs(self.exprs); - self.analysis.fresh_from_exprs(self.exprs); - } - - self - } - - pub fn replace_anonymous_function_calls_with_plain_lets(&mut self) -> &mut Self { - let mut re_run_analysis = false; - - let func = |_: &Analysis, anon: &mut ExprKind| { - if let ExprKind::List(l) = anon { - // Don't replace anonymous function calls that have rest args - those are not yet handled - // with a blind let replacement - if let ExprKind::LambdaFunction(f) = l.args.get(0).unwrap() { - if f.rest { - return false; - } - } - - let function = l.args.remove(0); - - if let ExprKind::LambdaFunction(mut f) = function { - let mut function_body = ExprKind::List(List::new(Vec::new())); - std::mem::swap(&mut f.body, &mut function_body); - - let let_expr = Let::new( - f.args - .iter() - .zip(l.args.iter()) - .map(|x| (x.0.clone(), x.1.clone())) - .collect(), - function_body, - f.location.clone(), - ); - - *anon = ExprKind::Let(let_expr.into()); - - re_run_analysis = true; - // log::debug!("Replaced anonymous function call with let"); - - true - } else { - unreachable!() - } - } else { - unreachable!() - } - - // false - }; - - self.find_anonymous_function_calls_and_mutate_with(func); - - if re_run_analysis { - // log::debug!("Re-running the semantic analysis after modifications"); - - self.analysis.fresh_from_exprs(self.exprs); - } - - self - } - - pub fn refresh_variables(&mut self) -> &mut Self { - for expr in self.exprs.iter_mut() { - RefreshVars.visit(expr); - } - - self.analysis.fresh_from_exprs(self.exprs); - - self - } - - // Modify the call site to point to another kind of expression - pub fn find_call_sites_and_mutate_with(&mut self, name: &str, func: F) - where - F: FnMut(&Analysis, &mut ExprKind), - { - let mut find_call_sites = MutateCallSites::new(name, &self.analysis, func); - - for expr in self.exprs.iter_mut() { - find_call_sites.visit(expr); - } - } - - // Locate the call sites of the given global function name, and calls the given function - // on the node - pub fn find_call_sites_and_call(&self, name: &str, func: F) - where - F: FnMut(&Analysis, &crate::parser::ast::List), - { - let mut find_call_sites = FindCallSites::new(name, &self.analysis, func); - - for expr in self.exprs.iter() { - find_call_sites.visit(expr); - } - } - - // Locate the call sites of the given global function, and calls the given function - // on the node - pub fn find_call_sites_and_modify_with(&mut self, name: &str, func: F) - where - F: FnMut(&Analysis, &mut crate::parser::ast::List), - { - let mut find_call_sites = FindCallSites::new(name, &self.analysis, func); - - for expr in self.exprs.iter_mut() { - find_call_sites.visit(expr); - } - } - - pub fn last_usages(&self) -> impl Iterator { - self.analysis.info.values().filter(|x| x.last_usage) - } - - pub fn free_identifiers(&self) -> impl Iterator { - self.analysis - .info - .values() - .filter(|x| x.kind == IdentifierStatus::Free) - } - - pub fn free_identifiers_with_globals<'b: 'a>( - &self, - globals: &'b SymbolMap, - ) -> Vec<(InternedString, &'_ SemanticInformation)> { - let mut visitor = FreeIdentifierVisitor { - analysis: &self.analysis, - globals, - diagnostics: Vec::new(), - }; - - for expr in self.exprs.iter() { - visitor.visit(expr); - } - - visitor.diagnostics - } - - pub fn unused_variables(&self) -> impl Iterator { - self.analysis.info.values().filter(|x| { - x.usage_count == 0 - && matches!(x.kind, IdentifierStatus::Local | IdentifierStatus::Global) - }) - } - - pub fn unused_local_variables(&self) -> impl Iterator { - self.analysis - .info - .values() - .filter(|x| x.usage_count == 0 && matches!(x.kind, IdentifierStatus::Local)) - } - - pub fn global_defs(&self) -> impl Iterator { - self.analysis - .info - .values() - .filter(|x| x.kind == IdentifierStatus::Global) - } - - pub fn find_global_defs(&self) -> Vec<(InternedString, Span)> { - let mut global_finder = GlobalDefinitionFinder { - analysis: &self.analysis, - globals: Vec::new(), - }; - - for expr in self.exprs.iter() { - global_finder.visit(expr); - } - - global_finder.globals - } - - pub fn built_ins(&self) -> impl Iterator { - self.analysis.info.values().filter(|x| x.builtin) - } - - pub fn find_free_identifiers(&self) -> impl Iterator { - self.analysis - .info - .values() - .filter(|x| x.kind == IdentifierStatus::Free) - } - - pub fn find_unused_arguments(&self) -> Vec<(InternedString, Span)> { - let mut unused = UnusedArguments::new(&self.analysis); - - for expr in self.exprs.iter() { - unused.visit(expr); - } - - unused.unused_args - } - - pub fn find_identifier_at_offset( - &self, - offset: usize, - source_id: SourceId, - ) -> Option<(&SyntaxObjectId, &SemanticInformation)> { - self.analysis.info.iter().find(|(_, x)| { - x.span.range().contains(&offset) && x.span.source_id() == Some(source_id) - }) - } - - // Find semantic context where this offset exist. - pub fn find_contexts_with_offset( - &self, - offset: usize, - source_id: SourceId, - ) -> Vec { - let mut context_finder = FindContextsWithOffset { - analysis: &self.analysis, - offset, - contexts: Vec::new(), - source_id, - }; - - for expr in self.exprs.iter() { - context_finder.visit(expr); - } - - context_finder.contexts - } - - // Convert the syntax object ids back to interned strings. Could end up - // returning nothing if the ids are not found in the target AST, which could - // happen if the analysis gets invalidated by refreshing the vars. - pub fn syntax_object_ids_to_identifiers<'b>( - &self, - ids: &'a mut HashMap>, - ) -> &mut HashMap> { - let mut identifier_finder = IdentifierFinder { ids }; - - for expr in self.exprs.iter() { - identifier_finder.visit(expr); - } - - identifier_finder.ids - } - - /// Converts function applications of the form: - /// - /// ((lambda (x) (global-function-call x)) foo) - /// - /// Into: - /// - /// (global-function-call foo) - /// - /// Note: It is important to run this before lambda lifting, since currently - /// all `lets` are implemented naively as lambdas. - pub fn elide_single_argument_lambda_applications(&mut self) -> &mut Self { - let mut elider = ElideSingleArgumentLambdaApplications { - analysis: &self.analysis, - re_run_analysis: false, - }; - - for expr in self.exprs.iter_mut() { - elider.visit(expr); - } - - if elider.re_run_analysis { - // log::debug!( - // "Re-running the semantic analysis after modifications during lambda lifting" - // ); - - self.analysis.fresh_from_exprs(self.exprs); - self.analysis.populate_captures(self.exprs); - } - - self - } - - pub fn check_if_values_are_redefined(&mut self) -> Result<&mut Self, SteelErr> { - let mut non_builtin_definitions = HashSet::new(); - - for expr in self.exprs.iter() { - match expr { - ExprKind::Define(d) => { - if is_a_builtin_definition(d) || is_a_require_definition(d) { - continue; - } - - let name = d.name.atom_identifier().unwrap(); - - if non_builtin_definitions.contains(name) { - stop!(BadSyntax => format!("Variable re defined within the top level definition: {}", name); d.location.span); - } - - non_builtin_definitions.insert(d.name.atom_identifier().unwrap()); - } - ExprKind::Begin(b) => { - for e in &b.exprs { - if let ExprKind::Define(d) = e { - if is_a_builtin_definition(d) || is_a_require_definition(d) { - continue; - } - - let name = d.name.atom_identifier().unwrap(); - - if non_builtin_definitions.contains(name) { - stop!(BadSyntax => format!("Variable re defined within the top level definition: {}", name); d.location.span); - } - - non_builtin_definitions.insert(d.name.atom_identifier().unwrap()); - } - } - } - _ => {} - } - } - - Ok(self) - } - - // TODO: Right now this lifts and renames, but it does not handle - // The extra arguments necessary for this to work - pub fn lift_pure_local_functions(&mut self) -> &mut Self { - let mut overall_lifted = Vec::new(); - let mut re_run_analysis = false; - - let exprs_len = self.exprs.len(); - - // Window in to the individual values - we're going to want to get access to each of the other ones individually - for i in 0..exprs_len { - let mut local_funcs = LiftLocallyDefinedFunctions::new(&self.analysis); - - // Just borrow for exactly how long we need it for - local_funcs.visit(self.exprs.get_mut(i).unwrap()); - - if !local_funcs.lifted_functions.is_empty() { - re_run_analysis = true; - } - - // Move out the local functions - let mut local_functions = local_funcs.lifted_functions; - - let ids = local_functions - .iter() - .map(|x| { - if let ExprKind::Define(d) = x { - // log::debug!("Found a local function to lift: {}", d.name); - d.name.atom_syntax_object().unwrap().syntax_object_id - } else { - unreachable!() - } - }) - .collect::>(); - - for id in ids { - let mut find_call_site_by_id = - FindUsages::new(id, &self.analysis, |_: &Analysis, usage: &mut Atom| { - if let Some(ident) = usage.ident_mut() { - *ident = ("##lambda-lifting##".to_string() - + ident.resolve() - + id.0.to_string().as_str()) - .into(); - true - } else { - false - } - }); - - // Mutate the call sites of the original expression we were visiting - // Similar to here - just borrow exactly how long we need it for - find_call_site_by_id.visit(self.exprs.get_mut(i).unwrap()); - // Now also mutate the bodies of any of the functions as well - for local_function in &mut local_functions { - find_call_site_by_id.visit(local_function); - // println!("Function: {}", local_function); - - // Also update the name - if let ExprKind::Define(define) = local_function { - if let Some(syntax_object) = define.name.atom_syntax_object() { - if syntax_object.syntax_object_id != id { - continue; - } - } - - if let Some(name) = define.name.atom_identifier_mut() { - *name = ("##lambda-lifting##".to_string() - + name.resolve() - + id.0.to_string().as_str()) - .into(); - } else { - unreachable!("This should explicitly be an identifier here - perhaps a macro was invalid?"); - } - } else { - unreachable!("These should only be defines by design"); - } - } - - // Visit the other ones - for j in 0..exprs_len { - if i != j { - // println!("Revisiting: {}", self.exprs.get_mut(j).unwrap()); - find_call_site_by_id.visit(self.exprs.get_mut(j).unwrap()); - } - } - - // Also visit those that have been lifted out - // for expr in &mut overall_lifted { - // println!("Visiting: {}", expr); - // find_call_site_by_id.visit(expr); - // } - - // Check if we need to re run this analysis - if we've made any modifications up to this point, we - // Need to re run the analysis afterwards - re_run_analysis |= find_call_site_by_id.modified; - } - - // Put the lifted expressions back at the end - they _probably_ should go to the front, but for now - // Lets just put them at the back - overall_lifted.append(&mut local_functions); - } - - // Same as the above - just getting around a double mutable borrow. - // Move the lifted functions to the back of the original expression list - // self.exprs.append(&mut overall_lifted); - - overall_lifted.append(self.exprs); - *self.exprs = overall_lifted; - - if re_run_analysis { - // log::debug!( - // "Re-running the semantic analysis after modifications during lambda lifting" - // ); - - self.analysis.fresh_from_exprs(self.exprs); - // = Analysis::from_exprs(self.exprs); - // self.analysis.populate_captures(self.exprs); - } - - self - } - - pub fn resolve_alias(&self, id: SyntaxObjectId) -> Option { - self.analysis.resolve_alias(id) - } - - pub fn flatten_anonymous_functions(&mut self) { - FlattenAnonymousFunctionCalls::flatten(&self.analysis, self.exprs); - } - - pub fn remove_unused_imports(&mut self) { - let mut unused = RemovedUnusedImports::new(&self.analysis); - for expr in self.exprs.iter_mut() { - unused.visit(expr); - } - } - - pub fn remove_unused_define_imports(&mut self) { - let mut unused = RemoveUnusedDefineImports::new(&self.analysis); - for expr in self.exprs.iter_mut() { - unused.visit(expr); - } - } -} - -#[cfg(test)] -mod analysis_pass_tests { - - use crate::{ - parser::{ast::AstTools, parser::Parser}, - rerrs::ErrorKind, - }; - - use super::*; - - #[test] - fn test_unused_arguments() { - let script = r#" - -(define ##lambda-lifting##loop119067 - (λ (port sum) - (%plain-let - ((next-line (read-line-from-port port))) - (if (equal? (quote eof) next-line) - sum - (##lambda-lifting##loop119067 - port - (+ sum - (%plain-let ((digits (filter char-digit? next-line))) - (%plain-let ((first-digit (first digits)) (second-digit (last digits))) - (string->number (list->string (list first-digit - second-digit))))))))))) - -(define ##lambda-lifting##trie-contains-inner?119977 - (λ (node char-list) - (if (empty? char-list) - #true - (if (char=? (trie-char node) (car char-list)) - (%plain-let ((children-matched - (map (λ (node4) - (##lambda-lifting##trie-contains-inner?119977 node4 (cdr char-list))) - (trie-children node)))) - (if (empty? children-matched) #true (list? (member #true children-matched)))) - #false)))) - -(define ##lambda-lifting##loop120600 - (λ (port sum) - (%plain-let ((next-line (read-line-from-port port))) - (if (equal? (quote eof) next-line) - sum - (##lambda-lifting##loop120600 port (+ sum (process-line next-line))))))) - -(define scan - (λ (path) (%plain-let ((file (open-input-file path))) (##lambda-lifting##loop119067 file 0)))) - -(displayln (scan "aoc/day1.data")) - -(define word-map - (transduce (list (cons "one" "1") - (cons "two" "2") - (cons "three" "3") - (cons "four" "4") - (cons "five" "5") - (cons "six" "6") - (cons "seven" "7") - (cons "eight" "8") - (cons "nine" "9") - (cons "1" "1") - (cons "2" "2") - (cons "3" "3") - (cons "4" "4") - (cons "5" "5") - (cons "6" "6") - (cons "7" "7") - (cons "8" "8") - (cons "9" "9")) - (mapping (λ (pair) (cons (string->list (car pair)) (cadr pair)))) - (into-hashmap))) - -(define sample - (map - symbol->string - (quote - (two1nine eightwothree abcone2threexyz xtwone3four 4nineeightseven2 zoneight234 7pqrstsixteen)))) - -(displayln sample) - -(begin - (define ___trie-options___ - (hash (quote #:transparent) - #true - (quote #:name) - (quote trie) - (quote #:fields) - (quote (char children end-word? word-up-to)) - (quote #:printer) - (λ (obj printer-function) - (begin - (display "(") - (display (symbol->string (quote trie))) - (display " ") - (printer-function (trie-char obj)) - (display " ") - (printer-function (trie-children obj)) - (display " ") - (printer-function (trie-end-word? obj)) - (display " ") - (printer-function (trie-word-up-to obj)) - (display ")"))) - (quote #:mutable) - #false)) - (define trie (quote unintialized)) - (define struct:trie (quote uninitialized)) - (define trie? (quote uninitialized)) - (define trie-char (quote uninitialized)) - (define trie-children (quote uninitialized)) - (define trie-end-word? (quote uninitialized)) - (define trie-word-up-to (quote uninitialized)) - (%plain-let ((prototypes (make-struct-type (quote trie) 4))) - (%plain-let ((struct-type-descriptor (list-ref prototypes 0)) - (constructor-proto (list-ref prototypes 1)) - (predicate-proto (list-ref prototypes 2)) - (getter-proto (list-ref prototypes 3))) - (begin - (set! struct:trie struct-type-descriptor) - (#%vtable-update-entry! struct-type-descriptor #false ___trie-options___) - (set! trie constructor-proto) - (set! trie? predicate-proto) - (set! trie-char (λ (this) (getter-proto this 0))) - (set! trie-children (λ (this) (getter-proto this 1))) - (set! trie-end-word? (λ (this) (getter-proto this 2))) - (set! trie-word-up-to (λ (this) (getter-proto this 3))) - void)))) - -(define empty (quote ())) - -(define empty-trie (trie void empty #false empty)) - -(define flatten - (λ (lst) - (if (null? lst) - empty - (if (list? lst) (append (flatten (car lst)) (flatten (cdr lst))) (list lst))))) - -(define create-children - (λ (char-list lst prefix-chars) - (if (= (length char-list) 1) - (handle-last-letter char-list lst prefix-chars) - (handle-intern-letter char-list lst prefix-chars)))) - -(define handle-last-letter - (λ (char-list lst prefix-chars) - (%plain-let - ((char (first char-list))) - (%plain-let - ((next-prefix (push-back prefix-chars char))) - (if (empty? lst) - (list (trie char empty #true next-prefix)) - (if (< char (trie-char (first lst))) - (cons (trie char empty #true next-prefix) lst) - (if (equal? char (trie-char (first lst))) - (cons (trie char (trie-children (first lst)) #true next-prefix) (rest lst)) - (cons (first lst) (create-children char-list (rest lst) prefix-chars))))))))) - -(define handle-intern-letter - (λ (char-list lst prefix-chars) - (%plain-let - ((char (first char-list))) - (%plain-let - ((next-prefix (push-back prefix-chars char))) - (if (empty? lst) - (list (trie char (create-children (rest char-list) empty next-prefix) #false next-prefix)) - (if (< char (trie-char (first lst))) - (cons - (trie char (create-children (rest char-list) empty next-prefix) #false next-prefix) - lst) - (if (equal? char (trie-char (first lst))) - (cons - (trie char - (create-children (rest char-list) (trie-children (first lst)) next-prefix) - (trie-end-word? (first lst)) - (trie-word-up-to (first lst))) - (rest lst)) - (cons (first lst) (create-children char-list (rest lst) prefix-chars))))))))) - -(define insert - (λ (root-trie word) - (%plain-let ((char-list (string->list word))) - (trie (trie-char root-trie) - (create-children char-list (trie-children root-trie) empty) - (trie-end-word? root-trie) - (trie-word-up-to root-trie))))) - -(define triestring (trie-word-up-to trie-node)) - (flatten (map pre-order (trie-children trie-node)))) - (flatten (map pre-order (trie-children trie-node)))))) - -(define trie-contains-prefix? - (λ (root word) - (%plain-let - ((root-word-char-list (if (string? word) (string->list word) word))) - (list? (member #true - (map (λ (node) - (##lambda-lifting##trie-contains-inner?119977 node root-word-char-list)) - (trie-children root))))))) - -(define my-trie - (build-trie-from-list-of-words empty-trie - (quote ("one" "two" - "three" - "four" - "five" - "six" - "seven" - "eight" - "nine" - "1" - "2" - "3" - "4" - "5" - "6" - "7" - "8" - "9")))) - -(define check-slices - (λ (trie-root word-map line) - (%plain-let - ((line-length (length line)) (loop 123)) - (%plain-let - ((loop4 (#%box loop))) - (%plain-let ((_____loop0 (λ (offset amount accum) - (if (> (+ offset amount) line-length) - (reverse accum) - (if (trie-contains-prefix? trie-root (slice line offset amount)) - (if (hash-contains? word-map (slice line offset amount)) - ((#%unbox loop4) - (+ offset 1) - 1 - (cons (hash-get word-map (slice line offset amount)) - accum)) - ((#%unbox loop4) offset (+ 1 amount) accum)) - ((#%unbox loop4) (+ 1 offset) 1 accum)))))) - (begin - (#%set-box! loop4 _____loop0) - ((#%unbox loop4) 0 1 (quote ())))))))) - -(define process-line - (λ (line) - (%plain-let ((result (check-slices my-trie word-map (string->list line)))) - (string->number (apply string-append (list (first result) (last result))))))) - -(define scan2 - (λ (path) (%plain-let ((file (open-input-file path))) (##lambda-lifting##loop120600 file 0)))) - -(displayln (scan2 "aoc/day1.data")) - "#; - - let mut exprs = Parser::parse(script).unwrap(); - let analysis = SemanticAnalysis::new(&mut exprs); - // analysis.replace_pure_empty_lets_with_body(); - - // Log the free identifiers - let free_vars = analysis.find_unused_arguments(); - - for var in free_vars { - crate::rerrs::report_info( - ErrorKind::FreeIdentifier.to_error_code(), - "input.rkt", - script, - "unused arguments".to_string(), - var.1, - ); - } - } - - #[test] - fn test_free_identifiers() { - let script = r#" - -(begin - (define ___mcons-options___ - (hash - (quote - #:fields) - (quote - (mcar mcdr)) - (quote - #:transparent) - #false - (quote - #:name) - (quote - mcons) - (quote - #:printer) - (λ (obj printer) - (if (mlist? obj) - (begin - (simple-display "'") - (printer (mcons->list obj))) - (begin - (simple-display "'(") - (printer (mcons-mcar obj)) - (simple-display " . ") - (printer (mcons-mcdr obj)) - (simple-display ")")))) - (quote - #:mutable) - #true)) - (define mcons - (quote - unintialized)) - (define struct:mcons - (quote - uninitialized)) - (define mcons? - (quote - uninitialized)) - (define mcons-mcar - (quote - uninitialized)) - (define mcons-mcdr - (quote - uninitialized)) - (define set-mcons-mcar! - (quote - unintialized)) - (define set-mcons-mcdr! - (quote - unintialized)) - (%plain-let ((prototypes (make-struct-type - (quote - mcons) - 2))) - (%plain-let ((struct-type-descriptor (list-ref - prototypes - 0)) - (constructor-proto (list-ref prototypes 1)) - (predicate-proto (list-ref prototypes 2)) - (getter-proto (list-ref prototypes 3))) - (begin - (set! struct:mcons struct-type-descriptor) - (#%vtable-update-entry! - struct-type-descriptor - #false - ___mcons-options___) - (set! mcons - (λ (mcar mcdr) - (constructor-proto - (#%box mcar) - (#%box mcdr)))) - (set! mcons? predicate-proto) - (set! mcons-mcar - (λ (this) - (#%unbox (getter-proto this 0)))) - (set! mcons-mcdr - (λ (this) - (#%unbox (getter-proto this 1)))) - (set! set-mcons-mcar! - (λ (this value) - (#%set-box! - (getter-proto this 0) - value))) - (set! set-mcons-mcdr! - (λ (this value) - (#%set-box! - (getter-proto this 1) - value))) - void)))) - -(define ##lambda-lifting##loop118915 - (λ (mutable-cons3 builder) - (if (not - (mcons? - (%plain-let ((result (mcons-mcdr - mutable-cons3))) - (begin - (simple-display - (quote - (mcons-mcdr mutable-cons))) - (simple-display " = ") - (simple-displayln result) - result)))) - (#%prim.cons (mcons-mcar mutable-cons3) builder) - (##lambda-lifting##loop118915 - (mcons-mcdr mutable-cons3) - (#%prim.cons - (mcons-mcar mutable-cons3) - builder))))) - -(define set-car! - set-mcons-mcar!) - -(define set-cdr! - set-mcons-mcdr!) - -(define mcons->list - (λ (mutable-cons) - (reverse - (##lambda-lifting##loop118915 - mutable-cons - (quote - ()))))) - -(define mlist? - (λ (cell) - (%plain-let ((next (mcons-mcdr cell))) - (%plain-let ((z (mcons? next))) - (if z z (null? next)))))) - -(define pair? - (λ (x) - (%plain-let ((z (mcons? x))) - (if z z (#%prim.pair? x))))) - -(define cons - (λ (a b !!dummy-rest-arg!!) - (%plain-let ((!!dummy-rest-arg!!3 (apply - %keyword-hash - !!dummy-rest-arg!!))) - (%plain-let ((mutable (%plain-let ((mutable (hash-try-get - !!dummy-rest-arg!!3 - (quote - #:mutable)))) - (if (hash-contains? - !!dummy-rest-arg!!3 - (quote - #:mutable)) - mutable - #false)))) - (if mutable - (mcons a b) - (if (list? b) - (#%prim.cons a b) - (if (mcons? b) - (mcons a b) - (#%prim.cons a b)))))))) - -(define car - (λ (a) - (if (mcons? a) (mcons-mcar a) (#%prim.car a)))) - -(define cdr - (λ (a) - (if (mcons? a) (mcons-mcdr a) (#%prim.cdr a)))) - - "#; - - let mut exprs = Parser::parse(script).unwrap(); - let mut analysis = SemanticAnalysis::new(&mut exprs); - analysis.replace_pure_empty_lets_with_body(); - - // Log the free identifiers - let free_vars = analysis.find_free_identifiers(); - - for var in free_vars { - crate::rerrs::report_info( - ErrorKind::FreeIdentifier.to_error_code(), - "input.rkt", - script, - "Free identifier".to_string(), - var.span, - ); - } - } - - #[test] - fn local_defines() { - let script = r#" - (define (applesauce x y z) (list x y z)) - - (define (bananas blegh) (applesauce blegh 10 20)) - - "#; - - let mut exprs = Parser::parse(script).unwrap(); - let mut analysis = SemanticAnalysis::new(&mut exprs); - analysis.populate_captures(); - - // Inline top level definition -> naively just replace the value with the updated value - // This should allow constant propagation to take place. TODO: Log optimization misses - analysis.inline_function_call("applesauce").unwrap(); - - analysis.exprs.pretty_print(); - - // analysis. - - // for var in analysis.last_usages() { - // crate::rerrs::report_info( - // ErrorKind::FreeIdentifier.to_error_code(), - // "input.rkt", - // script, - // format!("last usage"), - // var.span, - // ); - // } - } - - #[test] - fn transducer_last_usages() { - let script = r#" - - (define tmap - (λ (f) - (λ (reducer) - (λ args - (let ((l (length args))) - (if (= l (length (quote ()))) - (apply (λ () (reducer)) args) - (if (= l (length (quote (result)))) - (apply (λ (result) (reducer result)) args) - (if (= l (length (quote (result input)))) - (apply - (λ (result input) - (reducer result (f input))) - args) - (error! "Arity mismatch"))))))))) - "#; - - let mut exprs = Parser::parse(script).unwrap(); - let mut analysis = SemanticAnalysis::new(&mut exprs); - analysis.populate_captures(); - - for var in analysis.last_usages() { - crate::rerrs::report_info( - ErrorKind::FreeIdentifier.to_error_code(), - "input.rkt", - script, - "last usage".to_string(), - var.span, - ); - } - } - - #[test] - fn escaping_functions() { - let script = r#" - (define generate-one-element-at-a-time - (λ (lst) - ((λ (control-state) - ((λ (control-state0) - (begin - (set! control-state control-state0) - (λ () - (call/cc control-state)))) - (λ (return) - (begin - (for-each - (λ (element) - (set! return - (call/cc - (λ (resume-here) - (begin - (set! control-state resume-here) - (return element)))))) - lst) - (return (quote you-fell-off-the-end)))))) - 123))) - - "#; - - let mut exprs = Parser::parse(script).unwrap(); - let mut analysis = SemanticAnalysis::new(&mut exprs); - analysis.populate_captures(); - - let let_vars = analysis - .analysis - .info - .values() - .filter(|x| x.kind == IdentifierStatus::HeapAllocated); - - for var in let_vars { - println!("{var:?}"); - crate::rerrs::report_info( - ErrorKind::FreeIdentifier.to_error_code(), - "input.rkt", - script, - "let-var".to_string(), - var.span, - ); - } - } - - #[test] - fn last_usages_test() { - let script = r#" - (define Y - (lambda (f) - ((lambda (x) (x x)) - (lambda (x) (f (lambda (y) ((x x) y))))))) - "#; - - let mut exprs = Parser::parse(script).unwrap(); - let mut analysis = SemanticAnalysis::new(&mut exprs); - analysis.populate_captures(); - - for var in analysis.last_usages() { - crate::rerrs::report_info( - ErrorKind::FreeIdentifier.to_error_code(), - "input.rkt", - script, - "last usage".to_string(), - var.span, - ); - } - } - - #[test] - fn mutated_and_captured() { - let script = r#" - (define (foo x) - (lambda (y) - (displayln x) - (set! x y))) - "#; - - let mut exprs = Parser::parse(script).unwrap(); - - let mut analysis = SemanticAnalysis::new(&mut exprs); - analysis.populate_captures(); - } - - #[test] - fn local_vars() { - let script = r#" - (define (applesauce) - (+ 10 20 30 - (%plain-let ((a 10) (b 20)) - (+ a b)))) - "#; - - let mut exprs = Parser::parse(script).unwrap(); - - let analysis = SemanticAnalysis::new(&mut exprs); - - let let_vars = analysis - .analysis - .info - .values() - .filter(|x| x.kind == IdentifierStatus::Local); - - for var in let_vars { - println!("{var:?}"); - crate::rerrs::report_info( - ErrorKind::FreeIdentifier.to_error_code(), - "input.rkt", - script, - "let-var".to_string(), - var.span, - ); - } - } - - #[test] - fn tail_call_eligible_test_with_let() { - let script = r#" - (define (loop value accum) - ;; This should not get registered as a tail call - (loop value (cons value accum)) - (let ((applesauce 10)) - (if #true - (if #true - (loop value (cons value accum)) - (loop value (cons value accum))) - (loop value accum)))) - "#; - - let mut exprs = Parser::parse(script).unwrap(); - let mut analysis = SemanticAnalysis::new(&mut exprs); - analysis.populate_captures(); - - let tail_calls = analysis - .analysis - .call_info - .values() - .filter(|x| matches!(x.kind, CallKind::SelfTailCall(_))) - .collect::>(); - - assert_eq!(tail_calls.len(), 3); - - for var in tail_calls { - println!("{var:?}"); - - crate::rerrs::report_info( - ErrorKind::FreeIdentifier.to_error_code(), - "input.rkt", - script, - "tail call".to_string(), - var.span, - ); - } - } - - #[test] - fn tail_call_eligible_test_apply() { - let script = r#" - (define list-transduce - (λ args - (displayln args) - (let ((l (length args))) - (if (= l (length (quote (xform f coll)))) - (apply - (λ (xform f coll) - (displayln f) - (displayln (multi-arity? f)) - (displayln list-transduce) - (list-transduce xform f (f) coll)) - args) - (if (= l (length (quote (xform f init coll)))) - (apply - (λ (xform f init coll) - (let ((xf (xform f))) - (let ((result (list-reduce xf init coll))) - (xf result)))) - args) - (error! "Arity mismatch")))))) - "#; - - let mut exprs = Parser::parse(script).unwrap(); - let mut analysis = SemanticAnalysis::new(&mut exprs); - analysis.populate_captures(); - - let tail_calls = analysis - .analysis - .call_info - .values() - .filter(|x| matches!(x.kind, CallKind::SelfTailCall(_))); - - for var in tail_calls { - crate::rerrs::report_info( - ErrorKind::FreeIdentifier.to_error_code(), - "input.rkt", - script, - "tail call".to_string(), - var.span, - ); - } - } - - #[test] - fn tail_call_eligible_test() { - let script = r#" - (begin - (define loop1 - (λ (x) - (if (= x 100) x (loop1 (+ x 1)))))) - - (define (loop value accum) - ;; This should not get registered as a tail call - (loop value (cons value accum)) - (if #true - (if #true - (loop value (cons value accum)) - (loop value (cons value accum))) - (loop value accum))) - "#; - - let mut exprs = Parser::parse(script).unwrap(); - let mut analysis = SemanticAnalysis::new(&mut exprs); - analysis.populate_captures(); - - let tail_calls = analysis - .analysis - .call_info - .values() - .filter(|x| matches!(x.kind, CallKind::SelfTailCall(_))); - - for var in tail_calls { - crate::rerrs::report_info( - ErrorKind::FreeIdentifier.to_error_code(), - "input.rkt", - script, - "tail call".to_string(), - var.span, - ); - } - } - - #[test] - fn find_last_usages() { - let script = r#" - (define (loop value accum) - (+ value accum) - (loop value (cons value accum))) - "#; - - let mut exprs = Parser::parse(script).unwrap(); - let analysis = SemanticAnalysis::new(&mut exprs); - - let last_usages = analysis.last_usages().collect::>(); - // In this case, we should be identifying just the two usages for - // value and accum in the recursive call - assert_eq!(last_usages.len(), 2); - } - - #[test] - fn analysis_pass_finds_call_sites() { - let script = r#" - (define (foo) (+ 1 2 3 4 5)) - (define (test) (let ((a 10) (b 20)) (foo))) - (foo) - (foo) - (begin - (foo) - (foo) - (foo)) - "#; - - let mut exprs = Parser::parse(script).unwrap(); - let analysis = SemanticAnalysis::new(&mut exprs); - - let mut count = 0; - - analysis.find_call_sites_and_call("foo", |_, _| count += 1); - - assert_eq!(count, 6); - } - - #[test] - fn resolve_alias() { - let script = r#" - (define list (%module-get%)) - (define alias-list list) - (define alias-list2 alias-list) - (define alias-list3 alias-list2) - (define alias-list4 alias-list3) - "#; - - // let mut analysis = Analysis::default(); - let mut exprs = Parser::parse(script).unwrap(); - { - let analysis = SemanticAnalysis::new(&mut exprs); - - let list_id = analysis - .query_top_level_define("list") - .and_then(|x| x.name_id()) - .unwrap(); - - let alias_list_4_id = analysis - .query_top_level_define("alias-list4") - .and_then(|x| x.name_id()) - .unwrap(); - - let found = analysis.resolve_alias(alias_list_4_id); - - assert_eq!(list_id, found.unwrap()); - } - } - - #[test] - fn test_capture() { - let script = r#" - - (define (adder x y z) - (set! x 100) - (lambda () - (+ x y z) - (lambda () - (+ x y z) - (lambda () - (+ x y z) - (lambda () (+ x y z)))))) - - "#; - - // let mut analysis = Analysis::default(); - let mut exprs = Parser::parse(script).unwrap(); - { - let mut analysis = SemanticAnalysis::new(&mut exprs); - analysis.populate_captures(); - - // This _should_ print I think 4 lambdas that will have captured x -> each one should actually cascade the capture downward, copying in each value directly - // Since x is an immutable capture, we're okay with this - for function in analysis - .analysis - .function_info - .values() - .filter(|x| !x.captured_vars().is_empty()) - { - println!("{:?}", function.captured_vars()); - } - - println!("{:?}", analysis.analysis.info.get(&SyntaxObjectId(2))); - - // println!("{:#?}", escaping_functions); - } - } - - #[test] - fn test_complicated_escape_analysis() { - let script = r#" - - (define (func-lift x) x) - - (define (adder x) - - ;; x == 2 - - (define func (lambda () x)) - ;; This is pretty eligible for some sort of - ;; lifting? closure conversion? - (define func - (let ((a 10) (b 20) (c 30)) - (lambda () (+ a b c)))) - - (func) -> (func-lift x) ;; replace known callsites with the lifted version - (func) -> (func-lift x) - - (set! x 20) - - (func) -> (func-lift x) - (func) -> (func-lift x) - - func ;; replace last callsite with something like - ;; (lambda () x) -> doesn't interfere with previous calls, and now captures the var - ) - "#; - - // let mut analysis = Analysis::default(); - let mut exprs = Parser::parse(script).unwrap(); - { - let analysis = SemanticAnalysis::new(&mut exprs); - - let escaping_functions = analysis - .analysis - .function_info - .values() - .filter(|x| x.escapes) - .collect::>(); - - println!("{escaping_functions:#?}"); - } - } - - #[test] - fn test_lifting_local_functions_to_global_scope() { - let script = r#" - (define (test) - (mapping (lambda (x) 10) (list 1 2 3 4 5))) - "#; - - let mut exprs = Parser::parse(script).unwrap(); - - { - let mut analysis = SemanticAnalysis::new(&mut exprs); - - analysis.lift_all_local_functions(); - } - - for expr in exprs { - println!("{}", expr.to_pretty(60)) - } - } - - #[test] - fn test_rudimentary_escape_analysis() { - let script = r#" - (define (adder x) - - (map (lambda (y) (+ x y)) (list 1 2 3 4 5)) - - (black-box (lambda (y) (+ x y)))) - "#; - - // let mut analysis = Analysis::default(); - let mut exprs = Parser::parse(script).unwrap(); - { - let analysis = SemanticAnalysis::new(&mut exprs); - - let escaping_functions = analysis - .analysis - .function_info - .values() - .filter(|x| x.escapes) - .collect::>(); - - println!("{escaping_functions:#?}"); - } - } - - #[test] - fn test_black_box_mangling() { - let script = r#" - (define (fib n) - (if (<= n 2) - 1 - (+ (fib (- n 1)) (fib (- n 2))))) - - (define (obviously-not-function-that-works) - (if (list 100 20 30 40 50 60) - (random-call-not-const) - (other-random-call-not-const))) - - (fib 100) - (obviously-not-function-that-works) - - "#; - - // let mut analysis = Analysis::default(); - let mut exprs = Parser::parse(script).unwrap(); - { - let mut analysis = SemanticAnalysis::new(&mut exprs); - - let constants = im_rc::hashmap! { - "+".into() => SteelVal::Void, - "<=".into() => SteelVal::Void, - "-".into() => SteelVal::Void, - }; - - let result = analysis.run_black_box(constants, true); - - println!("{:#?}", result); - - // Only select expressions which are identified as constant - - analysis - .exprs - .iter() - .filter(|expr| { - if let ExprKind::Define(define) = expr { - if let ExprKind::LambdaFunction(_) = &define.body { - let name = define.name.atom_identifier().unwrap().clone(); - - return result.contains_key(&name); - - // collector.functions.entry(name).or_default(); - } - } - - false - }) - .collect::>() - .pretty_print(); - - // let constant_exprs = analysis.exprs.iter().filter(|x| ) - - // analysis.exprs.pretty_print(); - } - } - - #[test] - fn check_analysis_pass() { - // let mut builder = Builder::new(); - - let script = r#" - - (define + (%module-get%)) - (define list (%module-get%)) - - (define alias-list list) - (define alias-list2 alias-list) - (define alias-list3 alias-list2) - (define alias-list4 alias-list3) - - (let () - (let () - (let () - (let () (+ 1 2 3 4 5))))) - - (+ applesauce 20) - - (define applesauce 100) - - ;(let ((a 10) (b 20)) - ; (+ a b c)) - - ;(define (foo x y z) - ; (define (inner-func-that-captures a b c) - ; (inner-func-that-captures x y z)) - ; (inner-func-that-captures 1 2 3)) - - (define (test) - (define (foo) - (bar)) - (define (bar) - (foo)) - (foo)) - - (define (loop value accum) - (loop (cons value accum))) - - ;(define (foo x y z) - ; (let ((x x) (y y)) - ; (lambda (extra-arg x) - ; (set! x 100) - ; (set! foo "hello world") - ; (+ z z z)))) - - ;(define (test x y z) - ; (let ((foo foo)) - ; (foo x y z) - ; (foo 1 2 3) - ; (foo 10 20 30))) - - ;(foo "applesauce" "bananas" "sauce") - "#; - - // let mut analysis = Analysis::default(); - let mut exprs = Parser::parse(script).unwrap(); - { - let mut analysis = SemanticAnalysis::new(&mut exprs); - analysis.replace_pure_empty_lets_with_body(); - - // Log the free identifiers - let free_vars = analysis.find_free_identifiers(); - - for var in free_vars { - crate::rerrs::report_info( - ErrorKind::FreeIdentifier.to_error_code(), - "input.rkt", - script, - "Free identifier".to_string(), - var.span, - ); - } - - let _unused_args = analysis.find_unused_arguments(); - - // println!("Unused args: {unused_args:?}"); - - // for var in analysis.unused_variables() { - // crate::rerrs::report_warning( - // ErrorKind::FreeIdentifier.to_error_code(), - // "input.rkt", - // script, - // "Unused variable".to_string(), - // var.span, - // ); - // } - - // for var in analysis.global_defs() { - // crate::rerrs::report_info( - // ErrorKind::FreeIdentifier.to_error_code(), - // "input.rkt", - // script, - // "global var".to_string(), - // var.span, - // ); - // } - - // for var in analysis.last_usages() { - // crate::rerrs::report_info( - // ErrorKind::FreeIdentifier.to_error_code(), - // "input.rkt", - // script, - // "last usage of variable".to_string(), - // var.span, - // ); - // } - - analysis.lift_pure_local_functions(); - // analysis.lift_local_functions(); - - for expr in analysis.exprs.iter() { - println!("{expr}"); - } - - let list_id = analysis - .query_top_level_define("list") - .unwrap() - .name - .atom_syntax_object() - .unwrap() - .syntax_object_id; - - let alias_list_4_id = analysis - .query_top_level_define("alias-list4") - .unwrap() - .name - .atom_syntax_object() - .unwrap() - .syntax_object_id; - - let found = analysis.resolve_alias(alias_list_4_id); - - println!( - "List id: {list_id}, list 4 id: {alias_list_4_id}, resolved alias id: {found:?}" - ); - } - } -} diff --git a/crates/steel-core/src/compiler/passes/begin.rs b/crates/steel-core/src/compiler/passes/begin.rs deleted file mode 100644 index f021edbaa..000000000 --- a/crates/steel-core/src/compiler/passes/begin.rs +++ /dev/null @@ -1,656 +0,0 @@ -use log::{debug, log_enabled}; -use steel_parser::tokens::MaybeBigInt; - -use crate::parser::{ - ast::{Atom, Begin, ExprKind, LambdaFunction, List, Set}, - parser::SyntaxObject, -}; -use crate::parser::{interner::InternedString, tokens::TokenType}; -use std::{collections::HashSet, time::Instant}; - -use super::{Folder, VisitorMutUnit}; - -pub(crate) struct FlattenBegin {} -impl FlattenBegin { - pub(crate) fn flatten(expr: ExprKind) -> ExprKind { - FlattenBegin {}.visit(expr) - } -} - -impl Folder for FlattenBegin { - #[inline] - fn visit_begin(&mut self, begin: Begin) -> ExprKind { - let span = begin.location; - - if begin.exprs.len() == 1 { - return self.visit(begin.exprs.into_iter().next().unwrap()); - } - - // Flatten begins - let flattened_exprs = begin - .exprs - .into_iter() - .flat_map(|x| { - if let ExprKind::Begin(b) = x { - b.exprs - .into_iter() - .map(|x| self.visit(x)) - .collect::>() - } else { - vec![self.visit(x)] - } - }) - .collect::>(); - - if flattened_exprs.len() == 1 { - flattened_exprs.into_iter().next().unwrap() - } else { - ExprKind::Begin(Begin::new(flattened_exprs, span)) - } - } -} - -pub fn flatten_begins_and_expand_defines(exprs: Vec) -> Vec { - let flatten_begins_and_expand_defines_time = Instant::now(); - - let res = exprs - .into_iter() - .map(FlattenBegin::flatten) - .map(ConvertDefinesToLets::convert_defines) - .collect(); - - if log_enabled!(log::Level::Debug) { - debug!( - target: "pipeline_time", - "Flatten begins and expand defines time: {:?}", - flatten_begins_and_expand_defines_time.elapsed() - ); - } - - res -} - -struct DefinedVars { - defined_identifiers: HashSet, - output: bool, -} - -impl DefinedVars { - fn new() -> Self { - DefinedVars { - defined_identifiers: HashSet::new(), - output: false, - } - } - - fn insert(&mut self, name: InternedString) { - self.defined_identifiers.insert(name); - } - - fn check_output(&mut self) -> bool { - let output = self.output; - self.output = false; - output - } -} - -impl VisitorMutUnit for DefinedVars { - fn visit_atom(&mut self, a: &Atom) { - if let TokenType::Identifier(ident) = &a.syn.ty { - self.output = self.defined_identifiers.contains(ident) || self.output; - } - } -} - -struct ConvertDefinesToLets { - depth: usize, -} - -impl ConvertDefinesToLets { - fn new() -> Self { - Self { depth: 0 } - } - - fn convert_defines(expr: ExprKind) -> ExprKind { - ConvertDefinesToLets::new().visit(expr) - } -} - -impl Folder for ConvertDefinesToLets { - #[inline] - fn visit_lambda_function(&mut self, mut lambda_function: Box) -> ExprKind { - self.depth += 1; - lambda_function.body = self.visit(lambda_function.body); - self.depth -= 1; - ExprKind::LambdaFunction(lambda_function) - } - - // TODO - #[inline] - fn visit_begin(&mut self, mut begin: Begin) -> ExprKind { - if self.depth > 0 { - match convert_exprs_to_let(begin) { - ExprKind::Begin(mut b) => { - b.exprs = b.exprs.into_iter().map(|e| self.visit(e)).collect(); - ExprKind::Begin(b) - } - ExprKind::List(mut l) => { - l.args = l.args.into_iter().map(|x| self.visit(x)).collect(); - ExprKind::List(l) - } - ExprKind::Let(mut l) => { - let mut visited_bindings = Vec::new(); - - for (binding, expr) in l.bindings { - visited_bindings.push((self.visit(binding), self.visit(expr))); - } - - l.bindings = visited_bindings; - l.body_expr = self.visit(l.body_expr); - - ExprKind::Let(l) - } - other => panic!("Something went wrong in define conversion, found: {other:?}"), - } - } else { - // println!("Ignoring begin"); - begin.exprs = begin.exprs.into_iter().map(|e| self.visit(e)).collect(); - ExprKind::Begin(begin) - } - } -} - -#[derive(PartialEq, Debug)] -enum ExpressionType { - DefineConst(InternedString), - DefineFlat(InternedString), - DefineFlatStar(InternedString), - DefineFunction(InternedString), - Expression, -} - -impl ExpressionType { - fn is_expression(&self) -> bool { - matches!(self, ExpressionType::Expression) - } - - fn eval_atom(t: &SyntaxObject) -> bool { - matches!( - t.ty, - TokenType::BooleanLiteral(_) - | TokenType::NumberLiteral(_) - | TokenType::StringLiteral(_) - | TokenType::CharacterLiteral(_) - | TokenType::IntegerLiteral(_) - ) - } - - fn is_constant(expr: &ExprKind) -> bool { - match expr { - ExprKind::Atom(Atom { syn, .. }) => Self::eval_atom(syn), - _ => false, - } - } - - fn generate_expression_types(exprs: &[ExprKind]) -> Vec { - let mut expression_types = Vec::with_capacity(exprs.len()); - let mut defined_idents = DefinedVars::new(); - - for expr in exprs { - match expr { - ExprKind::Define(d) => { - let name = d - .name - .atom_identifier_or_else(|| {}) - .expect("Define without a legal name"); - - defined_idents.insert(*name); - - match &d.body { - ExprKind::LambdaFunction(_) => { - expression_types.push(ExpressionType::DefineFunction(*name)); - } - _ => { - defined_idents.visit(&d.body); - if defined_idents.check_output() { - expression_types.push(ExpressionType::DefineFlatStar(*name)); - } else if Self::is_constant(&d.body) { - expression_types.push(ExpressionType::DefineConst(*name)); - } else { - expression_types.push(ExpressionType::DefineFlat(*name)); - } - } - } - } - _ => expression_types.push(ExpressionType::Expression), - } - } - - // println!("Expression types: {:?}", expression_types); - - expression_types - } -} - -fn atom(name: String) -> ExprKind { - ExprKind::Atom(Atom::new(SyntaxObject::default(TokenType::Identifier( - name.into(), - )))) -} - -fn set(var: ExprKind, expr: ExprKind) -> ExprKind { - ExprKind::Set(Box::new(Set::new( - var, - expr, - SyntaxObject::default(TokenType::Set), - ))) -} - -fn apply_ident(func: ExprKind) -> ExprKind { - ExprKind::List(List::new(vec![func])) -} - -fn convert_exprs_to_let(begin: Begin) -> ExprKind { - // let defines = collect_defines_from_current_scope(&exprs); - - let expression_types = ExpressionType::generate_expression_types(&begin.exprs); - - // Go ahead and quit if there are only expressions here - if expression_types.iter().all(|x| x.is_expression()) { - return ExprKind::Begin(begin); - } - - if !expression_types - .iter() - .any(|x| matches!(x, ExpressionType::DefineFunction(_))) - { - // let starting_iter = ExprKind::atom("void".to_string()) - - // TODO: last expression needs to be something, otherwise this doesn't work - // if let Some(last) = expression_types.last() { - // if !last.is_expression() {} - // } - - // println!("IN HERE"); - // println!("Expression_types") - - return begin - .exprs - .into_iter() - .rev() - .reduce(|accum, expr| { - // println!("Accum: {:?}, Expr: {:?}", accum, expr); - - match expr { - ExprKind::Define(d) => { - // let constructed_function = - - let constructed_function = ExprKind::LambdaFunction( - LambdaFunction::new( - vec![d.name], - accum, - SyntaxObject::default(TokenType::Lambda), - ) - .into(), - ); - - ExprKind::List(List::new(vec![constructed_function, d.body])) - } - other => ExprKind::Begin(Begin::new( - vec![other, accum], - SyntaxObject::default(TokenType::Begin), - )), - } - }) - .expect("Empty expression"); - - // return todo!(); - } - - let mut exprs = begin.exprs.clone(); - - // let mut last_expression = expression_types.len(); - - let idx = expression_types - .iter() - .rev() - .position(|x| !x.is_expression()) - .expect("Convert exprs to let in define conversion found no trailing expressions in begin"); - - // println!("Last expression index: {:?}", idx); - - let idx = expression_types.len() - 1 - idx; - - let mut body = exprs.split_off(idx + 1); - - // These are going to be the - let args = exprs - .iter() - .map(|x| { - if let ExprKind::Define(d) = x { - d.body.clone() - } else { - x.clone() - } - }) - .collect::>(); - - // This corresponds to the (let ((apple ..) (banana ..) (cucumber ..))) - // ^^^^^^ ^^^^^^^ ^^^^^^^^ - let mut top_level_arguments: Vec = Vec::new(); - - // This corresponds to the set expressions - // (set! apple #####apple0) - // (set! banana #####banana1) - // (set! cucumber #####cucumber1) - let mut set_expressions: Vec = Vec::new(); - - // corresponds to #####apple0, #####banana1, #####cucumber1, etc - let mut bound_names: Vec = Vec::new(); - - // TODO - check that the last expression does not contain any usages of the constant? - // if expression_types[0..idx + 1] - // .iter() - // .all(|x| matches!(x, ExpressionType::DefineConst(_))) - // { - // return ExprKind::Begin(Begin::new(body, begin.location)); - // } - - // Top level application with dummy arguments that will immediately get overwritten - let mut top_level_dummy_args = vec![ - // ExprKind::Atom(Atom::new(SyntaxObject::default( - // TokenType::IntegerLiteral(123) - // ))); - // top_level_arguments.len() - ]; - - let mut new_args = Vec::new(); - - // println!("{:#?}", expression_types); - - // TODO: - // If there are functions at all, go through this weird path - // Otherwise, we should do the following transformation: - // - // If there are no functions at all, just do a series of lets, similar to let* - - // TODO: Move this up so we don't have to use raw_exprs anymore - - for ((i, expression), arg) in expression_types[0..idx + 1].iter().enumerate().zip(args) { - match expression { - ExpressionType::DefineFunction(name) => { - if let ExprKind::Define(d) = &exprs[i] { - top_level_arguments.push(d.name.clone()); - top_level_dummy_args.push(ExprKind::Atom(Atom::new(SyntaxObject::default( - TokenType::IntegerLiteral(MaybeBigInt::Small(123)), - )))); - let name_prime = - atom("_____".to_string() + name.resolve() + i.to_string().as_str()); - let set_expr = set(d.name.clone(), name_prime.clone()); - bound_names.push(name_prime); - set_expressions.push(set_expr); - new_args.push(arg); - } else { - panic!("expected define, found: {}", &exprs[i]); - }; - - // let name = Atom::new(SyntaxObject::new) - } - ExpressionType::DefineFlat(name) => { - if let ExprKind::Define(d) = &exprs[i] { - top_level_arguments.push(d.name.clone()); - top_level_dummy_args.push(ExprKind::Atom(Atom::new(SyntaxObject::default( - TokenType::IntegerLiteral(MaybeBigInt::Small(123)), - )))); - let name_prime = - atom("_____".to_string() + name.resolve() + i.to_string().as_str()); - let set_expr = set(d.name.clone(), name_prime.clone()); - bound_names.push(name_prime); - set_expressions.push(set_expr); - new_args.push(arg); - } else { - panic!("expected define, found: {}", &exprs[i]); - }; - } - ExpressionType::DefineConst(_name) => { - if let ExprKind::Define(d) = &exprs[i] { - top_level_dummy_args.push(arg); - top_level_arguments.push(d.name.clone()); - // top_level_arguments.push(d.name.clone()); - // let name_prime = atom("#####".to_string() + name + i.to_string().as_str()); - // let set_expr = set(d.name.clone(), name_prime.clone()); - // bound_names.push(name_prime); - // set_expressions.push(set_expr); - } else { - panic!("expected define, found: {}", &exprs[i]); - }; - } - ExpressionType::DefineFlatStar(name) => { - if let ExprKind::Define(d) = &exprs[i] { - top_level_arguments.push(d.name.clone()); - top_level_dummy_args.push(ExprKind::Atom(Atom::new(SyntaxObject::default( - TokenType::IntegerLiteral(MaybeBigInt::Small(123)), - )))); - let name_prime = - atom("_____".to_string() + name.resolve() + i.to_string().as_str()); - - // Make this a (set! x (x')) - // Applying the function - let set_expr = set(d.name.clone(), apply_ident(name_prime.clone())); - - // Set this to be an empty function (lambda () ) - new_args.push( - LambdaFunction::new( - Vec::new(), - arg.clone(), - SyntaxObject::default(TokenType::Lambda), - ) - .into(), - ); - - bound_names.push(name_prime); - set_expressions.push(set_expr); - } else { - panic!("expected define, found: {}", &exprs[i]); - }; - } - // TODO: Move this down, don't put it with the lets, put it down in order with the set expressions - // That way we're not at risk of accidentally goofing up the ordering of the expressions. - // If will _only_ go in the right order of assignment - ExpressionType::Expression => { - // TODO: This is definitly not right - // let expr = atom("#####define-conversion".to_string() + i.to_string().as_str()); - // top_level_dummy_args.push(ExprKind::Atom(Atom::new(SyntaxObject::default( - // TokenType::IntegerLiteral(123), - // )))); - - // // This also gets bound in the inner function for now - // bound_names.push(expr.clone()); - - // top_level_arguments.push(expr); - // new_args.push(arg); - - set_expressions.push(arg) - } - } - } - - // // Append the body instructions to the set! - set_expressions.append(&mut body); - - let inner_lambda = LambdaFunction::new( - bound_names, - ExprKind::Begin(Begin::new( - set_expressions, - SyntaxObject::default(TokenType::Begin), - )), - SyntaxObject::default(TokenType::Lambda), - ); - - new_args.insert(0, ExprKind::LambdaFunction(Box::new(inner_lambda))); - - let inner_application = ExprKind::List(List::new(new_args)); - - let outer_lambda = LambdaFunction::new( - top_level_arguments, - inner_application, - SyntaxObject::default(TokenType::Lambda), - ); - - top_level_dummy_args.insert(0, ExprKind::LambdaFunction(Box::new(outer_lambda))); - - ExprKind::List(List::new(top_level_dummy_args)) - - // TODO: This is the real transformation that needs to take place once lets are fixed - // follow-up - let rec is going to be completely broken - - // let pairs = bound_names - // .into_iter() - // .zip(new_args.into_iter()) - // .collect::>(); - - // let inner_let = Let::new( - // pairs, - // ExprKind::Begin(Begin::new( - // set_expressions, - // SyntaxObject::default(TokenType::Begin), - // )), - // SyntaxObject::default(TokenType::Let), - // ); - - // let outer_pairs = top_level_arguments - // .into_iter() - // .zip(top_level_dummy_args.into_iter()) - // .collect::>(); - - // let outer_let = Let::new( - // outer_pairs, - // ExprKind::Let(Box::new(inner_let)), - // SyntaxObject::default(TokenType::Let), - // ); - - // let output = ExprKind::Let(Box::new(outer_let)); - - // println!("-----------------"); - // println!("{}", output.to_pretty(60)); - - // output -} - -#[cfg(test)] -mod flatten_begin_test { - - use super::*; - use crate::parser::ast::ExprKind; - use crate::parser::ast::{Atom, Begin, List}; - - use crate::parser::parser::SyntaxObject; - use crate::parser::tokens::TokenType; - use crate::parser::tokens::TokenType::*; - - // #[test] - // fn defines_translates_to_simple_let() { - // let expr = r#" - // (lambda () - // (begin - // (define x 10) - // (define y 20) - // (define z 30) - // (+ x y z)))"#; - - // let expected = r#" - // (lambda () ((lambda (x y z) ((lambda () (begin (+ x y z))))) 10 20 30)) - // "#; - - // let parsed = Parser::parse(expr).unwrap(); - // let expected_parsed = Parser::parse(expected).unwrap(); - - // let result = flatten_begins_and_expand_defines(parsed); - - // assert_eq!(result, expected_parsed); - // } - - fn atom(ident: &str) -> ExprKind { - ExprKind::Atom(Atom::new(SyntaxObject::default(Identifier(ident.into())))) - } - - fn int(num: isize) -> ExprKind { - ExprKind::Atom(Atom::new(SyntaxObject::default(IntegerLiteral( - MaybeBigInt::Small(num), - )))) - } - - #[test] - fn basic_flatten_one_level() { - let expr = ExprKind::Begin(Begin::new( - vec![ - ExprKind::Begin(Begin::new( - vec![ExprKind::List(List::new(vec![ - atom("+"), - atom("x"), - int(10), - ]))], - SyntaxObject::default(TokenType::Begin), - )), - ExprKind::List(List::new(vec![atom("+"), atom("y"), int(20)])), - ExprKind::List(List::new(vec![atom("+"), atom("z"), int(30)])), - ], - SyntaxObject::default(TokenType::Begin), - )); - - let expected = ExprKind::Begin(Begin::new( - vec![ - ExprKind::List(List::new(vec![atom("+"), atom("x"), int(10)])), - ExprKind::List(List::new(vec![atom("+"), atom("y"), int(20)])), - ExprKind::List(List::new(vec![atom("+"), atom("z"), int(30)])), - ], - SyntaxObject::default(TokenType::Begin), - )); - - assert_eq!(FlattenBegin::flatten(expr), expected); - } - - #[test] - fn basic_flatten_multiple_levels() { - let expr = ExprKind::Begin(Begin::new( - vec![ - ExprKind::Begin(Begin::new( - vec![ExprKind::List(List::new(vec![ - atom("+"), - atom("x"), - int(10), - ]))], - SyntaxObject::default(TokenType::Begin), - )), - ExprKind::Begin(Begin::new( - vec![ExprKind::List(List::new(vec![ - atom("+"), - atom("y"), - int(20), - ]))], - SyntaxObject::default(TokenType::Begin), - )), - ExprKind::Begin(Begin::new( - vec![ExprKind::List(List::new(vec![ - atom("+"), - atom("z"), - int(30), - ]))], - SyntaxObject::default(TokenType::Begin), - )), - ], - SyntaxObject::default(TokenType::Begin), - )); - - let expected = ExprKind::Begin(Begin::new( - vec![ - ExprKind::List(List::new(vec![atom("+"), atom("x"), int(10)])), - ExprKind::List(List::new(vec![atom("+"), atom("y"), int(20)])), - ExprKind::List(List::new(vec![atom("+"), atom("z"), int(30)])), - ], - SyntaxObject::default(TokenType::Begin), - )); - - assert_eq!(FlattenBegin::flatten(expr), expected); - } -} diff --git a/crates/steel-core/src/compiler/passes/manager.rs b/crates/steel-core/src/compiler/passes/manager.rs deleted file mode 100644 index af55b7a1e..000000000 --- a/crates/steel-core/src/compiler/passes/manager.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::parser::ast::ExprKind; - -use super::Folder; - -type PassFn = fn() -> Box; - -pub struct PassManager { - passes: Vec, -} - -impl Default for PassManager { - fn default() -> Self { - Self::new() - } -} - -impl PassManager { - pub fn new() -> Self { - PassManager { passes: Vec::new() } - } - - pub fn register_pass(&mut self, pass: PassFn) -> &mut Self { - self.passes.push(pass); - self - } - - pub fn run(&self, mut ast: Vec) -> Vec { - let mut changed = false; - while changed { - for thunk in &self.passes { - let mut pass = thunk(); - ast = pass.fold(ast); - changed = pass.modified(); - } - } - ast - } -} diff --git a/crates/steel-core/src/compiler/passes/mangle.rs b/crates/steel-core/src/compiler/passes/mangle.rs deleted file mode 100644 index 59a18dd74..000000000 --- a/crates/steel-core/src/compiler/passes/mangle.rs +++ /dev/null @@ -1,265 +0,0 @@ -use steel_parser::ast::DEFINE; - -use crate::{ - compiler::program::BEGIN, - parser::{ - ast::{Atom, ExprKind, Quote}, - interner::InternedString, - tokens::TokenType, - }, -}; - -use super::VisitorMutRefUnit; -use std::collections::HashSet; - -/* -Steps for doing having scoped macros - -- A requires a macro (or macros) from B - - Expand A with macros from A - - Then expand A with macros from B / C / D - - A must be done first, but then the rest can be done in phases - - Copy the code for B, mangle it and then include it in A directly -*/ - -pub fn collect_globals(exprs: &[ExprKind]) -> HashSet { - let mut global_defs = HashSet::new(); - - for expr in exprs { - match expr { - ExprKind::Define(d) => { - if let Some(name) = d.name.atom_identifier() { - if name.resolve().starts_with("mangler") { - continue; - } - global_defs.insert(*name); - } - } - ExprKind::List(l) - if l.first_ident() == Some(&*DEFINE) - || l.first() - .and_then(|x| x.atom_syntax_object()) - .map(|x| x.ty == TokenType::Define) - .unwrap_or_default() => - { - match l.get(1) { - Some(ExprKind::Atom(_)) => { - if let Some(name) = l.second_ident() { - if name.resolve().starts_with("mangler") { - continue; - } - // println!("Inserting: {}", name); - global_defs.insert(*name); - } - } - - Some(ExprKind::List(l)) => { - if let Some(name) = l.first_ident() { - if name.resolve().starts_with("mangler") { - continue; - } - // println!("Inserting: {}", name); - global_defs.insert(*name); - } - } - - _ => {} - } - } - ExprKind::List(l) - if l.first_ident() == Some(&*BEGIN) - || l.first() - .and_then(|x| x.atom_syntax_object()) - .map(|x| x.ty == TokenType::Begin) - .unwrap_or_default() => - { - if l.len() > 1 { - let collected_defs = collect_globals(&l[1..]); - global_defs.extend(collected_defs); - } - - // let collected_defs =collect_globals(&l[]) - } - ExprKind::Begin(b) => { - let collected_defs = collect_globals(&b.exprs); - global_defs.extend(collected_defs); - } - _ => {} - } - } - - global_defs -} - -pub struct NameUnMangler<'a> { - prefix: &'a str, -} - -impl<'a> NameUnMangler<'a> { - pub fn new(prefix: &'a str) -> Self { - Self { prefix } - } - - pub fn unmangle_vars(&mut self, exprs: &mut [ExprKind]) { - for expr in exprs { - self.visit(expr); - } - } - - pub fn unmangle_expr(&mut self, expr: &mut ExprKind) { - self.visit(expr); - } -} - -impl<'a> VisitorMutRefUnit for NameUnMangler<'a> { - #[inline] - fn visit_quote(&mut self, q: &mut Quote) { - if let Some(expression) = q.expr.atom_identifier_mut() { - // TODO: Should roll up this into one thing, since strip prefix checks if - // it starts with the right value - if expression.resolve().starts_with(self.prefix) { - *expression = expression - .resolve() - .strip_prefix(self.prefix) - .unwrap() - .into(); - } - } - } -} - -pub struct NameMangler { - pub(crate) globals: HashSet, - prefix: String, -} - -impl NameMangler { - pub fn new(globals: HashSet, prefix: String) -> Self { - Self { globals, prefix } - } - - pub fn mangle_vars(&mut self, exprs: &mut [ExprKind]) { - for expr in exprs { - self.visit(expr); - } - } -} - -pub fn mangle_vars_with_prefix(prefix: String, exprs: &mut [ExprKind]) { - let globals = collect_globals(exprs); - - let mut name_mangler = NameMangler { globals, prefix }; - - for expr in exprs { - name_mangler.visit(expr); - } -} - -impl VisitorMutRefUnit for NameMangler { - #[inline] - fn visit_atom(&mut self, a: &mut Atom) { - if let TokenType::Identifier(i) = &mut a.syn.ty { - if self.globals.contains(i) { - let new_str = i.resolve(); - - *i = (self.prefix.clone() + new_str).into(); - - // i.insert_str(0, &self.prefix); - } - } - } - - /// We don't want quoted values to be mangled since those should match - /// the real name given - #[inline] - fn visit_quote(&mut self, _q: &mut Quote) {} -} - -#[cfg(test)] -mod name_mangling_tests { - use super::*; - - use crate::parser::parser::Parser; - - #[test] - fn basic_mangling() { - let expr = r#" - (define (foo x y z) (let ((a 10) (b 20)) (bar (+ x y z a b)))) - (define (bar applesauce) (+ applesauce 10)) - "#; - - let mut parsed = Parser::parse(expr).unwrap(); - - mangle_vars_with_prefix("--test--".to_string(), &mut parsed); - - let expected = Parser::parse( - r#" - (define (--test--foo x y z) (let ((a 10) (b 20)) (--test--bar (+ x y z a b)))) - (define (--test--bar applesauce) (+ applesauce 10)) - "#, - ) - .unwrap(); - - assert_eq!(parsed, expected); - } - - #[test] - fn shadowed_global_still_mangled() { - let expr = r#" - (define (foo x y z) (let ((foo 10) (b 20)) (foo (+ bar y z a b)))) - (define (bar applesauce) (+ applesauce 10)) - "#; - - let mut parsed = Parser::parse(expr).unwrap(); - - mangle_vars_with_prefix("--test--".to_string(), &mut parsed); - - let expected = Parser::parse( - r#" - (define (--test--foo x y z) (let ((--test--foo 10) (b 20)) (--test--foo (+ --test--bar y z a b)))) - (define (--test--bar applesauce) (+ applesauce 10)) - "#, - ) - .unwrap(); - - assert_eq!(parsed, expected); - } - - #[test] - fn still_collect_defines_in_begins() { - let expr = r#" - (begin - (begin - (begin - (begin - (define x 10) - ) - (define y 20) - ) - ) - ) - "#; - - let mut parsed = Parser::parse(expr).unwrap(); - - mangle_vars_with_prefix("--test--".to_string(), &mut parsed); - - let expected = Parser::parse( - r#" - (begin - (begin - (begin - (begin - (define --test--x 10) - ) - (define --test--y 20) - ) - ) - ) - "#, - ) - .unwrap(); - - assert_eq!(parsed, expected); - } -} diff --git a/crates/steel-core/src/compiler/passes/mod.rs b/crates/steel-core/src/compiler/passes/mod.rs deleted file mode 100644 index fedff42c6..000000000 --- a/crates/steel-core/src/compiler/passes/mod.rs +++ /dev/null @@ -1,510 +0,0 @@ -pub mod analysis; -pub mod begin; -pub mod manager; -pub mod mangle; -pub mod reader; -pub mod shadow; - -use std::ops::ControlFlow; - -use crate::parser::ast::ExprKind; -// use crate::parser::ast::*; - -use crate::parser::ast::{ - Atom, Begin, Define, If, LambdaFunction, Let, List, Macro, Quote, Require, Return, Set, - SyntaxRules, -}; - -pub trait Folder { - fn fold(&mut self, ast: Vec) -> Vec { - ast.into_iter().map(|x| self.visit(x)).collect() - } - - // Whether or not the pass modified the input AST - fn modified(&self) -> bool { - false - } - - fn visit(&mut self, expr: ExprKind) -> ExprKind { - match expr { - ExprKind::If(f) => self.visit_if(f), - ExprKind::Define(d) => self.visit_define(d), - ExprKind::LambdaFunction(l) => self.visit_lambda_function(l), - ExprKind::Begin(b) => self.visit_begin(b), - ExprKind::Return(r) => self.visit_return(r), - ExprKind::Quote(q) => self.visit_quote(q), - ExprKind::Macro(m) => self.visit_macro(m), - ExprKind::Atom(a) => self.visit_atom(a), - ExprKind::List(l) => self.visit_list(l), - ExprKind::SyntaxRules(s) => self.visit_syntax_rules(s), - ExprKind::Set(s) => self.visit_set(s), - ExprKind::Require(r) => self.visit_require(r), - ExprKind::Let(l) => self.visit_let(l), - } - } - - #[inline] - fn visit_if(&mut self, mut f: Box) -> ExprKind { - f.test_expr = self.visit(f.test_expr); - f.then_expr = self.visit(f.then_expr); - f.else_expr = self.visit(f.else_expr); - ExprKind::If(f) - } - - #[inline] - fn visit_let(&mut self, mut l: Box) -> ExprKind { - let mut visited_bindings = Vec::new(); - - for (binding, expr) in l.bindings { - visited_bindings.push((self.visit(binding), self.visit(expr))); - } - - l.bindings = visited_bindings; - l.body_expr = self.visit(l.body_expr); - - ExprKind::Let(l) - } - - #[inline] - fn visit_define(&mut self, mut define: Box) -> ExprKind { - define.body = self.visit(define.body); - ExprKind::Define(define) - } - - #[inline] - fn visit_lambda_function(&mut self, mut lambda_function: Box) -> ExprKind { - lambda_function.body = self.visit(lambda_function.body); - ExprKind::LambdaFunction(lambda_function) - } - - #[inline] - fn visit_begin(&mut self, mut begin: Begin) -> ExprKind { - begin.exprs = begin.exprs.into_iter().map(|e| self.visit(e)).collect(); - ExprKind::Begin(begin) - } - - #[inline] - fn visit_return(&mut self, mut r: Box) -> ExprKind { - r.expr = self.visit(r.expr); - ExprKind::Return(r) - } - - #[inline] - fn visit_quote(&mut self, quote: Box) -> ExprKind { - // quote.expr = self.visit(quote.expr); - ExprKind::Quote(quote) - } - - #[inline] - fn visit_macro(&mut self, m: Box) -> ExprKind { - ExprKind::Macro(m) - } - - #[inline] - fn visit_atom(&mut self, a: Atom) -> ExprKind { - ExprKind::Atom(a) - } - - #[inline] - fn visit_list(&mut self, mut l: List) -> ExprKind { - l.args = l.args.into_iter().map(|e| self.visit(e)).collect(); - ExprKind::List(l) - } - - #[inline] - fn visit_syntax_rules(&mut self, l: Box) -> ExprKind { - ExprKind::SyntaxRules(l) - } - - #[inline] - fn visit_set(&mut self, mut s: Box) -> ExprKind { - s.variable = self.visit(s.variable); - s.expr = self.visit(s.expr); - ExprKind::Set(s) - } - - #[inline] - fn visit_require(&mut self, s: Require) -> ExprKind { - ExprKind::Require(s) - } -} - -pub trait VisitorMutUnit { - fn visit(&mut self, expr: &ExprKind) { - match expr { - ExprKind::If(f) => self.visit_if(f), - ExprKind::Define(d) => self.visit_define(d), - ExprKind::LambdaFunction(l) => self.visit_lambda_function(l), - ExprKind::Begin(b) => self.visit_begin(b), - ExprKind::Return(r) => self.visit_return(r), - ExprKind::Quote(q) => self.visit_quote(q), - ExprKind::Macro(m) => self.visit_macro(m), - ExprKind::Atom(a) => self.visit_atom(a), - ExprKind::List(l) => self.visit_list(l), - ExprKind::SyntaxRules(s) => self.visit_syntax_rules(s), - ExprKind::Set(s) => self.visit_set(s), - ExprKind::Require(r) => self.visit_require(r), - ExprKind::Let(l) => self.visit_let(l), - } - } - - #[inline] - fn visit_if(&mut self, f: &If) { - self.visit(&f.test_expr); - self.visit(&f.then_expr); - self.visit(&f.else_expr); - } - - #[inline] - fn visit_let(&mut self, l: &Let) { - l.bindings.iter().for_each(|x| self.visit(&x.1)); - self.visit(&l.body_expr); - } - - #[inline] - fn visit_define(&mut self, define: &Define) { - self.visit(&define.body); - } - - #[inline] - fn visit_lambda_function(&mut self, lambda_function: &LambdaFunction) { - self.visit(&lambda_function.body); - } - - #[inline] - fn visit_begin(&mut self, begin: &Begin) { - for expr in &begin.exprs { - self.visit(expr); - } - } - - #[inline] - fn visit_return(&mut self, r: &Return) { - self.visit(&r.expr); - } - - #[inline] - fn visit_quote(&mut self, quote: &Quote) { - self.visit("e.expr); - } - - #[inline] - fn visit_macro(&mut self, _m: &Macro) {} - - #[inline] - fn visit_atom(&mut self, _a: &Atom) {} - - #[inline] - fn visit_list(&mut self, l: &List) { - for expr in &l.args { - self.visit(expr); - } - } - - #[inline] - fn visit_syntax_rules(&mut self, _l: &SyntaxRules) {} - - #[inline] - fn visit_set(&mut self, s: &Set) { - self.visit(&s.variable); - self.visit(&s.expr); - } - - #[inline] - fn visit_require(&mut self, _s: &Require) {} -} - -pub trait VisitorMutControlFlow { - fn visit(&mut self, expr: &ExprKind) -> ControlFlow<()> { - match expr { - ExprKind::If(f) => self.visit_if(f), - ExprKind::Define(d) => self.visit_define(d), - ExprKind::LambdaFunction(l) => self.visit_lambda_function(l), - ExprKind::Begin(b) => self.visit_begin(b), - ExprKind::Return(r) => self.visit_return(r), - ExprKind::Quote(q) => self.visit_quote(q), - ExprKind::Macro(m) => self.visit_macro(m), - ExprKind::Atom(a) => self.visit_atom(a), - ExprKind::List(l) => self.visit_list(l), - ExprKind::SyntaxRules(s) => self.visit_syntax_rules(s), - ExprKind::Set(s) => self.visit_set(s), - ExprKind::Require(r) => self.visit_require(r), - ExprKind::Let(l) => self.visit_let(l), - } - } - - #[inline] - fn visit_if(&mut self, f: &If) -> ControlFlow<()> { - self.visit(&f.test_expr)?; - self.visit(&f.then_expr)?; - self.visit(&f.else_expr)?; - - ControlFlow::Continue(()) - } - - #[inline] - fn visit_let(&mut self, l: &Let) -> ControlFlow<()> { - for binding in &l.bindings { - self.visit(&binding.1)?; - } - - self.visit(&l.body_expr)?; - - ControlFlow::Continue(()) - } - - #[inline] - fn visit_define(&mut self, define: &Define) -> ControlFlow<()> { - self.visit(&define.body)?; - - ControlFlow::Continue(()) - } - - #[inline] - fn visit_lambda_function(&mut self, lambda_function: &LambdaFunction) -> ControlFlow<()> { - self.visit(&lambda_function.body)?; - - ControlFlow::Continue(()) - } - - #[inline] - fn visit_begin(&mut self, begin: &Begin) -> ControlFlow<()> { - for expr in &begin.exprs { - self.visit(expr)?; - } - - ControlFlow::Continue(()) - } - - #[inline] - fn visit_return(&mut self, r: &Return) -> ControlFlow<()> { - self.visit(&r.expr)?; - - ControlFlow::Continue(()) - } - - #[inline] - fn visit_quote(&mut self, quote: &Quote) -> ControlFlow<()> { - self.visit("e.expr)?; - - ControlFlow::Continue(()) - } - - #[inline] - fn visit_macro(&mut self, _m: &Macro) -> ControlFlow<()> { - ControlFlow::Continue(()) - } - - #[inline] - fn visit_atom(&mut self, _a: &Atom) -> ControlFlow<()> { - ControlFlow::Continue(()) - } - - #[inline] - fn visit_list(&mut self, l: &List) -> ControlFlow<()> { - for expr in &l.args { - self.visit(expr)?; - } - - ControlFlow::Continue(()) - } - - #[inline] - fn visit_syntax_rules(&mut self, _l: &SyntaxRules) -> ControlFlow<()> { - ControlFlow::Continue(()) - } - - #[inline] - fn visit_set(&mut self, s: &Set) -> ControlFlow<()> { - self.visit(&s.variable)?; - self.visit(&s.expr)?; - - ControlFlow::Continue(()) - } - - #[inline] - fn visit_require(&mut self, _s: &Require) -> ControlFlow<()> { - ControlFlow::Continue(()) - } -} - -pub trait VisitorMutUnitRef<'a> { - fn visit(&mut self, expr: &'a ExprKind) { - match expr { - ExprKind::If(f) => self.visit_if(f), - ExprKind::Define(d) => self.visit_define(d), - ExprKind::LambdaFunction(l) => self.visit_lambda_function(l), - ExprKind::Begin(b) => self.visit_begin(b), - ExprKind::Return(r) => self.visit_return(r), - ExprKind::Quote(q) => self.visit_quote(q), - ExprKind::Macro(m) => self.visit_macro(m), - ExprKind::Atom(a) => self.visit_atom(a), - ExprKind::List(l) => self.visit_list(l), - ExprKind::SyntaxRules(s) => self.visit_syntax_rules(s), - ExprKind::Set(s) => self.visit_set(s), - ExprKind::Require(r) => self.visit_require(r), - ExprKind::Let(l) => self.visit_let(l), - } - } - - #[inline] - fn visit_if(&mut self, f: &'a If) { - self.visit(&f.test_expr); - self.visit(&f.then_expr); - self.visit(&f.else_expr); - } - - #[inline] - fn visit_let(&mut self, l: &'a Let) { - l.bindings.iter().for_each(|x| self.visit(&x.1)); - self.visit(&l.body_expr); - } - - #[inline] - fn visit_define(&mut self, define: &'a Define) { - self.visit(&define.name); - self.visit(&define.body); - } - - #[inline] - fn visit_lambda_function(&mut self, lambda_function: &'a LambdaFunction) { - self.visit(&lambda_function.body); - } - - #[inline] - fn visit_begin(&mut self, begin: &'a Begin) { - for expr in &begin.exprs { - self.visit(expr); - } - } - - #[inline] - fn visit_return(&mut self, r: &'a Return) { - self.visit(&r.expr); - } - - #[inline] - fn visit_quote(&mut self, quote: &'a Quote) { - self.visit("e.expr); - } - - #[inline] - fn visit_macro(&mut self, m: &'a Macro) { - self.visit_syntax_rules(&m.syntax_rules); - } - - #[inline] - fn visit_atom(&mut self, _a: &'a Atom) {} - - #[inline] - fn visit_list(&mut self, l: &'a List) { - for expr in &l.args { - self.visit(expr); - } - } - - #[inline] - fn visit_syntax_rules(&mut self, s: &'a SyntaxRules) { - for pattern in &s.patterns { - self.visit(&pattern.body); - } - } - - #[inline] - fn visit_set(&mut self, s: &'a Set) { - self.visit(&s.variable); - self.visit(&s.expr); - } - - #[inline] - fn visit_require(&mut self, _s: &'a Require) {} -} - -pub trait VisitorMutRefUnit { - fn visit(&mut self, expr: &mut ExprKind) { - match expr { - ExprKind::If(f) => self.visit_if(f), - ExprKind::Define(d) => self.visit_define(d), - ExprKind::LambdaFunction(l) => self.visit_lambda_function(l), - ExprKind::Begin(b) => self.visit_begin(b), - ExprKind::Return(r) => self.visit_return(r), - ExprKind::Quote(q) => self.visit_quote(q), - ExprKind::Macro(m) => self.visit_macro(m), - ExprKind::Atom(a) => self.visit_atom(a), - ExprKind::List(l) => self.visit_list(l), - ExprKind::SyntaxRules(s) => self.visit_syntax_rules(s), - ExprKind::Set(s) => self.visit_set(s), - ExprKind::Require(r) => self.visit_require(r), - ExprKind::Let(l) => self.visit_let(l), - } - } - - #[inline] - fn visit_if(&mut self, f: &mut If) { - self.visit(&mut f.test_expr); - self.visit(&mut f.then_expr); - self.visit(&mut f.else_expr); - } - - #[inline] - fn visit_let(&mut self, l: &mut Let) { - l.bindings.iter_mut().for_each(|x| self.visit(&mut x.1)); - self.visit(&mut l.body_expr); - } - - #[inline] - fn visit_define(&mut self, define: &mut Define) { - self.visit(&mut define.name); - self.visit(&mut define.body); - } - - #[inline] - fn visit_lambda_function(&mut self, lambda_function: &mut LambdaFunction) { - for var in &mut lambda_function.args { - self.visit(var); - } - self.visit(&mut lambda_function.body); - } - - #[inline] - fn visit_begin(&mut self, begin: &mut Begin) { - for expr in &mut begin.exprs { - self.visit(expr); - } - } - - #[inline] - fn visit_return(&mut self, r: &mut Return) { - self.visit(&mut r.expr); - } - - #[inline] - fn visit_quote(&mut self, quote: &mut Quote) { - self.visit(&mut quote.expr); - } - - #[inline] - fn visit_macro(&mut self, _m: &mut Macro) {} - - #[inline] - fn visit_atom(&mut self, _a: &mut Atom) {} - - #[inline] - fn visit_list(&mut self, l: &mut List) { - for expr in &mut l.args { - self.visit(expr); - } - } - - #[inline] - fn visit_syntax_rules(&mut self, _l: &mut SyntaxRules) {} - - #[inline] - fn visit_set(&mut self, s: &mut Set) { - self.visit(&mut s.variable); - self.visit(&mut s.expr); - } - - #[inline] - fn visit_require(&mut self, _s: &mut Require) {} -} diff --git a/crates/steel-core/src/compiler/passes/reader.rs b/crates/steel-core/src/compiler/passes/reader.rs deleted file mode 100644 index 55913877c..000000000 --- a/crates/steel-core/src/compiler/passes/reader.rs +++ /dev/null @@ -1,122 +0,0 @@ -use crate::parser::{ - ast::{ExprKind, List}, - interner::InternedString, -}; - -use super::Folder; - -pub struct ExpandMethodCalls {} - -impl ExpandMethodCalls { - pub fn expand_methods(exprs: Vec) -> Vec { - ExpandMethodCalls {}.fold(exprs) - } -} - -impl Folder for ExpandMethodCalls { - #[inline] - fn visit_list(&mut self, mut l: List) -> ExprKind { - if l.args.is_empty() { - l.args = l.args.into_iter().map(|e| self.visit(e)).collect(); - return ExprKind::List(l); - } - - // println!("Visiting list: {:?}", l); - - if let Some(func) = l.first_ident().map(|x| x.to_string()) { - // println!("Inside this if statement"); - // println!("Func: {:?}", func); - - if func.contains('.') { - let words = func.split('.').collect::>(); - - if words.len() != 2 { - l.args = l.args.into_iter().map(|e| self.visit(e)).collect(); - return ExprKind::List(l); - } else { - // Here we're going to transform (struct.method ...) - // into (method struct) - let mut first = l.args.remove(0); - let mut second = first.clone(); - - // method - first.update_string_in_atom(words[0].into()); - - // struct - second.update_string_in_atom(words[1].into()); - - // method pushed on first - l.args.insert(0, first); - - // then the struct - l.args.insert(0, second); - - l.args = l.args.into_iter().map(|e| self.visit(e)).collect(); - - // println!("Expanded method call: {:?}", l); - - return ExprKind::List(l); - } - } - } - - l.args = l.args.into_iter().map(|e| self.visit(e)).collect(); - ExprKind::List(l) - } -} - -pub struct MultipleArityFunctions { - dot: InternedString, -} - -impl MultipleArityFunctions { - pub fn new() -> Self { - MultipleArityFunctions { dot: ".".into() } - } - - pub fn expand_multiple_arity_functions(exprs: Vec) -> Vec { - MultipleArityFunctions { dot: ".".into() }.fold(exprs) - } -} - -impl Folder for MultipleArityFunctions { - #[inline] - fn visit_lambda_function( - &mut self, - mut lambda_function: Box, - ) -> ExprKind { - // Visit the body - lambda_function.body = self.visit(lambda_function.body); - - let mut dot_count = 0; - - lambda_function - .args - .iter() - .filter_map(|x| x.atom_identifier()) - .for_each(|x| { - if *x == self.dot { - dot_count += 1; - } - }); - - // If we found one dot in the correct position - // Adjust this lambda function to be a rest function - if dot_count == 1 { - let dot_index = lambda_function.args.len() - 2; - - if let Some(dot) = lambda_function.args[dot_index].atom_identifier() { - if *dot == self.dot { - lambda_function.args.remove(dot_index); - lambda_function.rest = true; - - log::debug!(target: "reader-macros", "transformed multi-arity function"); - } else { - return ExprKind::LambdaFunction(lambda_function); - } - } - } - - ExprKind::LambdaFunction(lambda_function) - } -} diff --git a/crates/steel-core/src/compiler/passes/shadow.rs b/crates/steel-core/src/compiler/passes/shadow.rs deleted file mode 100644 index da9ab7417..000000000 --- a/crates/steel-core/src/compiler/passes/shadow.rs +++ /dev/null @@ -1,199 +0,0 @@ -use std::collections::HashMap; - -use quickscope::{ScopeMap, ScopeSet}; - -use crate::parser::{ - ast::{Atom, ExprKind}, - interner::InternedString, -}; - -use super::VisitorMutRefUnit; - -pub struct RenameShadowedVariables { - modified: bool, - scope: ScopeSet, - // Modify the variable with the depth - shadows: ScopeMap, - str_modifiers: HashMap, -} - -impl Default for RenameShadowedVariables { - fn default() -> Self { - Self::new() - } -} - -impl RenameShadowedVariables { - pub fn new() -> Self { - Self { - scope: ScopeSet::new(), - shadows: ScopeMap::new(), - modified: false, - str_modifiers: HashMap::new(), - } - } - - pub fn rename_shadowed_vars(exprs: &mut [ExprKind]) { - let mut renamer = Self::new(); - - for expr in exprs.iter_mut() { - renamer.visit(expr); - } - } -} - -impl VisitorMutRefUnit for RenameShadowedVariables { - fn visit_lambda_function(&mut self, lambda_function: &mut crate::parser::ast::LambdaFunction) { - self.scope.push_layer(); - self.shadows.push_layer(); - - // TODO: Insert the code here to mark these variables as in scope - - for variable in lambda_function.arguments_mut() { - if self.scope.contains(variable) { - let modifier = self.scope.depth(); - self.shadows.define(*variable, modifier); - - // Create a mutable string to mangle - let mut mut_var = "##".to_string() + variable.resolve(); - - if let Some(char_modifier) = char::from_digit(modifier as u32, 10) { - mut_var.push(char_modifier); - } else if let Some(str_modifier) = self.str_modifiers.get(&modifier) { - mut_var.push_str(str_modifier); - } else { - self.str_modifiers.insert(modifier, modifier.to_string()); - mut_var.push_str(self.str_modifiers.get(&modifier).unwrap()); - } - - // println!("Mangling variable: {}", mut_var); - - *variable = mut_var.into(); - - self.scope.define(*variable); - - continue; - } - - self.scope.define(*variable); - } - - self.visit(&mut lambda_function.body); - - self.scope.pop_layer(); - self.shadows.pop_layer(); - } - - fn visit_quote(&mut self, _quote: &mut steel_parser::ast::Quote) {} - - fn visit_atom(&mut self, a: &mut Atom) { - if let Some(ident) = a.ident_mut() { - if let Some(modifier) = self.shadows.get(ident) { - // Append the variable with the depth it occurs at. - // Now, shadowing shouldn't actually _be_ a problem - // ident.push(char::from_digit(*modifier as u32, 10).unwrap()); - - let mut mut_ident = "##".to_string() + ident.resolve(); - - if let Some(char_modifier) = char::from_digit(*modifier as u32, 10) { - mut_ident.push(char_modifier) - } else if let Some(str_modifier) = self.str_modifiers.get(modifier) { - mut_ident.push_str(str_modifier) - } else { - panic!("The modifier should be defined by now") - } - - *ident = mut_ident.into(); - - self.modified = true; - } - } - } - - fn visit_let(&mut self, l: &mut crate::parser::ast::Let) { - self.scope.push_layer(); - self.shadows.push_layer(); - - for variable in l - .bindings - .iter_mut() - .filter_map(|x| x.0.atom_identifier_mut()) - { - if self.scope.contains(variable) { - let modifier = self.scope.depth(); - self.shadows.define(*variable, modifier); - - let mut mut_var = "##".to_string() + variable.resolve(); - - if let Some(char_modifier) = char::from_digit(modifier as u32, 10) { - mut_var.push(char_modifier); - } else if let Some(str_modifier) = self.str_modifiers.get(&modifier) { - mut_var.push_str(str_modifier); - } else { - self.str_modifiers.insert(modifier, modifier.to_string()); - mut_var.push_str(self.str_modifiers.get(&modifier).unwrap()); - } - - *variable = mut_var.into(); - - self.scope.define(*variable); - continue; - } - - self.scope.define(*variable); - } - - // - l.bindings.iter_mut().for_each(|x| self.visit(&mut x.1)); - self.visit(&mut l.body_expr); - - // TODO: Insert the code here to mark these variables as in scope - - self.scope.pop_layer(); - self.shadows.pop_layer(); - } -} - -#[cfg(test)] -mod shadow_mangling_tests { - use crate::parser::{ast::AstTools, parser::Parser}; - - use super::*; - - #[test] - fn check_basic_shadowing() { - let script = r#" - (define (foo x) - (let ((x x)) - (+ x 10))) - "#; - - let mut exprs = Parser::parse(script).unwrap(); - let mut renamer = RenameShadowedVariables::new(); - - for expr in exprs.iter_mut() { - renamer.visit(expr); - } - - exprs.pretty_print(); - } - - #[test] - fn check_basic_shadowing_multiple_levels() { - let script = r#" - (define (foo x) - (let ((x x)) - (let ((x x)) - (+ x 10)))) - "#; - - let mut exprs = Parser::parse(script).unwrap(); - let mut renamer = RenameShadowedVariables::new(); - - for expr in exprs.iter_mut() { - renamer.visit(expr); - } - - exprs.pretty_print(); - } -} diff --git a/crates/steel-core/src/compiler/program.rs b/crates/steel-core/src/compiler/program.rs deleted file mode 100644 index 6cce79221..000000000 --- a/crates/steel-core/src/compiler/program.rs +++ /dev/null @@ -1,1108 +0,0 @@ -use crate::core::labels::Expr; -use crate::parser::span_visitor::get_span; -use crate::rvals::Result; -use crate::{ - compiler::constants::ConstantMap, - core::{instructions::Instruction, opcode::OpCode}, - stop, SteelVal, -}; -use crate::{core::instructions::DenseInstruction, parser::span::Span}; -use crate::{ - parser::{ - ast::ExprKind, - interner::InternedString, - parser::{RawSyntaxObject, SyntaxObject}, - tokens::TokenType, - }, - rvals::IntoSteelVal, -}; - -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, convert::TryInto, rc::Rc, time::SystemTime}; -use steel_parser::tokens::MaybeBigInt; - -#[cfg(feature = "profiling")] -use std::time::Instant; - -#[cfg(feature = "profiling")] -use log::{debug, log_enabled}; - -use super::{compiler::DebruijnIndicesInterner, map::SymbolMap}; - -const _TILE_SUPER_INSTRUCTIONS: bool = false; - -/// evaluates an atom expression in given environment -fn eval_atom(t: &SyntaxObject) -> Result { - match &t.ty { - TokenType::BooleanLiteral(b) => Ok((*b).into()), - // TokenType::Identifier(s) => env.borrow().lookup(&s), - TokenType::NumberLiteral(n) => Ok(SteelVal::NumV(*n)), - TokenType::StringLiteral(s) => Ok(SteelVal::StringV(s.into())), - TokenType::CharacterLiteral(c) => Ok(SteelVal::CharV(*c)), - TokenType::IntegerLiteral(MaybeBigInt::Small(n)) => Ok(SteelVal::IntV(*n)), - // TODO: @Matt - This doesn't need to happen at all. It will just lead to unnecessary cloning. - TokenType::IntegerLiteral(MaybeBigInt::Big(b)) => b.clone().into_steelval(), - // TODO: Keywords shouldn't be misused as an expression - only in function calls are keywords allowed - TokenType::Keyword(k) => Ok(SteelVal::SymbolV(k.clone().into())), - what => { - println!("getting here in the eval_atom - code_generator"); - stop!(UnexpectedToken => what; t.span) - } - } -} - -pub fn specialize_read_local(instructions: &mut [Instruction]) { - for i in 0..instructions.len() { - let read_local = instructions.get(i); - - match read_local { - Some(Instruction { - op_code: OpCode::MOVEREADLOCAL, - payload_size, - .. - }) => { - let op_code = match payload_size { - 0 => OpCode::MOVEREADLOCAL0, - 1 => OpCode::MOVEREADLOCAL1, - 2 => OpCode::MOVEREADLOCAL2, - 3 => OpCode::MOVEREADLOCAL3, - _ => continue, - }; - - if let Some(x) = instructions.get_mut(i) { - x.op_code = op_code; - } - } - - Some(Instruction { - op_code: OpCode::READLOCAL, - payload_size, - .. - }) => { - let op_code = match payload_size { - 0 => OpCode::READLOCAL0, - 1 => OpCode::READLOCAL1, - 2 => OpCode::READLOCAL2, - 3 => OpCode::READLOCAL3, - _ => continue, - }; - - if let Some(x) = instructions.get_mut(i) { - x.op_code = op_code; - } - } // instructions[i + 1].op_code = OpCode::PASS; - // instructions[i + 2].op_code = OpCode::PASS; - // instructions[i + 4].op_code = OpCode::PASS; - _ => continue, - } - } -} - -pub fn specialize_constants(instructions: &mut [Instruction]) -> Result<()> { - if instructions.is_empty() { - return Ok(()); - } - - for i in 0..instructions.len() { - match instructions.get(i) { - Some(Instruction { - op_code: OpCode::PUSHCONST, - contents: - Some(Expr::Atom(SyntaxObject { - ty: TokenType::Identifier(_), - .. - })), - .. - }) => continue, - Some(Instruction { - op_code: OpCode::PUSHCONST, - contents: Some(Expr::Atom(syn)), - .. - }) => { - let value = eval_atom(syn)?; - - let opcode = match &value { - SteelVal::IntV(0) => OpCode::LOADINT0, - SteelVal::IntV(1) => OpCode::LOADINT1, - SteelVal::IntV(2) => OpCode::LOADINT2, - _ => continue, - }; - - instructions.get_mut(i).unwrap().op_code = opcode; - } - _ => continue, - } - } - - Ok(()) -} - -pub fn convert_call_globals(instructions: &mut [Instruction]) { - if instructions.is_empty() { - return; - } - - for i in 0..instructions.len() - 1 { - let push = instructions.get(i); - let func = instructions.get(i + 1); - - match (push, func) { - ( - Some(Instruction { - op_code: OpCode::PUSH, - payload_size: index, - contents: Some(Expr::Atom(ident)), - .. - }), - Some(Instruction { - op_code: OpCode::FUNC, - payload_size: arity, - .. - }), - ) => { - let arity = *arity; - let index = *index; - - if let TokenType::Identifier(ident) = ident.ty { - match ident { - _ if ident == *PRIM_CONS_SYMBOL => { - if let Some(x) = instructions.get_mut(i) { - x.op_code = OpCode::CONS; - x.payload_size = 2; - continue; - } - } - - // Specialize lists, cons, hashmap, etc. - anything that we expect to be used often in - // real code. - // _ if ident == *PRIM_LIST_SYMBOL => { - // if let Some(x) = instructions.get_mut(i) { - // x.op_code = OpCode::LIST; - // x.payload_size = arity; - // continue; - // } - // } - _ if ident == *BOX || ident == *PRIM_BOX => { - if let Some(x) = instructions.get_mut(i) { - x.op_code = OpCode::NEWBOX; - continue; - } - } - - _ if ident == *UNBOX || ident == *PRIM_UNBOX => { - if let Some(x) = instructions.get_mut(i) { - x.op_code = OpCode::UNBOX; - continue; - } - } - - _ if ident == *SETBOX || ident == *PRIM_SETBOX => { - if let Some(x) = instructions.get_mut(i) { - x.op_code = OpCode::SETBOX; - continue; - } - } - - _ if ident == *PRIM_CAR => { - if let Some(x) = instructions.get_mut(i) { - x.op_code = OpCode::CAR; - continue; - } - } - - // _ if ident == *CDR_SYMBOL || ident == *PRIM_CAR => { - // if let Some(x) = instructions.get_mut(i) { - // x.op_code = OpCode::CAR; - // continue; - // } - // } - _ => { - // println!("Converting call global: {}", ident); - } - } - } - - // TODO: - if let Some(x) = instructions.get_mut(i) { - x.op_code = OpCode::CALLGLOBAL; - x.payload_size = index; - } - - if let Some(x) = instructions.get_mut(i + 1) { - // Leave this as the OpCode::FUNC; - // x.op_code = OpCode::Arity; - x.payload_size = arity; - } - } - ( - Some(Instruction { - op_code: OpCode::PUSH, - payload_size: index, - contents: Some(Expr::Atom(ident)), - .. - }), - Some(Instruction { - op_code: OpCode::TAILCALL, - // payload_size: arity, - .. - }), - ) => { - // let arity = *arity; - let index = *index; - - if let TokenType::Identifier(ident) = ident.ty { - match ident { - _ if ident == *PRIM_CONS_SYMBOL => { - if let Some(x) = instructions.get_mut(i) { - x.op_code = OpCode::CONS; - x.payload_size = 2; - continue; - } - } - - _ if ident == *BOX || ident == *PRIM_BOX => { - if let Some(x) = instructions.get_mut(i) { - x.op_code = OpCode::NEWBOX; - continue; - } - } - - _ if ident == *UNBOX || ident == *PRIM_UNBOX => { - if let Some(x) = instructions.get_mut(i) { - x.op_code = OpCode::UNBOX; - continue; - } - } - - _ if ident == *SETBOX || ident == *PRIM_SETBOX => { - if let Some(x) = instructions.get_mut(i) { - x.op_code = OpCode::SETBOX; - continue; - } - } - - _ if ident == *PRIM_CAR => { - if let Some(x) = instructions.get_mut(i) { - x.op_code = OpCode::CAR; - continue; - } - } - - // Specialize lists, cons, hashmap, etc. - anything that we expect to be used often in - // real code. - _ if ident == *LIST_SYMBOL || ident == *PRIM_LIST_SYMBOL => {} - - _ => {} - } - } - - if let Some(x) = instructions.get_mut(i) { - x.op_code = OpCode::CALLGLOBALTAIL; - x.payload_size = index; - } - - // if let Some(x) = instructions.get_mut(i + 1) { - // x.op_code = OpCode::Arity; - // x.payload_size = arity; - // } - } - _ => {} - } - } -} - -#[macro_export] -macro_rules! define_primitive_symbols { - ($(($prim_name:tt, $name:tt) => $str:expr,) * ) => { - $( - pub static $name: once_cell::sync::Lazy = once_cell::sync::Lazy::new(|| InternedString::from_static($str)); - - pub static $prim_name: once_cell::sync::Lazy = once_cell::sync::Lazy::new(|| InternedString::from_static(concat!("#%prim.", $str))); - )* - }; -} - -#[macro_export] -macro_rules! define_symbols { - ($($name:tt => $str:expr,) * ) => { - $( - pub static $name: once_cell::sync::Lazy = once_cell::sync::Lazy::new(|| InternedString::from_static($str)); - )* - }; -} - -define_primitive_symbols! { - (PRIM_PLUS, PLUS) => "+", - (PRIM_MINUS, MINUS) => "-", - (PRIM_DIV, DIV) => "/", - (PRIM_STAR, STAR) => "*", - (PRIM_EQUAL, EQUAL) => "equal?", - (PRIM_LTE, LTE) => "<=", - (PRIM_CAR, CAR_SYMBOL) => "car", - (PRIM_CDR, CDR_SYMBOL) => "cdr", -} - -define_symbols! { - UNREADABLE_MODULE_GET => "##__module-get", - STANDARD_MODULE_GET => "%module-get%", - CONTRACT_OUT => "contract/out", - REQUIRE_IDENT_SPEC => "%require-ident-spec", - PROVIDE => "provide", - FOR_SYNTAX => "for-syntax", - PREFIX_IN => "prefix-in", - ONLY_IN => "only-in", - DATUM_SYNTAX => "datum->syntax", - SYNTAX_SPAN => "#%syntax-span", - IF => "if", - DEFINE => "define", - LET => "let", - QUOTE =>"quote", - RETURN => "return!", - REQUIRE => "require", - SET => "set!", - PLAIN_LET => "%plain-let", - LAMBDA => "lambda", - LAMBDA_SYMBOL => "λ", - LAMBDA_FN => "fn", - BEGIN => "begin", - DOC_MACRO => "@doc", - REQUIRE_BUILTIN => "require-builtin", - REQUIRE_DYLIB => "#%require-dylib", - STRUCT_KEYWORD => "struct", - BETTER_LAMBDA => "#%better-lambda", - DEFINE_VALUES => "define-values", - AS_KEYWORD => "as", - SYNTAX_CONST_IF => "syntax-const-if", - UNQUOTE => "unquote", - UNQUOTE_COMMA => "#%unquote-comma", - RAW_UNQUOTE => "#%unquote", - UNQUOTE_SPLICING => "unquote-splicing", - RAW_UNQUOTE_SPLICING => "#%unquote-splicing", - QUASIQUOTE => "quasiquote", - RAW_QUOTE => "#%quote", - QUASISYNTAX => "quasisyntax", - UNSYNTAX => "unsyntax", - RAW_UNSYNTAX => "#%unsyntax", - UNSYNTAX_SPLICING => "unsyntax-splicing", - RAW_UNSYNTAX_SPLICING => "#%unsyntax-splicing", - SYNTAX_QUOTE => "syntax", - CONS_SYMBOL => "cons", - PRIM_CONS_SYMBOL => "#%prim.cons", - LIST_SYMBOL => "list", - PRIM_LIST_SYMBOL => "#%prim.list", - BOX => "#%box", - PRIM_BOX => "#%prim.box", - UNBOX => "#%unbox", - PRIM_UNBOX => "#%prim.unbox", - SETBOX => "#%set-box!", - PRIM_SETBOX => "#%prim.set-box!", - DEFMACRO => "defmacro", - BEGIN_FOR_SYNTAX => "begin-for-syntax", -} - -pub fn inline_num_operations(instructions: &mut [Instruction]) { - for i in 0..instructions.len() - 1 { - let push = instructions.get(i); - let func = instructions.get(i + 1); - - if let ( - Some(Instruction { - op_code: OpCode::PUSH, - .. - }), - Some(Instruction { - op_code: OpCode::FUNC | OpCode::TAILCALL, - contents: - Some(Expr::Atom(RawSyntaxObject { - ty: TokenType::Identifier(ident), - .. - })), - payload_size, - .. - }), - ) = (push, func) - { - let replaced = match *ident { - x if x == *PRIM_PLUS && *payload_size == 2 => Some(OpCode::BINOPADD), - x if x == *PRIM_PLUS => Some(OpCode::ADD), - x if x == *PRIM_MINUS => Some(OpCode::SUB), - x if x == *PRIM_DIV => Some(OpCode::DIV), - x if x == *PRIM_STAR => Some(OpCode::MUL), - x if x == *PRIM_EQUAL => Some(OpCode::EQUAL), - x if x == *PRIM_LTE => Some(OpCode::LTE), - _ => None, - }; - - if let Some(new_op_code) = replaced { - let payload_size = *payload_size; - if let Some(x) = instructions.get_mut(i) { - x.op_code = new_op_code; - x.payload_size = payload_size; - } - - if let Some(x) = instructions.get_mut(i + 1) { - x.op_code = OpCode::PASS; - } - } - } - } -} - -pub const fn sequence_to_opcode(pattern: &[(OpCode, usize)]) -> &'static [steel_gen::Pattern] { - match pattern { - &[(OpCode::MOVEREADLOCAL, _)] => &[steel_gen::Pattern::Single(OpCode::MOVEREADLOCAL)], - _ => todo!(), - } -} - -pub fn tile_super_instructions(_instructions: &mut [Instruction]) { - #[cfg(feature = "dynamic")] - { - pub fn tile(instructions: &mut [Instruction]) { - // let mut list: List<(usize, OpCode)> = List::new(); - - let mut buffer = [(OpCode::VOID, 0); N]; - - let mut pattern_buffer = Vec::with_capacity(N); - - // Cell::from_mut() - - if N > instructions.len() { - return; - } - - for i in 0..instructions.len() - N { - for j in 0..N { - buffer[j] = ( - instructions[i + j].op_code, - instructions[i + j].payload_size, - ); - } - - // If this is a candidate to match the pattern, let's try to apply it! - if let Some(op_code) = steel_gen::opcode::sequence_to_opcode(&buffer) { - // Check if this pattern genuinely matches one of the code gen'd ones - steel_gen::Pattern::from_opcodes_with_buffer(&buffer, &mut pattern_buffer); - - if crate::steel_vm::vm::pattern_exists(&pattern_buffer) { - log::debug!(target: "super-instructions", "Applying tiling for: {:?}", op_code); - - instructions[i].op_code = op_code; - - continue; - } - } - } - - // for (index, op) in list { - // instructions[index].op_code = op; - // } - } - - // Super instruction tiling here! - - if _TILE_SUPER_INSTRUCTIONS { - tile::<9>(instructions); - tile::<8>(instructions); - tile::<7>(instructions); - tile::<6>(instructions); - tile::<5>(instructions); - tile::<4>(instructions); - tile::<3>(instructions); - tile::<2>(instructions); - } - } -} - -pub fn merge_conditions_with_if(instructions: &mut [Instruction]) { - for i in 0..instructions.len() - 1 { - let condition = instructions.get(i); - let guard = instructions.get(i + 2); - - if let ( - Some(Instruction { - op_code: OpCode::LTEIMMEDIATE, - .. - }), - Some(Instruction { - op_code: OpCode::IF, - .. - }), - ) = (condition, guard) - { - if let Some(x) = instructions.get_mut(i) { - x.op_code = OpCode::LTEIMMEDIATEIF; - } - - // let replaced = match *ident { - // x if x == *PLUS && *payload_size == 2 => Some(OpCode::BINOPADD), - // x if x == *PLUS => Some(OpCode::ADD), - // x if x == *MINUS => Some(OpCode::SUB), - // x if x == *DIV => Some(OpCode::DIV), - // x if x == *STAR => Some(OpCode::MUL), - // x if x == *EQUAL => Some(OpCode::EQUAL), - // x if x == *LTE => Some(OpCode::LTE), - // _ => None, - // }; - - // if let Some(new_op_code) = replaced { - // let payload_size = *payload_size; - // if let Some(x) = instructions.get_mut(i) { - // x.op_code = new_op_code; - // x.payload_size = payload_size; - // } - - // if let Some(x) = instructions.get_mut(i + 1) { - // x.op_code = OpCode::PASS; - // } - // } - } - } -} - -pub struct ProgramBuilder(Vec>); -impl Default for ProgramBuilder { - fn default() -> Self { - Self::new() - } -} - -impl ProgramBuilder { - pub fn new() -> Self { - ProgramBuilder(Vec::new()) - } - - pub fn push(&mut self, val: Vec) { - self.0.push(val); - } -} - -#[derive(Serialize, Deserialize)] -pub struct SerializableProgram { - pub instructions: Vec>, - pub constant_map: Vec, -} - -impl SerializableProgram { - pub fn write_to_file(&self, filename: &str) -> Result<()> { - use std::io::prelude::*; - - let mut file = File::create(format!("{filename}.txt")).unwrap(); - - let buffer = bincode::serialize(self).unwrap(); - - file.write_all(&buffer)?; - Ok(()) - } - - pub fn read_from_file(filename: &str) -> Result { - use std::io::prelude::*; - - let mut file = File::open(format!("{filename}.txt")).unwrap(); - - let mut buffer = Vec::new(); - - let _ = file.read_to_end(&mut buffer).unwrap(); - - let program: SerializableProgram = bincode::deserialize(&buffer).unwrap(); - - Ok(program) - } - - pub fn into_program(self) -> Program { - let constant_map = ConstantMap::from_bytes(&self.constant_map).unwrap(); - Program { - constant_map, - instructions: self.instructions, - ast: HashMap::new(), - } - } -} - -/// Represents a Steel program -/// The program holds the instructions and the constant map, serialized to bytes -pub struct Program { - pub instructions: Vec>, - pub constant_map: ConstantMap, - pub ast: HashMap, -} - -impl Program { - pub fn new( - instructions: Vec>, - constant_map: ConstantMap, - ast: HashMap, - ) -> Self { - Program { - instructions, - constant_map, - ast, - } - } - - pub fn into_serializable_program(self) -> Result { - Ok(SerializableProgram { - instructions: self.instructions, - constant_map: self.constant_map.to_bytes()?, - }) - } -} - -// An inspectable program with debug symbols still included on the instructions -// ConstantMap needs to get passed in to the run time to execute the program -// This way, the VM knows where to look up values -#[derive(Clone)] -pub struct RawProgramWithSymbols { - pub(crate) instructions: Vec>, - pub(crate) constant_map: ConstantMap, - version: String, // TODO -> this should be semver -} - -#[derive(Serialize, Deserialize)] -pub struct SerializableRawProgramWithSymbols { - instructions: Vec>, - constant_map: Vec, - version: String, -} - -impl SerializableRawProgramWithSymbols { - pub fn write_to_file(&self, filename: &str) -> Result<()> { - use std::io::prelude::*; - - let mut file = File::create(format!("{filename}.txt")).unwrap(); - - let buffer = bincode::serialize(self).unwrap(); - - file.write_all(&buffer)?; - Ok(()) - } - - pub fn read_from_file(filename: &str) -> Result { - use std::io::prelude::*; - - let mut file = File::open(format!("{filename}.txt")).unwrap(); - let mut buffer = Vec::new(); - let _ = file.read_to_end(&mut buffer).unwrap(); - let program: Self = bincode::deserialize(&buffer).unwrap(); - - Ok(program) - } - - pub fn into_raw_program(self) -> RawProgramWithSymbols { - let constant_map = ConstantMap::from_bytes(&self.constant_map).unwrap(); - RawProgramWithSymbols { - // struct_functions: self.struct_functions, - instructions: self.instructions, - constant_map, - version: self.version, - } - } -} - -use std::fs::File; -use std::io::{self, BufRead}; -use std::path::Path; - -// The output is wrapped in a Result to allow matching on errors -// Returns an Iterator to the Reader of the lines of the file. -fn _read_lines

(filename: P) -> io::Result>> -where - P: AsRef, -{ - let file = File::open(filename)?; - Ok(io::BufReader::new(file).lines()) -} - -// trait Profiler { -// #[inline(always)] -// fn process() -> bool; - -// fn report(&self); -// } - -impl RawProgramWithSymbols { - pub fn new( - // struct_functions: Vec, - instructions: Vec>, - constant_map: ConstantMap, - version: String, - ) -> Self { - Self { - // struct_functions, - instructions, - constant_map, - version, - } - } - - pub fn profile_instructions(&self) { - let iter = self - .instructions - .iter() - .flat_map(|x| x.iter()) - .filter(|x| !matches!(x.op_code, OpCode::PASS)); - - let mut occurrences = HashMap::new(); - for instr in iter { - *occurrences.entry(instr.op_code).or_default() += 1; - } - - let total: usize = occurrences.values().sum(); - - let mut counts = occurrences - .into_iter() - .map(|x| (x.0, (x.1 as f64 / total as f64) * 100.0)) - .collect::>(); - - counts.sort_by(|x, y| y.1.partial_cmp(&x.1).unwrap()); - - println!("{counts:#?}"); - } - - // Definitely can be improved - // pub fn parse_from_self_hosted_file

(file: P) -> Result - // where - // P: AsRef, - // { - // let mut lines = read_lines(file)?; - - // // First line should be the constant map label - // // let constant_map = - - // if let Some(constant_map_label) = lines.next() { - // if constant_map_label? != "'ConstantMap" { - // stop!(Generic => "Compiled file expected constant map label") - // } - // } else { - // stop!(Generic => "Missing constant map label") - // } - - // // Temportary interner - // let mut intern = HashMap::new(); - - // let constant_map = if let Some(constant_map) = lines.next() { - // let constant_map = constant_map?; - - // let constant_map = constant_map - // .trim_start_matches('[') - // .trim_end_matches(']') - // .split(',') - // .map(|x| { - // // Parse the input - // let parsed: std::result::Result, ParseError> = - // Parser::new(&x, &mut intern).collect(); - // let parsed = parsed?; - - // Ok(SteelVal::try_from(parsed[0].clone()).unwrap()) - // }) - // .collect::>>() - // .map(ConstantMap::from_vec)?; - - // constant_map - // } else { - // stop!(Generic => "Missing constant map") - // }; - - // if let Some(instructions_label) = lines.next() { - // if instructions_label? != "'Instructions" { - // stop!(Generic => "Compiled file expected instructions label") - // } - // } else { - // stop!(Generic => "Missing instructions label") - // } - - // let mut instruction_set = Vec::new(); - - // let mut instructions = Vec::new(); - - // // Skip past the first 'Expression - // lines.next(); - - // for instruction_string in lines { - // let instruction_string = instruction_string?; - - // if instruction_string == "'Expression" { - // // instructions = Vec::new(); - // // if instruction_set.is_empty() { - // instruction_set.push(instructions); - // instructions = Vec::new(); - // // } - - // continue; - // } - - // let parsed: std::result::Result, ParseError> = - // Parser::new(&instruction_string, &mut intern).collect(); - // let parsed = parsed?; - - // let value = SteelVal::try_from(parsed[0].clone()).unwrap(); - - // if let SteelVal::ListV(v) = value { - // // Get the op code here - // let op_code = - // OpCode::from_str(v.get(1).unwrap().symbol_or_else(|| unreachable!()).unwrap()); - - // // Get the payload - // let payload = v.get(2).unwrap().int_or_else(|| unreachable!()).unwrap() as usize; - - // // Get the contents - // // If I can't parse the object, just move on - // let contents = ExprKind::try_from(v.get(3).unwrap()) - // .ok() - // .and_then(|x| x.atom_syntax_object().cloned()); - - // let instruction = Instruction::new_from_parts(op_code, payload, contents); - - // instructions.push(instruction) - // } else { - // stop!(Generic => "Instruction serialized incorrectly") - // } - // } - - // instruction_set.push(instructions); - - // Ok(Self::new( - // instruction_set, - // constant_map, - // "0.0.1".to_string(), - // )) - // } - - pub fn into_serializable_program(self) -> Result { - Ok(SerializableRawProgramWithSymbols { - instructions: self.instructions, - constant_map: self.constant_map.to_bytes()?, - version: self.version, - }) - } - - pub fn debug_print(&self) { - self.instructions - .iter() - .for_each(|i| println!("{}\n\n", crate::core::instructions::disassemble(i))) - } - - /// Applies a peephole style optimization to the underlying instruction set - pub fn with_optimization(&mut self, f: F) { - for instructions in &mut self.instructions { - f(instructions) - } - } - - // Apply the optimizations to raw bytecode - pub(crate) fn apply_optimizations(&mut self) -> &mut Self { - // if std::env::var("CODE_GEN_V2").is_err() { - // Run down the optimizations here - for instructions in &mut self.instructions { - inline_num_operations(instructions); - convert_call_globals(instructions); - - // gimmick_super_instruction(instructions); - // move_read_local_call_global(instructions); - specialize_read_local(instructions); - - merge_conditions_with_if(instructions); - - specialize_constants(instructions).unwrap(); - - // Apply the super instruction tiling! - tile_super_instructions(instructions); - - // specialize_exit_jmp(instructions); - - // loop_condition_local_const_arity_two(instructions); - } - - self - } - - pub fn debug_build(mut self, _name: String, symbol_map: &mut SymbolMap) -> Result<()> { - #[cfg(feature = "profiling")] - let now = Instant::now(); - - // let mut struct_instructions = Vec::new(); - - // for builder in &self.struct_functions { - // // Add the eventual function names to the symbol map - // let indices = symbol_map.insert_struct_function_names_from_concrete(builder); - - // // Get the value we're going to add to the constant map for eventual use - // // Throw the bindings in as well - // let constant_values = builder.to_constant_val(indices); - // let idx = self.constant_map.add_or_get(constant_values); - - // struct_instructions.push(vec![Instruction::new_struct(idx), Instruction::new_pop()]); - // } - - let mut interner = DebruijnIndicesInterner::default(); - - for expression in &mut self.instructions { - interner.collect_first_pass_defines(expression, symbol_map)? - } - - for expression in &mut self.instructions { - interner.collect_second_pass_defines(expression, symbol_map)? - } - - // TODO try here - the loop condition local const arity two seems to rely on the - // existence of having been already adjusted by the interner - for instructions in &mut self.instructions { - // loop_condition_local_const_arity_two(instructions); - specialize_constants(instructions)?; - } - - // Put the new struct functions at the front - // struct_instructions.append(&mut self.instructions); - // self.instructions = struct_instructions; - - self.instructions - .iter() - .for_each(|i| println!("{}\n\n", crate::core::instructions::disassemble(i))); - - #[cfg(feature = "profiling")] - if log_enabled!(target: "pipeline_time", log::Level::Debug) { - debug!(target: "pipeline_time", "Executable Build Time: {:?}", now.elapsed()); - } - - // let mut sorted_symbol_map = symbol_map.map.iter().collect::>(); - // sorted_symbol_map.sort_by_key(|x| x.1); - - // println!("Symbol Map: {:#?}", sorted_symbol_map); - - Ok(()) - } - - // TODO -> check out the spans part of this - // also look into having the constant map be correct mapping - // I think the run time will have to swap the constant map in and out - pub fn build(mut self, name: String, symbol_map: &mut SymbolMap) -> Result { - #[cfg(feature = "profiling")] - let now = Instant::now(); - - let mut interner = DebruijnIndicesInterner::default(); - - for expression in &mut self.instructions { - interner.collect_first_pass_defines(expression, symbol_map)? - } - - for expression in &mut self.instructions { - interner.collect_second_pass_defines(expression, symbol_map)? - } - - // if std::env::var("CODE_GEN_V2").is_err() { - // TODO try here - the loop condition local const arity two seems to rely on the - // existence of having been already adjusted by the interner - for instructions in &mut self.instructions { - // TODO: Re-enable optimizations - // loop_condition_local_const_arity_two(instructions); - specialize_constants(instructions)?; - // gimmick_super_instruction(instructions); - // move_read_local_call_global(instructions); - specialize_read_local(instructions); - } - // } - - let (spans, instructions) = extract_spans(self.instructions); - - // let mut sorted_symbol_map = symbol_map.map.iter().collect::>(); - // sorted_symbol_map.sort_by_key(|x| x.1); - - // println!("Symbol Map: {:#?}", sorted_symbol_map); - - #[cfg(feature = "profiling")] - if log_enabled!(target: "pipeline_time", log::Level::Debug) { - debug!(target: "pipeline_time", "Executable Build Time: {:?}", now.elapsed()); - } - - Ok(Executable { - name: Rc::new(name), - version: Rc::new(self.version), - time_stamp: SystemTime::now(), - instructions: instructions - .into_iter() - .map(|x| Rc::from(x.into_boxed_slice())) - .collect(), - constant_map: self.constant_map, - spans, - }) - } -} - -// TODO -> replace spans on instructions with index into span vector -// this is kinda nasty but it _should_ work -fn extract_spans( - instructions: Vec>, -) -> (Vec>, Vec>) { - // let mut span_vec = Vec::with_capacity(instructions.iter().map(|x| x.len()).sum()); - - // for instruction_set in &instructions { - // for instruction in instruction_set { - // if let Some(syn) = &instruction.contents { - // span_vec.push(syn.span) - // } else { - // span_vec.push(Span::default()) - // } - // } - // } - - let span_vec = instructions - .iter() - .map(|x| { - x.iter() - .map(|x| { - x.contents - .as_ref() - .map(|x| match x { - Expr::Atom(a) => a.span, - Expr::List(l) => get_span(l), - }) - .unwrap_or_default() - }) - .collect() - }) - .collect(); - - let instructions: Vec<_> = instructions - .into_iter() - .map(|x| { - // let len = x.len(); - x.into_iter() - .map(|i| { - DenseInstruction::new( - i.op_code, - i.payload_size.try_into().unwrap_or_else(|_| { - // println!("{:?}", len); - println!("{:?}", i); - panic!("Uh oh ") - }), - ) - }) - .collect() - }) - .collect(); - - (span_vec, instructions) -} - -// A program stripped of its debug symbols, but only constructable by running a pass -// over it with the symbol map to intern all of the symbols in the order they occurred -#[allow(unused)] -#[derive(Clone)] -pub struct Executable { - pub(crate) name: Rc, - pub(crate) version: Rc, - pub(crate) time_stamp: SystemTime, // TODO -> don't use system time, probably not as portable, prefer date time - pub(crate) instructions: Vec>, - pub(crate) constant_map: ConstantMap, - pub(crate) spans: Vec>, -} - -impl Executable { - pub fn name(&self) -> &str { - &self.name - } - - pub fn time_stamp(&self) -> &SystemTime { - &self.time_stamp - } -} diff --git a/crates/steel-core/src/containers.rs b/crates/steel-core/src/containers.rs deleted file mode 100644 index 980032de9..000000000 --- a/crates/steel-core/src/containers.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::SteelVal; - -mod private { - use crate::steel_vm::{builtin::BuiltInModule, engine::Engine}; - - pub trait Sealed {} - - // Implement for those same types, but no others. - impl Sealed for BuiltInModule {} - impl Sealed for Engine {} -} - -/// Blanket implementation for things that can register values into the container, -/// used for RegisterFn -pub(crate) trait RegisterValue: private::Sealed { - fn register_value_inner(&mut self, name: &str, value: SteelVal) -> &mut Self; -} diff --git a/crates/steel-core/src/conversions.rs b/crates/steel-core/src/conversions.rs deleted file mode 100644 index 06cb7b5e3..000000000 --- a/crates/steel-core/src/conversions.rs +++ /dev/null @@ -1,470 +0,0 @@ -use crate::values::lists::List; - -use crate::{ - gc::Gc, - rerrs::ErrorKind, - rvals::{AsRefSteelValFromUnsized, FromSteelVal, IntoSteelVal, Result}, - SteelErr, SteelVal, -}; -use std::{ - borrow::Cow, - collections::{HashMap, HashSet}, -}; - -#[cfg(feature = "anyhow")] -mod anyhow_conversion { - use crate::{rvals::IntoSteelVal, SteelVal}; - - impl IntoSteelVal for anyhow::Error { - fn into_steelval(self) -> crate::rvals::Result { - Ok(SteelVal::StringV(format!("{:#?}", self).into())) - } - } -} - -impl IntoSteelVal for SteelVal { - fn into_steelval(self) -> Result { - Ok(self) - } -} - -// impl IntoSteelVal for Result { -// fn into_steelval(self) -> Result { -// self -// } -// } - -impl> FromSteelVal - for im_lists::list::GenericList -{ - fn from_steelval(val: &SteelVal) -> Result { - if let SteelVal::ListV(l) = val { - l.iter().map(T::from_steelval).collect() - } else { - stop!(TypeMismatch => "Unable to convert SteelVal to List, found: {}", val); - } - } -} - -impl> IntoSteelVal - for im_lists::list::GenericList -{ - fn into_steelval(self) -> Result { - self.into_iter() - .map(|x| x.into_steelval()) - .collect::>>() - .map(SteelVal::ListV) - } -} - -impl FromSteelVal for Gc> { - fn from_steelval(val: &SteelVal) -> Result { - if let SteelVal::HashMapV(hm) = val { - Ok(hm.0.clone()) - } else { - stop!(TypeMismatch => "Unable to convert Steelval to HashMap, found: {}", val); - } - } -} - -// impl IntoSteelVal for str { -// fn into_steelval(self) -> Result { -// Ok(SteelVal::StringV(self.to_string().into())) -// } -// } - -// impl IntoSteelVal for Cow<'_, T> { -// fn into_steelval(self) -> Result { -// match self { -// Cow::Borrowed(b) => b.clone().into_steelval(), -// Cow::Owned(o) => o.into_steelval(), -// } -// } -// } - -impl IntoSteelVal for Cow<'_, str> { - fn into_steelval(self) -> Result { - match self { - Cow::Borrowed(b) => b.into_steelval(), - Cow::Owned(o) => o.into_steelval(), - } - } -} - -impl FromSteelVal for Cow<'_, str> { - fn from_steelval(val: &SteelVal) -> Result { - if let SteelVal::StringV(s) = val { - Ok(Cow::Owned(s.to_string())) - } else { - stop!(TypeMismatch => "expected string, found {:?}", val) - } - } -} - -impl IntoSteelVal for &[T] { - fn into_steelval(self) -> Result { - self.iter() - .map(|x| x.clone().into_steelval()) - .collect::>>() - .map(SteelVal::ListV) - } -} - -// TODO: @Matt - figure out how to get this to actually return the right value. At the moment, -// it seems I can't get this to return the correct value -impl AsRefSteelValFromUnsized for T { - type Output = Vec; - - fn as_ref_from_unsized(val: &SteelVal) -> Result { - if let SteelVal::ListV(v) = val { - v.iter() - .map(|x| T::from_steelval(x)) - .collect::>>() - } else { - stop!(TypeMismatch => "expected list, found: {:?}", val); - } - } -} - -impl IntoSteelVal for (A, B) { - fn into_steelval(self) -> Result { - Ok(SteelVal::ListV( - vec![self.0.into_steelval()?, self.1.into_steelval()?].into(), - )) - } -} - -// Vectors should translate into vectors in rust -impl IntoSteelVal for Vec { - fn into_steelval(self) -> Result { - let vec_vals: Result> = self.into_iter().map(|x| x.into_steelval()).collect(); - - match vec_vals { - Ok(l) => Ok(SteelVal::ListV(l.into())), - _ => Err(SteelErr::new( - ErrorKind::ConversionError, - "Could not convert vector of values to SteelVal list".to_string(), - )), - } - } -} - -impl FromSteelVal for Vec { - fn from_steelval(val: &SteelVal) -> Result { - match val { - SteelVal::ListV(l) => { - let result_vec_vals: Result = l - .into_iter() - .map(|x| FromSteelVal::from_steelval(x)) - .collect(); - - match result_vec_vals { - Ok(x) => Ok(x), - _ => Err(SteelErr::new( - ErrorKind::ConversionError, - "Could not convert SteelVal list to Vector of values".to_string(), - )), - } - } - // SteelVal::Pair(_) => { - // let result_vec_vals: Result = SteelVal::iter(val.clone()) - // .into_iter() - // .map(FromSteelVal::from_steelval) - // .collect(); - - // match result_vec_vals { - // Ok(x) => Ok(x), - // _ => Err(SteelErr::new( - // ErrorKind::ConversionError, - // "Could not convert SteelVal list to Vector of values".to_string(), - // )), - // } - // } - SteelVal::VectorV(v) => { - let result_vec_vals: Result = - v.iter().map(FromSteelVal::from_steelval).collect(); - match result_vec_vals { - Ok(x) => Ok(x), - _ => Err(SteelErr::new( - ErrorKind::ConversionError, - "Could not convert SteelVal list to Vector of values".to_string(), - )), - } - } // TODO - _ => Err(SteelErr::new( - ErrorKind::ConversionError, - "Could not convert SteelVal list to Vector of values".to_string(), - )), - } - } -} - -impl FromSteelVal for Box<[T]> { - fn from_steelval(val: &SteelVal) -> Result { - match val { - SteelVal::ListV(l) => { - let result_vec_vals: Result = l - .into_iter() - .map(|x| FromSteelVal::from_steelval(x)) - .collect(); - - match result_vec_vals { - Ok(x) => Ok(x), - _ => Err(SteelErr::new( - ErrorKind::ConversionError, - "Could not convert SteelVal list to Vector of values".to_string(), - )), - } - } - SteelVal::VectorV(v) => { - let result_vec_vals: Result = - v.iter().map(FromSteelVal::from_steelval).collect(); - match result_vec_vals { - Ok(x) => Ok(x), - _ => Err(SteelErr::new( - ErrorKind::ConversionError, - "Could not convert SteelVal list to Vector of values".to_string(), - )), - } - } // TODO - _ => Err(SteelErr::new( - ErrorKind::ConversionError, - "Could not convert SteelVal list to Vector of values".to_string(), - )), - } - } -} - -impl FromSteelVal for Box { - fn from_steelval(val: &SteelVal) -> Result { - if let SteelVal::StringV(s) = val { - Ok(s.as_str().into()) - } else { - stop!(TypeMismatch => "Unable to convert {} into Box", val); - } - } -} - -// HashMap -impl IntoSteelVal for HashMap { - fn into_steelval(mut self) -> Result { - let mut hm = im_rc::HashMap::new(); - for (key, val) in self.drain() { - hm.insert(key.into_steelval()?, val.into_steelval()?); - } - Ok(SteelVal::HashMapV(Gc::new(hm).into())) - } -} - -impl FromSteelVal for HashMap { - fn from_steelval(val: &SteelVal) -> Result { - // todo!() - if let SteelVal::HashMapV(hm) = val { - let mut h = HashMap::new(); - for (key, value) in hm.0.unwrap().into_iter() { - h.insert(K::from_steelval(&key)?, V::from_steelval(&value)?); - } - Ok(h) - } else { - Err(SteelErr::new( - ErrorKind::ConversionError, - "Could not convert SteelVal to HashMap".to_string(), - )) - } - } -} - -// BTreeMap -// impl IntoSteelVal for BTreeMap { -// fn into_steelval(self) -> Result { -// let mut hm = im_rc::HashMap::new(); -// for (key, val) in self.drain() { -// hm.insert(key.into_steelval()?, val.into_steelval()?); -// } -// Ok(SteelVal::HashMapV(Gc::new(hm))) -// } -// } - -// impl FromSteelVal for BTreeMap { -// fn from_steelval(val: SteelVal) -> Result { -// todo!() -// } -// } - -impl FromSteelVal for (A, B) { - fn from_steelval(val: &SteelVal) -> Result { - if let SteelVal::ListV(l) = val { - if l.len() != 2 { - return Err(SteelErr::new(ErrorKind::ConversionError, format!("Could not convert steelval to (A, B): {:?} - list was not length of 2, found length: {}", val, l.len()))); - } - - Ok(( - A::from_steelval(l.get(0).unwrap())?, - B::from_steelval(l.get(1).unwrap())?, - )) - } else { - Err(SteelErr::new( - ErrorKind::ConversionError, - format!("Could not convert SteelVal to (A, B): {:?}", val), - )) - } - } -} - -// HashSet -impl IntoSteelVal for HashSet { - fn into_steelval(mut self) -> Result { - let mut hs = im_rc::HashSet::new(); - for value in self.drain() { - hs.insert(value.into_steelval()?); - } - Ok(SteelVal::HashSetV(Gc::new(hs).into())) - } -} - -impl FromSteelVal for HashSet { - fn from_steelval(val: &SteelVal) -> Result { - if let SteelVal::HashSetV(hs) = val { - let mut h = HashSet::new(); - for k in hs.0.unwrap().into_iter() { - h.insert(K::from_steelval(&k)?); - } - Ok(h) - } else { - Err(SteelErr::new( - ErrorKind::ConversionError, - "Could not convert SteelVal to HashSet".to_string(), - )) - } - } -} - -// BTreeSet -// impl IntoSteelVal for BTreeSet { -// fn into_steelval(self) -> Result { -// todo!() -// } -// } - -// impl FromSteelVal for BTreeSet { -// fn from_steelval(val: SteelVal) -> Result { -// todo!() -// } -// } - -#[cfg(test)] -mod conversion_tests { - - use super::*; - - use im_rc::vector; - - #[test] - fn vec_into_list() { - let input_vec = vec![1, 2]; - // let expected = SteelVal::Pair(Gc::new(ConsCell::new( - // SteelVal::IntV(1), - // Some(Gc::new(ConsCell::new(SteelVal::IntV(2), None))), - // ))); - - let expected = SteelVal::ListV(vec![SteelVal::IntV(1), SteelVal::IntV(2)].into()); - - assert_eq!(input_vec.into_steelval().unwrap(), expected) - } - - #[test] - fn vec_from_list() { - let input_list = SteelVal::ListV(vec![SteelVal::IntV(1), SteelVal::IntV(2)].into()); - - let expected = vec![1, 2]; - let result = >::from_steelval(&input_list).unwrap(); - - assert_eq!(result, expected) - } - - #[test] - fn vec_from_vector() { - let input_vector = vector![SteelVal::IntV(1), SteelVal::IntV(2)].into(); - - let expected = vec![1, 2]; - let result = >::from_steelval(&input_vector).unwrap(); - assert_eq!(result, expected); - } - - #[test] - fn vec_from_steelval_error() { - let input = SteelVal::IntV(2); - assert!(>::from_steelval(&input).is_err()); - } - - #[test] - fn hashmap_into_steelval() { - let mut input = HashMap::new(); - input.insert("foo".to_string(), "bar".to_string()); - input.insert("foo2".to_string(), "bar2".to_string()); - - let expected = SteelVal::HashMapV( - Gc::new(im_rc::hashmap! { - SteelVal::StringV("foo".into()) => SteelVal::StringV("bar".into()), - SteelVal::StringV("foo2".into()) => SteelVal::StringV("bar2".into()) - }) - .into(), - ); - - assert_eq!(input.into_steelval().unwrap(), expected); - } - - #[test] - fn hashmap_from_steelval_hashmap() { - let input = SteelVal::HashMapV( - Gc::new(im_rc::hashmap! { - SteelVal::StringV("foo".into()) => SteelVal::StringV("bar".into()), - SteelVal::StringV("foo2".into()) => SteelVal::StringV("bar2".into()) - }) - .into(), - ); - - let mut expected = HashMap::new(); - expected.insert("foo".to_string(), "bar".to_string()); - expected.insert("foo2".to_string(), "bar2".to_string()); - - assert_eq!( - >::from_steelval(&input).unwrap(), - expected - ); - } - - #[test] - fn hashset_into_steelval() { - let mut input = HashSet::new(); - input.insert("foo".to_string()); - input.insert("bar".to_string()); - - let expected = SteelVal::HashSetV( - Gc::new(im_rc::hashset! { - SteelVal::StringV("foo".into()), - SteelVal::StringV("bar".into()) - }) - .into(), - ); - - assert_eq!(input.into_steelval().unwrap(), expected); - } - - #[test] - fn hashset_from_steelval_hashset() { - let input = SteelVal::HashSetV( - Gc::new(im_rc::hashset! { - SteelVal::StringV("foo".into()), - SteelVal::StringV("bar".into()) - }) - .into(), - ); - - let mut expected = HashSet::new(); - expected.insert("foo".to_string()); - expected.insert("bar".to_string()); - - assert_eq!(>::from_steelval(&input).unwrap(), expected); - } -} diff --git a/crates/steel-core/src/core/instructions.rs b/crates/steel-core/src/core/instructions.rs deleted file mode 100644 index 159e39f14..000000000 --- a/crates/steel-core/src/core/instructions.rs +++ /dev/null @@ -1,139 +0,0 @@ -use crate::core::opcode::OpCode; -use serde::{Deserialize, Serialize}; -use std::convert::TryInto; - -use super::labels::Expr; - -/// Instruction loaded with lots of information prior to being condensed -/// Includes the opcode and the payload size, plus some information -/// used for locating spans and pretty error messages -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct Instruction { - pub op_code: OpCode, - pub payload_size: usize, - pub contents: Option, -} - -impl Instruction { - pub fn new_from_parts( - op_code: OpCode, - payload_size: usize, - contents: Option, - ) -> Instruction { - Instruction { - op_code, - payload_size, - contents, - } - } -} - -// Want to turn a steel struct directly into this struct -// If we values themselves can be mapped -// impl TryFrom for Instruction { -// type Error = crate::SteelErr; - -// fn try_from(value: SteelStruct) -> Result { -// if value.name == "Instruction" { - -// } -// } -// } - -pub fn densify(instructions: Vec) -> Vec { - instructions.into_iter().map(|x| x.into()).collect() -} - -pub fn pretty_print_dense_instructions(instrs: &[DenseInstruction]) { - for (i, instruction) in instrs.iter().enumerate() { - println!( - "{} {:?} : {}", - i, instruction.op_code, instruction.payload_size - ); - } -} - -pub fn disassemble(instructions: &[Instruction]) -> String { - let first_column_width = instructions.len().to_string().len(); - let second_column_width = instructions - .iter() - .map(|x| format!("{:?}", x.op_code).len()) - .max() - .unwrap(); - let third_column_width = instructions - .iter() - .map(|x| x.payload_size.to_string().len()) - .max() - .unwrap(); - - let mut buffer = String::new(); - - for (i, instruction) in instructions.iter().enumerate() { - let index = i.to_string(); - - buffer.push_str(index.as_str()); - for _ in 0..(first_column_width - index.len()) { - buffer.push(' '); - } - - buffer.push_str(" "); - - let op_code = format!("{:?}", instruction.op_code); - buffer.push_str(op_code.as_str()); - for _ in 0..(second_column_width - op_code.len()) { - buffer.push(' '); - } - - buffer.push_str(" : "); - - let payload_size = instruction.payload_size.to_string(); - buffer.push_str(payload_size.as_str()); - for _ in 0..(third_column_width - payload_size.len()) { - buffer.push(' '); - } - - buffer.push_str(" "); - - if let Some(syn) = instruction.contents.as_ref() { - match syn { - Expr::Atom(syn) => { - let contents = syn.ty.to_string(); - buffer.push_str(contents.as_str()); - } - Expr::List(l) => { - let contents = l.to_string(); - buffer.push_str(contents.as_str()); - } - } - } - - buffer.push('\n'); - } - - buffer -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct DenseInstruction { - pub op_code: OpCode, - // Function IDs need to be interned _again_ before patched into the code? - pub payload_size: u32, -} - -impl DenseInstruction { - pub fn new(op_code: OpCode, payload_size: u32) -> DenseInstruction { - DenseInstruction { - op_code, - payload_size, - } - } -} - -// TODO don't actually pass around the span w/ the instruction -// pass around an index into the span to reduce the size of the instructions -// generate an equivalent -impl From for DenseInstruction { - fn from(val: Instruction) -> DenseInstruction { - DenseInstruction::new(val.op_code, val.payload_size.try_into().unwrap()) - } -} diff --git a/crates/steel-core/src/core/labels.rs b/crates/steel-core/src/core/labels.rs deleted file mode 100644 index ff2b97da2..000000000 --- a/crates/steel-core/src/core/labels.rs +++ /dev/null @@ -1,114 +0,0 @@ -use serde::{Deserialize, Serialize}; -use steel_parser::ast::ExprKind; - -use super::instructions::Instruction; -use super::opcode::OpCode; -use crate::parser::parser::SyntaxObject; - -use std::sync::atomic::{AtomicUsize, Ordering}; - -pub(crate) static LABEL_ID: AtomicUsize = AtomicUsize::new(0); - -#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)] -pub struct Label(usize); - -pub fn fresh() -> Label { - Label(LABEL_ID.fetch_add(1, Ordering::Relaxed)) -} - -#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] -pub enum Expr { - Atom(SyntaxObject), - List(ExprKind), -} - -#[derive(Clone, Debug)] -pub struct LabeledInstruction { - pub op_code: OpCode, - pub payload_size: usize, - pub contents: Option, - pub tag: Option